xiaoyi7894 commited on
Commit
a3bcf3f
·
1 Parent(s): 239b333

fix: convert admin panel buttons to server-side forms

Browse files
Files changed (1) hide show
  1. app.py +87 -39
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
- if not authed:
984
- return """<!doctype html>
985
- <html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
986
- <title>Umans2API</title><style>
987
- *{box-sizing:border-box;margin:0;padding:0}body{font-family:-apple-system,sans-serif;background:#0b1020;color:#e8eefc;padding:20px}
988
- .card{background:#141b34;border:1px solid #2a355e;border-radius:14px;padding:18px;margin-bottom:16px;max-width:720px}
989
- 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}
990
- input{width:100%;border-radius:8px;border:1px solid #3a4b80;background:#0d1430;color:#fff;padding:10px 12px;font-size:14px;margin-bottom:10px}
991
- button{padding:10px 16px;border-radius:8px;border:none;background:#3451ff;color:#fff;font-weight:600;cursor:pointer;font-size:14px}
992
- </style></head><body>
993
- <div class="card"><h1>🤖 Umans2API</h1><p class="muted">Anthropic + OpenAI 兼容 · 适配 Claude Code</p><p class="muted warn">先输入管理员密码,再进入 Cookie 管理页面。</p></div>
994
- <div class="card"><h2>管理员登录</h2><form method="get" action="/"><input name="password" type="password" placeholder="输入管理员密码" autofocus /><button type="submit">🔓 进入管理页</button></form></div>
995
- </body></html>"""
 
 
 
 
 
 
 
 
 
 
 
 
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;display:none;white-space:pre-wrap;word-break:break-all}.status.ok{display:block;background:#0d3a1a;color:#8dffb2}.status.err{display:block;background:#3a0d0d;color:#ff9d9d}
1012
- .row{display:flex;gap:8px;flex-wrap:wrap}
1013
  </style></head><body>
1014
- <div class="card"><h1>🤖 Umans2API 管理页</h1><p class="muted">Anthropic + OpenAI 兼容 · 适配 Claude Code</p><p class="muted">POST /v1/messages · POST /v1/chat/completions · GET /v1/models · GET /health</p><p class="muted">已保存状态:<span class="badge">__SAVED__</span></p></div>
1015
- <div class="card"><h2>Cookie 管理</h2><p class="muted">页面不会显示完整 cookie,只显示掩码。保存时会写入服务端。</p>
1016
- <label>__Host-authjs.csrf-token</label><input id="csrf" value="__CSRF__" placeholder="从浏览器 Cookie 复制"/>
1017
- <label>__Secure-authjs.session-token <span class="warn">(必填)</span></label><input id="session" value="__SESSION__" placeholder="从浏览器 Cookie 复制"/>
1018
- <div class="row"><button onclick="saveCookies()">💾 保存</button><button class="secondary" onclick="showHelp()">🧩 获取 Cookie 说明</button><button class="secondary" onclick="reloadMasked()">🔄 查看已保存状态</button></div>
1019
- <textarea id="maskedCookies" readonly>__MASKED__</textarea>
1020
- <label>测试对话</label><input id="testPrompt" value="你好,请回复 test ok" />
1021
- <div class="row"><button onclick="runTest()">🧪 测试对话</button><button class="danger" onclick="clearCookies()">🗑 清空 Cookie</button><a href="/" style="text-decoration:none"><button type="button" class="secondary">🚪 退出</button></a></div>
1022
- <div class="status" id="panelStatus"></div></div>
1023
- <script>
1024
- const ADMIN_PASSWORD = __PW_JSON__;
1025
- function hdrs(){return {"Content-Type":"application/json","X-Admin-Password":ADMIN_PASSWORD}}
1026
- function show(el,msg,ok){el.textContent=msg;el.className='status '+(ok?'ok':'err')}
1027
- function showHelp(){show(document.getElementById('panelStatus'),'获取方式:登录 app.umans.ai F12 → Application → Cookies → 复制 csrf-token 和 session-token。',true)}
1028
- async function saveCookies(){const csrf=document.getElementById('csrf').value.trim(); const session=document.getElementById('session').value.trim(); if(!session){show(document.getElementById('panelStatus'),'session-token 必填',false); return;} const cookies={"__Host-authjs.csrf-token":csrf,"__Secure-authjs.callback-url":"https%3A%2F%2Fapp.umans.ai%2F","__Secure-authjs.session-token":session}; try{const r=await fetch('/admin/cookies',{method:'POST',headers:hdrs(),body:JSON.stringify({cookies})}); 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) await reloadMasked();}catch(e){show(document.getElementById('panelStatus'),'❌ '+e.message,false)}}
1029
- async function reloadMasked(){try{const r=await fetch('/admin/cookies',{headers:hdrs()}); const t=await r.text(); let d={}; try{d=JSON.parse(t)}catch{d={raw:t}}; if(r.ok){document.getElementById('maskedCookies').value=JSON.stringify(d.cookies,null,2);} show(document.getElementById('panelStatus'), r.ok?'✅ 已读取当前状态':'❌ '+JSON.stringify(d), r.ok);}catch(e){show(document.getElementById('panelStatus'),'❌ '+e.message,false)}}
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('__PW_JSON__', json.dumps(pw, ensure_ascii=False))
 
 
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)