From 4d4ddfe2558b1976ece3eb27b44e545e02313a90 Mon Sep 17 00:00:00 2001 From: Janis Hutz Date: Fri, 4 Sep 2026 14:57:40 +0200 Subject: [PATCH] feat: set up backend --- .gitignore | 1 + backend/package.json | 8 +++- backend/src/app.ts | 67 ++++++++++++++++++++++++----- backend/src/dtype/file.d.ts | 55 +++++++++++++++++++++++ backend/src/manager/rooms/index.ts | 9 ++++ backend/src/manager/rooms/room.d.ts | 26 +++++++++++ backend/src/manager/users/index.ts | 0 backend/src/sdk/index.ts | 14 ++++++ backend/src/sdk/store-sdk-stub.ts | 12 +++++- web/package.json | 6 ++- 10 files changed, 182 insertions(+), 16 deletions(-) create mode 100644 backend/src/dtype/file.d.ts create mode 100644 backend/src/manager/rooms/index.ts create mode 100644 backend/src/manager/rooms/room.d.ts create mode 100644 backend/src/manager/users/index.ts create mode 100644 backend/src/sdk/index.ts diff --git a/.gitignore b/.gitignore index 442ab34..3534fa9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ apple_private_key.p8 musicplayerv2-server.zip dist package-lock.json +yarn.lock diff --git a/backend/package.json b/backend/package.json index 3b1a23c..fe880c8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,14 +1,18 @@ { "dependencies": { - "@types/cors": "^2.8.19", + "@janishutz/login-sdk-server": "^1.4.0", + "@janishutz/login-sdk-server-stubs": "^1.4.0", + "@janishutz/store-sdk": "^1.2.1", "cors": "^2.8.6", "express": "^5.2.1", "express-session": "^1.19.0", - "jsonwebtoken": "^9.0.3" + "jsonwebtoken": "^9.0.3", + "typescript": "^6.0.3" }, "devDependencies": { "@eslint/js": "^10.0.1", "@stylistic/eslint-plugin": "^5.10.0", + "@types/cors": "^2.8.19", "@types/express": "^5.0.6", "@types/express-session": "^1.19.0", "@types/jsonwebtoken": "^9.0.10", diff --git a/backend/src/app.ts b/backend/src/app.ts index c6df1b9..636c28e 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -1,33 +1,78 @@ +import { + getLoginSdk, + getStoreSdk +} from './sdk'; import devtoken from './routes/devtoken'; import express from 'express'; -import expressSession from 'express-session'; +import fs from 'fs'; import path from 'path'; +// TODO: configurable +const sdk = getLoginSdk( false ); +const storeSdk = getStoreSdk( false ); + const run = () => { const app = express(); + const sdkConfig = JSON.parse( fs.readFileSync( path.join( + __dirname, + '/config/sdk.config.testing.json' + ) ).toString() ); - // TODO: Use db store for this (via sdk or stubs) - app.use( expressSession( { - 'secret': 'dev', - 'resave': true, - 'saveUninitialized': false - } ) ); + // Load id.janishutz.com SDK and allow signing in + sdk.setUp( + { + 'prod': false, + 'service': { + 'serviceID': 'jh-music', + 'serviceToken': sdkConfig[ 'token' ] + }, + 'user-agent': sdkConfig[ 'ua' ], + 'sessionType': 'memory', + 'frontendURL': 'https://music.janishutz.com', + 'corsWhitelist': [ 'https://music.janishutz.com' ], + 'recheckTimeout': 300 * 1000, + 'advancedVerification': 'sdk' + }, + app, + async () => { + return true; + }, + async ( uid: string ) => { + fs.writeFileSync( path.join( __dirname, '/data/', uid ), '{}' ); + return true; + }, + async () => { + return true; + }, + async () => { + return true; + } + ); + + + // Load store sdk + const storeConfig = JSON.parse( fs.readFileSync( path.join( + __dirname, + '/config/store-sdk.config.secret.json' + ) ).toString() ); + + storeSdk.configure( storeConfig ); app.get( '/', ( _request: express.Request, response: express.Response ) => { response.send( 'HELLO WORLD' ); } ); + // Load extra routes + devtoken.routes( app ); + + app.use( ( _request: express.Request, response: express.Response ) => { response.sendFile( path.join( __dirname, '' ) ); } ); - - devtoken.routes( app ); - - const PORT = process.env.PORT || 8080; app.listen( PORT ); diff --git a/backend/src/dtype/file.d.ts b/backend/src/dtype/file.d.ts new file mode 100644 index 0000000..a036fc7 --- /dev/null +++ b/backend/src/dtype/file.d.ts @@ -0,0 +1,55 @@ +export interface UserPlaylistFile { + 'version': '1', + 'userid': string, + 'playlists': Playlist[] +} + +export interface Playlist { + 'name': string; + 'songs': PlaylistSongs; +} + +export type PlaylistSongs = Song[]; + +export interface Song { + /** + * Where the song can be found + */ + 'source': 'local' | 'applemusic'; + + /** + * The identifier for the song so it can be found again (such as Apple Music ID or URL for playback) + */ + 'identifier': string; + + /** + * Any additional identifiers (such as filename, etc) to associate. + * Even if this is not used, set this value if local files are needed + */ + 'additional-identifier'?: string; + + /** + * Additional info, such as dance type to be displayed on remote screens + */ + 'additional-info': string; + + /** + * The artist of the song + */ + 'artist': string; + + /** + * The name of the song + */ + 'name': string; + + /** + * The cover image as a URL + */ + 'artwork': string; + + /** + * Song duration + */ + 'duration': number; +} diff --git a/backend/src/manager/rooms/index.ts b/backend/src/manager/rooms/index.ts new file mode 100644 index 0000000..8ff011e --- /dev/null +++ b/backend/src/manager/rooms/index.ts @@ -0,0 +1,9 @@ +import { + RoomStore +} from './room'; + +const rooms: RoomStore = {}; + +const getRoom = ( room: string ) => {}; + +const createRoom = ( name: string ) => {}; diff --git a/backend/src/manager/rooms/room.d.ts b/backend/src/manager/rooms/room.d.ts new file mode 100644 index 0000000..8cbc383 --- /dev/null +++ b/backend/src/manager/rooms/room.d.ts @@ -0,0 +1,26 @@ +import { + Playlist +} from '../../dtype/file'; +import { + Response +} from 'express'; + +export interface Room { + 'playlist': Playlist; + 'playing': boolean; + 'index': number; + 'start': number; + 'token': string; + 'lastUpdate': number; + + /** + * This is used to determine if the playlist needs to be sent in polling mode + */ + 'lastPlaylistUpdate': number; + + 'clients': Response[] +} + +export interface RoomStore { + [id: string]: Room +} diff --git a/backend/src/manager/users/index.ts b/backend/src/manager/users/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/sdk/index.ts b/backend/src/sdk/index.ts new file mode 100644 index 0000000..47f1229 --- /dev/null +++ b/backend/src/sdk/index.ts @@ -0,0 +1,14 @@ +import loginSdk from '@janishutz/login-sdk-server'; +import loginSdkStub from '@janishutz/login-sdk-server-stubs'; +import storeSdk from '@janishutz/store-sdk'; +import storeSdkStub from './store-sdk-stub'; + +export const getLoginSdk = ( foss: boolean ) => { + if ( foss ) return loginSdkStub; + else return loginSdk; +}; + +export const getStoreSdk = ( foss: boolean ) => { + if ( foss ) return storeSdkStub; + else return storeSdk; +}; diff --git a/backend/src/sdk/store-sdk-stub.ts b/backend/src/sdk/store-sdk-stub.ts index 5052d2f..ec97683 100644 --- a/backend/src/sdk/store-sdk-stub.ts +++ b/backend/src/sdk/store-sdk-stub.ts @@ -1,3 +1,10 @@ +interface StoreSDKConfig { + 'backendURL': string; + 'signingSecret': string; + 'name': string; + 'loglevel': 'debug' | 'info' | 'log' | 'warn' | 'error' | 'none'; +} + // eslint-disable-next-line @typescript-eslint/no-unused-vars const getSubscriptions = ( _uid: string ) => { return [ { @@ -7,6 +14,9 @@ const getSubscriptions = ( _uid: string ) => { } ]; }; +const configure = ( _config: StoreSDKConfig ) => {} + export default { - getSubscriptions + getSubscriptions, + configure }; diff --git a/web/package.json b/web/package.json index 0a90bb4..cb124ca 100644 --- a/web/package.json +++ b/web/package.json @@ -15,6 +15,7 @@ "@fortawesome/fontawesome-svg-core": "^7.3.1", "@fortawesome/free-regular-svg-icons": "^7.3.1", "@globalhive/vuejs-tour": "^3.0.1", + "@janishutz/login-sdk-browser": "^1.1.0", "@kyvg/vue3-notification": "^3.4.2", "music-metadata": "^11.15.0", "nprogress": "^0.2.0", @@ -29,13 +30,14 @@ "@types/node": "^24.13.3", "@vitejs/plugin-vue": "^6.0.8", "@vue/tsconfig": "^0.9.1", + "eslint": "^10.9.1", "eslint-plugin-vue": "^10.10.0", - "globals": "^17.11.0", + "globals": "^17.12.0", "musickitjs-v3-types": "^1.2.1", "npm-run-all2": "^9.0.2", "sass-embedded": "^1.103.1", "typescript": "~6.0.0", - "typescript-eslint": "^8.67.0", + "typescript-eslint": "^8.69.0", "vite": "^8.1.5", "vite-plugin-vue-devtools": "^8.1.5", "vue-tsc": "^3.3.7"