diff --git a/database/app.py b/database/app.py index cf63a07..dbf0afd 100644 --- a/database/app.py +++ b/database/app.py @@ -69,6 +69,31 @@ async def func(*args, **kwargs): return func +def login_or_token_required(f): + @wraps(f) + async def func(*args, **kwargs): + import_token = request.headers.get('Import-Token', default='') + if import_token != '': + try: + g.user = Player.get(Player.import_token == import_token) + g.username = g.user.username + return await f(*args, **kwargs) + except Exception: + return {"status": "error", "message": "导入token有误"}, 400 + else: + try: + token = decode(request.cookies['jwt_token']) + except KeyError: + return {"status": "error", "message": "尚未登录"}, 403 + if token == {}: + return {"status": "error", "message": "尚未登录"}, 403 + if token['exp'] < ts(): + return {"status": "error", "message": "会话过期"}, 403 + g.username = token['username'] + g.user = Player.get(Player.username == g.username) + return await f(*args, **kwargs) + + def is_developer(token): if token == "": return False, {"status": "error", "msg": "请先联系水鱼申请开发者token"}, 400 diff --git a/database/models/base.py b/database/models/base.py index 54afcde..eef1e8d 100644 --- a/database/models/base.py +++ b/database/models/base.py @@ -1,5 +1,6 @@ import json import time +import hashlib from typing import List, Optional, Dict, Text, Union, Any, Tuple from peewee import Model, CharField, IntegerField, BigIntegerField, BooleanField, ForeignKeyField, DoubleField, TextField @@ -35,12 +36,14 @@ class Player(BaseModel): chuni_rating = DoubleField() nickname = CharField() bind_qq = CharField() + qq_channel_uid = CharField() plate = CharField() privacy = BooleanField() user_id = IntegerField() user_data = TextField() user_general_data = TextField() access_time = BigIntegerField() + import_token = CharField() def user_json(self): try: @@ -52,11 +55,18 @@ def user_json(self): "nickname": self.nickname, "additional_rating": self.additional_rating, "bind_qq": self.bind_qq, + "qq_channel_uid": self.qq_channel_uid, "privacy": self.privacy, "plate": self.plate, - "user_general_data": j + "user_general_data": j, + "import_token": self.import_token } + def generate_import_token(self): + self.import_token = hashlib.sha512((self.username + str(time.time())).encode()).hexdigest() + self.save() + return self.import_token + class Developer(BaseModel): nickname = CharField() diff --git a/database/models/maimai.py b/database/models/maimai.py index ff0333f..d81945f 100644 --- a/database/models/maimai.py +++ b/database/models/maimai.py @@ -126,6 +126,18 @@ def get_plate_name(version, plate_type): }[plate_type] +def verify_plate(player, version, plate_type) -> Tuple[bool, str]: + try: + if version == "无": + return True, "" + plate_name = get_plate_name(version, plate_type) + if plate_name == "真将": + return False, "" + return True, plate_name + except Exception: + return False, "" + + def record_json(record: NewRecord): data = { "title": record.title, diff --git a/database/routes/chunithm.py b/database/routes/chunithm.py index cf482cf..f888353 100644 --- a/database/routes/chunithm.py +++ b/database/routes/chunithm.py @@ -10,7 +10,7 @@ from audioop import reverse from collections import defaultdict from math import floor -from app import app, developer_required, login_required, md5 +from app import app, developer_required, login_required, login_or_token_required, md5 from quart import Quart, request, g, make_response from models.maimai import NewRecord from tools._jwt import * @@ -51,7 +51,7 @@ async def get_music_data_chuni(): @app.route("/chuni/player/update_records_html", methods=['POST']) -@login_required +@login_or_token_required async def update_records_chuni(): """ *需要登录 @@ -130,7 +130,7 @@ async def update_records_chuni(): return {"message": "更新成功"} @app.route("/chuni/player/delete_records", methods=['DELETE']) -@login_required +@login_or_token_required async def delete_records_chuni(): """ *需要登录 @@ -219,7 +219,7 @@ async def compute_ra(player: Player): @app.route("/chuni/player/records") -@login_required +@login_or_token_required async def player_records_chuni(): """ *需要登录 diff --git a/database/routes/maimai.py b/database/routes/maimai.py index 540e6e9..8203624 100644 --- a/database/routes/maimai.py +++ b/database/routes/maimai.py @@ -8,7 +8,7 @@ import asyncio import time from collections import defaultdict -from app import app, developer_required, login_required, md5, is_developer +from app import app, developer_required, login_required, login_or_token_required, md5, is_developer from quart import Quart, request, g, make_response from tools._jwt import * from models.maimai import * @@ -68,6 +68,7 @@ async def profile(): if verified: g.user.__setattr__("plate", plate_label) del obj["plate"] + if "bind_qq" in obj: # check duplicate bind_qq = obj["bind_qq"] @@ -80,14 +81,29 @@ async def profile(): }, 400 except Exception: pass + + if "qq_channel_uid" in obj: + # check duplicate + qq_channel_uid = obj["qq_channel_uid"] + if qq_channel_uid != "": + try: + player = Player.get((Player.qq_channel_uid == qq_channel_uid) & (Player.id != g.user.id)) + # Not found -> except + return { + "message": f"此频道 ID 已经被用户名为{player.username}的用户绑定,请先解绑再进行操作~" + }, 400 + except Exception: + pass + for key in obj: - if key in ("nickname", "bind_qq", "additional_rating", "privacy"): + if key in ("nickname", "bind_qq", "additional_rating", "privacy", "qq_channel_uid"): g.user.__setattr__(key, obj[key]) if key == "user_general_data": g.user.__setattr__(key, json.dumps(obj[key])) g.user.save() u: Player = g.user return u.user_json() + except Exception as e: print(e) return { @@ -95,16 +111,16 @@ async def profile(): }, 400 -def verify_plate(player, version, plate_type) -> Tuple[bool, str]: - try: - if version == "无": - return True, "" - plate_name = get_plate_name(version, plate_type) - if plate_name == "真将": - return False, "" - return True, plate_name - except Exception: - return False, "" +@app.route("/player/import_token", methods=['PUT']) +@login_required +async def import_token(): + """ + *需要登录 + 生成一个新的导入 Token,并覆盖旧 Token。 + """ + return { + "token": g.user.generate_import_token() + } @app.route("/music_data", methods=['GET']) @@ -124,7 +140,7 @@ async def get_music_data(): @app.route("/player/records", methods=['GET']) -@login_required +@login_or_token_required async def get_records(): """ *需要登录 @@ -326,7 +342,7 @@ async def compute_ra(player: Player): @app.route("/player/update_records", methods=['POST']) -@login_required +@login_or_token_required async def update_records(): """ *需要登录 @@ -483,7 +499,7 @@ async def update_records_html(): @app.route("/player/update_record", methods=['POST']) -@login_required +@login_or_token_required async def update_record(): """ *需要登录 @@ -514,7 +530,7 @@ async def update_record(): @app.route("/player/delete_records", methods=['DELETE']) -@login_required +@login_or_token_required async def delete_records(): """ *需要登录