diff --git a/.vscode/launch.json b/.vscode/launch.json index 46c9c21..8a015ce 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,8 @@ "command": "npm start", "name": "Run npm start", "request": "launch", - "type": "node-terminal" + "type": "node-terminal", + "cwd": "${workspaceFolder}/frontend/ng-karaoqueue" }, { "preLaunchTask": "mariadb", diff --git a/.vscode/settings.json b/.vscode/settings.json index aa69af9..b36026a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,8 +9,6 @@ ], "python.testing.pytestEnabled": false, "python.testing.unittestEnabled": true, - "python.linting.pylintEnabled": false, - "python.linting.flake8Enabled": true, "emmet.includeLanguages": { "django-html": "html" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 43b643d..a7c1fbb 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -23,7 +23,6 @@ "type": "shell", "command": "docker-compose -f docker-compose.yml up --remove-orphans", "isBackground": true, - "activeOnStart": false } ] } \ No newline at end of file diff --git a/backend/app.py b/backend/app.py index 7b117bf..a535b5a 100644 --- a/backend/app.py +++ b/backend/app.py @@ -272,17 +272,17 @@ def get_current_event(): return Response('{"status": "OK", "event": "' + helpers.get_current_event_id(app) + '"}', mimetype='text/json') -@app.before_first_request def activate_job(): - helpers.load_dbconfig(app) - helpers.load_version(app) - database.create_entry_table() - database.create_song_table() - database.create_done_song_table() - database.create_list_view() - database.create_done_song_view() - database.create_config_table() - helpers.setup_config(app) + with app.app_context(): + helpers.load_dbconfig(app) + helpers.load_version(app) + database.create_entry_table() + database.create_song_table() + database.create_done_song_table() + database.create_list_view() + database.create_done_song_view() + database.create_config_table() + helpers.setup_config(app) @app.after_request @@ -300,6 +300,8 @@ def add_header(response): def inject_version(): return dict(karaoqueue_version=app.config['VERSION']) +# Perform setup here so it will be executed when the module is imported by the WSGI server. +activate_job() if __name__ == "__main__": app.run(host='127.0.0.1', port=8080, debug=True) diff --git a/docs/swagger.yaml b/docs/swagger.yaml index ceaf6be..54dac35 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -9,41 +9,9 @@ servers: description: Production API paths: - /queue: - get: - summary: 'Fetch entry Queue content' - description: 'Fetch entry Queue' - parameters: - - name: index - in: query - description: Position from which on to return results - required: false - schema: - type: integer - - name: limit - in: query - description: How many items to return at one time (max 100, default 20) - required: false - schema: - type: integer - responses: - '200': - description: OK - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/QueueEntry' - '400': - description: Invalid request. Check your parameters. - '404': - description: No Entries found in specified range. - '5XX': - description: Unexpected error. + /api/enqueue: post: - description: 'Add entry to Queue' - summary: 'Add entry to Queue' + summary: "Enqueue a song" requestBody: required: true content: @@ -51,342 +19,215 @@ paths: schema: type: object properties: - singer_name: + client_id: + type: string + name: + type: string + id: type: string - song_id: - type: integer responses: '200': - description: OK - content: - application/json: - schema: - type: object - properties: - entry_id: - type: string - format: bson.ObjectID - pattern: '/^[a-f\d]{24}$/i' - entry_auth: - type: string + description: "Song enqueued successfully" '400': - description: Malformed request. - '405': - description: Currently not accepting entries. - delete: - summary: 'Clear queue' - security: - - cookieAuth: [] - responses: - '200': - description: OK. Successfully cleared Queue - '401': - description: Not Authorized - - description: clear queue - /queue/{entry_id}: - get: - summary: GET single queue entry - parameters: - - in: path - name: entry_id - schema: - type: integer - required: true - description: ID of the Entry to get - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/QueueEntry' - patch: - summary: Change entry - security: - - cookieAuth: [] - parameters: - - in: path - name: entry_id - schema: - type: string - format: bson.ObjectID - pattern: '/^[a-f\d]{24}$/i' - required: true - description: > - ID of the entry to modify. One of the following is needed: - - Proper Bearer-Token authorization - - The entry_auth string corresponding to the entry - requestBody: - required: false - content: - application/json: - schema: - type: object - required: - - entry_auth - properties: - singer_name: - type: string - song_id: - type: integer - entry_auth: - type: string - responses: - '200': - description: OK - '404': - description: Entry not found - '405': - description: Method not allowed. Check your entry_auth or authorization. - delete: - summary: 'Delete entry' - security: - - cookieAuth: [] - parameters: - - in: path - name: entry_id - schema: - type: string - format: bson.ObjectID - pattern: '/^[a-f\d]{24}$/i' - required: true - description: > - ID of the entry to modify. One of the following is needed: - - Proper Bearer-Token authorization - - The entry_auth string corresponding to the entry - requestBody: - required: false - content: - application/json: - schema: - type: object - required: - - entry_auth - properties: - entry_auth: - type: string - responses: - '200': - description: OK - '404': - description: Entry not found - '405': - description: Method not allowed. Check your entry_auth or authorization. + description: "Bad request, JSON is required" + '423': + description: "Cannot enqueue, conditions not met" - - /songs: + /api/queue: get: - summary: Search in Songs - parameters: - - in: query - name: query - schema: - type: string - required: true - - name: limit - in: query - description: How many items to return at one time (max 100, default 20) - required: false - schema: - type: integer + summary: "Get the queue" responses: '200': - description: OK. An array of Songs according to the Query string. + description: "Queue retrieved successfully" content: application/json: schema: type: array items: - $ref: '#/components/schemas/SongEntry' - '400': - description: Malformed Request - put: - summary: Update Song Database - description: > - Trigger an update of the database using the source CSV defined - in the config. - security: - - cookieAuth: [] - responses: - '200': - description: OK. Songs have been updated - '401': - description: Authorization required. Check your auth. - /statistics: + type: object + + /api/songs: get: - summary: Statistics about the Database + summary: "Get the list of songs" responses: '200': - description: Statistics as JSON + description: "List of songs retrieved successfully" content: application/json: schema: - type: object - properties: - num_songs: - type: integer - num_entries: - type: integer + type: array + items: + type: object + + /api/songs/update: + get: + summary: "Update the list of songs" + responses: + '200': + description: "Songs updated successfully" + '400': + description: "Bad request" + + /api/songs/compl: + get: + summary: "Get song completions" + parameters: + - name: search + in: query + required: false + schema: + type: string + responses: + '200': + description: "Song completions retrieved successfully" + content: + application/json: + schema: + type: array + items: + type: object + '400': + description: "Bad request" + + /api/entries/delete/{entry_id}: + get: + summary: "Admin deletes an entry" + parameters: + - name: entry_id + in: path + required: true + schema: + type: string + responses: + '200': + description: "Entry deleted successfully" + '400': + description: "Entry deletion failed" - /auth/login: post: - summary: Logs in and returns the authentication cookie + summary: "User deletes an entry" + parameters: + - name: entry_id + in: path + required: true + schema: + type: string requestBody: required: true - description: A JSON object containing the login and password. content: application/json: schema: - $ref: '#/components/schemas/LoginRequest' - security: [] # no authentication + type: object + properties: + client_id: + type: string responses: '200': - description: > - Successfully authenticated. - The session ID is returned in a cookie named `jwt`. You need to include this cookie in subsequent requests. - headers: - Set-Cookie: - schema: + description: "Entry deleted successfully" + '400': + description: "Bad request, JSON is required" + '403': + description: "Forbidden, client_id mismatch" + + /api/entries/delete: + post: + summary: "Delete multiple entries" + requestBody: + required: true + content: + application/json: + schema: + type: array + items: type: string - example: jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiJhZG1pbiIsImlhdCI6MTYwMTY1MDYwNSwiZXhwIjoxNjAxNjU0MjA1fQ.uGvOlBAZdbPT8U9s7jEt5PUWyxLrpgaf02EoPVC_Zlsd; Path=/; HttpOnly - /auth/logout: - get: - summary: Logs the user out and invalidates the session on the server - security: - - cookieAuth: [] responses: '200': - description: OK. - '401': - description: Authorization required. + description: "Entries deleted successfully" + '400': + description: "Bad request, JSON is required" - /rpc/end_event: + /api/entries/mark_sung/{entry_id}: get: - summary: End the current event - description: Locks entries and does not allow reopening without reset - security: - - cookieAuth: [] - responses: - '200': - description: OK. - - /rpc/start_event: - get: - summary: Start new event. Clears entries and stats. - description: Sets up a clean state. - security: - - cookieAuth: [] - responses: - '200': - description: OK. - - /rpc/enable_registration: - get: - summary: Enables registration in the queue - description: Makes it possible for guests to register in the queue. - security: - - cookieAuth: [] - responses: - '200': - description: OK. - - - /rpc/disable_registration: - get: - summary: Disables registration in the queue - description: Makes it impossible for guests to register in the queue. - security: - - cookieAuth: [] - responses: - '200': - description: OK. - - /rpc/get_playstats: - get: - summary: Get stats of played songs in the current event. - description: Returns the stats for the current evening as JSON - security: - - cookieAuth: [] - responses: - '200': - description: OK. - - /rpc/download_playstats: - get: - summary: Get stats of played songs in the current event for download. - description: Returns the stats for the current evening as PDF (for example for GEMA) - security: - - cookieAuth: [] - responses: - '200': - description: OK. - - /rpc/entry_fulfilled: - get: - summary: Mark an entry as fulfilled. - description: Mark an entry as fulfilled. This adds it to the statistics. - security: - - cookieAuth: [] - responses: - '200': - description: OK. + summary: "Mark an entry as sung" parameters: - - in: query - name: entry_id + - name: entry_id + in: path + required: true schema: type: string - description: The id of the entry to mark as done. + responses: + '200': + description: "Entry marked as sung successfully" + '400': + description: "Marking entry as sung failed" + /api/entries/mark_transferred/{entry_id}: + get: + summary: "Toggle transferred status of an entry" + parameters: + - name: entry_id + in: path + required: true + schema: + type: string + responses: + '200': + description: "Entry transferred status toggled successfully" + '400': + description: "Toggling transferred status failed" + /api/entries/accept/{value}: + get: + summary: "Set accept entries status" + parameters: + - name: value + in: path + required: true + schema: + type: string + responses: + '200': + description: "Accept entries status set successfully" + '400': + description: "Setting accept entries status failed" + + /api/entries/accept: + get: + summary: "Get accept entries status" + responses: + '200': + description: "Accept entries status retrieved successfully" + + /api/played/clear: + get: + summary: "Clear played songs" + responses: + '200': + description: "Played songs cleared successfully" + '400': + description: "Clearing played songs failed" + + /api/entries/delete_all: + get: + summary: "Delete all entries" + responses: + '200': + description: "All entries deleted successfully" + '400': + description: "Deleting all entries failed" + + /api/events/current: + get: + summary: "Get the current event" + responses: + '200': + description: "Current event retrieved successfully" + +security: + - BasicAuth: [] components: securitySchemes: - cookieAuth: # arbitrary name for the security scheme; will be used in the "security" key later - type: apiKey - in: cookie - name: jwt # cookie name - schemas: - QueueEntry: - type: object - properties: - _id: - type: string - format: bson.ObjectID - pattern: '/^[a-f\d]{24}$/i' - singer_name: - type: string - song_id: - type: integer - SongEntry: - type: object - properties: - id: - type: integer - title: - type: string - artist: - type: string - year: - type: integer - duet: - type: boolean - explicit: - type: boolean - styles: - type: array - items: - type: string - languages: - type: array - items: - type: string - LoginRequest: - type: object - properties: - username: - type: string - password: - type: string - format: password - \ No newline at end of file + BasicAuth: + type: http + scheme: basic diff --git a/frontend/ng-karaoqueue/angular.json b/frontend/ng-karaoqueue/angular.json index 2ed75ad..1068516 100644 --- a/frontend/ng-karaoqueue/angular.json +++ b/frontend/ng-karaoqueue/angular.json @@ -131,5 +131,8 @@ "@schematics/angular:component": { "style": "scss" } + }, + "cli": { + "analytics": false } } diff --git a/frontend/ng-karaoqueue/src/app/song-service.service.ts b/frontend/ng-karaoqueue/src/app/song-service.service.ts index 46598b0..4177637 100644 --- a/frontend/ng-karaoqueue/src/app/song-service.service.ts +++ b/frontend/ng-karaoqueue/src/app/song-service.service.ts @@ -4,6 +4,7 @@ import { Song } from './models/song.model'; import { Artist } from './models/artist.model'; import { Genre } from './models/genre.model'; import { Language } from './models/language.model'; +import { RuntimeConfigLoaderService} from 'runtime-config-loader'; import { Observable } from 'rxjs'; @@ -25,11 +26,11 @@ export class SongServiceService { let out = new Array(); - this.http.get(this.api +"/songs/compl?search="+text).subscribe((data: Observable) => { +/* this.http.get(this.api +"/songs/compl?search="+text).subscribe((data: JSON) => { data.forEach(element => { out.push(new Song(element["title"],element["artist"],element["karafun_id"],element["duo"],element["explicit"],element["_id"],element["styles"],element["languages"])); }); - }); + });*/ const observable = new Observable>( subscriber => { subscriber.next(out); diff --git a/frontend/ng-karaoqueue/src/main.ts b/frontend/ng-karaoqueue/src/main.ts new file mode 100644 index 0000000..e69de29 diff --git a/frontend/ng-karaoqueue/tsconfig.json b/frontend/ng-karaoqueue/tsconfig.json index f9f8df3..0612a31 100644 --- a/frontend/ng-karaoqueue/tsconfig.json +++ b/frontend/ng-karaoqueue/tsconfig.json @@ -2,7 +2,7 @@ { "compileOnSave": false, "compilerOptions": { - "baseUrl": "./", + "baseUrl": "src", "outDir": "./dist/out-tsc", "forceConsistentCasingInFileNames": true, "strict": true, diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..acad9a2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,141 @@ +{ + "name": "karaoqueue", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "runtime-config-loader": "^5.0.2" + } + }, + "node_modules/@angular/common": { + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-16.2.7.tgz", + "integrity": "sha512-vcKbbtDXNmJ8dj1GF52saJRT5U3P+phnIwnv+hQ2c+VVj/S2alWlBkT12iM+KlvnWdxsa0q4yW0G4WvpPJPaMQ==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/core": "16.2.7", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-16.2.7.tgz", + "integrity": "sha512-Sp+QjHFYjBMhjag/YbIV5skqr/UrpBjCPo1WFBBhj5DKkvgWC7T00yYJn+aBj0DU5ZuMmO/P8Vb7bRIHIRNL4w==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/core": "16.2.7" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + } + } + }, + "node_modules/@angular/core": { + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-16.2.7.tgz", + "integrity": "sha512-JQOxo+Ja9ThQjUa4vdOMLZfIK2dhR3cnPbqB1tV2WuTmIv49QASbFHsae8zZsS4Au5/TafBaW3KkK9aRU8G5gg==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.13.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-16.2.7.tgz", + "integrity": "sha512-yQ/4FB33Jc1Xs+slWfddZpbKdkCHdhCh39Mfjxa1wTen6YJZKmvjBbMNCkvnvNbLqc2IFWRwTQdG8s0n1jfl3A==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/animations": "16.2.7", + "@angular/common": "16.2.7", + "@angular/core": "16.2.7" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "16.2.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-16.2.7.tgz", + "integrity": "sha512-raeuYEQfByHByLnA5YRR7fYD/5u6hMjONH77p08IjmtdmLb0XYP18l/C4YqsIOQG6kZLNCVWknEHZu3kuvAwtQ==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/common": "16.2.7", + "@angular/compiler": "16.2.7", + "@angular/core": "16.2.7", + "@angular/platform-browser": "16.2.7" + } + }, + "node_modules/runtime-config-loader": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/runtime-config-loader/-/runtime-config-loader-5.0.2.tgz", + "integrity": "sha512-6LnDfuV79wPuwykgxK0nDx/b28z6iXsKChlnc5OIr+iYPWNzgIe9MBaH7nNw9ACitWq4T1+Rkc+fvJeWNBBpug==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=13.0.0", + "@angular/core": ">=13.0.0", + "@angular/platform-browser-dynamic": ">=13.0.0", + "rxjs": ">=6.6.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "peer": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/zone.js": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.13.3.tgz", + "integrity": "sha512-MKPbmZie6fASC/ps4dkmIhaT5eonHkEt6eAy80K42tAm0G2W+AahLJjbfi6X9NPdciOE9GRFTTM8u2IiF6O3ww==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..fe52530 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "runtime-config-loader": "^5.0.2" + } +}