mirror of
https://github.com/qwerfd2/Groove_Coaster_2_Server.git
synced 2025-12-21 19:20:11 +00:00
server notice hot update
This commit is contained in:
@@ -189,7 +189,7 @@ async def register(request: Request):
|
||||
if not decrypted_fields:
|
||||
return inform_page("FAILED:<br>Invalid request data.", 0)
|
||||
|
||||
user_info, _ = await decrypt_fields_to_user_info(decrypted_fields)
|
||||
user_info = await user_name_to_user_info(username)
|
||||
|
||||
if user_info:
|
||||
return inform_page("FAILED:<br>Another user already has this name.", 0)
|
||||
|
||||
@@ -4,6 +4,8 @@ from starlette.responses import HTMLResponse, JSONResponse, RedirectResponse
|
||||
import sqlalchemy
|
||||
import json
|
||||
from datetime import datetime
|
||||
import os
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from api.database import player_database, accounts, results, devices, whitelists, blacklists, batch_tokens, binds, webs, logs, is_admin, read_user_save_file, write_user_save_file
|
||||
from api.misc import crc32_decimal
|
||||
@@ -11,11 +13,11 @@ from api.misc import crc32_decimal
|
||||
TABLE_MAP = {
|
||||
"accounts": (accounts, ["id", "username", "password_hash", "save_crc", "save_timestamp", "save_id", "coin_mp", "title", "avatar", "mobile_delta", "arcade_delta", "total_delta", "created_at", "updated_at"]),
|
||||
"results": (results, ["id", "device_id", "stts", "song_id", "mode", "avatar", "score", "high_score", "play_rslt", "item", "os", "os_ver", "ver", "created_at"]),
|
||||
"devices": (devices, ["device_id", "user_id", "my_stage", "my_avatar", "item", "daily_day", "coin", "lvl", "title", "avatar", "created_at", "updated_at", "last_login_at"]),
|
||||
"devices": (devices, ["device_id", "user_id", "my_stage", "my_avatar", "item", "daily_day", "coin", "lvl", "title", "avatar", "created_at", "updated_at", "bind_token", "last_login_at"]),
|
||||
"whitelist": (whitelists, ["id", "device_id"]),
|
||||
"blacklist": (blacklists, ["id", "ban_terms", "reason"]),
|
||||
"batch_tokens": (batch_tokens, ["id", "batch_token", "expire_at", "uses_left", "auth_id", "created_at", "updated_at"]),
|
||||
"binds": (binds, ["id", "user_id", "bind_account", "bind_code", "is_verified", "auth_token", "bind_date"]),
|
||||
"binds": (binds, ["id", "user_id", "bind_account", "bind_code", "is_verified", "bind_date"]),
|
||||
"webs": (webs, ["id", "user_id", "permission", "web_token", "last_save_export", "created_at", "updated_at"]),
|
||||
"logs": (logs, ["id", "user_id", "filename", "filesize", "timestamp"]),
|
||||
}
|
||||
@@ -346,6 +348,40 @@ async def web_admin_data_save(request: Request):
|
||||
|
||||
return JSONResponse({"status": "success", "message": "Data saved successfully."})
|
||||
|
||||
async def web_admin_update_maintenance(request: Request):
|
||||
adm = await is_admin(request.cookies.get("token"))
|
||||
if not adm:
|
||||
return JSONResponse({"status": "failed", "message": "Invalid token."}, status_code=400)
|
||||
|
||||
params = await request.json()
|
||||
status = params.get("status")
|
||||
message_en = params.get("message_en")
|
||||
message_ja = params.get("message_ja")
|
||||
message_fr = params.get("message_fr")
|
||||
message_it = params.get("message_it")
|
||||
|
||||
# Create the XML structure directly
|
||||
notice_xml = f"""<?xml version="1.0" encoding="UTF-8"?>
|
||||
<response>
|
||||
<code>{status}</code>
|
||||
<message>
|
||||
<en>{message_en}</en>
|
||||
<ja>{message_ja}</ja>
|
||||
<fr>{message_fr}</fr>
|
||||
<it>{message_it}</it>
|
||||
</message>
|
||||
</response>
|
||||
"""
|
||||
|
||||
# Save the XML to the file
|
||||
notice_file_path = os.path.join('files/notice.xml')
|
||||
try:
|
||||
with open(notice_file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(notice_xml)
|
||||
return JSONResponse({"status": "success", "message": "Maintenance settings updated successfully."})
|
||||
except Exception as e:
|
||||
return JSONResponse({"status": "failed", "message": f"An error occurred: {str(e)}"}, status_code=500)
|
||||
|
||||
routes = [
|
||||
Route("/admin", web_admin_page, methods=["GET"]),
|
||||
Route("/admin/", web_admin_page, methods=["GET"]),
|
||||
@@ -355,4 +391,5 @@ routes = [
|
||||
Route("/admin/table/insert", web_admin_table_insert, methods=["POST"]),
|
||||
Route("/admin/data", web_admin_data_get, methods=["GET"]),
|
||||
Route("/admin/data/save", web_admin_data_save, methods=["POST"]),
|
||||
Route("/admin/update_maintenance", web_admin_update_maintenance, methods=["POST"])
|
||||
]
|
||||
@@ -64,7 +64,8 @@ devices = Table(
|
||||
Column("avatar", Integer, default=1),
|
||||
Column("created_at", DateTime, default=datetime.utcnow),
|
||||
Column("updated_at", DateTime, default=datetime.utcnow, onupdate=datetime.utcnow),
|
||||
Column("last_login_at", DateTime, default=None)
|
||||
Column("last_login_at", DateTime, default=None),
|
||||
Column("bind_token", String(64), unique=True, nullable=True)
|
||||
)
|
||||
|
||||
results = Table(
|
||||
@@ -141,7 +142,6 @@ binds = Table(
|
||||
Column("bind_account", String(128), unique=True, nullable=False),
|
||||
Column("bind_code", String(6), nullable=False),
|
||||
Column("is_verified", Integer, default=0),
|
||||
Column("auth_token", String(64), unique=True),
|
||||
Column("bind_date", DateTime, default=datetime.utcnow)
|
||||
)
|
||||
|
||||
@@ -191,13 +191,13 @@ async def ensure_user_columns():
|
||||
import aiosqlite
|
||||
|
||||
async with aiosqlite.connect(DB_PATH) as db:
|
||||
async with db.execute("PRAGMA table_info(user);") as cursor:
|
||||
async with db.execute("PRAGMA table_info(devices);") as cursor:
|
||||
columns = [row[1] async for row in cursor]
|
||||
|
||||
alter_needed = False
|
||||
#if "save_id" not in columns:
|
||||
# await db.execute("ALTER TABLE user ADD COLUMN save_id TEXT;")
|
||||
# alter_needed = True
|
||||
if "bind_token" not in columns:
|
||||
await db.execute("ALTER TABLE devices ADD COLUMN bind_token TEXT;")
|
||||
alter_needed = True
|
||||
#if "coin_mp" not in columns:
|
||||
# await db.execute("ALTER TABLE user ADD COLUMN coin_mp INTEGER DEFAULT 1;")
|
||||
# alter_needed = True
|
||||
@@ -212,14 +212,16 @@ async def get_bind(user_id):
|
||||
result = await player_database.fetch_one(query)
|
||||
return dict(result) if result else None
|
||||
|
||||
async def refresh_bind(user_id):
|
||||
async def refresh_bind(user_id, device_id):
|
||||
existing_bind = await get_bind(user_id)
|
||||
if existing_bind and existing_bind['is_verified'] == 1:
|
||||
new_auth_token = base64.urlsafe_b64encode(os.urandom(64)).decode("utf-8")
|
||||
update_query = update(binds).where(binds.c.id == existing_bind['id']).values(
|
||||
auth_token=new_auth_token
|
||||
update_query = update(devices).where(devices.c.device_id == device_id).values(
|
||||
bind_token=new_auth_token
|
||||
)
|
||||
await player_database.execute(update_query)
|
||||
return new_auth_token
|
||||
return ""
|
||||
|
||||
async def log_download(user_id, filename, filesize):
|
||||
query = logs.insert().values(
|
||||
@@ -255,7 +257,7 @@ async def verify_user_code(code, user_id):
|
||||
|
||||
update_query = update(binds).where(binds.c.id == result['id']).values(
|
||||
is_verified=1,
|
||||
auth_token=base64.urlsafe_b64encode(os.urandom(64)).decode("utf-8")
|
||||
bind_date=datetime.utcnow()
|
||||
)
|
||||
await player_database.execute(update_query)
|
||||
return "Verified and account successfully bound."
|
||||
@@ -276,6 +278,12 @@ async def decrypt_fields_to_user_info(decrypted_fields):
|
||||
|
||||
return None, None
|
||||
|
||||
async def get_device_info(device_id):
|
||||
query = devices.select().where(devices.c.device_id == device_id)
|
||||
device_record = await player_database.fetch_one(query)
|
||||
device_record = dict(device_record) if device_record else None
|
||||
return device_record
|
||||
|
||||
async def user_id_to_user_info(user_id):
|
||||
user_query = accounts.select().where(accounts.c.id == user_id)
|
||||
user_record = await player_database.fetch_one(user_query)
|
||||
@@ -317,7 +325,7 @@ async def check_blacklist(decrypted_fields):
|
||||
return result is None
|
||||
|
||||
async def get_user_entitlement_from_devices(user_id):
|
||||
devices_query = devices.select().where(devices.c.user_id == user_id)
|
||||
devices_query = select(devices.c.my_stage, devices.c.my_avatar).where(devices.c.user_id == user_id)
|
||||
devices_list = await player_database.fetch_all(devices_query)
|
||||
devices_list = [dict(dev) for dev in devices_list] if devices_list else []
|
||||
|
||||
|
||||
@@ -34,14 +34,19 @@ async def serve_file(request: Request):
|
||||
pass
|
||||
|
||||
else:
|
||||
existing_bind = select(binds).where((binds.c.auth_token == auth_token) & (binds.c.is_verified == 1))
|
||||
result = await player_database.fetch_one(existing_bind)
|
||||
if not result:
|
||||
return Response("Unauthorized", status_code=403)
|
||||
else:
|
||||
daily_bytes = await get_downloaded_bytes(result['user_id'], 24)
|
||||
if daily_bytes >= DAILY_DOWNLOAD_LIMIT:
|
||||
return Response("Daily download limit exceeded", status_code=403)
|
||||
existing_device_query = select(devices).where((devices.c.bind_token == auth_token))
|
||||
existing_device = await player_database.fetch_one(existing_device_query)
|
||||
if not existing_device:
|
||||
return Response("Unauthorized - device not found", status_code=403)
|
||||
|
||||
existing_bind_query = select(binds).where((binds.c.user_id == existing_device['user_id']) & (binds.c.is_verified == 1))
|
||||
bind_result = await player_database.fetch_one(existing_bind_query)
|
||||
if not bind_result:
|
||||
return Response("Unauthorized - bind not verified", status_code=403)
|
||||
|
||||
daily_bytes = await get_downloaded_bytes(bind_result['user_id'], 24)
|
||||
if daily_bytes >= DAILY_DOWNLOAD_LIMIT:
|
||||
return Response("Daily download limit exceeded", status_code=403)
|
||||
|
||||
safe_filename = os.path.realpath(os.path.join(os.getcwd(), "files", "gc2", folder, filename))
|
||||
base_directory = os.path.realpath(os.path.join(os.getcwd(), "files", "gc2", folder))
|
||||
@@ -55,7 +60,7 @@ async def serve_file(request: Request):
|
||||
# get size of file
|
||||
if AUTHORIZATION_MODE != 0:
|
||||
file_size = os.path.getsize(file_path)
|
||||
await log_download(result['user_id'], filename, file_size)
|
||||
await log_download(bind_result['user_id'], filename, file_size)
|
||||
return FileResponse(file_path)
|
||||
else:
|
||||
return Response("File not found", status_code=404)
|
||||
|
||||
@@ -7,8 +7,11 @@ import bcrypt
|
||||
import hashlib
|
||||
import re
|
||||
import xml.etree.ElementTree as ET
|
||||
import copy
|
||||
import os
|
||||
from config import MODEL, TUNEFILE, SKIN, AUTHORIZATION_NEEDED, AUTHORIZATION_MODE, GRANDFATHERED_ACCOUNT_LIMIT, BIND_SALT
|
||||
from api.database import get_bind, check_whitelist, check_blacklist, decrypt_fields_to_user_info, user_id_to_user_info_simple
|
||||
from api.database import get_bind, check_whitelist, check_blacklist, decrypt_fields_to_user_info, user_id_to_user_info_simple, get_device_info, refresh_bind
|
||||
from api.template import START_XML
|
||||
|
||||
FMAX_VER = None
|
||||
FMAX_RES = None
|
||||
@@ -75,18 +78,22 @@ async def get_model_pak(decrypted_fields, user_id):
|
||||
mid = ET.Element("model_pak")
|
||||
rid = ET.Element("date")
|
||||
uid = ET.Element("url")
|
||||
device_id = decrypted_fields[b'vid'][0].decode()
|
||||
|
||||
host = await get_host_string()
|
||||
|
||||
if AUTHORIZATION_MODE == 0:
|
||||
auth_token = decrypted_fields[b'vid'][0].decode()
|
||||
auth_token = device_id
|
||||
rid.text = MODEL
|
||||
uid.text = host + "files/gc2/" + auth_token + "/pak/model" + MODEL + ".pak"
|
||||
else:
|
||||
if user_id:
|
||||
device_info = await get_device_info(device_id)
|
||||
bind_info = await get_bind(user_id)
|
||||
if bind_info and bind_info['is_verified'] == 1:
|
||||
auth_token = bind_info['auth_token']
|
||||
auth_token = device_info['bind_token']
|
||||
if not auth_token:
|
||||
auth_token = await refresh_bind(user_id, device_id)
|
||||
rid.text = MODEL
|
||||
uid.text = host + "files/gc2/" + auth_token + "/pak/model" + MODEL + ".pak"
|
||||
else:
|
||||
@@ -104,18 +111,20 @@ async def get_tune_pak(decrypted_fields, user_id):
|
||||
mid = ET.Element("tuneFile_pak")
|
||||
rid = ET.Element("date")
|
||||
uid = ET.Element("url")
|
||||
device_id = decrypted_fields[b'vid'][0].decode()
|
||||
|
||||
host = await get_host_string()
|
||||
|
||||
if AUTHORIZATION_MODE == 0:
|
||||
auth_token = decrypted_fields[b'vid'][0].decode()
|
||||
auth_token = device_id
|
||||
rid.text = TUNEFILE
|
||||
uid.text = host + "files/gc2/" + auth_token + "/pak/tuneFile" + TUNEFILE + ".pak"
|
||||
else:
|
||||
if user_id:
|
||||
device_info = await get_device_info(device_id)
|
||||
bind_info = await get_bind(user_id)
|
||||
if bind_info and bind_info['is_verified'] == 1:
|
||||
auth_token = bind_info['auth_token']
|
||||
auth_token = device_info['bind_token']
|
||||
rid.text = TUNEFILE
|
||||
uid.text = host + "files/gc2/" + auth_token + "/pak/tuneFile" + TUNEFILE + ".pak"
|
||||
else:
|
||||
@@ -133,18 +142,20 @@ async def get_skin_pak(decrypted_fields, user_id):
|
||||
mid = ET.Element("skin_pak")
|
||||
rid = ET.Element("date")
|
||||
uid = ET.Element("url")
|
||||
device_id = decrypted_fields[b'vid'][0].decode()
|
||||
|
||||
host = await get_host_string()
|
||||
|
||||
if AUTHORIZATION_MODE == 0:
|
||||
auth_token = decrypted_fields[b'vid'][0].decode()
|
||||
auth_token = device_id
|
||||
rid.text = SKIN
|
||||
uid.text = host + "files/gc2/" + auth_token + "/pak/skin" + SKIN + ".pak"
|
||||
else:
|
||||
if user_id:
|
||||
device_info = await get_device_info(device_id)
|
||||
bind_info = await get_bind(user_id)
|
||||
if bind_info and bind_info['is_verified'] == 1:
|
||||
auth_token = bind_info['auth_token']
|
||||
auth_token = device_info['bind_token']
|
||||
rid.text = SKIN
|
||||
uid.text = host + "files/gc2/" + auth_token + "/pak/skin" + SKIN + ".pak"
|
||||
else:
|
||||
@@ -160,16 +171,18 @@ async def get_skin_pak(decrypted_fields, user_id):
|
||||
|
||||
async def get_m4a_path(decrypted_fields, user_id):
|
||||
host = await get_host_string()
|
||||
device_id = decrypted_fields[b'vid'][0].decode()
|
||||
if AUTHORIZATION_MODE == 0:
|
||||
auth_token = decrypted_fields[b'vid'][0].decode()
|
||||
auth_token = device_id
|
||||
mid = ET.Element("m4a_path")
|
||||
mid.text = host + "files/gc2/" + auth_token + "/audio/"
|
||||
else:
|
||||
if user_id:
|
||||
device_info = await get_device_info(device_id)
|
||||
bind_info = await get_bind(user_id)
|
||||
if bind_info and bind_info['is_verified'] == 1:
|
||||
mid = ET.Element("m4a_path")
|
||||
mid.text = host + "files/gc2/" + bind_info['auth_token'] + "/audio/"
|
||||
mid.text = host + "files/gc2/" + device_info['bind_token'] + "/audio/"
|
||||
else:
|
||||
mid = ET.Element("m4a_path")
|
||||
mid.text = host
|
||||
@@ -181,16 +194,18 @@ async def get_m4a_path(decrypted_fields, user_id):
|
||||
|
||||
async def get_stage_path(decrypted_data, user_id):
|
||||
host = await get_host_string()
|
||||
device_id = decrypted_data[b'vid'][0].decode()
|
||||
if AUTHORIZATION_MODE == 0:
|
||||
auth_token = decrypted_data[b'vid'][0].decode()
|
||||
auth_token = device_id
|
||||
mid = ET.Element("stage_path")
|
||||
mid.text = host + "files/gc2/" + auth_token + "/stage/"
|
||||
else:
|
||||
if user_id:
|
||||
device_info = await get_device_info(device_id)
|
||||
bind_info = await get_bind(user_id)
|
||||
if bind_info and bind_info['is_verified'] == 1:
|
||||
mid = ET.Element("stage_path")
|
||||
mid.text = host + "files/gc2/" + bind_info['auth_token'] + "/stage/"
|
||||
mid.text = host + "files/gc2/" + device_info['bind_token'] + "/stage/"
|
||||
else:
|
||||
mid = ET.Element("stage_path")
|
||||
mid.text = host
|
||||
@@ -293,3 +308,14 @@ async def get_host_string():
|
||||
from config import OVERRIDE_HOST, HOST, PORT
|
||||
host_string = OVERRIDE_HOST if OVERRIDE_HOST is not None else ("http://" + HOST + ":" + str(PORT) + "/")
|
||||
return host_string
|
||||
|
||||
async def get_start_xml():
|
||||
root = copy.deepcopy(START_XML.getroot())
|
||||
with open(os.path.join('files/notice.xml'), 'r', encoding='utf-8') as f:
|
||||
response_xml = ET.parse(f)
|
||||
response_root = response_xml.getroot()
|
||||
|
||||
for child in response_root:
|
||||
root.append(child)
|
||||
|
||||
return root
|
||||
@@ -1,14 +1,13 @@
|
||||
from starlette.responses import Response, FileResponse, HTMLResponse
|
||||
from starlette.requests import Request
|
||||
from starlette.routing import Route
|
||||
import os
|
||||
from datetime import datetime
|
||||
import xml.etree.ElementTree as ET
|
||||
import copy
|
||||
|
||||
from config import START_COIN
|
||||
|
||||
from api.misc import get_model_pak, get_tune_pak, get_skin_pak, get_m4a_path, get_stage_path, get_stage_zero, should_serve_init, inform_page
|
||||
from api.misc import get_model_pak, get_tune_pak, get_skin_pak, get_m4a_path, get_stage_path, get_stage_zero, should_serve_init, inform_page, get_start_xml
|
||||
from api.database import decrypt_fields_to_user_info, refresh_bind, get_user_entitlement_from_devices, set_device_data_using_decrypted_fields, create_device
|
||||
from api.crypt import decrypt_fields
|
||||
from api.template import START_AVATARS, START_STAGES, START_XML, SYNC_XML
|
||||
@@ -62,7 +61,7 @@ async def start(request: Request):
|
||||
return Response("""<response><code>10</code><message><ja>Invalid request data.</ja><en>Invalid request data.</en></message></response>""", media_type="application/xml"
|
||||
)
|
||||
|
||||
root = copy.deepcopy(START_XML.getroot())
|
||||
root = await get_start_xml()
|
||||
|
||||
user_info, device_info = await decrypt_fields_to_user_info(decrypted_fields)
|
||||
username = user_info['username'] if user_info else None
|
||||
@@ -74,7 +73,7 @@ async def start(request: Request):
|
||||
return Response("""<response><code>403</code><message>Access denied.</message></response>""", media_type="application/xml")
|
||||
|
||||
if user_id:
|
||||
await refresh_bind(user_id)
|
||||
_ = await refresh_bind(user_id, device_id)
|
||||
|
||||
root.append(await get_model_pak(decrypted_fields, user_id))
|
||||
root.append(await get_tune_pak(decrypted_fields, user_id))
|
||||
@@ -265,7 +264,7 @@ async def bonus(request: Request):
|
||||
device_id = decrypted_fields[b'vid'][0].decode()
|
||||
user_info, device_info = await decrypt_fields_to_user_info(decrypted_fields)
|
||||
|
||||
root = copy.deepcopy(START_XML.getroot())
|
||||
root = await get_start_xml()
|
||||
|
||||
daily_reward_elem = root.find(".//login_bonus")
|
||||
last_count_elem = daily_reward_elem.find("last_count")
|
||||
|
||||
11
new_server_7003/files/notice.xml
Normal file
11
new_server_7003/files/notice.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<response>
|
||||
<code>1</code>
|
||||
<message>
|
||||
<en>testtest</en>
|
||||
<ja>Welcome to the private server!</ja>
|
||||
<fr>Welcome to the private server!</fr>
|
||||
<it>Welcome to the private server!</it>
|
||||
</message>
|
||||
</response>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><response>
|
||||
<code>0</code>
|
||||
<area>us</area>
|
||||
<is_tutorial>0</is_tutorial>
|
||||
<cm_button_level>10</cm_button_level>
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
<li class="nav-item"><a class="nav-link" href="#" id="bindTab">Bind</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#" id="websTab">Webs</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#" id="logsTab">Logs</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#" id="maintenanceTab">Maintenance</a></li>
|
||||
</ul>
|
||||
<div class="d-flex ms-auto">
|
||||
<button class="btn btn-primary me-2" id="searchBtn">
|
||||
@@ -173,6 +174,38 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Maintenance Settings Modal -->
|
||||
<div class="modal fade" id="maintenanceModal" tabindex="-1" aria-labelledby="maintenanceModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<form id="maintenanceForm">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="maintenanceModalLabel">Maintenance Settings</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" style="max-height: 60vh; overflow-y: auto;">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Status</label>
|
||||
<input type="number" class="form-control" id="ServerMaintenanceStatus" placeholder="Enter status code">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Message</label>
|
||||
<input type="text" class="form-control mb-1" id="ServerTipsEn" placeholder="English">
|
||||
<input type="text" class="form-control mb-1" id="ServerTipsJa" placeholder="Japanese">
|
||||
<input type="text" class="form-control mb-1" id="ServerTipsFr" placeholder="French">
|
||||
<input type="text" class="form-control mb-1" id="ServerTipsIt" placeholder="Italian">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary" onclick="updateMaintenance()">Save Changes</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
window.currentTableName = null;
|
||||
let dataUserId = -1;
|
||||
@@ -483,6 +516,62 @@ document.getElementById('batchTokensTab').onclick = () => showTable("batch_token
|
||||
document.getElementById('bindTab').onclick = () => showTable("binds");
|
||||
document.getElementById('websTab').onclick = () => showTable("webs");
|
||||
document.getElementById('logsTab').onclick = () => showTable("logs");
|
||||
document.getElementById('maintenanceTab').onclick = () => editMaintenance();
|
||||
|
||||
function load_maintenance() {
|
||||
fetch('/files/notice.xml?timestamp=' + new Date().getTime())
|
||||
.then(response => response.text())
|
||||
.then(xmlData => {
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(xmlData, "application/xml");
|
||||
|
||||
const statusCode = xmlDoc.querySelector("response > code").textContent;
|
||||
const messageEn = xmlDoc.querySelector("response > message > en").textContent;
|
||||
const messageJa = xmlDoc.querySelector("response > message > ja").textContent;
|
||||
const messageFr = xmlDoc.querySelector("response > message > fr").textContent;
|
||||
const messageIt = xmlDoc.querySelector("response > message > it").textContent;
|
||||
|
||||
document.getElementById("ServerMaintenanceStatus").value = parseInt(statusCode, 10);
|
||||
document.getElementById("ServerTipsEn").value = messageEn;
|
||||
document.getElementById("ServerTipsJa").value = messageJa;
|
||||
document.getElementById("ServerTipsFr").value = messageFr;
|
||||
document.getElementById("ServerTipsIt").value = messageIt;
|
||||
|
||||
console.log('Maintenance data loaded successfully.');
|
||||
})
|
||||
.catch(error => console.error('Error loading notice.xml:', error));
|
||||
}
|
||||
|
||||
load_maintenance();
|
||||
|
||||
function editMaintenance() {
|
||||
const maintenanceModal = new bootstrap.Modal(document.getElementById('maintenanceModal'));
|
||||
maintenanceModal.show();
|
||||
}
|
||||
|
||||
async function updateMaintenance() {
|
||||
const status = parseInt(document.getElementById('ServerMaintenanceStatus').value, 10) || 0;
|
||||
const message_en = document.getElementById('ServerTipsEn').value || "";
|
||||
const message_ja = document.getElementById('ServerTipsJa').value || "";
|
||||
const message_fr = document.getElementById('ServerTipsFr').value || "";
|
||||
const message_it = document.getElementById('ServerTipsIt').value || "";
|
||||
|
||||
const response = await fetch('/admin/update_maintenance', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ status: status, message_en: message_en, message_ja: message_ja, message_fr: message_fr, message_it: message_it })
|
||||
});
|
||||
const result = await response.json();
|
||||
if (result.status === "success") {
|
||||
const modal = bootstrap.Modal.getOrCreateInstance(document.getElementById('maintenanceModal'));
|
||||
modal.hide();
|
||||
load_maintenance();
|
||||
} else {
|
||||
alert(result.message || "Update failed.");
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('logoutBtn').addEventListener('click', function() {
|
||||
document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
|
||||
|
||||
Reference in New Issue
Block a user