- Python 后端 + 单页前端 + Android(Chaquopy) 壳 - 曲库 1343 首 / 称号 2800 个 - 机台身份与店铺信息已抹除,字段留空 - 打包脚本含 16KB 页对齐与自动脱敏
161 lines
4.6 KiB
Python
161 lines
4.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""多账号:把扫码换来的 userId + token 存本机,之后直接用它重新登录。
|
|
|
|
为什么能省掉扫码:二维码只是一次性入口,扫出来的 token 才是账号凭证。
|
|
`session.login(state, user_id, token)` 每天都用它做 UserLogin——只要机台上
|
|
没有重新扫码把旧凭证顶掉,同一个 token 可以反复登录。
|
|
|
|
★ token 属于敏感信息:
|
|
· 只写 `data/accounts.json`(本机,不进任何仓库、不上传);
|
|
· 接口返回给前端的只有打码 userId 与昵称,token 不出这个模块。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import time
|
|
from pathlib import Path
|
|
|
|
_STORE = Path(__file__).resolve().parent.parent / "data" / "accounts.json"
|
|
|
|
|
|
def mask_user_id(user_id) -> str:
|
|
"""12345678901 -> 123****01(和面板其它地方同一口径)。"""
|
|
s = str(user_id or "")
|
|
if not s:
|
|
return "-"
|
|
if len(s) <= 6:
|
|
return s[:1] + "***"
|
|
return s[:3] + "****" + s[-2:] # 固定 4 星,不泄露位数
|
|
|
|
|
|
def ref_of(user_id) -> str:
|
|
"""对外引用:userId 的短哈希。
|
|
|
|
前端拿到的永远是打码 id 与这个 ref,切换账号时只回传 ref,
|
|
完整 userId 与 token 都不出后端。
|
|
"""
|
|
return hashlib.sha256(str(int(user_id)).encode("utf-8")).hexdigest()[:12]
|
|
|
|
|
|
def get_by_ref(ref):
|
|
ref = str(ref or "")
|
|
if not ref:
|
|
return None
|
|
for row in _load():
|
|
if ref_of(row["userId"]) == ref:
|
|
return row
|
|
return None
|
|
|
|
|
|
def _load() -> list:
|
|
try:
|
|
raw = json.loads(_STORE.read_text(encoding="utf-8"))
|
|
except (OSError, ValueError):
|
|
return []
|
|
rows = raw.get("accounts") if isinstance(raw, dict) else raw
|
|
out = []
|
|
for row in (rows or []):
|
|
if not isinstance(row, dict):
|
|
continue
|
|
try:
|
|
uid = int(row.get("userId") or 0)
|
|
except (TypeError, ValueError):
|
|
continue
|
|
token = str(row.get("token") or "")
|
|
if uid <= 0 or not token:
|
|
continue
|
|
out.append({
|
|
"userId": uid,
|
|
"token": token,
|
|
"name": str(row.get("name") or ""),
|
|
"savedAt": str(row.get("savedAt") or ""),
|
|
"lastUsed": str(row.get("lastUsed") or ""),
|
|
})
|
|
return out
|
|
|
|
|
|
def _save(rows: list) -> None:
|
|
_STORE.parent.mkdir(parents=True, exist_ok=True)
|
|
tmp = _STORE.with_suffix(".tmp")
|
|
payload = {"accounts": rows}
|
|
tmp.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
os.replace(tmp, _STORE) # 原子替换,写一半断电不会把存档写坏
|
|
|
|
|
|
def _now() -> str:
|
|
return time.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
|
|
def list_accounts(current_user_id=None) -> list:
|
|
"""给前端看的列表:没有 token,只有打码 id / 昵称 / 时间。"""
|
|
cur = str(current_user_id or "")
|
|
rows = _load()
|
|
rows.sort(key=lambda r: r.get("lastUsed") or "", reverse=True)
|
|
return [{
|
|
"ref": ref_of(r["userId"]),
|
|
"userId": mask_user_id(r["userId"]),
|
|
"name": r["name"] or "(未命名)",
|
|
"lastUsed": r["lastUsed"] or r["savedAt"] or "-",
|
|
"current": str(r["userId"]) == cur,
|
|
} for r in rows]
|
|
|
|
|
|
def upsert(user_id, token, name: str = "") -> None:
|
|
"""登录成功后保存 / 刷新这个账号。"""
|
|
uid = int(user_id)
|
|
token = str(token or "")
|
|
if uid <= 0 or not token:
|
|
return
|
|
rows = _load()
|
|
for row in rows:
|
|
if row["userId"] == uid:
|
|
row["token"] = token
|
|
row["lastUsed"] = _now()
|
|
if name:
|
|
row["name"] = name
|
|
break
|
|
else:
|
|
rows.append({"userId": uid, "token": token, "name": name or "",
|
|
"savedAt": _now(), "lastUsed": _now()})
|
|
_save(rows)
|
|
|
|
|
|
def get(user_id):
|
|
uid = int(user_id)
|
|
for row in _load():
|
|
if row["userId"] == uid:
|
|
return row
|
|
return None
|
|
|
|
|
|
def touch(user_id) -> None:
|
|
uid = int(user_id)
|
|
rows = _load()
|
|
for row in rows:
|
|
if row["userId"] == uid:
|
|
row["lastUsed"] = _now()
|
|
_save(rows)
|
|
return
|
|
|
|
|
|
def rename(user_id, name: str) -> bool:
|
|
uid = int(user_id)
|
|
rows = _load()
|
|
for row in rows:
|
|
if row["userId"] == uid:
|
|
row["name"] = str(name or "")[:40]
|
|
_save(rows)
|
|
return True
|
|
return False
|
|
|
|
|
|
def remove(user_id) -> bool:
|
|
uid = int(user_id)
|
|
rows = _load()
|
|
left = [r for r in rows if r["userId"] != uid]
|
|
if len(left) == len(rows):
|
|
return False
|
|
_save(left)
|
|
return True |