138 lines
4.8 KiB
Python
138 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
One-shot: create a dedicated NewAPI sk- token for the downstream
|
|
"蜂群程序" (swarm bee program) and print the base URL + key.
|
|
|
|
Why direct DB INSERT instead of the /api/token POST endpoint:
|
|
- that endpoint requires a logged-in admin session cookie we
|
|
don't have a clean way to mint headless;
|
|
- the token table shape is stable (new-api fork has tracked
|
|
these columns for two years).
|
|
|
|
Safety:
|
|
- Attaches the token to the platform owner user (whose email
|
|
we look up first).
|
|
- Marks UnlimitedQuota=true so this token consumes user-row
|
|
quota (no separate billing surface to manage).
|
|
- HideFromUserUI=false so the operator can revoke it from the
|
|
/keys page if needed.
|
|
- Sets a descriptive Name + Remark per the user's "备注好"
|
|
requirement.
|
|
"""
|
|
import os
|
|
import sys
|
|
import secrets
|
|
import string
|
|
|
|
import paramiko
|
|
|
|
HOST = "20.24.50.121"
|
|
USER = "heicode"
|
|
PASS = os.environ.get("MGR_PASS", "")
|
|
if not PASS:
|
|
print("MGR_PASS not set", file=sys.stderr)
|
|
sys.exit(2)
|
|
|
|
DB_PASS = "Myadmin@123456."
|
|
DB_HOST = "heicode.postgres.database.azure.com"
|
|
DB_USER = "heicode"
|
|
DB_NAME = "heicode"
|
|
|
|
# new-api token keys are 48-char alphanum (no padding). Match that
|
|
# exactly so admin tools / dashboards parse it.
|
|
ALPHABET = string.ascii_letters + string.digits
|
|
RAW_KEY = "".join(secrets.choice(ALPHABET) for _ in range(48))
|
|
# Token bearer format on the wire is "sk-<48 chars>".
|
|
|
|
SQL_FIND = (
|
|
"SELECT id, username, email FROM users "
|
|
"WHERE email IN ('zsbgnw@gmail.com','chenchen@xinghanlab.com') "
|
|
" OR username IN ('chenchen','root') "
|
|
" OR role >= 10 "
|
|
"ORDER BY role DESC, id ASC LIMIT 1;"
|
|
)
|
|
|
|
INSERT_SQL_TEMPLATE = (
|
|
"INSERT INTO tokens "
|
|
'(user_id, name, "key", status, created_time, accessed_time, '
|
|
"expired_time, remain_quota, used_quota, unlimited_quota, "
|
|
"model_limits_enabled, model_limits, allow_ips, "
|
|
'"group", cross_group_retry, hide_from_user_ui) '
|
|
"VALUES "
|
|
"({user_id}, '蜂群程序 (Swarm Bot)', '{key}', 1, "
|
|
"EXTRACT(EPOCH FROM NOW())::bigint, EXTRACT(EPOCH FROM NOW())::bigint, "
|
|
"-1, 0, 0, true, false, '', '', '', false, false) "
|
|
"RETURNING id, name, \"key\";"
|
|
)
|
|
|
|
|
|
def main() -> None:
|
|
c = paramiko.SSHClient()
|
|
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
c.connect(HOST, 22, USER, PASS, look_for_keys=False, allow_agent=False, timeout=30)
|
|
|
|
def psql(sql: str) -> str:
|
|
# Pipe SQL via stdin so quoted identifiers like "group" survive
|
|
# without shell-escape hell.
|
|
cmd = (
|
|
f"PGPASSWORD='{DB_PASS}' psql "
|
|
f"'sslmode=require host={DB_HOST} user={DB_USER} dbname={DB_NAME}' "
|
|
f"-At -F'|' -v ON_ERROR_STOP=1"
|
|
)
|
|
stdin, stdout, stderr = c.exec_command(cmd, timeout=60, get_pty=False)
|
|
stdin.write(sql)
|
|
stdin.channel.shutdown_write()
|
|
out = stdout.read().decode("utf-8", "replace").strip()
|
|
err = stderr.read().decode("utf-8", "replace").strip()
|
|
if err:
|
|
print(f"[psql stderr] {err}", file=sys.stderr)
|
|
return out
|
|
|
|
# Step 1: find owner user
|
|
print(f"--- Looking up platform owner user ---", flush=True)
|
|
owner_row = psql(SQL_FIND)
|
|
if not owner_row:
|
|
print("ERROR: no owner user found", file=sys.stderr)
|
|
sys.exit(3)
|
|
fields = owner_row.split("|")
|
|
owner_id = int(fields[0])
|
|
owner_username = fields[1] if len(fields) > 1 else ""
|
|
owner_email = fields[2] if len(fields) > 2 else ""
|
|
print(f"Owner: id={owner_id} username={owner_username} email={owner_email}")
|
|
|
|
# Step 2: insert the token row
|
|
print(f"\n--- Inserting dedicated 蜂群程序 token ---", flush=True)
|
|
insert_sql = INSERT_SQL_TEMPLATE.format(user_id=owner_id, key=RAW_KEY)
|
|
inserted = psql(insert_sql)
|
|
if not inserted:
|
|
print("ERROR: INSERT returned nothing", file=sys.stderr)
|
|
sys.exit(4)
|
|
fields = inserted.split("|")
|
|
token_id = int(fields[0])
|
|
token_name = fields[1]
|
|
token_key = fields[2]
|
|
print(f"Token row inserted: id={token_id} name={token_name}")
|
|
|
|
# Step 3: print result
|
|
base_url = "https://code.xinghanlab.com"
|
|
print("\n" + "=" * 60)
|
|
print("NEWAPI CREDENTIAL — 蜂群程序 (SWARM BOT)")
|
|
print("=" * 60)
|
|
print(f"Base URL (OpenAI compat): {base_url}/v1")
|
|
print(f"Base URL (Anthropic compat): {base_url}/v1/messages")
|
|
print(f"Base URL (raw root): {base_url}")
|
|
print(f"")
|
|
print(f"API Key (Authorization: Bearer ...):")
|
|
print(f" sk-{token_key}")
|
|
print(f"")
|
|
print(f"Token row id: {token_id}")
|
|
print(f"Owner user id: {owner_id} ({owner_email})")
|
|
print(f"Quota model: unlimited (consumes the owner-account quota)")
|
|
print(f"Expires: never")
|
|
print(f"Manage / revoke from: {base_url}/keys")
|
|
print("=" * 60)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|