diff --git a/new_server_7003/api/account.py b/new_server_7003/api/account.py
index c1915ee..d7f00b8 100644
--- a/new_server_7003/api/account.py
+++ b/new_server_7003/api/account.py
@@ -189,7 +189,7 @@ async def register(request: Request):
if not decrypted_fields:
return inform_page("FAILED:
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:
Another user already has this name.", 0)
diff --git a/new_server_7003/api/admin.py b/new_server_7003/api/admin.py
index c5eaeba..32290d5 100644
--- a/new_server_7003/api/admin.py
+++ b/new_server_7003/api/admin.py
@@ -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"""
+
+ {status}
+
+ {message_en}
+ {message_ja}
+ {message_fr}
+ {message_it}
+
+
+ """
+
+ # 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"])
]
\ No newline at end of file
diff --git a/new_server_7003/api/database.py b/new_server_7003/api/database.py
index 97accc5..765ab44 100644
--- a/new_server_7003/api/database.py
+++ b/new_server_7003/api/database.py
@@ -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 []
diff --git a/new_server_7003/api/file.py b/new_server_7003/api/file.py
index 62fe89e..41b1874 100644
--- a/new_server_7003/api/file.py
+++ b/new_server_7003/api/file.py
@@ -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)
diff --git a/new_server_7003/api/misc.py b/new_server_7003/api/misc.py
index 832bc0c..4cda206 100644
--- a/new_server_7003/api/misc.py
+++ b/new_server_7003/api/misc.py
@@ -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
\ No newline at end of file
diff --git a/new_server_7003/api/user.py b/new_server_7003/api/user.py
index 8523c73..552f825 100644
--- a/new_server_7003/api/user.py
+++ b/new_server_7003/api/user.py
@@ -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("""10Invalid request data.Invalid request data.""", 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("""403Access denied.""", 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")
diff --git a/new_server_7003/files/notice.xml b/new_server_7003/files/notice.xml
new file mode 100644
index 0000000..8a4123a
--- /dev/null
+++ b/new_server_7003/files/notice.xml
@@ -0,0 +1,11 @@
+
+
+ 1
+
+ testtest
+ Welcome to the private server!
+ Welcome to the private server!
+ Welcome to the private server!
+
+
+
\ No newline at end of file
diff --git a/new_server_7003/files/start.xml b/new_server_7003/files/start.xml
index 18599de..ff6c51f 100644
--- a/new_server_7003/files/start.xml
+++ b/new_server_7003/files/start.xml
@@ -1,5 +1,4 @@
-0
us
0
10
diff --git a/new_server_7003/web/admin.html b/new_server_7003/web/admin.html
index ee732e2..8e0b3d1 100644
--- a/new_server_7003/web/admin.html
+++ b/new_server_7003/web/admin.html
@@ -82,6 +82,7 @@
Bind
Webs
Logs
+ Maintenance
+
+