mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Merge branch 'feature/arcadia-notifications' into 'dev'
Add Arcadia Notifications to Front end See merge request crafty-controller/crafty-4!621
This commit is contained in:
commit
44653f1a67
@ -1,7 +1,7 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
## --- [4.2.0] - 2023/TBD
|
## --- [4.2.0] - 2023/TBD
|
||||||
### New features
|
### New features
|
||||||
TBD
|
- Finish and Activate Arcadia notification backend ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/621))
|
||||||
### Bug fixes
|
### 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))
|
- 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))
|
- Fix hidden servers appearing visible on public mobile status page ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/612))
|
||||||
|
@ -45,6 +45,7 @@ class Users(BaseModel):
|
|||||||
manager = IntegerField(default=None, null=True)
|
manager = IntegerField(default=None, null=True)
|
||||||
pfp = CharField(default="/static/assets/images/faces-clipart/pic-3.png")
|
pfp = CharField(default="/static/assets/images/faces-clipart/pic-3.png")
|
||||||
theme = CharField(default="default")
|
theme = CharField(default="default")
|
||||||
|
cleared_notifs = CharField(default="default")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table_name = "users"
|
table_name = "users"
|
||||||
@ -171,6 +172,7 @@ class HelperUsers:
|
|||||||
"roles": [],
|
"roles": [],
|
||||||
"servers": [],
|
"servers": [],
|
||||||
"support_logs": "",
|
"support_logs": "",
|
||||||
|
"cleared_notifs": "",
|
||||||
}
|
}
|
||||||
user = model_to_dict(Users.get(Users.user_id == user_id))
|
user = model_to_dict(Users.get(Users.user_id == user_id))
|
||||||
|
|
||||||
|
@ -579,20 +579,16 @@ class Helpers:
|
|||||||
|
|
||||||
return version_data
|
return version_data
|
||||||
|
|
||||||
@staticmethod
|
def get_announcements(self):
|
||||||
def get_announcements():
|
data = []
|
||||||
data = (
|
|
||||||
'[{"id":"1","date":"Unknown",'
|
|
||||||
'"title":"Error getting Announcements",'
|
|
||||||
'"desc":"Error getting Announcements","link":""}]'
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
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)
|
data = json.loads(response.content)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to fetch notifications with error: {e}")
|
logger.error(f"Failed to fetch notifications with error: {e}")
|
||||||
|
|
||||||
|
if self.update_available:
|
||||||
|
data.append(self.update_available)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_version_string(self):
|
def get_version_string(self):
|
||||||
|
@ -726,12 +726,21 @@ class TasksManager:
|
|||||||
def check_for_updates(self):
|
def check_for_updates(self):
|
||||||
logger.info("Checking for Crafty updates...")
|
logger.info("Checking for Crafty updates...")
|
||||||
self.helper.update_available = self.helper.check_remote_version()
|
self.helper.update_available = self.helper.check_remote_version()
|
||||||
|
remote = self.helper.update_available
|
||||||
if self.helper.update_available:
|
if self.helper.update_available:
|
||||||
logger.info(f"Found new version {self.helper.update_available}")
|
logger.info(f"Found new version {self.helper.update_available}")
|
||||||
else:
|
else:
|
||||||
logger.info(
|
logger.info(
|
||||||
"No updates found! You are on the most up to date Crafty version."
|
"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...")
|
logger.info("Refreshing Gravatar PFPs...")
|
||||||
for user in HelperUsers.get_all_users():
|
for user in HelperUsers.get_all_users():
|
||||||
if user.email:
|
if user.email:
|
||||||
|
@ -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.api import ApiUsersUserKeyHandler
|
||||||
from app.classes.web.routes.api.users.user.pfp import ApiUsersUserPfpHandler
|
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.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 (
|
from app.classes.web.routes.api.crafty.config.index import (
|
||||||
ApiCraftyConfigIndexHandler,
|
ApiCraftyConfigIndexHandler,
|
||||||
ApiCraftyCustomizeIndexHandler,
|
ApiCraftyCustomizeIndexHandler,
|
||||||
@ -77,6 +80,11 @@ def api_handlers(handler_args):
|
|||||||
ApiAuthInvalidateTokensHandler,
|
ApiAuthInvalidateTokensHandler,
|
||||||
handler_args,
|
handler_args,
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
r"/api/v2/crafty/announcements/?",
|
||||||
|
ApiAnnounceIndexHandler,
|
||||||
|
handler_args,
|
||||||
|
),
|
||||||
(
|
(
|
||||||
r"/api/v2/crafty/config/?",
|
r"/api/v2/crafty/config/?",
|
||||||
ApiCraftyConfigIndexHandler,
|
ApiCraftyConfigIndexHandler,
|
||||||
|
110
app/classes/web/routes/api/crafty/announcements/index.py
Normal file
110
app/classes/web/routes/api/crafty/announcements/index.py
Normal file
@ -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": {},
|
||||||
|
},
|
||||||
|
)
|
@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
from apscheduler.jobstores.base import JobLookupError
|
||||||
from jsonschema import validate
|
from jsonschema import validate
|
||||||
from jsonschema.exceptions import ValidationError
|
from jsonschema.exceptions import ValidationError
|
||||||
from app.classes.models.server_permissions import EnumPermissionsServer
|
from app.classes.models.server_permissions import EnumPermissionsServer
|
||||||
@ -192,8 +193,8 @@ class ApiServersServerBackupsBackupIndexHandler(BaseApiHandler):
|
|||||||
# remove old server's tasks
|
# remove old server's tasks
|
||||||
try:
|
try:
|
||||||
self.tasks_manager.remove_all_server_tasks(server_id)
|
self.tasks_manager.remove_all_server_tasks(server_id)
|
||||||
except:
|
except JobLookupError as e:
|
||||||
logger.info("No active tasks found for server")
|
logger.info("No active tasks found for server: {e}")
|
||||||
self.controller.remove_server(server_id, True)
|
self.controller.remove_server(server_id, True)
|
||||||
except Exception:
|
except Exception:
|
||||||
return self.finish_json(
|
return self.finish_json(
|
||||||
|
@ -20,11 +20,9 @@ function getDirView(event = false) {
|
|||||||
console.log("Well that failed");
|
console.log("Well that failed");
|
||||||
}
|
}
|
||||||
} else if ($("#root_files_button").hasClass("clicked")) {
|
} else if ($("#root_files_button").hasClass("clicked")) {
|
||||||
path = $("#zip_server_path").val();
|
getTreeView($("#zip_server_path").val(), true);
|
||||||
getTreeView(path, true);
|
|
||||||
} else {
|
} else {
|
||||||
path = $("#file-uploaded").val();
|
getTreeView($("#file-uploaded").val(), true, true);
|
||||||
getTreeView(path, true, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +130,7 @@ function process_tree_response(response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getToggleMain(event) {
|
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("files-tree").classList.toggle("d-block");
|
||||||
document.getElementById(path + "span").classList.toggle("tree-caret-down");
|
document.getElementById(path + "span").classList.toggle("tree-caret-down");
|
||||||
document.getElementById(path + "span").classList.toggle("tree-caret");
|
document.getElementById(path + "span").classList.toggle("tree-caret");
|
||||||
|
@ -1,27 +1,32 @@
|
|||||||
<ul class="navbar-nav ml-auto">
|
<ul class="navbar-nav ml-auto">
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link count-indicator">
|
<a class="nav-link count-indicator dropdown-toggle" id="notifDropdown" href="#" data-toggle="dropdown"
|
||||||
|
aria-expanded="false">
|
||||||
<i class="fas fa-broadcast-tower
|
<i class="fas fa-broadcast-tower
|
||||||
{% if data.get('update_available') %}
|
{% if data.get('update_available') %}
|
||||||
text-danger
|
text-danger
|
||||||
{% end %}
|
{% end %}
|
||||||
"></i>
|
"></i><span id="notif-count" class="badge badge-notify"></span> </a>
|
||||||
<!-- <span class="count bg-success">3</span>-->
|
<div class="dropdown-menu dropdown-menu-right navbar-dropdown notif-div" style="width: 40vw; max-height: 80vh;" aria-labelledby="notifDropdown">
|
||||||
</a>
|
<ul style="padding-top: 10px;" id="announcements">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link count-indicator" href="/panel/panel_config">
|
<a class="nav-link" href="/panel/panel_config">
|
||||||
<i class="fas fa-cogs"></i>
|
<i class="fas fa-cogs"></i>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item dropdown user-dropdown">
|
<li class="nav-item dropdown user-dropdown">
|
||||||
<a class="nav-link dropdown-toggle" id="UserDropdown" href="#" data-toggle="dropdown" aria-expanded="false">
|
<a class="nav-link dropdown-toggle" id="UserDropdown" href="#" data-toggle="dropdown" aria-expanded="false">
|
||||||
<img class="img-xs rounded-circle profile-picture" onerror="pfpError(this)" src="{{ data['user_data']['pfp'] }}" alt="Profile image"> </a>
|
<img class="img-xs rounded-circle profile-picture" onerror="pfpError(this)" src="{{ data['user_data']['pfp'] }}"
|
||||||
|
alt="Profile image"> </a>
|
||||||
<div class="dropdown-menu dropdown-menu-right navbar-dropdown" aria-labelledby="UserDropdown">
|
<div class="dropdown-menu dropdown-menu-right navbar-dropdown" aria-labelledby="UserDropdown">
|
||||||
<div class="dropdown-header text-center">
|
<div class="dropdown-header text-center">
|
||||||
<img class="img-md rounded-circle profile-picture" onerror="pfpError(this)" src="{{ data['user_data']['pfp'] }}" alt="Profile image">
|
<img class="img-md rounded-circle profile-picture" onerror="pfpError(this)" src="{{ data['user_data']['pfp'] }}"
|
||||||
|
alt="Profile image">
|
||||||
<p class="mb-1 mt-3 font-weight-semibold">{{ data['user_data']['username'] }}</p>
|
<p class="mb-1 mt-3 font-weight-semibold">{{ data['user_data']['username'] }}</p>
|
||||||
<p class="font-weight-light text-muted mb-0">Roles: </p>
|
<p class="font-weight-light text-muted mb-0">Roles: </p>
|
||||||
{% for r in data['user_role'] %}
|
{% for r in data['user_role'] %}
|
||||||
@ -33,27 +38,130 @@
|
|||||||
<p class="font-weight-light text-muted mb-0">Email: {{ data['user_data']['email'] }}</p>
|
<p class="font-weight-light text-muted mb-0">Email: {{ data['user_data']['email'] }}</p>
|
||||||
</div>
|
</div>
|
||||||
{% if data['user_data']['preparing'] %}
|
{% if data['user_data']['preparing'] %}
|
||||||
<span class="dropdown-item" id="support_progress"><i class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs', data['lang']) }}<br><br></span>
|
<span class="dropdown-item" id="support_progress"><i
|
||||||
|
class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs',
|
||||||
|
data['lang']) }}<br><br></span>
|
||||||
<span class="dropdown-item" id="support_progress">
|
<span class="dropdown-item" id="support_progress">
|
||||||
<div class="support_progress" style="height: 15px; width: 100%;">
|
<div class="support_progress" style="height: 15px; width: 100%;">
|
||||||
<div class="progress-bar progress-bar-striped progress-bar-animated" id="logs_progress_bar" role="progressbar" style="width:0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
|
<div class="progress-bar progress-bar-striped progress-bar-animated" id="logs_progress_bar" role="progressbar"
|
||||||
|
style="width:0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a class="dropdown-item" id="support_logs"><i class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs', data['lang']) }}</i></a>
|
<a class="dropdown-item" id="support_logs"><i
|
||||||
|
class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs',
|
||||||
|
data['lang']) }}</i></a>
|
||||||
{% end %}
|
{% end %}
|
||||||
{% if data['superuser'] %}
|
{% if data['superuser'] %}
|
||||||
<a class="dropdown-item" href="/panel/activity_logs"><i class="dropdown-item-icon mdi mdi-calendar-check-outline text-primary"></i>{{ translate('notify', 'activityLog', data['lang']) }}</a>
|
<a class="dropdown-item" href="/panel/activity_logs"><i
|
||||||
|
class="dropdown-item-icon mdi mdi-calendar-check-outline text-primary"></i>{{ translate('notify',
|
||||||
|
'activityLog', data['lang']) }}</a>
|
||||||
{% end %}
|
{% end %}
|
||||||
<a class="dropdown-item" href="/logout"><i class="dropdown-item-icon mdi mdi-power text-primary"></i>{{ translate('notify', 'logout', data['lang']) }}</a>
|
<a class="dropdown-item" href="/logout"><i class="dropdown-item-icon mdi mdi-power text-primary"></i>{{
|
||||||
|
translate('notify', 'logout', data['lang']) }}</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<style>
|
||||||
|
.badge-notify {
|
||||||
|
background: var(--purple);
|
||||||
|
position: absolute;
|
||||||
|
-moz-transform: translate(-70%, -70%);
|
||||||
|
/* For Firefox */
|
||||||
|
-ms-transform: translate(-70%, -70%);
|
||||||
|
/* for IE */
|
||||||
|
-webkit-transform: translate(-70%, -70%);
|
||||||
|
/* For Safari, Chrome, iOS */
|
||||||
|
-o-transform: translate(-70%, -70%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-button:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||||
|
.notif-div::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide scrollbar for IE, Edge and Firefox */
|
||||||
|
.notif-div {
|
||||||
|
-ms-overflow-style: none; /* IE and Edge */
|
||||||
|
scrollbar-width: none; /* Firefox */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<script>
|
<script>
|
||||||
function pfpError(image) {
|
function pfpError(image) {
|
||||||
image.onerror = "";
|
image.onerror = "";
|
||||||
image.src = "/static/assets/images/faces-clipart/pic-3.png";
|
image.src = "/static/assets/images/faces-clipart/pic-3.png";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
function updateAnnouncements(data) {
|
||||||
|
console.log(data)
|
||||||
|
let text = "";
|
||||||
|
for (let value of data) {
|
||||||
|
text += `<li class="card-header header-sm justify-content-between align-items-center" id="${value.id}"><p style="float: right;"><i data-id="${value.id}"class="clear-button fa-regular fa-x"></i></p><a style="color: var(--purple);" href=${value.link} target="_blank"><h6>${value.title}</h6><small><p>${value.date}</p></small><p>${value.desc}</p></li></a>`
|
||||||
|
}
|
||||||
|
if (data.length > 0) {
|
||||||
|
localStorage.setItem("notif-count", data.length);
|
||||||
|
$("#notif-count").html(data.length);
|
||||||
|
$("#announcements").html(text);
|
||||||
|
} else {
|
||||||
|
$("#announcements").html(`<p style='margin-top: 15px;' class='text-center'><i class="fa fa-bell-slash" aria-hidden="true"></i>
|
||||||
|
</p>`);
|
||||||
|
}
|
||||||
|
$(".clear-button").on("click", function (event) {
|
||||||
|
console.log("CLEAR BUTTON")
|
||||||
|
let uuid = event.target.getAttribute("data-id");
|
||||||
|
$(`#${uuid}`).remove();
|
||||||
|
send_clear(uuid);
|
||||||
|
let notif_count = localStorage.getItem("notif-count") - 1;
|
||||||
|
if (notif_count > 0) {
|
||||||
|
localStorage.setItem("notif-count", notif_count);
|
||||||
|
$("#notif-count").html(notif_count);
|
||||||
|
} else {
|
||||||
|
$("#announcements").html(`<p style='margin-top: 15px;' class='text-center'><i class="fa fa-bell-slash" aria-hidden="true"></i>
|
||||||
|
</p>`)
|
||||||
|
$("#notif-count").html("");
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function getAnnouncements() {
|
||||||
|
var token = getCookie("_xsrf");
|
||||||
|
let res = await fetch(`/api/v2/crafty/announcements/`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'X-XSRFToken': token
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let responseData = await res.json();
|
||||||
|
console.log(responseData);
|
||||||
|
if (responseData.status === "ok") {
|
||||||
|
updateAnnouncements(responseData.data)
|
||||||
|
} else {
|
||||||
|
updateAnnouncements("<li><p>Trouble Getting Annoucements</p></li>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function send_clear(uuid) {
|
||||||
|
var token = getCookie("_xsrf");
|
||||||
|
let body = JSON.stringify({ "id": uuid });
|
||||||
|
console.log(body)
|
||||||
|
let res = await fetch(`/api/v2/crafty/announcements/`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-XSRFToken': token,
|
||||||
|
},
|
||||||
|
body: body,
|
||||||
|
});
|
||||||
|
let responseData = await res.json();
|
||||||
|
console.log(responseData);
|
||||||
|
if (responseData.status === "ok") {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
bootbox.alert(responseData.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$(document).ready(function () {
|
||||||
|
getAnnouncements();
|
||||||
|
})
|
||||||
</script>
|
</script>
|
@ -168,10 +168,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" id="modal-cancel" class="btn btn-secondary" data-dismiss="modal">{{
|
<button type="button" id="modal-cancel" class="btn btn-secondary" data-dismiss="modal"><i class="fa-solid fa-xmark"></i></button>
|
||||||
translate('serverBackups', 'cancel', data['lang']) }}</button>
|
<button type="button" id="modal-okay" data-dismiss="modal" class="btn btn-primary"><i class="fa-solid fa-thumbs-up"></i></button>
|
||||||
<button type="button" id="modal-okay" data-dismiss="modal" class="btn btn-primary">{{
|
|
||||||
translate('serverWizard', 'save', data['lang']) }}</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -201,8 +201,8 @@
|
|||||||
let responseData = await res.json();
|
let responseData = await res.json();
|
||||||
let html = ``
|
let html = ``
|
||||||
if (responseData.status === "ok") {
|
if (responseData.status === "ok") {
|
||||||
for (let i = 0; i < responseData.data.length; i++) {
|
for (let value of responseData.data) {
|
||||||
html += `<span class='box'>${responseData.data[i]}<br /></span>`
|
html += `<span class='box'>${value}<br /></span>`
|
||||||
}
|
}
|
||||||
console.log('Got Log From Server')
|
console.log('Got Log From Server')
|
||||||
$('#virt_console').html(html);
|
$('#virt_console').html(html);
|
||||||
|
@ -240,8 +240,8 @@
|
|||||||
let responseData = await res.json();
|
let responseData = await res.json();
|
||||||
let html = ``
|
let html = ``
|
||||||
if (responseData.status === "ok") {
|
if (responseData.status === "ok") {
|
||||||
for (let i = 0; i < responseData.data.length; i++) {
|
for (let value of responseData.data) {
|
||||||
html += `<span class='box'>${responseData.data[i]}<br /></span>`
|
html += `<span class='box'>${value}<br /></span>`
|
||||||
}
|
}
|
||||||
console.log('Got Log From Server')
|
console.log('Got Log From Server')
|
||||||
$('#virt_console').html(html);
|
$('#virt_console').html(html);
|
||||||
@ -385,7 +385,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(() => {
|
$(document).ready(() => {
|
||||||
let scrolled = false;
|
|
||||||
$('#virt_console').on('scroll', chkScroll);
|
$('#virt_console').on('scroll', chkScroll);
|
||||||
$('#to-bottom').on('click', scrollToBottom)
|
$('#to-bottom').on('click', scrollToBottom)
|
||||||
});
|
});
|
||||||
|
@ -890,7 +890,6 @@
|
|||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let doUpload = false;
|
|
||||||
}
|
}
|
||||||
}, false);
|
}, false);
|
||||||
xmlHttpRequest.addEventListener('error', (e) => {
|
xmlHttpRequest.addEventListener('error', (e) => {
|
||||||
@ -1198,7 +1197,7 @@
|
|||||||
let idx = document.getElementById('server_type').selectedIndex;
|
let idx = document.getElementById('server_type').selectedIndex;
|
||||||
// get the value of the selected option
|
// get the value of the selected option
|
||||||
let cSelect = document.getElementById("server");
|
let cSelect = document.getElementById("server");
|
||||||
let which;
|
let which = {};
|
||||||
try {
|
try {
|
||||||
which = document.getElementById('server_type').options[idx].value;
|
which = document.getElementById('server_type').options[idx].value;
|
||||||
} catch {
|
} catch {
|
||||||
@ -1234,10 +1233,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function serverJarChange(selectObj) {
|
function serverJarChange(selectObj) {
|
||||||
let type_select = document.getElementById('server_jar')
|
const type_select = document.getElementById('server_jar')
|
||||||
let tidx = type_select.selectedIndex;
|
const tidx = type_select.selectedIndex;
|
||||||
let val = type_select.options[tidx].value;
|
const val = type_select.options[tidx].value;
|
||||||
let jcSelect = "";
|
let jcSelect = {};
|
||||||
if (val == 'None') {
|
if (val == 'None') {
|
||||||
jcSelect = document.getElementById("server_type");
|
jcSelect = document.getElementById("server_type");
|
||||||
while (jcSelect.options.length > 0) {
|
while (jcSelect.options.length > 0) {
|
||||||
@ -1251,7 +1250,7 @@
|
|||||||
// get the value of the selected option
|
// get the value of the selected option
|
||||||
let jwhich = selectObj.options[jidx].value;
|
let jwhich = selectObj.options[jidx].value;
|
||||||
// use the selected option value to retrieve the list of items from the serverTypesLists array
|
// 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
|
// get the country select element via its known id
|
||||||
jcSelect = document.getElementById("server_type");
|
jcSelect = document.getElementById("server_type");
|
||||||
// remove the current options from the country select
|
// remove the current options from the country select
|
||||||
|
16
app/migrations/20230901_user_notif.py
Normal file
16
app/migrations/20230901_user_notif.py
Normal file
@ -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.
|
||||||
|
"""
|
Loading…
Reference in New Issue
Block a user