diff --git a/backend/eslint.config.mjs b/backend/eslint.config.mjs index e0d93a7..48eb7aa 100644 --- a/backend/eslint.config.mjs +++ b/backend/eslint.config.mjs @@ -1,15 +1,15 @@ -import vue from 'eslint-plugin-vue'; import eslint from '@eslint/js'; import globals from 'globals'; -import typescript from '@typescript-eslint/eslint-plugin'; import stylistic from '@stylistic/eslint-plugin'; import tseslint from 'typescript-eslint'; +import typescript from '@typescript-eslint/eslint-plugin'; +import vue from 'eslint-plugin-vue'; const style = { 'plugins': { '@stylistic': stylistic, '@stylistic/js': stylistic, - '@stylistic/ts': stylistic, + '@stylistic/ts': stylistic }, 'files': [ '**/*.ts', @@ -20,11 +20,26 @@ const style = { '**/*.jsx' ], 'rules': { + 'sort-imports': [ + 'warn', + { + 'ignoreCase': false, + 'ignoreDeclarationSort': false, + 'ignoreMemberSort': false, + 'memberSyntaxSortOrder': [ + 'none', + 'all', + 'multiple', + 'single' + ], + 'allowSeparatedGroups': false + } + ], // Formatting '@stylistic/array-bracket-newline': [ 'error', { - 'multiline': true, + 'multiline': false, 'minItems': 2 } ], @@ -35,7 +50,8 @@ const style = { '@stylistic/array-element-newline': [ 'error', { - 'multiline': true, + 'consistent': false, + 'multiline': false, 'minItems': 2 } ], @@ -56,7 +72,14 @@ const style = { ], '@stylistic/brace-style': [ 'error', - '1tbs' + '1tbs', + { + 'allowSingleLine': false + } + ], + '@stylistic/comma-dangle': [ + 'error', + 'never' ], '@stylistic/comma-spacing': [ 'error', @@ -73,9 +96,9 @@ const style = { 'error', 'property' ], - '@stylistic/eol-last': [ + '@stylistic/function-call-argument-newline': [ 'error', - 'always' + 'consistent' ], '@stylistic/function-call-spacing': [ 'error', @@ -83,11 +106,7 @@ const style = { ], '@stylistic/function-paren-newline': [ 'error', - 'multiline' - ], - '@stylistic/function-call-argument-newline': [ - 'error', - 'consistent' + 'multiline-arguments' ], '@stylistic/implicit-arrow-linebreak': [ 'error', @@ -97,6 +116,10 @@ const style = { 'error', 4 ], + '@stylistic/indent-binary-ops': [ + 'error', + 4 + ], '@stylistic/key-spacing': [ 'error', { @@ -118,18 +141,31 @@ const style = { '@stylistic/max-len': [ 'warn', { - 'code': 90, - 'comments': 100, + 'code': 140, + 'comments': 160, 'ignoreComments': false, 'ignoreUrls': true, - 'ignoreStrings': false + 'ignoreStrings': true, + 'ignoreTemplateLiterals': true, + 'ignoreRegExpLiterals': true } ], + '@stylistic/max-statements-per-line': [ + 'error', + { + 'max': 1 + } + ], + '@stylistic/multiline-ternary': [ + 'error', + 'always-multiline' + ], '@stylistic/new-parens': [ 'error', 'always' ], - '@stylistic/newline-per-chained-call': [ 'error' ], + '@stylistic/newline-per-chained-call': 'error', + '@stylistic/no-confusing-arrow': 'error', '@stylistic/no-extra-parens': [ 'error', 'all', @@ -167,17 +203,17 @@ const style = { 'always' ], '@stylistic/object-property-newline': 'error', + '@stylistic/one-var-declaration-per-line': 'error', '@stylistic/operator-linebreak': [ 'error', 'before' ], - '@stylistic/one-var-declaration-per-line': 'error', '@stylistic/padded-blocks': [ 'error', { 'blocks': 'never', 'classes': 'always', - 'switches': 'never', + 'switches': 'never' } ], @@ -561,7 +597,7 @@ const style = { 'blankLine': 'always', 'prev': 'class', 'next': '*' - }, + } ], '@stylistic/quote-props': [ 'error', @@ -613,17 +649,23 @@ const style = { 'error', 'always' ], + '@stylistic/switch-colon-spacing': 'error', '@stylistic/template-curly-spacing': [ 'error', 'always' ], - '@stylistic/switch-colon-spacing': 'error', + '@stylistic/template-tag-spacing': [ + 'error', + 'always' + ], + '@stylistic/type-generic-spacing': 'error', + '@stylistic/type-named-tuple-spacing': 'error', '@stylistic/wrap-iife': [ 'error', 'inside' ], '@stylistic/wrap-regex': 'error', - '@stylistic/ts/type-annotation-spacing': 'error', + '@stylistic/ts/type-annotation-spacing': 'error' } }; @@ -642,15 +684,15 @@ export default tseslint.config( 'ecmaVersion': 'latest', 'globals': globals.browser, 'parserOptions': { - 'parser': tseslint.parser, - }, + 'parser': tseslint.parser + } }, 'plugins': { 'vue': vue, '@stylistic': stylistic, '@stylistic/js': stylistic, '@stylistic/ts': stylistic, - '@typescript-eslint': typescript, + '@typescript-eslint': typescript }, 'extends': [ eslint.configs.recommended, @@ -694,9 +736,9 @@ export default tseslint.config( 'error', { 'singleline': 3, - 'multiline': 1, + 'multiline': 1 } - ], - }, - }, + ] + } + } ); diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..89df566 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,19 @@ +{ + "dependencies": { + "express": "^5.2.1", + "express-session": "^1.19.0", + "jsonwebtoken": "^9.0.3" + }, + "devDependencies": { + "eslint": "^10.9.0", + "@types/express": "^5.0.6", + "@types/express-session": "^1.19.0", + "@types/node": "^26.2.0", + "@eslint/js": "^10.0.1", + "@stylistic/eslint-plugin": "^5.10.0", + "@types/jsonwebtoken": "^9.0.10", + "eslint-plugin-vue": "^10.10.0", + "globals": "^17.11.0", + "typescript-eslint": "^8.67.0" + } +} diff --git a/backend/src/app.ts b/backend/src/app.ts index e69de29..ffe7008 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -0,0 +1,54 @@ +import express from 'express'; +import expressSession from 'express-session'; +import fs from 'fs'; +import jwt from 'jsonwebtoken'; +import path from 'path'; + +const app = express(); + +app.use( expressSession( { + 'secret': '', + 'resave': true, + 'saveUninitialized': false +} ) ); + + +app.get( '/', ( request: express.Request, response: express.Response ) => { + response.send( 'HELLO WORLD' ); +} ); + + + +const privateKey = fs.readFileSync( path.join( + __dirname, + '/config/apple_private_key.p8' +) ).toString(); +const musicApiConfig = JSON.parse( fs.readFileSync( path.join( + __dirname, + '/config/apple-music-api.config.secret.json' +) ).toString() ); + +app.get( '/dev-token', ( request: express.Request, response: express.Response ) => { + // sign dev token + const now = new Date().getTime(); + const tomorrow = now + ( 24 * 3600 * 1000 ); + const jwtToken = jwt.sign( { + 'iss': musicApiConfig.teamID, + 'iat': Math.floor( now / 1000 ), + 'exp': Math.floor( tomorrow / 1000 ) + }, privateKey, { + 'algorithm': 'ES256', + 'keyid': musicApiConfig.keyID + } ); + + response.send( jwtToken ); +} ); + +app.use( ( _request: express.Request, response: express.Response ) => { + response.sendFile( path.join( __dirname, '' ) ); +} ); + + +const PORT = process.env.PORT || 8080; + +app.listen( PORT ); diff --git a/web/.env b/web/.env new file mode 100644 index 0000000..08a0e46 --- /dev/null +++ b/web/.env @@ -0,0 +1 @@ +VITE_BACKEND_URL=http://localhost:8080 diff --git a/web/package.json b/web/package.json index 1d0abe7..d6a43c3 100644 --- a/web/package.json +++ b/web/package.json @@ -25,6 +25,7 @@ "eslint-plugin-vue": "^10.10.0", "globals": "^17.11.0", "npm-run-all2": "^9.0.2", + "sass-embedded": "^1.103.1", "typescript": "~6.0.0", "typescript-eslint": "^8.67.0", "vite": "^8.1.5", diff --git a/web/src/ts/player/musickit/index.ts b/web/src/ts/player/musickit/index.ts index f4d2b40..c52649b 100644 --- a/web/src/ts/player/musickit/index.ts +++ b/web/src/ts/player/musickit/index.ts @@ -1,5 +1,23 @@ -export const useMusicKit = () => { - const init = () => { +export const useMusicKit = ( storefront: string = 'ch' ) => { + const init = async () => { + const res = await fetch( import.meta.env.VITE_BACKEND_URL + '/', { + 'credentials': 'include' + } ); + + if ( res.status === 200 ) { + const token = await res.text(); + + window.MusicKit.configure( { + 'developerToken': token, + 'app': { + 'name': 'MusicPlayer', + 'build': '4', + 'icon': 'https://music.janishutz.com/logo.jpg' + }, + 'storefront': storefront.toUpperCase() + } ); + } + isInit = true; }; diff --git a/web/src/views/404View.vue b/web/src/views/404View.vue index 9dbaddd..fe401bd 100644 --- a/web/src/views/404View.vue +++ b/web/src/views/404View.vue @@ -2,22 +2,24 @@

404

Page not found

- + Home
- - -