mirror of
https://github.com/qwerfd2/Groove_Coaster_2_Server.git
synced 2025-12-22 03:30:18 +00:00
server notice hot update
This commit is contained in:
@@ -189,7 +189,7 @@ async def register(request: Request):
|
|||||||
if not decrypted_fields:
|
if not decrypted_fields:
|
||||||
return inform_page("FAILED:<br>Invalid request data.", 0)
|
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:
|
if user_info:
|
||||||
return inform_page("FAILED:<br>Another user already has this name.", 0)
|
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 sqlalchemy
|
||||||
import json
|
import json
|
||||||
from datetime import datetime
|
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.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
|
from api.misc import crc32_decimal
|
||||||
@@ -11,11 +13,11 @@ from api.misc import crc32_decimal
|
|||||||
TABLE_MAP = {
|
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"]),
|
"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"]),
|
"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"]),
|
"whitelist": (whitelists, ["id", "device_id"]),
|
||||||
"blacklist": (blacklists, ["id", "ban_terms", "reason"]),
|
"blacklist": (blacklists, ["id", "ban_terms", "reason"]),
|
||||||
"batch_tokens": (batch_tokens, ["id", "batch_token", "expire_at", "uses_left", "auth_id", "created_at", "updated_at"]),
|
"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"]),
|
"webs": (webs, ["id", "user_id", "permission", "web_token", "last_save_export", "created_at", "updated_at"]),
|
||||||
"logs": (logs, ["id", "user_id", "filename", "filesize", "timestamp"]),
|
"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."})
|
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 = [
|
routes = [
|
||||||
Route("/admin", web_admin_page, methods=["GET"]),
|
Route("/admin", web_admin_page, methods=["GET"]),
|
||||||
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/table/insert", web_admin_table_insert, methods=["POST"]),
|
||||||
Route("/admin/data", web_admin_data_get, methods=["GET"]),
|
Route("/admin/data", web_admin_data_get, methods=["GET"]),
|
||||||
Route("/admin/data/save", web_admin_data_save, methods=["POST"]),
|
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("avatar", Integer, default=1),
|
||||||
Column("created_at", DateTime, default=datetime.utcnow),
|
Column("created_at", DateTime, default=datetime.utcnow),
|
||||||
Column("updated_at", DateTime, default=datetime.utcnow, onupdate=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(
|
results = Table(
|
||||||
@@ -141,7 +142,6 @@ binds = Table(
|
|||||||
Column("bind_account", String(128), unique=True, nullable=False),
|
Column("bind_account", String(128), unique=True, nullable=False),
|
||||||
Column("bind_code", String(6), nullable=False),
|
Column("bind_code", String(6), nullable=False),
|
||||||
Column("is_verified", Integer, default=0),
|
Column("is_verified", Integer, default=0),
|
||||||
Column("auth_token", String(64), unique=True),
|
|
||||||
Column("bind_date", DateTime, default=datetime.utcnow)
|
Column("bind_date", DateTime, default=datetime.utcnow)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -191,13 +191,13 @@ async def ensure_user_columns():
|
|||||||
import aiosqlite
|
import aiosqlite
|
||||||
|
|
||||||
async with aiosqlite.connect(DB_PATH) as db:
|
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]
|
columns = [row[1] async for row in cursor]
|
||||||
|
|
||||||
alter_needed = False
|
alter_needed = False
|
||||||
#if "save_id" not in columns:
|
if "bind_token" not in columns:
|
||||||
# await db.execute("ALTER TABLE user ADD COLUMN save_id TEXT;")
|
await db.execute("ALTER TABLE devices ADD COLUMN bind_token TEXT;")
|
||||||
# alter_needed = True
|
alter_needed = True
|
||||||
#if "coin_mp" not in columns:
|
#if "coin_mp" not in columns:
|
||||||
# await db.execute("ALTER TABLE user ADD COLUMN coin_mp INTEGER DEFAULT 1;")
|
# await db.execute("ALTER TABLE user ADD COLUMN coin_mp INTEGER DEFAULT 1;")
|
||||||
# alter_needed = True
|
# alter_needed = True
|
||||||
@@ -212,14 +212,16 @@ async def get_bind(user_id):
|
|||||||
result = await player_database.fetch_one(query)
|
result = await player_database.fetch_one(query)
|
||||||
return dict(result) if result else None
|
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)
|
existing_bind = await get_bind(user_id)
|
||||||
if existing_bind and existing_bind['is_verified'] == 1:
|
if existing_bind and existing_bind['is_verified'] == 1:
|
||||||
new_auth_token = base64.urlsafe_b64encode(os.urandom(64)).decode("utf-8")
|
new_auth_token = base64.urlsafe_b64encode(os.urandom(64)).decode("utf-8")
|
||||||
update_query = update(binds).where(binds.c.id == existing_bind['id']).values(
|
update_query = update(devices).where(devices.c.device_id == device_id).values(
|
||||||
auth_token=new_auth_token
|
bind_token=new_auth_token
|
||||||
)
|
)
|
||||||
await player_database.execute(update_query)
|
await player_database.execute(update_query)
|
||||||
|
return new_auth_token
|
||||||
|
return ""
|
||||||
|
|
||||||
async def log_download(user_id, filename, filesize):
|
async def log_download(user_id, filename, filesize):
|
||||||
query = logs.insert().values(
|
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(
|
update_query = update(binds).where(binds.c.id == result['id']).values(
|
||||||
is_verified=1,
|
is_verified=1,
|
||||||
auth_token=base64.urlsafe_b64encode(os.urandom(64)).decode("utf-8")
|
bind_date=datetime.utcnow()
|
||||||
)
|
)
|
||||||
await player_database.execute(update_query)
|
await player_database.execute(update_query)
|
||||||
return "Verified and account successfully bound."
|
return "Verified and account successfully bound."
|
||||||
@@ -276,6 +278,12 @@ async def decrypt_fields_to_user_info(decrypted_fields):
|
|||||||
|
|
||||||
return None, None
|
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):
|
async def user_id_to_user_info(user_id):
|
||||||
user_query = accounts.select().where(accounts.c.id == user_id)
|
user_query = accounts.select().where(accounts.c.id == user_id)
|
||||||
user_record = await player_database.fetch_one(user_query)
|
user_record = await player_database.fetch_one(user_query)
|
||||||
@@ -317,7 +325,7 @@ async def check_blacklist(decrypted_fields):
|
|||||||
return result is None
|
return result is None
|
||||||
|
|
||||||
async def get_user_entitlement_from_devices(user_id):
|
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 = await player_database.fetch_all(devices_query)
|
||||||
devices_list = [dict(dev) for dev in devices_list] if devices_list else []
|
devices_list = [dict(dev) for dev in devices_list] if devices_list else []
|
||||||
|
|
||||||
|
|||||||
@@ -34,14 +34,19 @@ async def serve_file(request: Request):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
else:
|
else:
|
||||||
existing_bind = select(binds).where((binds.c.auth_token == auth_token) & (binds.c.is_verified == 1))
|
existing_device_query = select(devices).where((devices.c.bind_token == auth_token))
|
||||||
result = await player_database.fetch_one(existing_bind)
|
existing_device = await player_database.fetch_one(existing_device_query)
|
||||||
if not result:
|
if not existing_device:
|
||||||
return Response("Unauthorized", status_code=403)
|
return Response("Unauthorized - device not found", status_code=403)
|
||||||
else:
|
|
||||||
daily_bytes = await get_downloaded_bytes(result['user_id'], 24)
|
existing_bind_query = select(binds).where((binds.c.user_id == existing_device['user_id']) & (binds.c.is_verified == 1))
|
||||||
if daily_bytes >= DAILY_DOWNLOAD_LIMIT:
|
bind_result = await player_database.fetch_one(existing_bind_query)
|
||||||
return Response("Daily download limit exceeded", status_code=403)
|
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))
|
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))
|
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
|
# get size of file
|
||||||
if AUTHORIZATION_MODE != 0:
|
if AUTHORIZATION_MODE != 0:
|
||||||
file_size = os.path.getsize(file_path)
|
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)
|
return FileResponse(file_path)
|
||||||
else:
|
else:
|
||||||
return Response("File not found", status_code=404)
|
return Response("File not found", status_code=404)
|
||||||
|
|||||||
@@ -7,8 +7,11 @@ import bcrypt
|
|||||||
import hashlib
|
import hashlib
|
||||||
import re
|
import re
|
||||||
import xml.etree.ElementTree as ET
|
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 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_VER = None
|
||||||
FMAX_RES = None
|
FMAX_RES = None
|
||||||
@@ -75,18 +78,22 @@ async def get_model_pak(decrypted_fields, user_id):
|
|||||||
mid = ET.Element("model_pak")
|
mid = ET.Element("model_pak")
|
||||||
rid = ET.Element("date")
|
rid = ET.Element("date")
|
||||||
uid = ET.Element("url")
|
uid = ET.Element("url")
|
||||||
|
device_id = decrypted_fields[b'vid'][0].decode()
|
||||||
|
|
||||||
host = await get_host_string()
|
host = await get_host_string()
|
||||||
|
|
||||||
if AUTHORIZATION_MODE == 0:
|
if AUTHORIZATION_MODE == 0:
|
||||||
auth_token = decrypted_fields[b'vid'][0].decode()
|
auth_token = device_id
|
||||||
rid.text = MODEL
|
rid.text = MODEL
|
||||||
uid.text = host + "files/gc2/" + auth_token + "/pak/model" + MODEL + ".pak"
|
uid.text = host + "files/gc2/" + auth_token + "/pak/model" + MODEL + ".pak"
|
||||||
else:
|
else:
|
||||||
if user_id:
|
if user_id:
|
||||||
|
device_info = await get_device_info(device_id)
|
||||||
bind_info = await get_bind(user_id)
|
bind_info = await get_bind(user_id)
|
||||||
if bind_info and bind_info['is_verified'] == 1:
|
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
|
rid.text = MODEL
|
||||||
uid.text = host + "files/gc2/" + auth_token + "/pak/model" + MODEL + ".pak"
|
uid.text = host + "files/gc2/" + auth_token + "/pak/model" + MODEL + ".pak"
|
||||||
else:
|
else:
|
||||||
@@ -104,18 +111,20 @@ async def get_tune_pak(decrypted_fields, user_id):
|
|||||||
mid = ET.Element("tuneFile_pak")
|
mid = ET.Element("tuneFile_pak")
|
||||||
rid = ET.Element("date")
|
rid = ET.Element("date")
|
||||||
uid = ET.Element("url")
|
uid = ET.Element("url")
|
||||||
|
device_id = decrypted_fields[b'vid'][0].decode()
|
||||||
|
|
||||||
host = await get_host_string()
|
host = await get_host_string()
|
||||||
|
|
||||||
if AUTHORIZATION_MODE == 0:
|
if AUTHORIZATION_MODE == 0:
|
||||||
auth_token = decrypted_fields[b'vid'][0].decode()
|
auth_token = device_id
|
||||||
rid.text = TUNEFILE
|
rid.text = TUNEFILE
|
||||||
uid.text = host + "files/gc2/" + auth_token + "/pak/tuneFile" + TUNEFILE + ".pak"
|
uid.text = host + "files/gc2/" + auth_token + "/pak/tuneFile" + TUNEFILE + ".pak"
|
||||||
else:
|
else:
|
||||||
if user_id:
|
if user_id:
|
||||||
|
device_info = await get_device_info(device_id)
|
||||||
bind_info = await get_bind(user_id)
|
bind_info = await get_bind(user_id)
|
||||||
if bind_info and bind_info['is_verified'] == 1:
|
if bind_info and bind_info['is_verified'] == 1:
|
||||||
auth_token = bind_info['auth_token']
|
auth_token = device_info['bind_token']
|
||||||
rid.text = TUNEFILE
|
rid.text = TUNEFILE
|
||||||
uid.text = host + "files/gc2/" + auth_token + "/pak/tuneFile" + TUNEFILE + ".pak"
|
uid.text = host + "files/gc2/" + auth_token + "/pak/tuneFile" + TUNEFILE + ".pak"
|
||||||
else:
|
else:
|
||||||
@@ -133,18 +142,20 @@ async def get_skin_pak(decrypted_fields, user_id):
|
|||||||
mid = ET.Element("skin_pak")
|
mid = ET.Element("skin_pak")
|
||||||
rid = ET.Element("date")
|
rid = ET.Element("date")
|
||||||
uid = ET.Element("url")
|
uid = ET.Element("url")
|
||||||
|
device_id = decrypted_fields[b'vid'][0].decode()
|
||||||
|
|
||||||
host = await get_host_string()
|
host = await get_host_string()
|
||||||
|
|
||||||
if AUTHORIZATION_MODE == 0:
|
if AUTHORIZATION_MODE == 0:
|
||||||
auth_token = decrypted_fields[b'vid'][0].decode()
|
auth_token = device_id
|
||||||
rid.text = SKIN
|
rid.text = SKIN
|
||||||
uid.text = host + "files/gc2/" + auth_token + "/pak/skin" + SKIN + ".pak"
|
uid.text = host + "files/gc2/" + auth_token + "/pak/skin" + SKIN + ".pak"
|
||||||
else:
|
else:
|
||||||
if user_id:
|
if user_id:
|
||||||
|
device_info = await get_device_info(device_id)
|
||||||
bind_info = await get_bind(user_id)
|
bind_info = await get_bind(user_id)
|
||||||
if bind_info and bind_info['is_verified'] == 1:
|
if bind_info and bind_info['is_verified'] == 1:
|
||||||
auth_token = bind_info['auth_token']
|
auth_token = device_info['bind_token']
|
||||||
rid.text = SKIN
|
rid.text = SKIN
|
||||||
uid.text = host + "files/gc2/" + auth_token + "/pak/skin" + SKIN + ".pak"
|
uid.text = host + "files/gc2/" + auth_token + "/pak/skin" + SKIN + ".pak"
|
||||||
else:
|
else:
|
||||||
@@ -160,16 +171,18 @@ async def get_skin_pak(decrypted_fields, user_id):
|
|||||||
|
|
||||||
async def get_m4a_path(decrypted_fields, user_id):
|
async def get_m4a_path(decrypted_fields, user_id):
|
||||||
host = await get_host_string()
|
host = await get_host_string()
|
||||||
|
device_id = decrypted_fields[b'vid'][0].decode()
|
||||||
if AUTHORIZATION_MODE == 0:
|
if AUTHORIZATION_MODE == 0:
|
||||||
auth_token = decrypted_fields[b'vid'][0].decode()
|
auth_token = device_id
|
||||||
mid = ET.Element("m4a_path")
|
mid = ET.Element("m4a_path")
|
||||||
mid.text = host + "files/gc2/" + auth_token + "/audio/"
|
mid.text = host + "files/gc2/" + auth_token + "/audio/"
|
||||||
else:
|
else:
|
||||||
if user_id:
|
if user_id:
|
||||||
|
device_info = await get_device_info(device_id)
|
||||||
bind_info = await get_bind(user_id)
|
bind_info = await get_bind(user_id)
|
||||||
if bind_info and bind_info['is_verified'] == 1:
|
if bind_info and bind_info['is_verified'] == 1:
|
||||||
mid = ET.Element("m4a_path")
|
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:
|
else:
|
||||||
mid = ET.Element("m4a_path")
|
mid = ET.Element("m4a_path")
|
||||||
mid.text = host
|
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):
|
async def get_stage_path(decrypted_data, user_id):
|
||||||
host = await get_host_string()
|
host = await get_host_string()
|
||||||
|
device_id = decrypted_data[b'vid'][0].decode()
|
||||||
if AUTHORIZATION_MODE == 0:
|
if AUTHORIZATION_MODE == 0:
|
||||||
auth_token = decrypted_data[b'vid'][0].decode()
|
auth_token = device_id
|
||||||
mid = ET.Element("stage_path")
|
mid = ET.Element("stage_path")
|
||||||
mid.text = host + "files/gc2/" + auth_token + "/stage/"
|
mid.text = host + "files/gc2/" + auth_token + "/stage/"
|
||||||
else:
|
else:
|
||||||
if user_id:
|
if user_id:
|
||||||
|
device_info = await get_device_info(device_id)
|
||||||
bind_info = await get_bind(user_id)
|
bind_info = await get_bind(user_id)
|
||||||
if bind_info and bind_info['is_verified'] == 1:
|
if bind_info and bind_info['is_verified'] == 1:
|
||||||
mid = ET.Element("stage_path")
|
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:
|
else:
|
||||||
mid = ET.Element("stage_path")
|
mid = ET.Element("stage_path")
|
||||||
mid.text = host
|
mid.text = host
|
||||||
@@ -293,3 +308,14 @@ async def get_host_string():
|
|||||||
from config import OVERRIDE_HOST, HOST, PORT
|
from config import OVERRIDE_HOST, HOST, PORT
|
||||||
host_string = OVERRIDE_HOST if OVERRIDE_HOST is not None else ("http://" + HOST + ":" + str(PORT) + "/")
|
host_string = OVERRIDE_HOST if OVERRIDE_HOST is not None else ("http://" + HOST + ":" + str(PORT) + "/")
|
||||||
return host_string
|
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.responses import Response, FileResponse, HTMLResponse
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from starlette.routing import Route
|
from starlette.routing import Route
|
||||||
import os
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
from config import START_COIN
|
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.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.crypt import decrypt_fields
|
||||||
from api.template import START_AVATARS, START_STAGES, START_XML, SYNC_XML
|
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"
|
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)
|
user_info, device_info = await decrypt_fields_to_user_info(decrypted_fields)
|
||||||
username = user_info['username'] if user_info else None
|
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")
|
return Response("""<response><code>403</code><message>Access denied.</message></response>""", media_type="application/xml")
|
||||||
|
|
||||||
if user_id:
|
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_model_pak(decrypted_fields, user_id))
|
||||||
root.append(await get_tune_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()
|
device_id = decrypted_fields[b'vid'][0].decode()
|
||||||
user_info, device_info = await decrypt_fields_to_user_info(decrypted_fields)
|
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")
|
daily_reward_elem = root.find(".//login_bonus")
|
||||||
last_count_elem = daily_reward_elem.find("last_count")
|
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>
|
<?xml version="1.0" encoding="UTF-8"?><response>
|
||||||
<code>0</code>
|
|
||||||
<area>us</area>
|
<area>us</area>
|
||||||
<is_tutorial>0</is_tutorial>
|
<is_tutorial>0</is_tutorial>
|
||||||
<cm_button_level>10</cm_button_level>
|
<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="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="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="logsTab">Logs</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#" id="maintenanceTab">Maintenance</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="d-flex ms-auto">
|
<div class="d-flex ms-auto">
|
||||||
<button class="btn btn-primary me-2" id="searchBtn">
|
<button class="btn btn-primary me-2" id="searchBtn">
|
||||||
@@ -173,6 +174,38 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
<script>
|
||||||
window.currentTableName = null;
|
window.currentTableName = null;
|
||||||
let dataUserId = -1;
|
let dataUserId = -1;
|
||||||
@@ -483,6 +516,62 @@ document.getElementById('batchTokensTab').onclick = () => showTable("batch_token
|
|||||||
document.getElementById('bindTab').onclick = () => showTable("binds");
|
document.getElementById('bindTab').onclick = () => showTable("binds");
|
||||||
document.getElementById('websTab').onclick = () => showTable("webs");
|
document.getElementById('websTab').onclick = () => showTable("webs");
|
||||||
document.getElementById('logsTab').onclick = () => showTable("logs");
|
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.getElementById('logoutBtn').addEventListener('click', function() {
|
||||||
document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
|
document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
|
||||||
|
|||||||
Reference in New Issue
Block a user