feat: set up backend

This commit is contained in:
2026-09-04 14:57:40 +02:00
parent c123673ca5
commit 4d4ddfe255
10 changed files with 182 additions and 16 deletions
+1
View File
@@ -5,3 +5,4 @@ apple_private_key.p8
musicplayerv2-server.zip musicplayerv2-server.zip
dist dist
package-lock.json package-lock.json
yarn.lock
+6 -2
View File
@@ -1,14 +1,18 @@
{ {
"dependencies": { "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", "cors": "^2.8.6",
"express": "^5.2.1", "express": "^5.2.1",
"express-session": "^1.19.0", "express-session": "^1.19.0",
"jsonwebtoken": "^9.0.3" "jsonwebtoken": "^9.0.3",
"typescript": "^6.0.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@stylistic/eslint-plugin": "^5.10.0", "@stylistic/eslint-plugin": "^5.10.0",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6", "@types/express": "^5.0.6",
"@types/express-session": "^1.19.0", "@types/express-session": "^1.19.0",
"@types/jsonwebtoken": "^9.0.10", "@types/jsonwebtoken": "^9.0.10",
+56 -11
View File
@@ -1,33 +1,78 @@
import {
getLoginSdk,
getStoreSdk
} from './sdk';
import devtoken from './routes/devtoken'; import devtoken from './routes/devtoken';
import express from 'express'; import express from 'express';
import expressSession from 'express-session'; import fs from 'fs';
import path from 'path'; import path from 'path';
// TODO: configurable
const sdk = getLoginSdk( false );
const storeSdk = getStoreSdk( false );
const run = () => { const run = () => {
const app = express(); 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) // Load id.janishutz.com SDK and allow signing in
app.use( expressSession( { sdk.setUp(
'secret': 'dev', {
'resave': true, 'prod': false,
'saveUninitialized': 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 ) => { app.get( '/', ( _request: express.Request, response: express.Response ) => {
response.send( 'HELLO WORLD' ); response.send( 'HELLO WORLD' );
} ); } );
// Load extra routes
devtoken.routes( app );
app.use( ( _request: express.Request, response: express.Response ) => { app.use( ( _request: express.Request, response: express.Response ) => {
response.sendFile( path.join( __dirname, '' ) ); response.sendFile( path.join( __dirname, '' ) );
} ); } );
devtoken.routes( app );
const PORT = process.env.PORT || 8080; const PORT = process.env.PORT || 8080;
app.listen( PORT ); app.listen( PORT );
+55
View File
@@ -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;
}
+9
View File
@@ -0,0 +1,9 @@
import {
RoomStore
} from './room';
const rooms: RoomStore = {};
const getRoom = ( room: string ) => {};
const createRoom = ( name: string ) => {};
+26
View File
@@ -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
}
View File
+14
View File
@@ -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;
};
+11 -1
View File
@@ -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 // eslint-disable-next-line @typescript-eslint/no-unused-vars
const getSubscriptions = ( _uid: string ) => { const getSubscriptions = ( _uid: string ) => {
return [ { return [ {
@@ -7,6 +14,9 @@ const getSubscriptions = ( _uid: string ) => {
} ]; } ];
}; };
const configure = ( _config: StoreSDKConfig ) => {}
export default { export default {
getSubscriptions getSubscriptions,
configure
}; };
+4 -2
View File
@@ -15,6 +15,7 @@
"@fortawesome/fontawesome-svg-core": "^7.3.1", "@fortawesome/fontawesome-svg-core": "^7.3.1",
"@fortawesome/free-regular-svg-icons": "^7.3.1", "@fortawesome/free-regular-svg-icons": "^7.3.1",
"@globalhive/vuejs-tour": "^3.0.1", "@globalhive/vuejs-tour": "^3.0.1",
"@janishutz/login-sdk-browser": "^1.1.0",
"@kyvg/vue3-notification": "^3.4.2", "@kyvg/vue3-notification": "^3.4.2",
"music-metadata": "^11.15.0", "music-metadata": "^11.15.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
@@ -29,13 +30,14 @@
"@types/node": "^24.13.3", "@types/node": "^24.13.3",
"@vitejs/plugin-vue": "^6.0.8", "@vitejs/plugin-vue": "^6.0.8",
"@vue/tsconfig": "^0.9.1", "@vue/tsconfig": "^0.9.1",
"eslint": "^10.9.1",
"eslint-plugin-vue": "^10.10.0", "eslint-plugin-vue": "^10.10.0",
"globals": "^17.11.0", "globals": "^17.12.0",
"musickitjs-v3-types": "^1.2.1", "musickitjs-v3-types": "^1.2.1",
"npm-run-all2": "^9.0.2", "npm-run-all2": "^9.0.2",
"sass-embedded": "^1.103.1", "sass-embedded": "^1.103.1",
"typescript": "~6.0.0", "typescript": "~6.0.0",
"typescript-eslint": "^8.67.0", "typescript-eslint": "^8.69.0",
"vite": "^8.1.5", "vite": "^8.1.5",
"vite-plugin-vue-devtools": "^8.1.5", "vite-plugin-vue-devtools": "^8.1.5",
"vue-tsc": "^3.3.7" "vue-tsc": "^3.3.7"