# 管理端:创建对外 API Key(登录用户;org 取自会话) # POST /rag/knowledge_bases_list/api_key_create.dspy # params: {name, scopes?, expires_days?} # 返回 widget:一次性展示明文 key(库里只存 SHA256) import secrets from rag import api_core as C ns = params_kw.copy() env = request._run_ns org_id = await env.get_userorgid() name = (ns.get('name') or '默认密钥').strip() scopes = ns.get('scopes') or '' if isinstance(scopes, (list, tuple)): scopes = ','.join(str(x) for x in scopes) scopes = str(scopes).strip() or 'kb.create,kb.delete,doc.upload,doc.delete,tag.write,search' try: exp_days = int(ns.get('expires_days') or 0) except (TypeError, ValueError): exp_days = 0 plain = 'rag-' + secrets.token_hex(24) kid = str(uuid()).replace('-', '')[:16] db = DBPools() async with db.sqlorContext(get_module_dbname('rag')) as sor: if exp_days > 0: await sor.sqlExe( "INSERT INTO rag_api_keys (id, org_id, name, key_hash, prefix, scopes, status, expires_at, created_at) " "VALUES (${id}$, ${o}$, ${n}$, ${h}$, ${p}$, ${s}$, 'active', DATE_ADD(NOW(), INTERVAL ${d}$ DAY), NOW())", {"id": kid, "o": org_id, "n": name, "h": C.hash_key(plain), "p": plain[:12], "s": scopes, "d": exp_days}) else: await sor.sqlExe( "INSERT INTO rag_api_keys (id, org_id, name, key_hash, prefix, scopes, status, created_at) " "VALUES (${id}$, ${o}$, ${n}$, ${h}$, ${p}$, ${s}$, 'active', NOW())", {"id": kid, "o": org_id, "n": name, "h": C.hash_key(plain), "p": plain[:12], "s": scopes}) await sor.sqlExe("COMMIT", {}) return {"widgettype": "VBox", "options": {"padding": "12px", "spacing": "8px"}, "subwidgets": [ {"widgettype": "Text", "options": {"text": "✅ API Key 已创建(仅此一次展示,请立即保存)", "cfontsize": 15, "color": "#10b981"}}, {"widgettype": "Text", "options": {"text": plain, "cfontsize": 13, "bgcolor": "#f5f7fa", "padding": "8px", "css": "selectable monospace"}}, {"widgettype": "Text", "options": {"text": "名称: " + name + " · 范围: " + scopes, "cfontsize": 12, "color": "#888"}}, ]}