From 865df5d58866ff0c1f2f17e505943fe4df2de89f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Phillip=20K=C3=BChne?= Date: Wed, 26 Apr 2023 18:08:03 +0200 Subject: [PATCH 1/2] Implement EventID to scope ClientIDs and Entry IDs Implement an EventID saved in settings. Currently this is used to scope clientIDs and entryIDs to an event. The client checks the event currently going on on the server, and discards its localstorage (containing the clientID) if it has changed --- backend/app.py | 32 +++++++++++++++++++++++++--- backend/database.py | 37 ++++++++++++++++++++++++++++++++- backend/helpers.py | 11 ++++++++++ backend/templates/base.html | 19 ++++++++++++++++- backend/templates/songlist.html | 3 ++- 5 files changed, 96 insertions(+), 6 deletions(-) diff --git a/backend/app.py b/backend/app.py index 6ea7dab..b3ebe97 100644 --- a/backend/app.py +++ b/backend/app.py @@ -138,6 +138,7 @@ def songs(): @basic_auth.required def update_songs(): database.delete_all_entries() + helpers.reset_current_event_id(app) status = database.import_songs( helpers.get_songs(helpers.get_catalog_url())) print(status) @@ -149,7 +150,6 @@ def update_songs(): def get_song_completions(input_string=""): input_string = request.args.get('search', input_string) if input_string != "": - print(input_string) result = [list(x) for x in database.get_song_completions(input_string=input_string)] return jsonify(result) @@ -157,10 +157,29 @@ def get_song_completions(input_string=""): return 400 -@app.route("/api/entries/delete/") +@app.route("/api/entries/delete/", methods=['GET']) @nocache @basic_auth.required -def delete_entry(entry_id): +def delete_entry_admin(entry_id): + if database.delete_entry(entry_id): + return Response('{"status": "OK"}', mimetype='text/json') + else: + return Response('{"status": "FAIL"}', mimetype='text/json') + + +@app.route("/api/entries/delete/", methods=['POST']) +@nocache +def delete_entry_user(entry_id): + if not request.json: + print(request.data) + abort(400) + client_id = request.json['client_id'] + if not helpers.is_valid_uuid(client_id): + print(request.data) + abort(400) + if database.get_raw_entry(entry_id)['client_id'] != client_id: # type: ignore + print(request.data) + abort(403) if database.delete_entry(entry_id): return Response('{"status": "OK"}', mimetype='text/json') else: @@ -235,6 +254,7 @@ def clear_played_songs(): @basic_auth.required def delete_all_entries(): if database.delete_all_entries(): + helpers.reset_current_event_id(app) return Response('{"status": "OK"}', mimetype='text/json') else: return Response('{"status": "FAIL"}', mimetype='text/json') @@ -246,6 +266,12 @@ def admin(): return redirect("/", code=303) +@app.route("/api/events/current") +@nocache +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) diff --git a/backend/database.py b/backend/database.py index fdf6d86..68e264a 100644 --- a/backend/database.py +++ b/backend/database.py @@ -4,6 +4,7 @@ from sqlalchemy import create_engine, engine, text import pandas from io import StringIO from flask import current_app +import uuid song_table = "songs" entry_table = "entries" @@ -16,7 +17,6 @@ sql_engine = None def get_db_engine() -> engine.base.Engine: global sql_engine if (not sql_engine): - print(current_app.config.get("DBCONNSTRING")) sql_engine = create_engine( current_app.config.get("DBCONNSTRING")) # type: ignore return sql_engine @@ -189,6 +189,26 @@ def clear_played_songs(): return True +def get_entry(id): + try: + with get_db_engine().connect() as conn: + cur = conn.execute(text("SELECT * FROM Liste WHERE entry_ID = :par_id"), + {"par_id": id}) # type: ignore + return cur.fetchall()[0] + except Exception: + return None + + +def get_raw_entry(id): + try: + with get_db_engine().connect() as conn: + cur = conn.execute(text("SELECT * FROM entries WHERE ID = :par_id"), + {"par_id": id}) # type: ignore + return cur.fetchall()[0] + except Exception: + return None + + def delete_entry(id): with get_db_engine().connect() as conn: conn.execute(text("DELETE FROM entries WHERE id= :par_id"), { @@ -260,3 +280,18 @@ def check_config_table() -> bool: return False else: return False + + +def init_event_id() -> bool: + if not get_config("EventID"): + set_config("EventID", str(uuid.uuid4())) + return True + + +def reset_event_id() -> bool: + set_config("EventID", str(uuid.uuid4())) + return True + + +def get_event_id() -> str: + return get_config("EventID") diff --git a/backend/helpers.py b/backend/helpers.py index fff6b8d..9dab0a5 100644 --- a/backend/helpers.py +++ b/backend/helpers.py @@ -98,6 +98,7 @@ def setup_config(app: Flask): for key, value in default_config.items(): database.set_config(key, value) print("Created new config") + database.init_event_id() config = database.get_config_list() app.config['BASIC_AUTH_USERNAME'] = config['username'] app.config['BASIC_AUTH_PASSWORD'] = config['password'] @@ -105,6 +106,7 @@ def setup_config(app: Flask): app.config['MAX_QUEUE'] = config['maxqueue'] app.config['ENTRIES_ALLOWED'] = bool(config['entries_allowed']) app.config['THEME'] = config['theme'] + app.config['EVENT_ID'] = database.get_event_id() # set queue admittance @@ -153,6 +155,15 @@ def set_theme(app: Flask, theme: str): print("Theme not found, not setting theme.") +def get_current_event_id(app: Flask): + return app.config['EVENT_ID'] + + +def reset_current_event_id(app: Flask): + database.reset_event_id() + app.config['EVENT_ID'] = database.get_event_id() + + def nocache(view): @wraps(view) def no_cache(*args, **kwargs): diff --git a/backend/templates/base.html b/backend/templates/base.html index 852d0eb..26d0ec2 100644 --- a/backend/templates/base.html +++ b/backend/templates/base.html @@ -108,7 +108,7 @@ {% block extrajs %}{% endblock %} diff --git a/backend/templates/songlist.html b/backend/templates/songlist.html index b1fb95a..1225a90 100644 --- a/backend/templates/songlist.html +++ b/backend/templates/songlist.html @@ -79,7 +79,7 @@ $.ajax({ type: 'POST', url: '/api/enqueue', - data: JSON.stringify(data), // or JSON.stringify ({name: 'jonas'}), + data: JSON.stringify(data), success: success_callback, statusCode: { 423: blocked_callback @@ -99,6 +99,7 @@ enqueue(localStorage.getItem("clientId"),id, name, function () { $("#enqueueModal").modal('hide'); window.location.href = '/#end'; + }, function (response) { bootbox.alert({ message: "Deine Eintragung konnte leider nicht vorgenommen werden.\nGrund: "+response.responseJSON.status, From add528fb8036de4ba023e70e960e5783a8737409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Phillip=20K=C3=BChne?= Date: Wed, 26 Apr 2023 19:20:21 +0200 Subject: [PATCH 2/2] =?UTF-8?q?L=C3=B6schung=20eigener=20Entr=C3=A4ge=20im?= =?UTF-8?q?plementiert.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 5 ++++- backend/app.py | 10 +++++----- backend/database.py | 8 ++++---- backend/templates/base.html | 23 +++++++++++++++++++++++ backend/templates/main.html | 33 +++++++++++++++++++++++++++++++++ backend/templates/songlist.html | 10 ++++++++-- 6 files changed, 77 insertions(+), 12 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 93f9daa..aa69af9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,5 +10,8 @@ "python.testing.pytestEnabled": false, "python.testing.unittestEnabled": true, "python.linting.pylintEnabled": false, - "python.linting.flake8Enabled": true + "python.linting.flake8Enabled": true, + "emmet.includeLanguages": { + "django-html": "html" + } } \ No newline at end of file diff --git a/backend/app.py b/backend/app.py index b3ebe97..50ce785 100644 --- a/backend/app.py +++ b/backend/app.py @@ -40,8 +40,8 @@ def enqueue(): name = request.json['name'] song_id = request.json['id'] if request.authorization: - database.add_entry(name, song_id, client_id) - return Response('{"status":"OK"}', mimetype='text/json') + entry_id = database.add_entry(name, song_id, client_id) + return Response(f"""{{"status":"OK", "entry_id":{entry_id}}}""", mimetype='text/json') else: if helpers.get_accept_entries(app): if not request.json: @@ -55,8 +55,8 @@ def enqueue(): song_id = request.json['id'] if database.check_queue_length() < int(app.config['MAX_QUEUE']): if database.check_entry_quota(client_id) < int(app.config['ENTRY_QUOTA']): - database.add_entry(name, song_id, client_id) - return Response('{"status":"OK"}', mimetype='text/json') + entry_id = database.add_entry(name, song_id, client_id) + return Response(f"""{{"status":"OK", "entry_id":{entry_id}}}""", mimetype='text/json') else: return Response('{"status":"Du hast bereits ' + str(database.check_entry_quota(client_id)) + ' Songs eingetragen, dies ist das Maximum an Einträgen die du in der Warteliste haben kannst."}', mimetype='text/json', status=423) else: @@ -177,7 +177,7 @@ def delete_entry_user(entry_id): if not helpers.is_valid_uuid(client_id): print(request.data) abort(400) - if database.get_raw_entry(entry_id)['client_id'] != client_id: # type: ignore + if database.get_raw_entry(entry_id)[3] != client_id: # type: ignore print(request.data) abort(403) if database.delete_entry(entry_id): diff --git a/backend/database.py b/backend/database.py index 68e264a..2c25b2c 100644 --- a/backend/database.py +++ b/backend/database.py @@ -134,11 +134,11 @@ def get_song_completions(input_string): def add_entry(name, song_id, client_id): with get_db_engine().connect() as conn: stmt = text( - "INSERT INTO entries (Song_Id,Name,Client_Id) VALUES (:par_song_id,:par_name,:par_client_id);") - conn.execute(stmt, {"par_song_id": song_id, "par_name": name, - "par_client_id": client_id}) # type: ignore + "INSERT INTO entries (Song_Id,Name,Client_Id) VALUES (:par_song_id,:par_name,:par_client_id) RETURNING entries.ID;") + cur = conn.execute(stmt, {"par_song_id": song_id, "par_name": name, + "par_client_id": client_id}) # type: ignore conn.commit() - return True + return cur.fetchone()[0] # type: ignore def add_sung_song(entry_id): diff --git a/backend/templates/base.html b/backend/templates/base.html index 26d0ec2..54676d3 100644 --- a/backend/templates/base.html +++ b/backend/templates/base.html @@ -147,6 +147,29 @@ loadOrGenerateClientId() } } + + function addEntry(entryId) { + entryArray = JSON.parse(localStorage.getItem("ownedEntries")) + if (entryArray == null) { + entryArray = [] + } + entryArray.push(entryId) + localStorage.setItem("ownedEntries", JSON.stringify(entryArray)) + } + + function removeEntry(entryId) { + entryArray = JSON.parse(localStorage.getItem("ownedEntries")) + if (entryArray == null) { + entryArray = [] + } + entryArray = entryArray.filter(function(value, index, arr){ return value != entryId;}); + localStorage.setItem("ownedEntries", JSON.stringify(entryArray)) + } + + function getOwnedEntries() { + return JSON.parse(localStorage.getItem("ownedEntries")) + } + diff --git a/backend/templates/main.html b/backend/templates/main.html index f0851ac..7af0e06 100644 --- a/backend/templates/main.html +++ b/backend/templates/main.html @@ -17,6 +17,7 @@ Name Song Künstler + @@ -34,5 +35,37 @@ $.getJSON("/api/entries/accept", (data) => { $('[data-toggle="tooltip"]').tooltip() } }) + +function TableActionsFormatter(value,row,index) { + console.log("Value: " + value + ", Row: " + row + ", Index: " + index) + console.log(row) + if (getOwnedEntries().includes(row.entry_ID)) { + return "" + } + return "" +} + +function requestDeletionAsUser(id) { + bootbox.confirm("Wirklich den Eintrag zurückziehen? Das könnte zu einer langen Wartezeit führen!", function (result) { + if (result) { + payload = { + "client_id": localStorage.getItem("clientId"), + "entry_id": id + } + $.ajax({ + url: "/api/entries/delete/"+id, + type: "POST", + data: JSON.stringify(payload), + contentType: "application/json; charset=utf-8", + dataType: "json", + success: function(result) { + bootbox.alert("Eintrag zurückgezogen!") + location.reload() + } + }) + } + }) +} + {% endblock %} \ No newline at end of file diff --git a/backend/templates/songlist.html b/backend/templates/songlist.html index 1225a90..7392a81 100644 --- a/backend/templates/songlist.html +++ b/backend/templates/songlist.html @@ -96,10 +96,16 @@ function submitModal() { var name = $("#singerNameInput").val(); var id = $("#selectedId").attr("value"); - enqueue(localStorage.getItem("clientId"),id, name, function () { + enqueue(localStorage.getItem("clientId"),id, name, function (response) { + console.log(response); + entryID = response["entry_id"]; + bootbox.alert({ + message: "Deine Eintragung wurde erfolgreich vorgenommen.", + }); + console.log("Entry ID: " + entryID); + addEntry(entryID); $("#enqueueModal").modal('hide'); window.location.href = '/#end'; - }, function (response) { bootbox.alert({ message: "Deine Eintragung konnte leider nicht vorgenommen werden.\nGrund: "+response.responseJSON.status,