Compare commits

3 Commits

Author SHA1 Message Date
576b6f9490 Fix some errors, style updates 2025-11-22 22:37:36 +01:00
e0dcfa6964 Finish bar utility 2025-11-22 17:43:25 +01:00
Janis Hutz
69d2db8c37 Update BarView.vue
Fix bar config caching
2025-11-22 12:40:11 +00:00
10 changed files with 191 additions and 32 deletions

View File

@@ -1,8 +1,8 @@
{ {
"ages": { "ages": {
"below": "red", "below": "Orange",
"16-18": "", "16-18": "Yellow",
"18+": "" "18+": "Turquoise"
}, },
"offering": { "offering": {
"big-bar": { "big-bar": {

View File

@@ -8,7 +8,10 @@
<!-- TODO: Make prettier --> <!-- TODO: Make prettier -->
</div> </div>
<div v-else-if="!$props.isLoggedIn" class="not-logged-in"> <div v-else-if="!$props.isLoggedIn" class="not-logged-in">
<p>You are not logged into Apple Music. We therefore can't show you your playlists. <a href="" title="Refreshes the page, allowing you to log in">Change that</a></p> <p>
You are not logged into Apple Music. We therefore can't show you your playlists.
<a href="" title="Refreshes the page, allowing you to log in">Change that</a>
</p>
<p>Use the button below to load songs from your local disk</p> <p>Use the button below to load songs from your local disk</p>
<input <input
id="pl-loader" id="pl-loader"

View File

@@ -1,7 +1,7 @@
// These functions handle connections to the backend with socket.io // These functions handle connections to the backend with socket.io
import { import {
io, type Socket type Socket, io
} from 'socket.io-client'; } from 'socket.io-client';
import type { import type {
SSEMap SSEMap

View File

@@ -1,7 +1,7 @@
// These functions handle connections to the backend with socket.io // These functions handle connections to the backend with socket.io
import { import {
io, type Socket type Socket, io
} from 'socket.io-client'; } from 'socket.io-client';
import type { import type {
SSEMap SSEMap
@@ -57,7 +57,8 @@ class NotificationHandler {
*/ */
connect ( roomName: string, useAntiTamper: boolean ): Promise<void> { connect ( roomName: string, useAntiTamper: boolean ): Promise<void> {
return new Promise( ( resolve, reject ) => { return new Promise( ( resolve, reject ) => {
fetch( localStorage.getItem( 'url' ) + '/createRoomToken?roomName=' + roomName + '&useAntiTamper=' + useAntiTamper, { fetch( localStorage.getItem( 'url' ) + '/createRoomToken?roomName='
+ roomName + '&useAntiTamper=' + useAntiTamper, {
'credentials': 'include' 'credentials': 'include'
} ).then( res => { } ).then( res => {
if ( res.status === 200 ) { if ( res.status === 200 ) {
@@ -110,7 +111,8 @@ class NotificationHandler {
'credentials': 'include' 'credentials': 'include'
} ).then( res => { } ).then( res => {
if ( res.status === 200 ) { if ( res.status === 200 ) {
this.eventSource = new EventSource( localStorage.getItem( 'url' ) + '/socket/connection?room=' + this.roomName, { this.eventSource = new EventSource( localStorage.getItem( 'url' )
+ '/socket/connection?room=' + this.roomName, {
'withCredentials': true 'withCredentials': true
} ); } );
@@ -118,7 +120,8 @@ class NotificationHandler {
this.isConnected = true; this.isConnected = true;
this.connectionWasSuccessful = true; this.connectionWasSuccessful = true;
this.reconnectRetryCount = 0; this.reconnectRetryCount = 0;
console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Connection successfully established!' ); console.log( '[ SSE Connection ] - '
+ new Date().toISOString() + ': Connection successfully established!' );
resolve(); resolve();
}; };
@@ -135,8 +138,10 @@ class NotificationHandler {
this.isConnected = false; this.isConnected = false;
this.eventSource?.close(); this.eventSource?.close();
this.openConnectionsCount -= 1; this.openConnectionsCount -= 1;
console.debug( e ); console.debug( '[ SSE Connection ] - Error encountered: ', e );
console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Reconnecting due to connection error!' ); console.log( '[ SSE Connection ] - '
+ new Date().toISOString()
+ ': Reconnecting due to connection error!' );
this.eventSource = undefined; this.eventSource = undefined;
@@ -146,7 +151,8 @@ class NotificationHandler {
}, 1000 * this.reconnectRetryCount ); }, 1000 * this.reconnectRetryCount );
} }
}; };
} else if ( res.status === 403 || res.status === 401 || res.status === 404 || res.status === 402 ) { } else if ( res.status === 403 || res.status === 401
|| res.status === 404 || res.status === 402 ) {
document.dispatchEvent( new Event( 'musicplayer:autherror' ) ); document.dispatchEvent( new Event( 'musicplayer:autherror' ) );
reject( 'ERR_UNAUTHORIZED' ); reject( 'ERR_UNAUTHORIZED' );
} else { } else {
@@ -158,7 +164,8 @@ class NotificationHandler {
reject( 'ERR_ROOM_CONNECTING' ); reject( 'ERR_ROOM_CONNECTING' );
} else { } else {
this.openConnectionsCount -= 1; this.openConnectionsCount -= 1;
console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Reconnecting due to severe connection error!' ); console.log( '[ SSE Connection ] - ' + new Date().toISOString()
+ ': Reconnecting due to severe connection error!' );
this.eventSource = undefined; this.eventSource = undefined;

View File

@@ -50,7 +50,9 @@
let cashinInDepot = false; let cashinInDepot = false;
fetch( '/bar-config.json' ).then( res => { fetch( '/bar-config.json', {
'cache': 'no-store'
} ).then( res => {
if ( res.status === 200 ) { if ( res.status === 200 ) {
res.json().then( json => { res.json().then( json => {
const data: FullConfig = json; const data: FullConfig = json;
@@ -83,18 +85,22 @@
const keys = Object.keys( selection.value ); const keys = Object.keys( selection.value );
let totalPrice = 0; let totalPrice = 0;
let totalDepot = 0;
for ( let i = 0; i < keys.length; i++ ) { for ( let i = 0; i < keys.length; i++ ) {
const o = selection.value[ keys[ i ] ]; const o = selection.value[ keys[ i ] ];
totalPrice += o * offering.value[ selectedBar.value ].offering[ keys[ i ] ].price; totalPrice += o * offering.value[ selectedBar.value ].offering[ keys[ i ] ].price;
totalPrice += o * ( offering.value[ selectedBar.value ].offering[ keys[ i ] ].depot ?? 0 ); totalDepot += o * ( offering.value[ selectedBar.value ].offering[ keys[ i ] ].depot ?? 0 );
}
if ( ( offering.value[ selectedBar.value ].offering[ keys[ i ] ].depot ?? 0 ) > 0 && o > 0 ) if ( totalDepot > 0 ) {
cashinInDepot = true; cashinInDepot = true;
} }
return totalPrice / 100; totalPrice += totalDepot;
return ( totalPrice / 100 ) + ( totalDepot ? ` (Depot = ${ totalDepot })` : '' );
} ); } );
const changeValue = ( id: string, amount: number ) => { const changeValue = ( id: string, amount: number ) => {
@@ -157,6 +163,9 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<p v-if="Object.keys( offering ).includes( selectedBar )">
Total: CHF {{ total }}
</p>
</div> </div>
</template> </template>

View File

@@ -21,6 +21,7 @@ import storeSDK from '@janishutz/store-sdk';
import sdk from '@janishutz/login-sdk-server'; import sdk from '@janishutz/login-sdk-server';
import sse from './sse'; import sse from './sse';
import socket from './socket'; import socket from './socket';
import logger from './logger';
// const isFossVersion = true; // const isFossVersion = true;
// //
@@ -42,15 +43,13 @@ const run = () => {
const httpServer = createServer( app ); const httpServer = createServer( app );
if ( !isFossVersion ) { if ( !isFossVersion ) {
console.error( '[ APP ] Starting in non-FOSS version' ); logger.info( '[ APP ] Starting in non-FOSS version' );
const storeConfig = JSON.parse( fs.readFileSync( path.join( const storeConfig = JSON.parse( fs.readFileSync( path.join(
__dirname, __dirname,
'/config/store-sdk.config.secret.json' '/config/store-sdk.config.secret.json'
) ).toString() ); ) ).toString() );
console.error( storeConfig );
storeSDK.configure( storeConfig ); storeSDK.configure( storeConfig );
// ─────────────────────────────────────────────────────────────────── // ───────────────────────────────────────────────────────────────────
@@ -139,6 +138,7 @@ const run = () => {
'useAntiTamper': request.query.useAntiTamper === 'true' 'useAntiTamper': request.query.useAntiTamper === 'true'
? true : false, ? true : false,
}; };
logger.debug( `Created room "${ roomName }"` );
response.send( roomToken ); response.send( roomToken );
} else { } else {
if ( if (
@@ -211,7 +211,7 @@ const run = () => {
} else { } else {
storeSDK.getSubscriptions( uid ) storeSDK.getSubscriptions( uid )
.then( stat => { .then( stat => {
console.error( 'Subscription check was successful' ); logger.log( 'Subscription check was successful' );
const now = new Date().getTime(); const now = new Date().getTime();
for ( const sub in stat ) { for ( const sub in stat ) {
@@ -232,7 +232,7 @@ const run = () => {
resolve( false ); resolve( false );
} ) } )
.catch( e => { .catch( e => {
console.error( 'Subscription check unsuccessful with error', e ); logger.error( 'Subscription check unsuccessful with error', e );
reject( 'ERR_NOT_OWNED' ); reject( 'ERR_NOT_OWNED' );
} ); } );
} }

104
backend/src/logger.ts Normal file
View File

@@ -0,0 +1,104 @@
import {
writeFile
} from 'node:fs';
const log = ( ...msg: string[] ) => {
output( 'log', log.caller.toString(), ...msg );
};
const info = ( ...msg: string[] ) => {
output( 'info', log.caller.toString(), ...msg );
};
const debug = ( ...msg: string[] ) => {
output( 'debug', log.caller.toString(), ...msg );
};
const warn = ( ...msg: string[] ) => {
output( 'warn', log.caller.toString(), ...msg );
};
const error = ( ...msg: string[] ) => {
output( 'error', log.caller.toString(), ...msg );
};
const fatal = ( ...msg: string[] ) => {
output( 'fatal', log.caller.toString(), ...msg );
};
let loc = 'stderr';
let lev = 0;
type LogLevel = 'debug' | 'info' | 'log' | 'warn' | 'error' | 'fatal';
const levels = [
'debug',
'info',
'log',
'warn',
'error',
'fatal'
];
const configure = ( location: 'stderr' | 'file', minLevel: LogLevel, file?: string ) => {
if ( location === 'file' && !file ) {
throw new Error( 'File parameter required when location is "file"' );
}
loc = location === 'stderr' ? 'stderr' : file;
lev = levels.indexOf( minLevel );
};
const logfile: string[] = [];
const output = ( level: LogLevel, caller: string, ...message: string[] ) => {
if ( levels.indexOf( level ) < lev ) {
return;
}
const msg = message.join( ' ' );
const out = `[${ level.toUpperCase() }] (${ new Date().toISOString() }) in ${ caller }: ${ msg }`;
if ( loc === 'stderr' ) {
console.error( out );
} else {
logfile.push( out );
save();
}
};
let isSaving = false;
let waitingOnSave = false;
const save = () => {
if ( isSaving ) {
waitingOnSave = true;
return;
}
isSaving = true;
writeFile( loc, JSON.stringify( logfile ), err => {
if ( err )
console.error( '[LOGGER] Failed to save with error ' + err );
if ( waitingOnSave ) {
waitingOnSave = false;
isSaving = false;
save();
}
isSaving = false;
} );
};
export default {
log,
info,
debug,
warn,
error,
fatal,
configure
};

View File

@@ -4,6 +4,7 @@ import bodyParser from 'body-parser';
import { import {
SocketData SocketData
} from './definitions'; } from './definitions';
import logger from './logger';
const useSSE = ( const useSSE = (
app: express.Application, app: express.Application,
@@ -86,7 +87,11 @@ const useSSE = (
for ( const c in cl ) { for ( const c in cl ) {
if ( cl[ c ] === sid ) { if ( cl[ c ] === sid ) {
try {
cl.splice( parseInt( c ), 1 ); cl.splice( parseInt( c ), 1 );
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch ( _ ) { /* empty */ }
break; break;
} }
} }
@@ -120,12 +125,13 @@ const useSSE = (
( request: express.Request, response: express.Response ) => { ( request: express.Request, response: express.Response ) => {
if ( request.query.room ) { if ( request.query.room ) {
if ( socketData[ String( request.query.room ) ] ) { if ( socketData[ String( request.query.room ) ] ) {
logger.debug( `Room "${ request.query.room }" was joined` );
response.send( 'ok' ); response.send( 'ok' );
} else { } else {
response.status( 404 ).send( 'ERR_ROOM_NOT_FOUND' ); response.status( 404 ).send( 'ERR_ROOM_NOT_FOUND' );
} }
} else { } else {
response.status( 404 ).send( 'ERR_NO_ROOM_SPECIFIED' ); response.status( 400 ).send( 'ERR_NO_ROOM_SPECIFIED' );
} }
} }
); );
@@ -138,8 +144,17 @@ const useSSE = (
( request: express.Request, response: express.Response ) => { ( request: express.Request, response: express.Response ) => {
if ( socketData[ request.body.roomName ] ) { if ( socketData[ request.body.roomName ] ) {
if ( request.body.event === 'tampering' ) { if ( request.body.event === 'tampering' ) {
logger.debug( `Room "${
request.query.roomName }" has new event: Tampering` );
const clients = clientReference[ request.body.roomName ]; const clients = clientReference[ request.body.roomName ];
if ( !clients ) {
response.send( 'ERR_CANNOT_SEND' );
return;
}
for ( const client in clients ) { for ( const client in clients ) {
if ( importantClients[ clients[ client ] ] ) { if ( importantClients[ clients[ client ] ] ) {
importantClients[ clients[ client ] ] importantClients[ clients[ client ] ]
@@ -181,9 +196,18 @@ const useSSE = (
.playlistIndex = request.body.data; .playlistIndex = request.body.data;
} }
logger.debug( `Room "${
request.query.roomName }" has new event: ${ update }` );
if ( send ) { if ( send ) {
const clients = clientReference[ request.body.roomName ]; const clients = clientReference[ request.body.roomName ];
if ( !clients ) {
response.send( 'ERR_CANNOT_SEND' );
return;
}
for ( const client in clients ) { for ( const client in clients ) {
if ( connectedClients[ clients[ client ] ] ) { if ( connectedClients[ clients[ client ] ] ) {
connectedClients[ clients[ client ] ] connectedClients[ clients[ client ] ]
@@ -222,9 +246,17 @@ const useSSE = (
socketData[ request.body.roomName ].roomToken socketData[ request.body.roomName ].roomToken
=== request.body.roomToken === request.body.roomToken
) { ) {
logger.debug( `Room "${
request.query.roomName }" was deleted` );
socketData[ request.body.roomName ] = undefined; socketData[ request.body.roomName ] = undefined;
const clients = clientReference[ request.body.roomName ]; const clients = clientReference[ request.body.roomName ];
if ( !clients ) {
response.send( 'ok' );
return;
}
for ( const client in clients ) { for ( const client in clients ) {
if ( connectedClients[ clients[ client ] ] ) { if ( connectedClients[ clients[ client ] ] ) {
connectedClients[ clients[ client ] ] connectedClients[ clients[ client ] ]
@@ -234,6 +266,8 @@ const useSSE = (
} ) + '\n\n' ); } ) + '\n\n' );
} }
} }
response.send( 'ok' );
} else { } else {
response.send( 403 ).send( 'ERR_UNAUTHORIZED' ); response.send( 403 ).send( 'ERR_UNAUTHORIZED' );
} }

View File

@@ -10,6 +10,7 @@
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import * as sqlDB from './mysqldb.js'; import * as sqlDB from './mysqldb.js';
import logger from '../logger.js';
declare let __dirname: string | undefined; declare let __dirname: string | undefined;
@@ -33,9 +34,9 @@ dbh.connect();
*/ */
const initDB = (): undefined => { const initDB = (): undefined => {
( async () => { ( async () => {
console.log( '[ DB ] Setting up...' ); logger.info( '[ DB ] Setting up...' );
dbh.setupDB(); dbh.setupDB();
console.log( '[ DB ] Setting up complete!' ); logger.info( '[ DB ] Setting up complete!' );
} )(); } )();
}; };

View File

@@ -10,6 +10,7 @@
import mysql from 'mysql'; import mysql from 'mysql';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import logger from '../logger';
declare let __dirname: string | undefined; declare let __dirname: string | undefined;
@@ -63,20 +64,20 @@ class SQLDB {
const self = this; const self = this;
if ( this.isRecovering ) { if ( this.isRecovering ) {
console.log( '[ SQL ] Attempting to recover from critical error' ); logger.info( '[ SQL ] Attempting to recover from critical error' );
this.sqlConnection = mysql.createConnection( this.config ); this.sqlConnection = mysql.createConnection( this.config );
this.isRecovering = false; this.isRecovering = false;
} }
this.sqlConnection.connect( err => { this.sqlConnection.connect( err => {
if ( err ) { if ( err ) {
console.error( '[ SQL ]: An error ocurred whilst connecting: ' + err.stack ); logger.error( '[ SQL ]: An error ocurred whilst connecting: ' + err.stack );
reject( err ); reject( err );
return; return;
} }
console.log( '[ SQL ] Connected to database successfully' ); logger.info( '[ SQL ] Connected to database successfully' );
self.sqlConnection.on( 'error', err => { self.sqlConnection.on( 'error', err => {
if ( err.code === 'ECONNRESET' ) { if ( err.code === 'ECONNRESET' ) {
self.isRecovering = true; self.isRecovering = true;
@@ -85,7 +86,7 @@ class SQLDB {
self.connect(); self.connect();
}, 1000 ); }, 1000 );
} else { } else {
console.error( err ); logger.error( err );
} }
} ); } );
resolve( 'connection' ); resolve( 'connection' );