diff --git a/CHANGELOG.md b/CHANGELOG.md index f2c1006f..4af8ce1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog ## --- [4.2.0] - 2023/TBD ### New features -TBD +- Finish and Activate Arcadia notification backend ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/621)) ### Bug fixes - PWA: Removed the custom offline page in favour of browser default ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/607)) - Fix hidden servers appearing visible on public mobile status page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/612)) diff --git a/app/classes/models/users.py b/app/classes/models/users.py index b0612017..ccd8f1b0 100644 --- a/app/classes/models/users.py +++ b/app/classes/models/users.py @@ -45,6 +45,7 @@ class Users(BaseModel): manager = IntegerField(default=None, null=True) pfp = CharField(default="/static/assets/images/faces-clipart/pic-3.png") theme = CharField(default="default") + cleared_notifs = CharField(default="default") class Meta: table_name = "users" @@ -171,6 +172,7 @@ class HelperUsers: "roles": [], "servers": [], "support_logs": "", + "cleared_notifs": "", } user = model_to_dict(Users.get(Users.user_id == user_id)) diff --git a/app/classes/shared/helpers.py b/app/classes/shared/helpers.py index 576dc654..10fee644 100644 --- a/app/classes/shared/helpers.py +++ b/app/classes/shared/helpers.py @@ -579,20 +579,16 @@ class Helpers: return version_data - @staticmethod - def get_announcements(): - data = ( - '[{"id":"1","date":"Unknown",' - '"title":"Error getting Announcements",' - '"desc":"Error getting Announcements","link":""}]' - ) - + def get_announcements(self): + data = [] try: - response = requests.get("https://craftycontrol.com/notify.json", timeout=2) + response = requests.get("https://craftycontrol.com/notify", timeout=2) data = json.loads(response.content) except Exception as e: logger.error(f"Failed to fetch notifications with error: {e}") + if self.update_available: + data.append(self.update_available) return data def get_version_string(self): diff --git a/app/classes/shared/tasks.py b/app/classes/shared/tasks.py index cc2a8671..bb15b5e7 100644 --- a/app/classes/shared/tasks.py +++ b/app/classes/shared/tasks.py @@ -726,12 +726,21 @@ class TasksManager: def check_for_updates(self): logger.info("Checking for Crafty updates...") self.helper.update_available = self.helper.check_remote_version() + remote = self.helper.update_available if self.helper.update_available: logger.info(f"Found new version {self.helper.update_available}") else: logger.info( "No updates found! You are on the most up to date Crafty version." ) + if self.helper.update_available: + self.helper.update_available = { + "id": str(remote), + "title": f"{remote} Update Available", + "date": "", + "desc": "Release notes are available by clicking this notification.", + "link": "https://gitlab.com/crafty-controller/crafty-4/-/releases", + } logger.info("Refreshing Gravatar PFPs...") for user in HelperUsers.get_all_users(): if user.email: diff --git a/app/classes/web/routes/api/api_handlers.py b/app/classes/web/routes/api/api_handlers.py index 1a4d99f5..0c50614d 100644 --- a/app/classes/web/routes/api/api_handlers.py +++ b/app/classes/web/routes/api/api_handlers.py @@ -52,6 +52,9 @@ from app.classes.web.routes.api.users.user.permissions import ( from app.classes.web.routes.api.users.user.api import ApiUsersUserKeyHandler from app.classes.web.routes.api.users.user.pfp import ApiUsersUserPfpHandler from app.classes.web.routes.api.users.user.public import ApiUsersUserPublicHandler +from app.classes.web.routes.api.crafty.announcements.index import ( + ApiAnnounceIndexHandler, +) from app.classes.web.routes.api.crafty.config.index import ( ApiCraftyConfigIndexHandler, ApiCraftyCustomizeIndexHandler, @@ -77,6 +80,11 @@ def api_handlers(handler_args): ApiAuthInvalidateTokensHandler, handler_args, ), + ( + r"/api/v2/crafty/announcements/?", + ApiAnnounceIndexHandler, + handler_args, + ), ( r"/api/v2/crafty/config/?", ApiCraftyConfigIndexHandler, diff --git a/app/classes/web/routes/api/crafty/announcements/index.py b/app/classes/web/routes/api/crafty/announcements/index.py new file mode 100644 index 00000000..409aceed --- /dev/null +++ b/app/classes/web/routes/api/crafty/announcements/index.py @@ -0,0 +1,110 @@ +import logging +import json +from jsonschema import ValidationError, validate +from app.classes.web.base_api_handler import BaseApiHandler + +logger = logging.getLogger(__name__) + +notif_schema = { + "type": "object", + "properties": { + "id": {"type": "string"}, + }, + "additionalProperties": False, + "minProperties": 1, +} + + +class ApiAnnounceIndexHandler(BaseApiHandler): + def get(self): + auth_data = self.authenticate_user() + if not auth_data: + return + ( + _, + _exec_user_crafty_permissions, + _, + _, + _user, + ) = auth_data + + data = self.helper.get_announcements() + cleared = str( + self.controller.users.get_user_by_id(auth_data[4]["user_id"])[ + "cleared_notifs" + ] + ).split(",") + res = [d.get("id", None) for d in data] + # remove notifs that are no longer in Crafty. + for item in cleared[:]: + if item not in res: + cleared.remove(item) + updata = {"cleared_notifs": ",".join(cleared)} + self.controller.users.update_user(auth_data[4]["user_id"], updata) + if len(cleared) > 0: + for item in data[:]: + if item["id"] in cleared: + data.remove(item) + + self.finish_json( + 200, + { + "status": "ok", + "data": data, + }, + ) + + def post(self): + auth_data = self.authenticate_user() + if not auth_data: + return + ( + _, + _exec_user_crafty_permissions, + _, + _, + _user, + ) = auth_data + try: + data = json.loads(self.request.body) + except json.decoder.JSONDecodeError as e: + return self.finish_json( + 400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)} + ) + + try: + validate(data, notif_schema) + except ValidationError as e: + return self.finish_json( + 400, + { + "status": "error", + "error": "INVALID_JSON_SCHEMA", + "error_data": str(e), + }, + ) + announcements = self.helper.get_announcements() + res = [d.get("id", None) for d in announcements] + cleared_notifs = str( + self.controller.users.get_user_by_id(auth_data[4]["user_id"])[ + "cleared_notifs" + ] + ).split(",") + # remove notifs that are no longer in Crafty. + for item in cleared_notifs[:]: + if item not in res: + cleared_notifs.remove(item) + if str(data["id"]) in str(res): + cleared_notifs.append(data["id"]) + else: + self.finish_json(200, {"status": "error", "error": "INVALID_DATA"}) + return + updata = {"cleared_notifs": ",".join(cleared_notifs)} + self.controller.users.update_user(auth_data[4]["user_id"], updata) + self.finish_json( + 200, + { + "status": "ok", + "data": {}, + }, + ) diff --git a/app/classes/web/routes/api/servers/server/backups/backup/index.py b/app/classes/web/routes/api/servers/server/backups/backup/index.py index 56a42cbb..20a9ded0 100644 --- a/app/classes/web/routes/api/servers/server/backups/backup/index.py +++ b/app/classes/web/routes/api/servers/server/backups/backup/index.py @@ -1,6 +1,7 @@ import logging import json import os +from apscheduler.jobstores.base import JobLookupError from jsonschema import validate from jsonschema.exceptions import ValidationError from app.classes.models.server_permissions import EnumPermissionsServer @@ -192,8 +193,8 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler): # remove old server's tasks try: self.tasks_manager.remove_all_server_tasks(server_id) - except: - logger.info("No active tasks found for server") + except JobLookupError as e: + logger.info("No active tasks found for server: {e}") self.controller.remove_server(server_id, True) except Exception: return self.finish_json( diff --git a/app/frontend/static/assets/js/shared/root-dir.js b/app/frontend/static/assets/js/shared/root-dir.js index 7513b9b8..6882b577 100644 --- a/app/frontend/static/assets/js/shared/root-dir.js +++ b/app/frontend/static/assets/js/shared/root-dir.js @@ -20,11 +20,9 @@ function getDirView(event = false) { console.log("Well that failed"); } } else if ($("#root_files_button").hasClass("clicked")) { - path = $("#zip_server_path").val(); - getTreeView(path, true); + getTreeView($("#zip_server_path").val(), true); } else { - path = $("#file-uploaded").val(); - getTreeView(path, true, true); + getTreeView($("#file-uploaded").val(), true, true); } } @@ -132,7 +130,7 @@ function process_tree_response(response) { } function getToggleMain(event) { - path = event.target.parentElement.getAttribute('data-path'); + const path = event.target.parentElement.getAttribute('data-path'); document.getElementById("files-tree").classList.toggle("d-block"); document.getElementById(path + "span").classList.toggle("tree-caret-down"); document.getElementById(path + "span").classList.toggle("tree-caret"); diff --git a/app/frontend/templates/notify.html b/app/frontend/templates/notify.html index 9d41d17d..98d366e7 100644 --- a/app/frontend/templates/notify.html +++ b/app/frontend/templates/notify.html @@ -1,27 +1,32 @@ + \ No newline at end of file diff --git a/app/frontend/templates/panel/server_backup.html b/app/frontend/templates/panel/server_backup.html index 284a392a..d8bdea51 100644 --- a/app/frontend/templates/panel/server_backup.html +++ b/app/frontend/templates/panel/server_backup.html @@ -168,10 +168,8 @@ diff --git a/app/frontend/templates/panel/server_logs.html b/app/frontend/templates/panel/server_logs.html index 3918f1d6..9a4033a1 100644 --- a/app/frontend/templates/panel/server_logs.html +++ b/app/frontend/templates/panel/server_logs.html @@ -201,8 +201,8 @@ let responseData = await res.json(); let html = `` if (responseData.status === "ok") { - for (let i = 0; i < responseData.data.length; i++) { - html += `${responseData.data[i]}
` + for (let value of responseData.data) { + html += `${value}
` } console.log('Got Log From Server') $('#virt_console').html(html); diff --git a/app/frontend/templates/panel/server_term.html b/app/frontend/templates/panel/server_term.html index 804c09dd..bb63fbe7 100644 --- a/app/frontend/templates/panel/server_term.html +++ b/app/frontend/templates/panel/server_term.html @@ -240,8 +240,8 @@ let responseData = await res.json(); let html = `` if (responseData.status === "ok") { - for (let i = 0; i < responseData.data.length; i++) { - html += `${responseData.data[i]}
` + for (let value of responseData.data) { + html += `${value}
` } console.log('Got Log From Server') $('#virt_console').html(html); @@ -385,7 +385,6 @@ } $(document).ready(() => { - let scrolled = false; $('#virt_console').on('scroll', chkScroll); $('#to-bottom').on('click', scrollToBottom) }); diff --git a/app/frontend/templates/server/wizard.html b/app/frontend/templates/server/wizard.html index 1c37a2fe..663fbafc 100644 --- a/app/frontend/templates/server/wizard.html +++ b/app/frontend/templates/server/wizard.html @@ -890,7 +890,6 @@ window.location.reload(); } }); - let doUpload = false; } }, false); xmlHttpRequest.addEventListener('error', (e) => { @@ -1198,7 +1197,7 @@ let idx = document.getElementById('server_type').selectedIndex; // get the value of the selected option let cSelect = document.getElementById("server"); - let which; + let which = {}; try { which = document.getElementById('server_type').options[idx].value; } catch { @@ -1234,10 +1233,10 @@ } function serverJarChange(selectObj) { - let type_select = document.getElementById('server_jar') - let tidx = type_select.selectedIndex; - let val = type_select.options[tidx].value; - let jcSelect = ""; + const type_select = document.getElementById('server_jar') + const tidx = type_select.selectedIndex; + const val = type_select.options[tidx].value; + let jcSelect = {}; if (val == 'None') { jcSelect = document.getElementById("server_type"); while (jcSelect.options.length > 0) { @@ -1251,7 +1250,7 @@ // get the value of the selected option let jwhich = selectObj.options[jidx].value; // use the selected option value to retrieve the list of items from the serverTypesLists array - jcList = Object.keys(serverTypesLists[jwhich]); + let jcList = Object.keys(serverTypesLists[jwhich]); // get the country select element via its known id jcSelect = document.getElementById("server_type"); // remove the current options from the country select diff --git a/app/migrations/20230901_user_notif.py b/app/migrations/20230901_user_notif.py new file mode 100644 index 00000000..eaefc159 --- /dev/null +++ b/app/migrations/20230901_user_notif.py @@ -0,0 +1,16 @@ +# Generated by database migrator +import peewee + + +def migrate(migrator, database, **kwargs): + migrator.add_columns("users", cleared_notifs=peewee.CharField(default="")) + """ + Write your migrations here. + """ + + +def rollback(migrator, database, **kwargs): + migrator.drop_columns("users", ["cleared_notifs"]) + """ + Write your rollback migrations here. + """