bugfix
This commit is contained in:
commit
59e0870f89
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# CRUD definition directories (auto-generated by Sage platform)
|
||||||
|
wwwroot/uapi/
|
||||||
|
wwwroot/uapiio/
|
||||||
|
wwwroot/upapp/
|
||||||
|
wwwroot/upappkey/
|
||||||
|
__pycache__/
|
||||||
31
README.md
31
README.md
@ -397,6 +397,37 @@ uapi
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 在 Sage 系统中的角色
|
||||||
|
|
||||||
|
uapi 是 Sage 平台的**配置化 API 网关层**,llmage(大模型管理模块)是其主要消费者。两者的协同关系:
|
||||||
|
|
||||||
|
```
|
||||||
|
llmage (模型管理) uapi (API 网关)
|
||||||
|
│ │
|
||||||
|
│ llm 表 │
|
||||||
|
│ upappid ──────────────→│ upapp 表 (baseurl, myappid, ownerid)
|
||||||
|
│ apiname ──────────────→│ uapi 表 (httpmethod, path, headers, ...)
|
||||||
|
│ │ uapiset 表 (auth_apiname)
|
||||||
|
│ │ upappkey 表 (apikey, secretkey)
|
||||||
|
│ │
|
||||||
|
│ UpAppApi(request) │
|
||||||
|
│ .stream_linify() ─────→│ StreamHttpClient → 外部 LLM API
|
||||||
|
│ .call() ──────────────→│ 同步/流式 HTTP 调用
|
||||||
|
│ │
|
||||||
|
```
|
||||||
|
|
||||||
|
**新增一个 LLM 的完整流程**:
|
||||||
|
1. 在 uapi 模块的 `uapiset` 中创建 API 集合(配置认证方式)
|
||||||
|
2. 在 `upapp` 中注册上位系统(baseurl、appkey 等)
|
||||||
|
3. 在 `uapi` 中定义具体的 API 端点(path、method、headers 模板、response 模板)
|
||||||
|
4. 在 `upappkey` 中分配 API 密钥给调用方
|
||||||
|
5. 在 llmage 模块的 `llm` 表中注册模型,关联 `upappid` + `apiname`
|
||||||
|
6. 用户在 llmage 前端页面点击模型卡片 → 推理 → 通过 uapi 网关调用外部 API
|
||||||
|
|
||||||
|
**优势**:新增模型无需修改 Python 代码,只需在数据库/CRUD 页面中配置 API 定义。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 开发注意事项
|
## 开发注意事项
|
||||||
|
|
||||||
1. **dbname 获取**:必须通过 `get_serverenv('get_module_dbname')('uapi')` 动态获取,禁止硬编码
|
1. **dbname 获取**:必须通过 `get_serverenv('get_module_dbname')('uapi')` 动态获取,禁止硬编码
|
||||||
|
|||||||
74
i18n/en/msg.txt
Normal file
74
i18n/en/msg.txt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
API: API
|
||||||
|
APIKEY: APIKEY
|
||||||
|
API接口: API Interface
|
||||||
|
API标题: API Title
|
||||||
|
API输入输出: API Input/Output
|
||||||
|
API集: API Collection
|
||||||
|
API集名称: API Collection Name
|
||||||
|
Add Error: Add Error
|
||||||
|
Add Success: Add Success
|
||||||
|
Authorization Error: Authorization Error
|
||||||
|
Cancel: Cancel
|
||||||
|
Conform: Confirm
|
||||||
|
Delete Error: Delete Error
|
||||||
|
Delete Success: Delete Success
|
||||||
|
Discard: Discard
|
||||||
|
Please login: Please login
|
||||||
|
Record no exist or with wrong ownership: Record no exist or with wrong ownership
|
||||||
|
Reset: Reset
|
||||||
|
Submit: Submit
|
||||||
|
Update Error: Update Error
|
||||||
|
Update Success: Update Success
|
||||||
|
api名称: API Name
|
||||||
|
api密码: API Password
|
||||||
|
api密钥: API Secret
|
||||||
|
api测试: API Test
|
||||||
|
api用户: API User
|
||||||
|
api集id: API Collection ID
|
||||||
|
failed: failed
|
||||||
|
headers模版: Headers Template
|
||||||
|
http方法: HTTP Method
|
||||||
|
id: id
|
||||||
|
ok: ok
|
||||||
|
path: path
|
||||||
|
上位应用名: Parent Application Name
|
||||||
|
上位系统: Parent System
|
||||||
|
上位系统ID: Parent System ID
|
||||||
|
上位系统密码: Parent System Password
|
||||||
|
上位系统密码表: Parent System Password Table
|
||||||
|
上游任务: Upstream Task
|
||||||
|
加密密钥: Encryption Key
|
||||||
|
动态headers函数: Dynamic Headers Function
|
||||||
|
参数模版: Parameter Template
|
||||||
|
响应模版: Response Template
|
||||||
|
响应转换函数名: Response Transform Function Name
|
||||||
|
回调url: Callback URL
|
||||||
|
外部系统: External System
|
||||||
|
属主id: Owner ID
|
||||||
|
属主机构id: Owner Organization ID
|
||||||
|
应用id: Application ID
|
||||||
|
开始时间: Start Time
|
||||||
|
我的appid: My App ID
|
||||||
|
所属机构: Organization
|
||||||
|
执行方任务id: Executor Task ID
|
||||||
|
授权api名: Authorized API Name
|
||||||
|
接口描述: Interface Description
|
||||||
|
描述: Description
|
||||||
|
数据模版: Data Template
|
||||||
|
是否第一用户: Is First User
|
||||||
|
本地业务id: Local Business ID
|
||||||
|
流式匹配串: Stream Match String
|
||||||
|
流式输出: Stream Output
|
||||||
|
状态: Status
|
||||||
|
用户id: User ID
|
||||||
|
相应数据: Response Data
|
||||||
|
类型名: Type Name
|
||||||
|
类型说明: Type Description
|
||||||
|
系统url: System URL
|
||||||
|
结束时间: End Time
|
||||||
|
认证API名: Auth API Name
|
||||||
|
跳转到: Jump to
|
||||||
|
输入字段: Input Field
|
||||||
|
输入输出: Input/Output
|
||||||
|
输入输出id: Input/Output ID
|
||||||
|
需要鉴权: Requires Auth
|
||||||
74
i18n/jp/msg.txt
Normal file
74
i18n/jp/msg.txt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
API: API
|
||||||
|
APIKEY: APIKEY
|
||||||
|
API接口: APIインターフェース
|
||||||
|
API标题: APIタイトル
|
||||||
|
API输入输出: API入出力
|
||||||
|
API集: APIコレクション
|
||||||
|
API集名称: APIコレクション名
|
||||||
|
Add Error: 追加エラー
|
||||||
|
Add Success: 追加成功
|
||||||
|
Authorization Error: 認証エラー
|
||||||
|
Cancel: キャンセル
|
||||||
|
Conform: 確認
|
||||||
|
Delete Error: 削除エラー
|
||||||
|
Delete Success: 削除成功
|
||||||
|
Discard: 破棄
|
||||||
|
Please login: ログインしてください
|
||||||
|
Record no exist or with wrong ownership: レコードが存在しないか、所有権が不正です
|
||||||
|
Reset: リセット
|
||||||
|
Submit: 送信
|
||||||
|
Update Error: 更新エラー
|
||||||
|
Update Success: 更新成功
|
||||||
|
api名称: API名称
|
||||||
|
api密码: APIパスワード
|
||||||
|
api密钥: APIシークレット
|
||||||
|
api测试: APIテスト
|
||||||
|
api用户: APIユーザー
|
||||||
|
api集id: APIコレクションID
|
||||||
|
failed: 失敗
|
||||||
|
headers模版: ヘッダーテンプレート
|
||||||
|
http方法: HTTPメソッド
|
||||||
|
id: id
|
||||||
|
ok: ok
|
||||||
|
path: path
|
||||||
|
上位应用名: 上位アプリケーション名
|
||||||
|
上位系统: 上位システム
|
||||||
|
上位系统ID: 上位システムID
|
||||||
|
上位系统密码: 上位システムパスワード
|
||||||
|
上位系统密码表: 上位システムパスワード表
|
||||||
|
上游任务: 上流タスク
|
||||||
|
加密密钥: 暗号化キー
|
||||||
|
动态headers函数: 動的ヘッダー関数
|
||||||
|
参数模版: パラメーターテンプレート
|
||||||
|
响应模版: レスポンステンプレート
|
||||||
|
响应转换函数名: レスポンス変換関数名
|
||||||
|
回调url: コールバックURL
|
||||||
|
外部系统: 外部システム
|
||||||
|
属主id: 所有者ID
|
||||||
|
属主机构id: 所有者組織ID
|
||||||
|
应用id: アプリケーションID
|
||||||
|
开始时间: 開始時間
|
||||||
|
我的appid: マイAppID
|
||||||
|
所属机构: 所属組織
|
||||||
|
执行方任务id: 実行タスクID
|
||||||
|
授权api名: 認可API名
|
||||||
|
接口描述: インターフェース説明
|
||||||
|
描述: 説明
|
||||||
|
数据模版: データテンプレート
|
||||||
|
是否第一用户: 最初のユーザーか
|
||||||
|
本地业务id: ローカル業務ID
|
||||||
|
流式匹配串: ストリームマッチ文字列
|
||||||
|
流式输出: ストリーム出力
|
||||||
|
状态: ステータス
|
||||||
|
用户id: ユーザーID
|
||||||
|
相应数据: 対応データ
|
||||||
|
类型名: 型名
|
||||||
|
类型说明: 型説明
|
||||||
|
系统url: システムURL
|
||||||
|
结束时间: 終了時間
|
||||||
|
认证API名: 認証API名
|
||||||
|
跳转到: ジャンプ先
|
||||||
|
输入字段: 入力フィールド
|
||||||
|
输入输出: 入出力
|
||||||
|
输入输出id: 入出力ID
|
||||||
|
需要鉴权: 認証が必要
|
||||||
74
i18n/ko/msg.txt
Normal file
74
i18n/ko/msg.txt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
API: API
|
||||||
|
APIKEY: APIKEY
|
||||||
|
API接口: API 인터페이스
|
||||||
|
API标题: API 제목
|
||||||
|
API输入输出: API 입출력
|
||||||
|
API集: API 컬렉션
|
||||||
|
API集名称: API 컬렉션 이름
|
||||||
|
Add Error: 추가 오류
|
||||||
|
Add Success: 추가 성공
|
||||||
|
Authorization Error: 인증 오류
|
||||||
|
Cancel: 취소
|
||||||
|
Conform: 확인
|
||||||
|
Delete Error: 삭제 오류
|
||||||
|
Delete Success: 삭제 성공
|
||||||
|
Discard: 폐기
|
||||||
|
Please login: 로그인해주세요
|
||||||
|
Record no exist or with wrong ownership: 레코드가 존재하지 않거나 소유권이 잘못되었습니다
|
||||||
|
Reset: 초기화
|
||||||
|
Submit: 제출
|
||||||
|
Update Error: 업데이트 오류
|
||||||
|
Update Success: 업데이트 성공
|
||||||
|
api名称: API 이름
|
||||||
|
api密码: API 비밀번호
|
||||||
|
api密钥: API 비밀키
|
||||||
|
api测试: API 테스트
|
||||||
|
api用户: API 사용자
|
||||||
|
api集id: API 컬렉션 ID
|
||||||
|
failed: 실패
|
||||||
|
headers模版: 헤더 템플릿
|
||||||
|
http方法: HTTP 메서드
|
||||||
|
id: id
|
||||||
|
ok: ok
|
||||||
|
path: path
|
||||||
|
上位应用名: 상위 애플리케이션 이름
|
||||||
|
上位系统: 상위 시스템
|
||||||
|
上位系统ID: 상위 시스템 ID
|
||||||
|
上位系统密码: 상위 시스템 비밀번호
|
||||||
|
上位系统密码表: 상위 시스템 비밀번호 테이블
|
||||||
|
上游任务: 업스트림 작업
|
||||||
|
加密密钥: 암호화 키
|
||||||
|
动态headers函数: 동적 헤더 함수
|
||||||
|
参数模版: 파라미터 템플릿
|
||||||
|
响应模版: 응답 템플릿
|
||||||
|
响应转换函数名: 응답 변환 함수 이름
|
||||||
|
回调url: 콜백 URL
|
||||||
|
外部系统: 외부 시스템
|
||||||
|
属主id: 소유자 ID
|
||||||
|
属主机构id: 소유자 조직 ID
|
||||||
|
应用id: 애플리케이션 ID
|
||||||
|
开始时间: 시작 시간
|
||||||
|
我的appid: 내 App ID
|
||||||
|
所属机构: 소속 조직
|
||||||
|
执行方任务id: 실행자 작업 ID
|
||||||
|
授权api名: 인증된 API 이름
|
||||||
|
接口描述: 인터페이스 설명
|
||||||
|
描述: 설명
|
||||||
|
数据模版: 데이터 템플릿
|
||||||
|
是否第一用户: 첫 번째 사용자 여부
|
||||||
|
本地业务id: 로컬 비즈니스 ID
|
||||||
|
流式匹配串: 스트림 매칭 문자열
|
||||||
|
流式输出: 스트림 출력
|
||||||
|
状态: 상태
|
||||||
|
用户id: 사용자 ID
|
||||||
|
相应数据: 응답 데이터
|
||||||
|
类型名: 타입 이름
|
||||||
|
类型说明: 타입 설명
|
||||||
|
系统url: 시스템 URL
|
||||||
|
结束时间: 종료 시간
|
||||||
|
认证API名: 인증 API 이름
|
||||||
|
跳转到: 이동
|
||||||
|
输入字段: 입력 필드
|
||||||
|
输入输出: 입출력
|
||||||
|
输入输出id: 입출력 ID
|
||||||
|
需要鉴权: 인증 필요
|
||||||
74
i18n/zh/msg.txt
Normal file
74
i18n/zh/msg.txt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
API: API
|
||||||
|
APIKEY: APIKEY
|
||||||
|
API接口: API接口
|
||||||
|
API标题: API标题
|
||||||
|
API输入输出: API输入输出
|
||||||
|
API集: API集
|
||||||
|
API集名称: API集名称
|
||||||
|
Add Error: Add Error
|
||||||
|
Add Success: Add Success
|
||||||
|
Authorization Error: Authorization Error
|
||||||
|
Cancel: Cancel
|
||||||
|
Conform: Conform
|
||||||
|
Delete Error: Delete Error
|
||||||
|
Delete Success: Delete Success
|
||||||
|
Discard: Discard
|
||||||
|
Please login: Please login
|
||||||
|
Record no exist or with wrong ownership: Record no exist or with wrong ownership
|
||||||
|
Reset: Reset
|
||||||
|
Submit: Submit
|
||||||
|
Update Error: Update Error
|
||||||
|
Update Success: Update Success
|
||||||
|
api名称: api名称
|
||||||
|
api密码: api密码
|
||||||
|
api密钥: api密钥
|
||||||
|
api测试: api测试
|
||||||
|
api用户: api用户
|
||||||
|
api集id: api集id
|
||||||
|
failed: failed
|
||||||
|
headers模版: headers模版
|
||||||
|
http方法: http方法
|
||||||
|
id: id
|
||||||
|
ok: ok
|
||||||
|
path: path
|
||||||
|
上位应用名: 上位应用名
|
||||||
|
上位系统: 上位系统
|
||||||
|
上位系统ID: 上位系统ID
|
||||||
|
上位系统密码: 上位系统密码
|
||||||
|
上位系统密码表: 上位系统密码表
|
||||||
|
上游任务: 上游任务
|
||||||
|
加密密钥: 加密密钥
|
||||||
|
动态headers函数: 动态headers函数
|
||||||
|
参数模版: 参数模版
|
||||||
|
响应模版: 响应模版
|
||||||
|
响应转换函数名: 响应转换函数名
|
||||||
|
回调url: 回调url
|
||||||
|
外部系统: 外部系统
|
||||||
|
属主id: 属主id
|
||||||
|
属主机构id: 属主机构id
|
||||||
|
应用id: 应用id
|
||||||
|
开始时间: 开始时间
|
||||||
|
我的appid: 我的appid
|
||||||
|
所属机构: 所属机构
|
||||||
|
执行方任务id: 执行方任务id
|
||||||
|
授权api名: 授权api名
|
||||||
|
接口描述: 接口描述
|
||||||
|
描述: 描述
|
||||||
|
数据模版: 数据模版
|
||||||
|
是否第一用户: 是否第一用户
|
||||||
|
本地业务id: 本地业务id
|
||||||
|
流式匹配串: 流式匹配串
|
||||||
|
流式输出: 流式输出
|
||||||
|
状态: 状态
|
||||||
|
用户id: 用户id
|
||||||
|
相应数据: 相应数据
|
||||||
|
类型名: 类型名
|
||||||
|
类型说明: 类型说明
|
||||||
|
系统url: 系统url
|
||||||
|
结束时间: 结束时间
|
||||||
|
认证API名: 认证API名
|
||||||
|
跳转到: 跳转到
|
||||||
|
输入字段: 输入字段
|
||||||
|
输入输出: 输入输出
|
||||||
|
输入输出id: 输入输出id
|
||||||
|
需要鉴权: 需要鉴权
|
||||||
@ -27,11 +27,11 @@
|
|||||||
|
|
||||||
"title":"API",
|
"title":"API",
|
||||||
"description":"API定义",
|
"description":"API定义",
|
||||||
"sortby":["apisetid", "name"],
|
"sortby":"name",
|
||||||
"browserfields":{
|
"browserfields":{
|
||||||
"exclouded":["id", "apisetid"],
|
"exclouded":["id"],
|
||||||
"alters":{}
|
"alters":{}
|
||||||
},
|
},
|
||||||
"editexclouded":["id", "apisetid"]
|
"editexclouded":["id","upappid"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,12 @@
|
|||||||
"title":"API输入输出",
|
"title":"API输入输出",
|
||||||
"description":"API的输入输出定义",
|
"description":"API的输入输出定义",
|
||||||
"sortby": "name",
|
"sortby": "name",
|
||||||
|
"data_filter":{
|
||||||
|
"fields":[
|
||||||
|
{"field":"name","title":"类型名","uitype":"str"},
|
||||||
|
{"field":"description","title":"类型说明","uitype":"str"}
|
||||||
|
]
|
||||||
|
},
|
||||||
"browserfields":{
|
"browserfields":{
|
||||||
"exclouded":["id"],
|
"exclouded":["id"],
|
||||||
"alters":{}
|
"alters":{}
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"tblname":"uapiset",
|
|
||||||
"params":{
|
|
||||||
"title":"API集",
|
|
||||||
"sortby":"name",
|
|
||||||
"browserfields":{
|
|
||||||
"exclouded":["id" ],
|
|
||||||
"alters":{}
|
|
||||||
},
|
|
||||||
"editexclouded":["id"],
|
|
||||||
"subtables":[
|
|
||||||
{
|
|
||||||
"field":"apisetid",
|
|
||||||
"subtable":"uapi",
|
|
||||||
"title":"定义api"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -16,27 +16,12 @@
|
|||||||
"field":"upappid",
|
"field":"upappid",
|
||||||
"subtable": "upappkey",
|
"subtable": "upappkey",
|
||||||
"title": "APIKEY"
|
"title": "APIKEY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field":"upappid",
|
||||||
|
"subtable": "uapi",
|
||||||
|
"title": "API"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"toolbar":{
|
|
||||||
"tools":[
|
|
||||||
{
|
|
||||||
"name":"jumpin",
|
|
||||||
"label":"跳转到",
|
|
||||||
"selected_data": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"binds":[
|
|
||||||
{
|
|
||||||
"wid":"self",
|
|
||||||
"event":"jumpin",
|
|
||||||
"actiontype":"urlwidget",
|
|
||||||
"target":"self",
|
|
||||||
"options":{
|
|
||||||
"url":"{{entire_url('/uapi/jump_in.dspy')}}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
153
models/uapi.json
Normal file
153
models/uapi.json
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
{
|
||||||
|
"summary": [
|
||||||
|
{
|
||||||
|
"name": "uapi",
|
||||||
|
"title": "API接口",
|
||||||
|
"primary": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"catelog": "entity"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"title": "id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "upappid",
|
||||||
|
"title": "上位系统ID",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"title": "api名称",
|
||||||
|
"type": "str",
|
||||||
|
"length": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "title",
|
||||||
|
"title": "API标题",
|
||||||
|
"type": "str",
|
||||||
|
"length": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"title": "接口描述",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "need_auth",
|
||||||
|
"title": "需要鉴权",
|
||||||
|
"type": "str",
|
||||||
|
"length": 1,
|
||||||
|
"default": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stream",
|
||||||
|
"title": "流式输出",
|
||||||
|
"type": "str",
|
||||||
|
"length": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "path",
|
||||||
|
"title": "path",
|
||||||
|
"type": "str",
|
||||||
|
"length": 4000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "httpmethod",
|
||||||
|
"title": "http方法",
|
||||||
|
"type": "str",
|
||||||
|
"length": 20,
|
||||||
|
"nullable": "yes",
|
||||||
|
"default": "GET"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "chunk_match",
|
||||||
|
"title": "流式匹配串",
|
||||||
|
"type": "str",
|
||||||
|
"length": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "headers",
|
||||||
|
"title": "headers模版",
|
||||||
|
"type": "text",
|
||||||
|
"nullable": "yes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "params",
|
||||||
|
"title": "参数模版",
|
||||||
|
"type": "text",
|
||||||
|
"nullable": "yes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "data",
|
||||||
|
"title": "数据模版",
|
||||||
|
"type": "text",
|
||||||
|
"nullable": "yes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "response",
|
||||||
|
"title": "响应模版",
|
||||||
|
"type": "text",
|
||||||
|
"nullable": "yes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ioid",
|
||||||
|
"title": "输入输出id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32,
|
||||||
|
"nullable": "yes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "callbackurl",
|
||||||
|
"title": "回调url",
|
||||||
|
"type": "str",
|
||||||
|
"length": 1000,
|
||||||
|
"nullable": "yes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
{
|
||||||
|
"name": "idx1",
|
||||||
|
"idxtype": "unique",
|
||||||
|
"idxfields": [
|
||||||
|
"upappid",
|
||||||
|
"name"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"codes": [
|
||||||
|
{
|
||||||
|
"field": "httpmethod",
|
||||||
|
"table": "appcodes_kv",
|
||||||
|
"valuefield": "k",
|
||||||
|
"textfield": "v",
|
||||||
|
"cond": "parentid='httpmethod'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "need_auth",
|
||||||
|
"table": "appcodes_kv",
|
||||||
|
"valuefield": "k",
|
||||||
|
"textfield": "v",
|
||||||
|
"cond": "parentid='yesno'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "stream",
|
||||||
|
"table": "appcodes_kv",
|
||||||
|
"valuefield": "k",
|
||||||
|
"textfield": "v",
|
||||||
|
"cond": "parentid='resp_mode'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "ioid",
|
||||||
|
"table": "uapiio",
|
||||||
|
"valuefield": "id",
|
||||||
|
"textfield": "name"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
36
models/uapiio.json
Normal file
36
models/uapiio.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"summary": [
|
||||||
|
{
|
||||||
|
"name": "uapiio",
|
||||||
|
"title": "输入输出",
|
||||||
|
"primary": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"catelog": "entity"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"title": "id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"title": "类型名",
|
||||||
|
"type": "str",
|
||||||
|
"length": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"title": "类型说明",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "input_fields",
|
||||||
|
"title": "输入字段",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
39
models/uapiset.json
Normal file
39
models/uapiset.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"summary": [
|
||||||
|
{
|
||||||
|
"name": "uapiset",
|
||||||
|
"title": "API集",
|
||||||
|
"primary": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"catelog": "entity"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"title": "id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"title": "API集名称",
|
||||||
|
"type": "str",
|
||||||
|
"length": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"title": "描述",
|
||||||
|
"type": "text",
|
||||||
|
"default": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "auth_apiname",
|
||||||
|
"title": "授权api名",
|
||||||
|
"type": "str",
|
||||||
|
"length": 200,
|
||||||
|
"nullable": "yes"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
83
models/upapp.json
Normal file
83
models/upapp.json
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"summary": [
|
||||||
|
{
|
||||||
|
"name": "upapp",
|
||||||
|
"title": "外部系统",
|
||||||
|
"primary": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"title": "id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"title": "上位应用名",
|
||||||
|
"type": "str",
|
||||||
|
"length": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"title": "描述",
|
||||||
|
"type": "text",
|
||||||
|
"default": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ownerid",
|
||||||
|
"title": "所属机构",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32,
|
||||||
|
"nullable": "yes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "apisetid",
|
||||||
|
"title": "api集id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "secretkey",
|
||||||
|
"title": "加密密钥",
|
||||||
|
"type": "str",
|
||||||
|
"length": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "baseurl",
|
||||||
|
"title": "系统url",
|
||||||
|
"type": "str",
|
||||||
|
"length": 500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "myappid",
|
||||||
|
"title": "我的appid",
|
||||||
|
"type": "str",
|
||||||
|
"length": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dynamic_func",
|
||||||
|
"title": "动态headers函数",
|
||||||
|
"type": "str",
|
||||||
|
"length": 255
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "auth_apiname",
|
||||||
|
"title": "认证API名",
|
||||||
|
"type": "str",
|
||||||
|
"length": 200,
|
||||||
|
"nullable": "yes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"codes": [
|
||||||
|
{
|
||||||
|
"field": "ownerid",
|
||||||
|
"table": "organization",
|
||||||
|
"valuefield": "id",
|
||||||
|
"textfield": "orgname"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
90
models/upappkey.json
Normal file
90
models/upappkey.json
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"summary": [
|
||||||
|
{
|
||||||
|
"name": "upappkey",
|
||||||
|
"title": "上位系统密码表",
|
||||||
|
"primary": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"title": "id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "upappid",
|
||||||
|
"title": "应用id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ownerid",
|
||||||
|
"title": "属主id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32,
|
||||||
|
"default": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "apikey",
|
||||||
|
"title": "api密钥",
|
||||||
|
"type": "str",
|
||||||
|
"length": 4000,
|
||||||
|
"default": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "apiuser",
|
||||||
|
"title": "api用户",
|
||||||
|
"type": "str",
|
||||||
|
"length": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "apipasswd",
|
||||||
|
"title": "api密码",
|
||||||
|
"type": "str",
|
||||||
|
"length": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "orgid",
|
||||||
|
"title": "属主机构id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "is_first",
|
||||||
|
"title": "是否第一用户",
|
||||||
|
"type": "str",
|
||||||
|
"length": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"codes": [
|
||||||
|
{
|
||||||
|
"field": "ownerid",
|
||||||
|
"table": "users",
|
||||||
|
"valuefield": "id",
|
||||||
|
"textfield": "username"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "upappid",
|
||||||
|
"table": "upapp",
|
||||||
|
"valuefield": "id",
|
||||||
|
"textfield": "name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "orgid",
|
||||||
|
"table": "organization",
|
||||||
|
"valuefield": "id",
|
||||||
|
"textfield": "orgname"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "is_first",
|
||||||
|
"table": "appcodes_kv",
|
||||||
|
"valuefield": "k",
|
||||||
|
"textfield": "v",
|
||||||
|
"cond": "parentid='yesno'"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
74
models/uptask.json
Normal file
74
models/uptask.json
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"summary": [
|
||||||
|
{
|
||||||
|
"name": "uptask",
|
||||||
|
"title": "上游任务",
|
||||||
|
"primary": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"catelog": "entity"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"title": "id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "userid",
|
||||||
|
"title": "用户id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "executor_taskid",
|
||||||
|
"title": "执行方任务id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "convert_func_name",
|
||||||
|
"title": "响应转换函数名",
|
||||||
|
"type": "str",
|
||||||
|
"length": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "local_bizid",
|
||||||
|
"title": "本地业务id",
|
||||||
|
"type": "str",
|
||||||
|
"length": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "status",
|
||||||
|
"title": "状态",
|
||||||
|
"type": "str",
|
||||||
|
"length": 24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "response_data",
|
||||||
|
"title": "相应数据",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "start_timestamp",
|
||||||
|
"title": "开始时间",
|
||||||
|
"type": "time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "end_timestamp",
|
||||||
|
"title": "结束时间",
|
||||||
|
"type": "time"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [
|
||||||
|
{
|
||||||
|
"name": "idx1",
|
||||||
|
"idxtype": "unique",
|
||||||
|
"idxfields": [
|
||||||
|
"executor_taskid"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
56
scripts/load_path.py
Normal file
56
scripts/load_path.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
"""Generate RBAC permissions for uapi module paths.
|
||||||
|
|
||||||
|
Run from Sage root with Sage venv:
|
||||||
|
cd ~/repos/sage && ./py3/bin/python ../uapi/scripts/load_path.py
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
sage_root = os.environ.get('SAGE_ROOT')
|
||||||
|
if sage_root and sage_root not in sys.path:
|
||||||
|
sys.path.insert(0, sage_root)
|
||||||
|
|
||||||
|
from sqlor.dbpools import DBPools
|
||||||
|
from appPublic.jsonConfig import getConfig
|
||||||
|
from appPublic.dictObject import DictObject
|
||||||
|
from appPublic.uniqueID import getID
|
||||||
|
|
||||||
|
|
||||||
|
paths = [
|
||||||
|
("/uapi", "logined"),
|
||||||
|
("/uapi/jump_in.dspy", "logined"),
|
||||||
|
("/uapi/minimax_callback.dspy", "any"),
|
||||||
|
("/uapi/uptask_callback.dspy", "any"),
|
||||||
|
("/uapi/viducallback", "any"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
config = getConfig('.')
|
||||||
|
DBPools(config.databases)
|
||||||
|
dbname = 'sage'
|
||||||
|
async with DBPools().sqlorContext(dbname) as sor:
|
||||||
|
cnt = 0
|
||||||
|
for path, role in paths:
|
||||||
|
r = await sor.sqlExe(
|
||||||
|
'select * from permission where permcode = ${permcode}$',
|
||||||
|
{'permcode': path}
|
||||||
|
)
|
||||||
|
if len(r) == 0:
|
||||||
|
await sor.sqlExe(
|
||||||
|
'''insert into permission (id, permcode, permname, permtype)
|
||||||
|
values (${id}$, ${permcode}$, ${permname}$, ${permtype}$)''',
|
||||||
|
{
|
||||||
|
'id': getID(),
|
||||||
|
'permcode': path,
|
||||||
|
'permname': path,
|
||||||
|
'permtype': role,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
cnt += 1
|
||||||
|
print(f'{cnt} path(s) inserted for uapi')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
asyncio.run(main())
|
||||||
133
scripts/migrate_uapi_upappid.py
Normal file
133
scripts/migrate_uapi_upappid.py
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
"""
|
||||||
|
Migration: uapi table apisetid -> upappid
|
||||||
|
|
||||||
|
Removes uapiset intermediate layer. Each upapp now owns its own uapi records.
|
||||||
|
For shared apisetid: copies uapi records so each upapp has its own copy.
|
||||||
|
|
||||||
|
Usage (in Sage virtual env):
|
||||||
|
./py3/bin/python3 ~/repos/uapi/scripts/migrate_uapi_upappid.py --output /tmp/migrate_uapi.sql
|
||||||
|
|
||||||
|
Review the output SQL, then execute on your database.
|
||||||
|
"""
|
||||||
|
from appPublic.jsonConfig import getConfig
|
||||||
|
import asyncio, json, sys, argparse
|
||||||
|
from sqlor.dbpools import DBPools
|
||||||
|
from appPublic.uniqueID import getID
|
||||||
|
|
||||||
|
config = getConfig('.')
|
||||||
|
db = DBPools(config.databases)
|
||||||
|
dbname = list(config.databases.keys())[0]
|
||||||
|
|
||||||
|
|
||||||
|
async def generate_migration_sql():
|
||||||
|
"""Generate SQL to migrate uapi.apisetid -> uapi.upappid."""
|
||||||
|
lines = [
|
||||||
|
"-- Migration: uapi table apisetid -> upappid",
|
||||||
|
"-- Removes uapiset intermediate layer.",
|
||||||
|
"",
|
||||||
|
"-- Step 1: Add upappid column to uapi",
|
||||||
|
"ALTER TABLE uapi ADD COLUMN upappid VARCHAR(32) DEFAULT NULL COMMENT '上位系统ID' AFTER id;",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
|
||||||
|
# Load data
|
||||||
|
try:
|
||||||
|
async with db.sqlorContext(dbname) as sor:
|
||||||
|
# Get all upapps
|
||||||
|
upapps = await sor.sqlExe('select id, name, apisetid from upapp', {})
|
||||||
|
# Get all uapis (removed non-existent auth_apiname field)
|
||||||
|
uapis = await sor.sqlExe('select id, apisetid, name, httpmethod, path, headers, ioid, response, params, data, chunk_match from uapi', {})
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error querying database: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Build mapping: apisetid -> [upapp1, upapp2, ...]
|
||||||
|
apiset_to_upapps = {}
|
||||||
|
for u in upapps:
|
||||||
|
aid = u.get('apisetid')
|
||||||
|
if aid:
|
||||||
|
apiset_to_upapps.setdefault(aid, []).append(u)
|
||||||
|
|
||||||
|
# Build mapping: apisetid -> [uapi_records]
|
||||||
|
apiset_to_uapis = {}
|
||||||
|
for a in uapis:
|
||||||
|
aid = a.get('apisetid')
|
||||||
|
if aid:
|
||||||
|
apiset_to_uapis.setdefault(aid, []).append(a)
|
||||||
|
|
||||||
|
inserts = []
|
||||||
|
for apisetid, upapp_list in apiset_to_upapps.items():
|
||||||
|
uapi_records = apiset_to_uapis.get(apisetid, [])
|
||||||
|
|
||||||
|
if len(upapp_list) == 1:
|
||||||
|
# Single upapp owns this apisetid -> just update
|
||||||
|
upapp = upapp_list[0]
|
||||||
|
for uapi in uapi_records:
|
||||||
|
inserts.append(
|
||||||
|
f"UPDATE uapi SET upappid = '{upapp['id']}' WHERE id = '{uapi['id']}';"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Multiple upapps share this apisetid -> pick first as owner, copy for rest
|
||||||
|
owner = upapp_list[0]
|
||||||
|
|
||||||
|
# Update existing records to point to owner
|
||||||
|
for uapi in uapi_records:
|
||||||
|
inserts.append(
|
||||||
|
f"UPDATE uapi SET upappid = '{owner['id']}' WHERE id = '{uapi['id']}';"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Copy for other upapps
|
||||||
|
for other_upapp in upapp_list[1:]:
|
||||||
|
for uapi in uapi_records:
|
||||||
|
new_id = getID()
|
||||||
|
old_id = uapi['id']
|
||||||
|
fields = ['id', 'upappid', 'name', 'httpmethod', 'path', 'headers', 'ioid', 'response', 'params', 'data', 'chunk_match']
|
||||||
|
vals = [f"'{new_id}'", f"'{other_upapp['id']}'"]
|
||||||
|
for f in fields[2:]:
|
||||||
|
v = uapi.get(f)
|
||||||
|
if v is None:
|
||||||
|
vals.append('NULL')
|
||||||
|
elif isinstance(v, str):
|
||||||
|
escaped = v.replace("'", "\\'")
|
||||||
|
vals.append(f"'{escaped}'")
|
||||||
|
else:
|
||||||
|
vals.append(str(v))
|
||||||
|
col_str = ', '.join(fields)
|
||||||
|
val_str = ', '.join(vals)
|
||||||
|
inserts.append(f"INSERT INTO uapi ({col_str}) VALUES ({val_str});")
|
||||||
|
|
||||||
|
if inserts:
|
||||||
|
lines.append("-- Step 2: Migrate data (updates + copies for shared apisetid)")
|
||||||
|
lines.extend(inserts)
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
lines.extend([
|
||||||
|
"-- Step 3: Drop apisetid column (verify upappid has no NULLs first)",
|
||||||
|
"-- UPDATE uapi SET upappid = (SELECT ownerid FROM organization LIMIT 1) WHERE upappid IS NULL;",
|
||||||
|
"-- ALTER TABLE uapi DROP COLUMN apisetid;",
|
||||||
|
"",
|
||||||
|
"-- Step 4: Add index",
|
||||||
|
"CREATE INDEX idx_uapi_upappid ON uapi (upappid);",
|
||||||
|
""
|
||||||
|
])
|
||||||
|
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--output', '-o', default='-')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
sql = await generate_migration_sql()
|
||||||
|
|
||||||
|
if args.output == '-':
|
||||||
|
print(sql)
|
||||||
|
else:
|
||||||
|
with open(args.output, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(sql)
|
||||||
|
print(f"Generated migration SQL -> {args.output}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
asyncio.run(main())
|
||||||
@ -8,10 +8,23 @@ from appPublic.streamhttpclient import StreamHttpClient, liner
|
|||||||
from appPublic.dictObject import DictObject
|
from appPublic.dictObject import DictObject
|
||||||
from appPublic.log import debug, exception, error
|
from appPublic.log import debug, exception, error
|
||||||
from appPublic.aes import aes_encode_b64
|
from appPublic.aes import aes_encode_b64
|
||||||
|
from appPublic.jsonConfig import getConfig
|
||||||
from ahserver.globalEnv import password_decode
|
from ahserver.globalEnv import password_decode
|
||||||
from ahserver.serverenv import get_serverenv, ServerEnv
|
from ahserver.serverenv import get_serverenv, ServerEnv
|
||||||
from random import randint
|
from random import randint
|
||||||
|
|
||||||
|
|
||||||
|
def _cache_enabled():
|
||||||
|
"""Check if cache is enabled for uapi module in config.json"""
|
||||||
|
try:
|
||||||
|
config = getConfig()
|
||||||
|
module_cache = config.module_cache
|
||||||
|
if module_cache is None:
|
||||||
|
return True
|
||||||
|
return getattr(module_cache, 'uapi', True)
|
||||||
|
except Exception:
|
||||||
|
return True
|
||||||
|
|
||||||
async def get_deerer(upappid, callerid):
|
async def get_deerer(upappid, callerid):
|
||||||
db = DBPools()
|
db = DBPools()
|
||||||
dbname = get_dbname()
|
dbname = get_dbname()
|
||||||
@ -25,10 +38,9 @@ async def get_deerer(upappid, callerid):
|
|||||||
|
|
||||||
async def sor_get_uapi(sor, upappid, apiname):
|
async def sor_get_uapi(sor, upappid, apiname):
|
||||||
sql = """select a.*,
|
sql = """select a.*,
|
||||||
c.auth_apiname
|
b.auth_apiname
|
||||||
from uapi a, upapp b, uapiset c
|
from uapi a, upapp b
|
||||||
where a.apisetid = b.apisetid
|
where a.upappid = b.id
|
||||||
and b.apisetid = c.id
|
|
||||||
and a.name = ${apiname}$
|
and a.name = ${apiname}$
|
||||||
and b.id = ${upappid}$"""
|
and b.id = ${upappid}$"""
|
||||||
recs = await sor.sqlExe(sql, {'upappid': upappid, 'apiname': apiname})
|
recs = await sor.sqlExe(sql, {'upappid': upappid, 'apiname': apiname})
|
||||||
@ -52,7 +64,15 @@ class UAPIData:
|
|||||||
self.apidata = {}
|
self.apidata = {}
|
||||||
self.apikeys = {}
|
self.apikeys = {}
|
||||||
self.org_users = {}
|
self.org_users = {}
|
||||||
|
|
||||||
|
def on_hot_reload(self, data=None):
|
||||||
|
"""Event handler for hot_reload event. Clears all caches."""
|
||||||
|
from appPublic.log import debug
|
||||||
|
debug(f'[uapi] on_hot_reload called, clearing caches (data={data})')
|
||||||
|
self.apidata.clear()
|
||||||
|
self.apikeys.clear()
|
||||||
|
self.org_users.clear()
|
||||||
|
|
||||||
async def get_userapikey(self, appid, callerid):
|
async def get_userapikey(self, appid, callerid):
|
||||||
users = await self.get_apiusers(appid)
|
users = await self.get_apiusers(appid)
|
||||||
for u in users:
|
for u in users:
|
||||||
@ -69,10 +89,11 @@ class UAPIData:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
async def get_apiusers(self, appid, orgid=None):
|
async def get_apiusers(self, appid, orgid=None):
|
||||||
key = appid
|
key = f'{appid}.{orgid}' if orgid else str(appid)
|
||||||
d = self.org_users.get(key)
|
if _cache_enabled():
|
||||||
if d:
|
d = self.org_users.get(key)
|
||||||
return d
|
if d:
|
||||||
|
return d
|
||||||
env = ServerEnv()
|
env = ServerEnv()
|
||||||
async with get_sor_context(env, 'uapi') as sor:
|
async with get_sor_context(env, 'uapi') as sor:
|
||||||
sql = """select
|
sql = """select
|
||||||
@ -105,7 +126,8 @@ where b.orgid = c.ownerid
|
|||||||
e = Exception(f'{appid=} {orgid=} get none user')
|
e = Exception(f'{appid=} {orgid=} get none user')
|
||||||
exception(f'{e}')
|
exception(f'{e}')
|
||||||
raise e
|
raise e
|
||||||
self.apidata[key] = d
|
if _cache_enabled():
|
||||||
|
self.org_users[key] = d
|
||||||
return d
|
return d
|
||||||
e = Exception(f'{appid=} {orgid=} get none user')
|
e = Exception(f'{appid=} {orgid=} get none user')
|
||||||
exception(f'{e}')
|
exception(f'{e}')
|
||||||
@ -123,6 +145,10 @@ where b.orgid = c.ownerid
|
|||||||
|
|
||||||
async def get_api_from_db(self, appid, apiname):
|
async def get_api_from_db(self, appid, apiname):
|
||||||
key = f'{appid}.{apiname}'
|
key = f'{appid}.{apiname}'
|
||||||
|
if _cache_enabled():
|
||||||
|
api = self.apidata.get(key)
|
||||||
|
if api:
|
||||||
|
return api
|
||||||
env = ServerEnv()
|
env = ServerEnv()
|
||||||
async with get_sor_context(env, 'uapi') as sor:
|
async with get_sor_context(env, 'uapi') as sor:
|
||||||
d = await sor_get_uapi(sor, appid, apiname)
|
d = await sor_get_uapi(sor, appid, apiname)
|
||||||
@ -130,7 +156,8 @@ where b.orgid = c.ownerid
|
|||||||
e = Exception(f'{appid=}, {apiname=} get none api')
|
e = Exception(f'{appid=}, {apiname=} get none api')
|
||||||
exception(f'{e}')
|
exception(f'{e}')
|
||||||
raise e
|
raise e
|
||||||
self.apidata[key] = d
|
if _cache_enabled():
|
||||||
|
self.apidata[key] = d
|
||||||
return d
|
return d
|
||||||
e = Exception(f'{appid=}, {apiname=} get none api')
|
e = Exception(f'{appid=}, {apiname=} get none api')
|
||||||
exception(f'{e}')
|
exception(f'{e}')
|
||||||
|
|||||||
@ -20,7 +20,7 @@ async def get_callerid(orgid):
|
|||||||
|
|
||||||
async def sor_get_uapi_by_appname_apiname(sor, appname, apiname):
|
async def sor_get_uapi_by_appname_apiname(sor, appname, apiname):
|
||||||
sql = """select a.* from uapi a, upapp b
|
sql = """select a.* from uapi a, upapp b
|
||||||
where a.apisetid = b.apisetid
|
where a.upappid = b.id
|
||||||
and b.name = ${appname}$
|
and b.name = ${appname}$
|
||||||
and a.name = ${apiname}$"""
|
and a.name = ${apiname}$"""
|
||||||
recs = await sor.sqlExe(sql, {'apiname': apiname, 'appname': appname})
|
recs = await sor.sqlExe(sql, {'apiname': apiname, 'appname': appname})
|
||||||
@ -90,10 +90,9 @@ async def get_userapikey(sor, upappid, callerid):
|
|||||||
|
|
||||||
async def sor_get_uapi(sor, upappid, apiname):
|
async def sor_get_uapi(sor, upappid, apiname):
|
||||||
sql = """select a.*,
|
sql = """select a.*,
|
||||||
c.auth_apiname
|
b.auth_apiname
|
||||||
from uapi a, upapp b, uapiset c
|
from uapi a, upapp b
|
||||||
where a.apisetid = b.apisetid
|
where a.upappid = b.id
|
||||||
and b.apisetid = c.id
|
|
||||||
and a.name = ${apiname}$
|
and a.name = ${apiname}$
|
||||||
and b.id = ${upappid}$"""
|
and b.id = ${upappid}$"""
|
||||||
recs = await sor.sqlExe(sql, {'upappid': upappid, 'apiname': apiname})
|
recs = await sor.sqlExe(sql, {'upappid': upappid, 'apiname': apiname})
|
||||||
|
|||||||
@ -32,6 +32,9 @@ def load_uapi():
|
|||||||
g.get_my_uptasks = get_my_uptasks
|
g.get_my_uptasks = get_my_uptasks
|
||||||
g.uptask_feedback = uptask_feedback
|
g.uptask_feedback = uptask_feedback
|
||||||
g.uptask_started = uptask_started
|
g.uptask_started = uptask_started
|
||||||
|
# Bind hot_reload event — instance method, WeakMethod safe (stored on g)
|
||||||
|
if hasattr(g, 'event_dispatcher'):
|
||||||
|
g.event_dispatcher.bind('hot_reload', g.uapi_data.on_hot_reload)
|
||||||
|
|
||||||
# USAGE in dspy
|
# USAGE in dspy
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
debug(f'{params_kw=}')
|
debug_params('params_kw', params_kw)
|
||||||
if parmas_kw.task_id is None:
|
if parmas_kw.task_id is None:
|
||||||
e = Exception(f'need a task_id')
|
e = Exception(f'need a task_id')
|
||||||
raise e
|
raise e
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
debug(f'{params_kw=}
|
debug_params('params_kw', params_kw)
|
||||||
taskid = params_kw.id
|
taskid = params_kw.id
|
||||||
async with get_sor_context(request._run_ns, 'uapi') as sor:
|
async with get_sor_context(request._run_ns, 'uapi') as sor:
|
||||||
llmusage = await get_llmlage_by_taskid(taskid)
|
llmusage = await get_llmlage_by_taskid(taskid)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user