Spaces:
Sleeping
Sleeping
Commit ·
a3bcf3f
1
Parent(s): 239b333
fix: convert admin panel buttons to server-side forms
Browse files
app.py
CHANGED
|
@@ -975,29 +975,63 @@ def admin_test_chat():
|
|
| 975 |
except Exception as e:
|
| 976 |
return jsonify({"ok": False, "error": str(e)}), 500
|
| 977 |
|
| 978 |
-
@app.route("/")
|
| 979 |
-
def admin_page():
|
| 980 |
-
pw = request.args.get("password", "")
|
| 981 |
-
authed = (not ADMIN_PASSWORD) or (pw == ADMIN_PASSWORD)
|
| 982 |
|
| 983 |
-
|
| 984 |
-
|
| 985 |
-
|
| 986 |
-
|
| 987 |
-
|
| 988 |
-
.
|
| 989 |
-
|
| 990 |
-
|
| 991 |
-
|
| 992 |
-
|
| 993 |
-
|
| 994 |
-
|
| 995 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 996 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 997 |
masked = masked_cookies_dict(COOKIES)
|
| 998 |
saved = bool(COOKIES.get("__Secure-authjs.session-token"))
|
| 999 |
csrf = COOKIES.get("__Host-authjs.csrf-token", "")
|
| 1000 |
session_token = COOKIES.get("__Secure-authjs.session-token", "")
|
|
|
|
| 1001 |
html = """<!doctype html>
|
| 1002 |
<html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
| 1003 |
<title>Umans2API Admin</title><style>
|
|
@@ -1008,34 +1042,48 @@ input,textarea{width:100%;border-radius:8px;border:1px solid #3a4b80;background:
|
|
| 1008 |
textarea{min-height:110px;resize:vertical}label{display:block;font-size:12px;color:#94c7ff;margin-bottom:4px}
|
| 1009 |
button{padding:9px 16px;border-radius:8px;border:none;background:#3451ff;color:#fff;font-weight:600;cursor:pointer;font-size:13px;margin-right:6px;margin-bottom:6px}
|
| 1010 |
button.secondary{background:#273053}button.danger{background:#8b1e3f}.badge{display:inline-block;padding:3px 8px;border-radius:999px;background:#273053;color:#cfe0ff;font-size:12px}
|
| 1011 |
-
.status{padding:8px 12px;border-radius:6px;margin-top:10px;font-size:12px;
|
| 1012 |
-
.
|
| 1013 |
</style></head><body>
|
| 1014 |
-
<div class="card"><h1>🤖 Umans2API 管理页</h1><p class="muted">Anthropic + OpenAI 兼容 · 适配 Claude Code</p><p class="muted">
|
| 1015 |
-
<div class="card"><h2>Cookie 管理</h2><p class="muted">页面不会显示完整 cookie,只显示掩码。
|
| 1016 |
-
<
|
| 1017 |
-
<
|
| 1018 |
-
<
|
| 1019 |
-
<
|
| 1020 |
-
<
|
| 1021 |
-
<div class="row">
|
| 1022 |
-
<
|
| 1023 |
-
<
|
| 1024 |
-
|
| 1025 |
-
|
| 1026 |
-
|
| 1027 |
-
|
| 1028 |
-
|
| 1029 |
-
|
| 1030 |
-
async function runTest(){try{const prompt=document.getElementById('testPrompt').value.trim(); const r=await fetch('/admin/test',{method:'POST',headers:hdrs(),body:JSON.stringify({prompt})}); const t=await r.text(); let d={}; try{d=JSON.parse(t)}catch{d={raw:t}}; show(document.getElementById('panelStatus'), r.ok?'✅ 测试完成\n'+JSON.stringify(d,null,2):'❌ '+JSON.stringify(d,null,2), r.ok);}catch(e){show(document.getElementById('panelStatus'),'❌ '+e.message,false)}}
|
| 1031 |
-
async function clearCookies(){if(!confirm('确定清空已保存的 cookie?')) return; try{const r=await fetch('/admin/cookies',{method:'DELETE',headers:hdrs()}); const t=await r.text(); let d={}; try{d=JSON.parse(t)}catch{d={raw:t}}; show(document.getElementById('panelStatus'), r.ok?'✅ 已清空':'❌ '+JSON.stringify(d), r.ok); if(r.ok){document.getElementById('csrf').value=''; document.getElementById('session').value=''; document.getElementById('maskedCookies').value='{}';}}catch(e){show(document.getElementById('panelStatus'),'❌ '+e.message,false)}}
|
| 1032 |
-
</script></body></html>"""
|
| 1033 |
html = html.replace('__SAVED__', '已保存' if saved else '未保存')
|
| 1034 |
html = html.replace('__CSRF__', csrf).replace('__SESSION__', session_token)
|
| 1035 |
html = html.replace('__MASKED__', json.dumps(masked, ensure_ascii=False, indent=2))
|
| 1036 |
-
html = html.replace('
|
|
|
|
|
|
|
| 1037 |
return html
|
| 1038 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1039 |
if __name__ == "__main__":
|
| 1040 |
log.info("启动 umans2api:http://%s:%d", HOST, PORT)
|
| 1041 |
log.info("默认模型: %s", DEFAULT_MODEL)
|
|
|
|
| 975 |
except Exception as e:
|
| 976 |
return jsonify({"ok": False, "error": str(e)}), 500
|
| 977 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 978 |
|
| 979 |
+
@app.route("/admin/form/save", methods=["POST"])
|
| 980 |
+
def admin_form_save():
|
| 981 |
+
if not check_admin():
|
| 982 |
+
return "Unauthorized", 401
|
| 983 |
+
global COOKIES
|
| 984 |
+
csrf = (request.form.get("csrf") or "").strip()
|
| 985 |
+
session = (request.form.get("session") or "").strip()
|
| 986 |
+
if not session:
|
| 987 |
+
return admin_render_page(request.args.get("password",""), status_msg="session-token 必填", status_ok=False)
|
| 988 |
+
COOKIES = {
|
| 989 |
+
"__Host-authjs.csrf-token": csrf,
|
| 990 |
+
"__Secure-authjs.callback-url": "https%3A%2F%2Fapp.umans.ai%2F",
|
| 991 |
+
"__Secure-authjs.session-token": session,
|
| 992 |
+
}
|
| 993 |
+
save_cookies(COOKIES)
|
| 994 |
+
return admin_render_page(request.args.get("password",""), status_msg="✅ 保存成功", status_ok=True)
|
| 995 |
+
|
| 996 |
+
@app.route("/admin/form/clear", methods=["POST"])
|
| 997 |
+
def admin_form_clear():
|
| 998 |
+
if not check_admin():
|
| 999 |
+
return "Unauthorized", 401
|
| 1000 |
+
global COOKIES
|
| 1001 |
+
COOKIES = {}
|
| 1002 |
+
save_cookies(COOKIES)
|
| 1003 |
+
return admin_render_page(request.args.get("password",""), status_msg="✅ 已清空", status_ok=True)
|
| 1004 |
|
| 1005 |
+
@app.route("/admin/form/test", methods=["POST"])
|
| 1006 |
+
def admin_form_test():
|
| 1007 |
+
if not check_admin():
|
| 1008 |
+
return "Unauthorized", 401
|
| 1009 |
+
prompt = (request.form.get("prompt") or "你好,请回复 test ok").strip()
|
| 1010 |
+
if not COOKIES.get("__Secure-authjs.session-token"):
|
| 1011 |
+
return admin_render_page(request.args.get("password",""), status_msg="❌ 未保存 session-token", status_ok=False)
|
| 1012 |
+
try:
|
| 1013 |
+
payload = {"model": DEFAULT_MODEL, "messages": [{"role": "user", "content": prompt}], "stream": False}
|
| 1014 |
+
headers = {"User-Agent": UA, "Content-Type": "application/json", "Origin": "https://app.umans.ai", "Referer": "https://app.umans.ai/"}
|
| 1015 |
+
r = requests.post(UPSTREAM_URL, headers=headers, cookies=COOKIES, json=payload, timeout=60)
|
| 1016 |
+
preview = r.text[:2000]
|
| 1017 |
+
return admin_render_page(request.args.get("password",""), status_msg=f"测试返回(HTTP {r.status_code}):\n{preview}", status_ok=r.ok)
|
| 1018 |
+
except Exception as e:
|
| 1019 |
+
return admin_render_page(request.args.get("password",""), status_msg=f"❌ {e}", status_ok=False)
|
| 1020 |
+
|
| 1021 |
+
@app.route("/admin/form/help", methods=["GET"])
|
| 1022 |
+
def admin_form_help():
|
| 1023 |
+
if not check_admin():
|
| 1024 |
+
return "Unauthorized", 401
|
| 1025 |
+
msg = "获取方式:登录 app.umans.ai → F12 → Application → Cookies → 复制 __Host-authjs.csrf-token 和 __Secure-authjs.session-token。"
|
| 1026 |
+
return admin_render_page(request.args.get("password",""), status_msg=msg, status_ok=True)
|
| 1027 |
+
|
| 1028 |
+
|
| 1029 |
+
def admin_render_page(pw: str, status_msg: str = "", status_ok: bool = True):
|
| 1030 |
masked = masked_cookies_dict(COOKIES)
|
| 1031 |
saved = bool(COOKIES.get("__Secure-authjs.session-token"))
|
| 1032 |
csrf = COOKIES.get("__Host-authjs.csrf-token", "")
|
| 1033 |
session_token = COOKIES.get("__Secure-authjs.session-token", "")
|
| 1034 |
+
status_cls = "ok" if status_ok else "err"
|
| 1035 |
html = """<!doctype html>
|
| 1036 |
<html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
| 1037 |
<title>Umans2API Admin</title><style>
|
|
|
|
| 1042 |
textarea{min-height:110px;resize:vertical}label{display:block;font-size:12px;color:#94c7ff;margin-bottom:4px}
|
| 1043 |
button{padding:9px 16px;border-radius:8px;border:none;background:#3451ff;color:#fff;font-weight:600;cursor:pointer;font-size:13px;margin-right:6px;margin-bottom:6px}
|
| 1044 |
button.secondary{background:#273053}button.danger{background:#8b1e3f}.badge{display:inline-block;padding:3px 8px;border-radius:999px;background:#273053;color:#cfe0ff;font-size:12px}
|
| 1045 |
+
.status{padding:8px 12px;border-radius:6px;margin-top:10px;font-size:12px;white-space:pre-wrap;word-break:break-all}.ok{background:#0d3a1a;color:#8dffb2}.err{background:#3a0d0d;color:#ff9d9d}.row{display:flex;gap:8px;flex-wrap:wrap}
|
| 1046 |
+
.inline{display:inline}
|
| 1047 |
</style></head><body>
|
| 1048 |
+
<div class="card"><h1>🤖 Umans2API 管理页</h1><p class="muted">Anthropic + OpenAI 兼容 · 适配 Claude Code</p><p class="muted">已保存状态:<span class="badge">__SAVED__</span></p></div>
|
| 1049 |
+
<div class="card"><h2>Cookie 管理</h2><p class="muted">页面不会显示完整 cookie,只显示掩码。所有按钮都走服务端提交,不依赖前端 JS。</p>
|
| 1050 |
+
<div class="status __STATUS_CLS__">__STATUS_MSG__</div>
|
| 1051 |
+
<form method="post" action="/admin/form/save?password=__PW__">
|
| 1052 |
+
<label>__Host-authjs.csrf-token</label><input name="csrf" value="__CSRF__" placeholder="从浏览器 Cookie 复制"/>
|
| 1053 |
+
<label>__Secure-authjs.session-token <span class="warn">(必填)</span></label><input name="session" value="__SESSION__" placeholder="从浏览器 Cookie 复制"/>
|
| 1054 |
+
<button type="submit">💾 保存</button></form>
|
| 1055 |
+
<div class="row">
|
| 1056 |
+
<form class="inline" method="get" action="/admin/form/help"><input type="hidden" name="password" value="__PW__"><button class="secondary" type="submit">🧩 获取 Cookie 说明</button></form>
|
| 1057 |
+
<form class="inline" method="get" action="/"><input type="hidden" name="password" value="__PW__"><button class="secondary" type="submit">🔄 查看已保存状态</button></form>
|
| 1058 |
+
</div>
|
| 1059 |
+
<textarea readonly>__MASKED__</textarea>
|
| 1060 |
+
<form method="post" action="/admin/form/test?password=__PW__"><label>测试对话</label><input name="prompt" value="你好,请回复 test ok" /><button type="submit">🧪 测试对话</button></form>
|
| 1061 |
+
<form method="post" action="/admin/form/clear?password=__PW__" onsubmit="return confirm('确定清空已保存的 cookie?')"><button class="danger" type="submit">🗑 清空 Cookie</button></form>
|
| 1062 |
+
<form method="get" action="/"><button class="secondary" type="submit">🚪 退出</button></form>
|
| 1063 |
+
</div></body></html>"""
|
|
|
|
|
|
|
|
|
|
| 1064 |
html = html.replace('__SAVED__', '已保存' if saved else '未保存')
|
| 1065 |
html = html.replace('__CSRF__', csrf).replace('__SESSION__', session_token)
|
| 1066 |
html = html.replace('__MASKED__', json.dumps(masked, ensure_ascii=False, indent=2))
|
| 1067 |
+
html = html.replace('__PW__', pw)
|
| 1068 |
+
html = html.replace('__STATUS_MSG__', status_msg or '就绪')
|
| 1069 |
+
html = html.replace('__STATUS_CLS__', status_cls)
|
| 1070 |
return html
|
| 1071 |
|
| 1072 |
+
@app.route("/")
|
| 1073 |
+
def admin_page():
|
| 1074 |
+
pw = request.args.get("password", "")
|
| 1075 |
+
authed = (not ADMIN_PASSWORD) or (pw == ADMIN_PASSWORD)
|
| 1076 |
+
if not authed:
|
| 1077 |
+
return """<!doctype html>
|
| 1078 |
+
<html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
| 1079 |
+
<title>Umans2API</title><style>
|
| 1080 |
+
*{box-sizing:border-box;margin:0;padding:0}body{font-family:-apple-system,sans-serif;background:#0b1020;color:#e8eefc;padding:20px}.card{background:#141b34;border:1px solid #2a355e;border-radius:14px;padding:18px;margin-bottom:16px;max-width:720px}h1{font-size:18px;margin-bottom:12px}h2{font-size:15px;margin-bottom:8px;color:#94c7ff}.muted{color:#9fb0dd;font-size:12px;line-height:1.6;margin-bottom:10px}.warn{color:#ffb4b4}input{width:100%;border-radius:8px;border:1px solid #3a4b80;background:#0d1430;color:#fff;padding:10px 12px;font-size:14px;margin-bottom:10px}button{padding:10px 16px;border-radius:8px;border:none;background:#3451ff;color:#fff;font-weight:600;cursor:pointer;font-size:14px}
|
| 1081 |
+
</style></head><body>
|
| 1082 |
+
<div class="card"><h1>🤖 Umans2API</h1><p class="muted">Anthropic + OpenAI 兼容 · 适配 Claude Code</p><p class="muted warn">先输入管理员密码,再进入 Cookie 管理页面。</p></div>
|
| 1083 |
+
<div class="card"><h2>管理员登录</h2><form method="get" action="/"><input name="password" type="password" placeholder="输入管理员密码" autofocus /><button type="submit">🔓 进入管理页</button></form></div>
|
| 1084 |
+
</body></html>"""
|
| 1085 |
+
return admin_render_page(pw)
|
| 1086 |
+
|
| 1087 |
if __name__ == "__main__":
|
| 1088 |
log.info("启动 umans2api:http://%s:%d", HOST, PORT)
|
| 1089 |
log.info("默认模型: %s", DEFAULT_MODEL)
|