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]}