This commit is contained in:
yumoqing 2025-07-09 17:23:32 +08:00
parent 90423ea035
commit 0b484b0adf
5 changed files with 112 additions and 56 deletions

View File

@ -1,6 +1,30 @@
{ {
"tblname":"uapi", "tblname":"uapi",
"params":{ "params":{
"toolbar":{
"tools":[
{
"selected_row":true,
"name":"test",
"icon":"{{entire_url('/imgs/test.svg')}}",
"label": "api测试"
}
]
},
"binds":[
{
"wid":"self",
"event":"test",
"actiontype": "urlwidget",
"target":"PopupWindow",
"options":{
"methid":"POST",
"params":{},
"url":"{{entire_url('/uapi/uapi_test.ui')}}"
}
}
],
"title":"API", "title":"API",
"description":"API定义", "description":"API定义",
"sortby":"name", "sortby":"name",

View File

@ -11,7 +11,7 @@
"editexclouded":["id"], "editexclouded":["id"],
"subtables":[ "subtables":[
{ {
"field":"uapiid", "field":"usid",
"subtable": "uapi", "subtable": "uapi",
"title": "API" "title": "API"
} }

View File

@ -1,6 +1,26 @@
{ {
"tblname":"upappapis", "tblname":"upappapis",
"params":{ "params":{
"toolbar":{
"tools":[
{
"name":"test",
"icon":"{{entire_url('/imgs/test.svg')}}",
"label": "测试",
"selected_row":true
}
]
},
"binds":[
{
"wid":"self",
"event":"test",
"actiontype": "urlwidget",
"options":{
"url":"{{entire_url('/uapi/apitest.dspy')}}"
}
}
],
"title":"使用api集", "title":"使用api集",
"description":"此系统使用的API集", "description":"此系统使用的API集",
"sortby":"name", "sortby":"name",

View File

@ -4,10 +4,9 @@
"title":"上位系统密码", "title":"上位系统密码",
"logined_userorgid":"ownerid", "logined_userorgid":"ownerid",
"description":"上位系统密码", "description":"上位系统密码",
"sortby":"name", "confidential_fields":["apikey", "apipasswd" ],
"confidential_fields":["akikey", "userpwd" ],
"browserfields":{ "browserfields":{
"exclouded":["id"], "exclouded":["id", "ownerid"],
"alters":{} "alters":{}
}, },
"editexclouded":["id", "upappid", "ownerid"] "editexclouded":["id", "upappid", "ownerid"]

View File

@ -4,55 +4,68 @@ from traceback import format_exc
from functools import partial from functools import partial
from sqlor.dbpools import DBPools from sqlor.dbpools import DBPools
from appPublic.streamhttpclient import StreamHttpClient, liner from appPublic.streamhttpclient import StreamHttpClient, liner
from appPublic.dictObject import DictObject
from appPublic.myTE import MyTemplateEngine
from appPublic.log import debug, exception, error from appPublic.log import debug, exception, error
from ahserver.globalEnv import password_decode from ahserver.globalEnv import password_decode
from ahserver.serverenv import get_serverenv
def get_dbname(): def get_dbname():
f = get_serverenv('get_module_dbname') f = get_serverenv('get_module_dbname')
dbname = f('uapi') dbname = f('uapi')
return dbname return dbname
class UAPI(StreamHttpClient): class UAPI:
def __init__(self, appid, env={}): def __init__(self, env={}):
self.appid = appid
super().__init__()
self.te = MyTemplateEngine([], env=env) self.te = MyTemplateEngine([], env=env)
self.env = env self.env = env
self.auth_api = None self.auth_api = None
self.auth_ret = None self.auth_ret = None
async def tmplrender(self, tmplstr, params={}): async def rendertmpl(self, tmplstr, params={}):
if tmplstr is None: if tmplstr is None:
return None return None
ns = self.env.copy() ns = self.env.copy()
ns.update(params) ns.update(params)
te = MyTemplateEngine([], env=self.env) te = MyTemplateEngine([], env=self.env)
return te.renders(headerstmpl, ns) return te.renders(tmplstr, ns)
async def make_call(self, request, upapiid, callerid, async def cvt_upappapi2uapi(self, upappapiid, callerid, params={}):
params={}, self.env.update(params)
stream=False,
env={}):
self.env.update(env)
db = DBPools() db = DBPools()
dbname = get_dbname() dbname = get_dbname()
uapi = None uapi = None
auth_uapi = None auth_uapi = None
async with db.sqlorContext(dbname) as sor: async with db.sqlorContext(dbname) as sor:
upapi = await self.get_upappapis(upapiid) upappapi = await self.get_upappapis(sor, upappapiid)
kinfo = await self.get_orgapikey(sor, upapi.upappid, callerid) kinfo = await self.get_orgapikey(sor, upappapi.upappid, callerid)
self.env.update(kinfo) self.env.update(kinfo)
auth_uapi = None auth_uapi = None
if kinfo.auth_apiid: if kinfo.auth_apiid:
auth_upapi = await self.get_upappapis(kinfo.auth_apiid) auth_upapi = await self.get_upappapis(sor, kinfo.auth_apiid)
auth_uapi = await self.get_uapi(sor, kinfo.auth_upapi.uapiid) auth_uapi = await self.get_uapi(sor, kinfo.auth_upapi.uapiid)
uapi = await self.get_uapi(sor, upapi.uapiid) uapi = await self.get_uapi(sor, upappapi.uapiid)
return auth_uapi, uapi
return None, None
async def __call__(self, upapiid, callerid, params={}):
"""
"""
auth_uapi, uapi = await self.cvt_upappapi2uapi(upapiid,
callerid, params=params)
if uapi is None:
return
if auth_uapi: if auth_uapi:
await self.do_auth(auth_uapi) await self.do_auth(auth_uapi)
if stream: async for line in self.stream_gen(uapi):
return await self.do_stream_call(self, request, uapi, params=params) yield line
else:
return await self.do_call(self, request, uapi, params=params) async def request(self, upapiid, callerid, params={}):
auth_uapi, uapi = await self.cvt_upappapi2uapi(upapiid,
callerid, params=params)
if auth_uapi:
await self.do_auth(auth_uapi)
return await self.do_call(uapi)
async def do_auth(self, auth_uapi): async def do_auth(self, auth_uapi):
b = await self.do_call(auth_uapi) b = await self.do_call(auth_uapi)
@ -60,65 +73,65 @@ class UAPI(StreamHttpClient):
self.env.update(d) self.env.update(d)
return return
async def do_call(self, request, api, params={}): async def do_call(self, api, params={}):
url = self.env.get('baseurl') + api.path url = self.env.get('baseurl') + api.path
method = auth_uapi.httpmethod method = api.httpmethod
header = self.rendertmpl(api.headers, params=params) header = await self.rendertmpl(api.headers)
header = json.loads(headers) header = json.loads(headers)
body = self.rendertmpl(api.body, params=params) body = await self.rendertmpl(api.body)
_params = self.rendertmpl(api.params, params=params) _params = await self.rendertmpl(api.params)
if _params: if _params:
_params = json.loads(_params) _params = json.loads(_params)
b = await self.request(method, url, shc = StreamHttpClient()
b = await shc.request(method, url,
headers=headers, headers=headers,
body=body, data=body,
params=_params) params=_params)
d = json.loads(b.encode('utf-8')) d = json.loads(b.encode('utf-8'))
if api.response: if api.response:
return self.rendertmpl(api.response, params=d) return await self.rendertmpl(api.response, params=d)
return d return d
async def do_stream_call(self, request, api, params={}): async def stream_gen(self, api, params={}):
self.resp = web.StreamResponse()
await self.resp.prepare(request)
url = self.env.get('baseurl') + api.path url = self.env.get('baseurl') + api.path
method = auth_uapi.httpmethod method = api.httpmethod
header = self.rendertmpl(api.headers, params=params) headers = await self.rendertmpl(api.headers)
header = json.loads(headers) headers = json.loads(headers)
body = self.rendertmpl(api.body, params=params) body = await self.rendertmpl(api.body)
if body: if body:
bdy = json.loads(body) bdy = json.loads(body)
bdy['stream'] = True bdy['stream'] = True
body = bdy.dumps(bdy, ensure_ascii=False) body = bdy.dumps(bdy, ensure_ascii=False)
_params = self.rendertmpl(api.params, params=params) _params = await self.rendertmpl(api.params)
if _params: if _params:
_params = json.loads(_params) _params = json.loads(_params)
hc = StreamHttpClient() shc = StreamHttpClient()
gen = hc(method, url, gen = shc(method, url,
headers=headers, headers=headers,
body=body, data=body,
params=_params) params=_params)
self.chunk_match = self.api.chunk_match or '' chunk_match = api.chunk_match or ''
cmlen = len(chunk_match)
async for line in liner(gen): async for line in liner(gen):
self.streamline_handle(line) if line.startswith(chunk_match):
return self.resp cvt_line = self.streamline_handle(line[cmlen:],
api.streamresponse)
if cvt_line is not None:
yield cvt_line
async def streamline_handle(self, line, resptmpl): async def streamline_handle(self, line, resptmpl):
cnt = len(self.chunk_match)
line = line[cnt:]
try: try:
dic = json.loads(line) dic = json.loads(line)
if resptmpl: if resptmpl:
jstr = self.rendertmpl(resptmpl, dic) + '\n' jstr = await self.rendertmpl(resptmpl, params=dic)
jstr += '\n'
else: else:
jstr = json.dumps(self.resp_json, ensure_ascii=False) + '\n' jstr = json.dumps(self.resp_json, ensure_ascii=False) + '\n'
bin = jstr.encode('utf-8') return jstr
await self.resp.write(bin)
await self.resp.drain()
except Exception as e: except Exception as e:
exception(f'{line=}\n{e=},{format_exc()}') exception(f'{line=}\n{e=},{format_exc()}')
return return None
async def get_upappapis(self, sor, upapiid): async def get_upappapis(self, sor, upapiid):
recs = await sor.R('upappapis', {'id': upapiid}) recs = await sor.R('upappapis', {'id': upapiid})
@ -150,7 +163,7 @@ a.myappid,
a.apisetid, a.apisetid,
a.auth_apiid, a.auth_apiid,
a.baseurl, a.baseurl,
b.*, b.*
from upapp a, upappkey b from upapp a, upappkey b
where a.id = b.upappid where a.id = b.upappid
and a.id = ${appid}$ and a.id = ${appid}$
@ -161,13 +174,13 @@ where a.id = b.upappid
exception(f'{e}, {format_exc()}') exception(f'{e}, {format_exc()}')
raise e raise e
r = recs[0] r = recs[0]
return { return DictObject(**{
'apikey':r.apikey, 'apikey':r.apikey,
'secretkey':r.secretkey, 'secretkey':r.secretkey,
'apisetid': r.apisetid, 'apisetid': r.apisetid,
'auth_apiid': r.auth.apiid, 'auth_apiid': r.auth_apiid,
'myappid': r.myappid 'myappid': r.myappid
} })
async def get_authapi(self, sor): async def get_authapi(self, sor):
sql = "select * from jsonhttpapi where name = 'auth' and appid=${appid}$" sql = "select * from jsonhttpapi where name = 'auth' and appid=${appid}$"