feat: 客户API Key管理 - 创建/更新/复制/删除功能
1. json/downapp.json: 添加更新和删除工具栏按钮,移除noedit限制 2. wwwroot/update_apikey.dspy: 查询并返回预填充的更新表单 3. wwwroot/do_update_apikey.dspy: 处理更新表单提交 4. wwwroot/delete_apikey.dspy: 删除API Key及关联应用 5. wwwroot/apikey_manage.ui: 独立API Key管理页面 6. scripts/load_path.py: 使用通配符%/dapi/%注册所有路径
This commit is contained in:
parent
0c184fd5fe
commit
c210f30322
@ -1,11 +1,10 @@
|
||||
{
|
||||
"tblname": "downapp",
|
||||
"title":"下位系统",
|
||||
"title":"API Key管理",
|
||||
"params": {
|
||||
"sortby":"id",
|
||||
"confidential_fields":["secretkey"],
|
||||
"logined_userorgid": "orgid",
|
||||
"noedit": true,
|
||||
"browserfields": {
|
||||
"exclouded": ["secretkey", "orgid" ],
|
||||
"alters": {}
|
||||
@ -17,14 +16,26 @@
|
||||
"tools":[
|
||||
{
|
||||
"name":"create_apikey",
|
||||
"label":"创建apikey",
|
||||
"label":"创建API Key",
|
||||
"icon":"{{entire_url('/imgs/newkey.svg')}}"
|
||||
},
|
||||
{
|
||||
"name":"copy_apikey",
|
||||
"label":"复制apikey",
|
||||
"label":"复制API Key",
|
||||
"selected_row":true,
|
||||
"icon":"{{entire_url('/imgs/copy.svg')}}"
|
||||
},
|
||||
{
|
||||
"name":"update_apikey",
|
||||
"label":"更新API Key",
|
||||
"selected_row":true,
|
||||
"icon":"{{entire_url('/imgs/edit.svg')}}"
|
||||
},
|
||||
{
|
||||
"name":"delete_apikey",
|
||||
"label":"删除API Key",
|
||||
"selected_row":true,
|
||||
"icon":"{{entire_url('/imgs/delete.svg')}}"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -56,6 +67,29 @@
|
||||
"options":{
|
||||
"url":"{{entire_url('/dapi/copy_apikey.dspy')}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"update_apikey",
|
||||
"actiontype":"urlwidget",
|
||||
"target":"PopupWindow",
|
||||
"popup_options":{
|
||||
"width":"50%",
|
||||
"height":"50%",
|
||||
"archor":"cr"
|
||||
},
|
||||
"options":{
|
||||
"url":"{{entire_url('/dapi/update_apikey.dspy')}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"delete_apikey",
|
||||
"actiontype":"urlwidget",
|
||||
"target":"self",
|
||||
"options":{
|
||||
"url":"{{entire_url('/dapi/delete_apikey.dspy')}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -19,13 +19,7 @@ from appPublic.uniqueID import getID
|
||||
|
||||
paths = [
|
||||
("/dapi", "logined"),
|
||||
("/dapi/create_apikey.ui", "logined"),
|
||||
("/dapi/apply_apikey.dspy", "logined"),
|
||||
("/dapi/copy_apikey.dspy", "logined"),
|
||||
("/dapi/create_apikey.dspy", "logined"),
|
||||
("/dapi/downapps.dspy", "logined"),
|
||||
("/dapi/get_apikey.dspy", "logined"),
|
||||
("/dapi/jumpin.dspy", "logined"),
|
||||
("/dapi/%", "logined"),
|
||||
]
|
||||
|
||||
|
||||
|
||||
103
wwwroot/apikey_manage.ui
Normal file
103
wwwroot/apikey_manage.ui
Normal file
@ -0,0 +1,103 @@
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"height": "100%"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "DataViewer",
|
||||
"options": {
|
||||
"title": "API Key管理",
|
||||
"data_url": "{{entire_url('/dapi/downapps.dspy')}}",
|
||||
"data_field": "apikeys",
|
||||
"pageSize": 20,
|
||||
"fields": [
|
||||
{"name": "id", "title": "应用ID", "width": "200px"},
|
||||
{"name": "name", "title": "应用名称", "width": "200px"},
|
||||
{"name": "apikeyid", "title": "API Key ID", "width": "200px"}
|
||||
],
|
||||
"toolbar": {
|
||||
"tools": [
|
||||
{
|
||||
"name": "create_apikey",
|
||||
"label": "创建API Key",
|
||||
"icon": "{{entire_url('/imgs/newkey.svg')}}"
|
||||
},
|
||||
{
|
||||
"name": "copy_apikey",
|
||||
"label": "复制API Key",
|
||||
"selected_row": true,
|
||||
"icon": "{{entire_url('/imgs/copy.svg')}}"
|
||||
},
|
||||
{
|
||||
"name": "update_apikey",
|
||||
"label": "更新API Key",
|
||||
"selected_row": true,
|
||||
"icon": "{{entire_url('/imgs/edit.svg')}}"
|
||||
},
|
||||
{
|
||||
"name": "delete_apikey",
|
||||
"label": "删除API Key",
|
||||
"selected_row": true,
|
||||
"icon": "{{entire_url('/imgs/delete.svg')}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "create_apikey",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "PopupWindow",
|
||||
"popup_options": {
|
||||
"width": "50%",
|
||||
"height": "50%",
|
||||
"archor": "cr"
|
||||
},
|
||||
"options": {
|
||||
"url": "{{entire_url('/dapi/create_apikey.ui')}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "copy_apikey",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "PopupWindow",
|
||||
"popup_options": {
|
||||
"width": "50%",
|
||||
"height": "50%",
|
||||
"archor": "cr"
|
||||
},
|
||||
"options": {
|
||||
"url": "{{entire_url('/dapi/copy_apikey.dspy')}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "update_apikey",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "PopupWindow",
|
||||
"popup_options": {
|
||||
"width": "50%",
|
||||
"height": "50%",
|
||||
"archor": "cr"
|
||||
},
|
||||
"options": {
|
||||
"url": "{{entire_url('/dapi/update_apikey.dspy')}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "delete_apikey",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "self",
|
||||
"options": {
|
||||
"url": "{{entire_url('/dapi/delete_apikey.dspy')}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
29
wwwroot/delete_apikey.dspy
Normal file
29
wwwroot/delete_apikey.dspy
Normal file
@ -0,0 +1,29 @@
|
||||
debug(f'{params_kw=}')
|
||||
dbname = get_module_dbname('dapi')
|
||||
db = DBPools()
|
||||
userid = await get_user()
|
||||
orgid = await get_userorgid()
|
||||
if not userid:
|
||||
return UiError(title='删除API Key', message='需要登录')
|
||||
|
||||
try:
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
# 验证权限
|
||||
ns = {
|
||||
"id": params_kw.id,
|
||||
"orgid": orgid
|
||||
}
|
||||
sql = """select * from downapp
|
||||
where id = ${id}$ and orgid = ${orgid}$"""
|
||||
recs = await sor.sqlExe(sql, ns)
|
||||
if not recs:
|
||||
return UiError(title='删除API Key', message='API Key不存在或无权访问')
|
||||
|
||||
# 删除downapikey
|
||||
await sor.D('downapikey', {"dappid": params_kw.id})
|
||||
# 删除downapp
|
||||
await sor.D('downapp', {"id": params_kw.id})
|
||||
|
||||
return UiMessage(title="删除API Key", message="API Key删除成功")
|
||||
except Exception as e:
|
||||
return UiError(title='删除API Key', message=f'删除失败: {e}')
|
||||
52
wwwroot/do_update_apikey.dspy
Normal file
52
wwwroot/do_update_apikey.dspy
Normal file
@ -0,0 +1,52 @@
|
||||
debug(f'{params_kw=}')
|
||||
dbname = get_module_dbname('dapi')
|
||||
db = DBPools()
|
||||
userid = await get_user()
|
||||
orgid = await get_userorgid()
|
||||
if not userid:
|
||||
return UiError(title='更新API Key', message='需要登录')
|
||||
|
||||
try:
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
# 验证权限
|
||||
ns = {
|
||||
"id": params_kw.id,
|
||||
"orgid": orgid
|
||||
}
|
||||
sql = """select * from downapp
|
||||
where id = ${id}$ and orgid = ${orgid}$"""
|
||||
recs = await sor.sqlExe(sql, ns)
|
||||
if not recs:
|
||||
return UiError(title='更新API Key', message='API Key不存在或无权访问')
|
||||
|
||||
# 更新downapp
|
||||
update_ns = {
|
||||
"id": params_kw.id,
|
||||
"name": params_kw.appname,
|
||||
"description": params_kw.description,
|
||||
"allowedips": params_kw.allowedips
|
||||
}
|
||||
await sor.U('downapp', update_ns)
|
||||
|
||||
# 如果需要重新生成apikey
|
||||
if params_kw.get('regenerate_apikey') == 'true':
|
||||
new_apikey = password_encode(uuid())
|
||||
keys = await sor.R('downapikey', {"dappid": params_kw.id})
|
||||
if keys:
|
||||
key_data = {"id": keys[0].id, "apikey": new_apikey}
|
||||
await sor.U('downapikey', key_data)
|
||||
|
||||
kw = {
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "dismissed",
|
||||
"actiontype": "script",
|
||||
"target": "app.sage_main_content",
|
||||
"script": "this.render()"
|
||||
}
|
||||
]
|
||||
}
|
||||
return UiMessage(title="更新API Key", message="API Key更新成功", **kw)
|
||||
except Exception as e:
|
||||
return UiError(title='更新API Key', message=f'更新失败: {e}')
|
||||
87
wwwroot/update_apikey.dspy
Normal file
87
wwwroot/update_apikey.dspy
Normal file
@ -0,0 +1,87 @@
|
||||
debug(f'{params_kw=}')
|
||||
dbname = get_module_dbname('dapi')
|
||||
db = DBPools()
|
||||
userid = await get_user()
|
||||
orgid = await get_userorgid()
|
||||
if not userid:
|
||||
return {
|
||||
"widgettype": "Text",
|
||||
"options": {"text": "需要登录"}
|
||||
}
|
||||
|
||||
try:
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
# 查询当前downapp信息
|
||||
ns = {
|
||||
"id": params_kw.id,
|
||||
"orgid": orgid
|
||||
}
|
||||
sql = """select * from downapp
|
||||
where id = ${id}$ and orgid = ${orgid}$"""
|
||||
recs = await sor.sqlExe(sql, ns)
|
||||
if not recs:
|
||||
return {
|
||||
"widgettype": "Text",
|
||||
"options": {"text": "API Key不存在或无权访问"}
|
||||
}
|
||||
|
||||
r = recs[0]
|
||||
# 返回预填充的表单
|
||||
return {
|
||||
"widgettype": "Form",
|
||||
"options": {
|
||||
"title": "更新API Key",
|
||||
"description": "更新应用信息和API Key配置",
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"label": "应用ID",
|
||||
"uitype": "str",
|
||||
"value": r.id,
|
||||
"readonly": True
|
||||
},
|
||||
{
|
||||
"name": "appname",
|
||||
"label": "应用名称",
|
||||
"uitype": "str",
|
||||
"value": r.name,
|
||||
"required": True
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"label": "应用描述",
|
||||
"uitype": "text",
|
||||
"value": r.description or ""
|
||||
},
|
||||
{
|
||||
"name": "allowedips",
|
||||
"label": "允许的IP集",
|
||||
"uitype": "str",
|
||||
"value": r.allowedips or "",
|
||||
"placeholder": "多个IP用逗号分隔"
|
||||
},
|
||||
{
|
||||
"name": "regenerate_apikey",
|
||||
"label": "重新生成API Key",
|
||||
"uitype": "checkbox",
|
||||
"description": "勾选后将重新生成API Key,原Key将失效"
|
||||
}
|
||||
]
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "submit",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "self",
|
||||
"options": {
|
||||
"url": "{{entire_url('/dapi/do_update_apikey.dspy')}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"widgettype": "Text",
|
||||
"options": {"text": f"错误: {e}"}
|
||||
}
|
||||
49
wwwroot/update_apikey.ui
Normal file
49
wwwroot/update_apikey.ui
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"widgettype":"Form",
|
||||
"options":{
|
||||
"title":"更新API Key",
|
||||
"description":"更新应用信息和API Key配置",
|
||||
"fields":[
|
||||
{
|
||||
"name":"id",
|
||||
"label":"应用ID",
|
||||
"uitype":"str",
|
||||
"readonly": true
|
||||
},
|
||||
{
|
||||
"name":"appname",
|
||||
"label":"应用名称",
|
||||
"uitype":"str",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name":"description",
|
||||
"label":"应用描述",
|
||||
"uitype":"text"
|
||||
},
|
||||
{
|
||||
"name":"allowedips",
|
||||
"label":"允许的IP集",
|
||||
"uitype":"str",
|
||||
"placeholder": "多个IP用逗号分隔"
|
||||
},
|
||||
{
|
||||
"name":"regenerate_apikey",
|
||||
"label":"重新生成API Key",
|
||||
"uitype":"checkbox",
|
||||
"description": "勾选后将重新生成API Key,原Key将失效"
|
||||
}
|
||||
]
|
||||
},
|
||||
"binds":[
|
||||
{
|
||||
"wid":"self",
|
||||
"event":"submit",
|
||||
"actiontype":"urlwidget",
|
||||
"target":"self",
|
||||
"options":{
|
||||
"url":"{{entire_url('./update_apikey.dspy')}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user