@tacoreai/web-sdk 1.22.0 → 1.22.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -45
- package/database/core/apps/AppServerRuntime.js +34 -2
- package/database/core/apps/AppsClient.Data.js +10 -15
- package/database/core/apps/AppsClient.IAM.js +77 -0
- package/database/core/apps/AppsClient.Users.js +30 -0
- package/database/core/apps/AppsClient.js +27 -3
- package/database/core/apps/AppsPublicClient.js +3 -1
- package/database/core/apps/BaseAppsClient.js +41 -11
- package/package.json +3 -2
- package/utils/index.js +2 -5
package/README.md
CHANGED
|
@@ -17,52 +17,63 @@ npm install @tacoreai/web-sdk
|
|
|
17
17
|
### AppsClient (Data & AI)
|
|
18
18
|
示例略(保持原有章节)
|
|
19
19
|
|
|
20
|
+
### AppsClient (Events)
|
|
21
|
+
|
|
22
|
+
应用自定义埋点应使用事件日志接口,不要把高频事件写入通用数据模型。
|
|
23
|
+
应用代码不要直接请求 `/plat` 平台接口,也不要读取平台登录 token;写入和读取都应通过 `AppsClient.invoke(...)` 走 `/apps/events/*`。事件日志不新增 WebSDK 专用方法,apiName 直接和后端 path 对齐。
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
const tracked = await client.invoke('events/track', {
|
|
27
|
+
eventName: 'signup_click',
|
|
28
|
+
properties: { plan: 'pro' },
|
|
29
|
+
visitorId: localStorage.getItem('visitor_id'),
|
|
30
|
+
sessionId: sessionStorage.getItem('session_id'),
|
|
31
|
+
path: window.location.pathname,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// 以下读取接口仅适用于已登录且具备 app owner/admin 角色的应用管理界面。
|
|
35
|
+
const event = await client.invoke('events/read', {
|
|
36
|
+
eventId: tracked?.data?.eventId,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const page = await client.invoke('events/query', {
|
|
40
|
+
day: '2026-05-14',
|
|
41
|
+
eventName: 'signup_click',
|
|
42
|
+
limit: 50,
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
可用接口包括 `events/track`、`events/batch-track`、`events/read`、`events/batch-read`、`events/query`。写入接口由后端校验应用部署域名或自定义域名来源;所有读取接口都要求 Apps 登录态且当前用户为应用 `owner/admin`。平台通用查看/搜索由平台工作视图承接。
|
|
47
|
+
|
|
20
48
|
---
|
|
21
49
|
|
|
22
|
-
## 文件存储 API(
|
|
50
|
+
## 文件存储 API(R2 / GCS)
|
|
23
51
|
|
|
24
52
|
先确保已通过 AppsAuthManager 登录,并使用 AppsClient 初始化时提供 appId。以下接口均作用于 /apps 路由,需携带 Authorization。
|
|
25
53
|
|
|
26
|
-
1)
|
|
54
|
+
1) 上传文件到 R2(公开访问 / 前端展示)
|
|
27
55
|
|
|
28
56
|
import { AppsClient } from '@tacoreai/web-sdk'
|
|
29
57
|
const client = new AppsClient('YOUR_APP_ID')
|
|
30
58
|
|
|
31
59
|
const input = document.querySelector('#fileInput')
|
|
32
60
|
const file = input.files[0]
|
|
33
|
-
const result = await client.uploadFile(file
|
|
34
|
-
// result
|
|
61
|
+
const result = await client.uploadFile(file)
|
|
62
|
+
// result => { success: true, url, key }
|
|
35
63
|
|
|
36
|
-
2)
|
|
64
|
+
2) 下载 R2 文件
|
|
37
65
|
|
|
38
|
-
const
|
|
39
|
-
|
|
66
|
+
const response = await client.downloadFromR2('<key>')
|
|
67
|
+
const blob = await response.blob()
|
|
40
68
|
|
|
41
|
-
3)
|
|
69
|
+
3) 删除 R2 文件
|
|
42
70
|
|
|
43
|
-
await client.
|
|
71
|
+
await client.deleteFromR2('<key>')
|
|
44
72
|
|
|
45
|
-
4)
|
|
73
|
+
4) 上传文件到 GCS(Gemini / 多模态分析输入)
|
|
46
74
|
|
|
47
|
-
const
|
|
48
|
-
//
|
|
49
|
-
const a = document.createElement('a')
|
|
50
|
-
a.href = URL.createObjectURL(blob)
|
|
51
|
-
a.download = 'download.bin'
|
|
52
|
-
a.click()
|
|
53
|
-
URL.revokeObjectURL(a.href)
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
## [新增] 文件上传与分析
|
|
58
|
-
|
|
59
|
-
此接口用于上传文件(如 PDF、图片)以供 AI 进行深度分析或作为图片编辑的输入。
|
|
60
|
-
|
|
61
|
-
### `uploadFileForAnalysis(file)`
|
|
62
|
-
- **功能**: 将文件上传至 Google 的专用存储,并返回一个可供 AI 模型引用的 `fileUri`。
|
|
63
|
-
- **参数**:
|
|
64
|
-
- `file` (File): 从文件输入框获取的 File 对象。
|
|
65
|
-
- **返回值**: `Promise<{fileUri: string, mimeType: string, ...}>`
|
|
75
|
+
const gcsResult = await client.uploadToGCS(file)
|
|
76
|
+
// gcsResult.fileUri => gs://... ,供 Gemini 等模型使用
|
|
66
77
|
|
|
67
78
|
---
|
|
68
79
|
|
|
@@ -111,12 +122,12 @@ const imageUri = result.data.uri;
|
|
|
111
122
|
- **返回**: `Promise<Object>` (后端响应对象,包含 `data.uri` 或 `data.images`)
|
|
112
123
|
|
|
113
124
|
### 核心流程
|
|
114
|
-
1. **上传图片**:
|
|
125
|
+
1. **上传图片**: Google / Gemini 链路下,所有源图片(包括底图、叠加图、掩膜图)都应先通过 `uploadToGCS(file)` 获取 `gs://` 形式的 `fileUri`。
|
|
115
126
|
2. **调用编辑**: 使用获取到的 `fileUri` 和 `mimeType`,通过 `editImage` 方法并采用**简化的 `prompt + inputs` 模式**进行调用。
|
|
116
127
|
|
|
117
128
|
### 前置要求
|
|
118
129
|
- **必须先登录** (AppsAuthManager)
|
|
119
|
-
- `
|
|
130
|
+
- `uploadToGCS`
|
|
120
131
|
|
|
121
132
|
### 1. 简化调用 (prompt + inputs 模式,推荐)
|
|
122
133
|
SDK 会自动将 `prompt` 和 `inputs` 数组组合成标准的多模态 `messages`。
|
|
@@ -125,13 +136,13 @@ import { AppsClient } from '@tacoreai/web-sdk'
|
|
|
125
136
|
const client = new AppsClient('YOUR_APP_ID')
|
|
126
137
|
|
|
127
138
|
// 1. 上传源文件
|
|
128
|
-
const baseFile = await client.
|
|
139
|
+
const baseFile = await client.uploadToGCS(file);
|
|
129
140
|
|
|
130
141
|
// 2. 调用编辑接口
|
|
131
142
|
const result = await client.editImage({
|
|
132
143
|
prompt: '仅移除图中叠加的文字,保持其他区域不变,输出高清。',
|
|
133
144
|
inputs: [
|
|
134
|
-
{ type: 'uploaded_file', fileUri: baseFile.fileUri, mimeType:
|
|
145
|
+
{ type: 'uploaded_file', fileUri: baseFile.fileUri, mimeType: file.type }
|
|
135
146
|
]
|
|
136
147
|
});
|
|
137
148
|
// 获取图片 URI (兼容不同后端返回结构)
|
|
@@ -142,41 +153,41 @@ const editedBase64 = result.data.uri || (result.data.images && result.data.image
|
|
|
142
153
|
|
|
143
154
|
- **场景 A:单图编辑(去字/去水印/局部替换)**
|
|
144
155
|
|
|
145
|
-
const base = await client.
|
|
156
|
+
const base = await client.uploadToGCS(file);
|
|
146
157
|
const result = await client.editImage({
|
|
147
158
|
prompt: '移除叠加文字,其他区域完全保持不变。',
|
|
148
159
|
inputs: [
|
|
149
|
-
{ type: 'uploaded_file', fileUri: base.fileUri, mimeType:
|
|
160
|
+
{ type: 'uploaded_file', fileUri: base.fileUri, mimeType: file.type }
|
|
150
161
|
]
|
|
151
162
|
});
|
|
152
163
|
const base64 = result.data.uri || result.data.images?.[0]?.uri;
|
|
153
164
|
|
|
154
165
|
- **场景 B:双图融合(将服装穿到模特身上)**
|
|
155
166
|
|
|
156
|
-
const garment = await client.
|
|
157
|
-
const modelPhoto = await client.
|
|
167
|
+
const garment = await client.uploadToGCS(garmentFile);
|
|
168
|
+
const modelPhoto = await client.uploadToGCS(modelFile);
|
|
158
169
|
|
|
159
170
|
const result = await client.editImage({
|
|
160
171
|
prompt: '将服装自然替换到模特身上,保持姿态与光照;去除服装图背景;保持背景不变;不改变面部和发型。',
|
|
161
172
|
inputs: [
|
|
162
173
|
// 建议顺序:底图(模特)放前,叠加/融合图(服装)放后
|
|
163
|
-
{ type: 'uploaded_file', fileUri: modelPhoto.fileUri, mimeType:
|
|
164
|
-
{ type: 'uploaded_file', fileUri: garment.fileUri, mimeType:
|
|
174
|
+
{ type: 'uploaded_file', fileUri: modelPhoto.fileUri, mimeType: modelFile.type },
|
|
175
|
+
{ type: 'uploaded_file', fileUri: garment.fileUri, mimeType: garmentFile.type }
|
|
165
176
|
]
|
|
166
177
|
});
|
|
167
178
|
const base64 = result.data.uri || result.data.images?.[0]?.uri;
|
|
168
179
|
|
|
169
180
|
- **场景 C:掩膜修复/扩展(Inpainting / Outpainting)**
|
|
170
181
|
|
|
171
|
-
const base = await client.
|
|
172
|
-
const mask = await client.
|
|
182
|
+
const base = await client.uploadToGCS(baseFile);
|
|
183
|
+
const mask = await client.uploadToGCS(maskFile); // 掩膜语义按后端约定
|
|
173
184
|
|
|
174
185
|
const result = await client.editImage({
|
|
175
186
|
prompt: '在掩膜区域进行修复,移除水印并保持纹理一致;不要改变未掩膜区域。',
|
|
176
187
|
inputs: [
|
|
177
188
|
// 建议顺序:底图 -> 掩膜
|
|
178
|
-
{ type: 'uploaded_file', fileUri: base.fileUri, mimeType:
|
|
179
|
-
{ type: 'uploaded_file', fileUri: mask.fileUri, mimeType:
|
|
189
|
+
{ type: 'uploaded_file', fileUri: base.fileUri, mimeType: baseFile.type },
|
|
190
|
+
{ type: 'uploaded_file', fileUri: mask.fileUri, mimeType: maskFile.type }
|
|
180
191
|
]
|
|
181
192
|
});
|
|
182
193
|
const base64 = result.data.uri || result.data.images?.[0]?.uri;
|
|
@@ -184,7 +195,7 @@ const base64 = result.data.uri || result.data.images?.[0]?.uri;
|
|
|
184
195
|
### 3. 高级模式 (messages 透传)
|
|
185
196
|
用于完全控制请求结构,不推荐常规使用。
|
|
186
197
|
|
|
187
|
-
const base = await client.
|
|
198
|
+
const base = await client.uploadToGCS(file)
|
|
188
199
|
|
|
189
200
|
const result = await client.editImage({
|
|
190
201
|
model: 'gemini-2.5-flash-image-preview',
|
|
@@ -193,7 +204,7 @@ const result = await client.editImage({
|
|
|
193
204
|
role: 'user',
|
|
194
205
|
content: [
|
|
195
206
|
{ type: 'text', text: '移除图中叠加的文字...' },
|
|
196
|
-
{ type: 'uploaded_file', uploaded_file: { fileUri: base.fileUri, mimeType:
|
|
207
|
+
{ type: 'uploaded_file', uploaded_file: { fileUri: base.fileUri, mimeType: file.type } }
|
|
197
208
|
]
|
|
198
209
|
}
|
|
199
210
|
]
|
|
@@ -31,17 +31,36 @@ const getRequestSource = (isPlatformProxyRequest, isSchedulerRequest) => {
|
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
const createRequestScopedAppsClient = (env, options = {}) => {
|
|
34
|
-
const {
|
|
34
|
+
const {
|
|
35
|
+
accessToken = null,
|
|
36
|
+
appServerApiKey = null,
|
|
37
|
+
clientAppId = env.dataSourceId || env.appId,
|
|
38
|
+
previewAppServerApiKey = null,
|
|
39
|
+
enableAppServerServiceAuth = false,
|
|
40
|
+
} = options;
|
|
35
41
|
const config = {
|
|
36
42
|
...env,
|
|
37
43
|
accessToken: accessToken || null,
|
|
44
|
+
enableAppServerServiceAuth,
|
|
38
45
|
};
|
|
39
46
|
|
|
47
|
+
if (enableAppServerServiceAuth) {
|
|
48
|
+
config.appServerOwnerAppId = env.appId;
|
|
49
|
+
config.tacoreAppServerServiceToken =
|
|
50
|
+
env.tacoreAppServerServiceToken ||
|
|
51
|
+
process.env.TACORE_APP_SERVER_SERVICE_TOKEN ||
|
|
52
|
+
null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (appServerApiKey) {
|
|
56
|
+
config.appServerApiKey = appServerApiKey;
|
|
57
|
+
}
|
|
58
|
+
|
|
40
59
|
if (previewAppServerApiKey) {
|
|
41
60
|
config.previewAppServerApiKey = previewAppServerApiKey;
|
|
42
61
|
}
|
|
43
62
|
|
|
44
|
-
return new AppsClient(
|
|
63
|
+
return new AppsClient(clientAppId, config);
|
|
45
64
|
};
|
|
46
65
|
|
|
47
66
|
const isPreviewApiKeyShapeValid = (apiKey) => {
|
|
@@ -273,7 +292,9 @@ export const createAppServerRuntime = async (options = {}) => {
|
|
|
273
292
|
process.env.CLOUDFLARE_WORKER_INTEROP_APP_SERVER_TOKEN
|
|
274
293
|
);
|
|
275
294
|
const requestSource = getRequestSource(isPlatformProxyRequest, isSchedulerRequest);
|
|
295
|
+
let appServerApiKey = null;
|
|
276
296
|
let previewAppServerApiKey = null;
|
|
297
|
+
let isExternalApiKeyRequest = false;
|
|
277
298
|
|
|
278
299
|
console.log(`[AppServer] invoke api="${apiName}" source="${requestSource}"`);
|
|
279
300
|
|
|
@@ -299,6 +320,7 @@ export const createAppServerRuntime = async (options = {}) => {
|
|
|
299
320
|
try {
|
|
300
321
|
const authClient = createRequestScopedAppsClient(env, {
|
|
301
322
|
accessToken: requestAccessToken || null,
|
|
323
|
+
clientAppId: env.appId,
|
|
302
324
|
});
|
|
303
325
|
isValidApiKey = await validateExternalApiKey(authClient, env.appId, requestApiKey);
|
|
304
326
|
} catch (error) {
|
|
@@ -318,6 +340,9 @@ export const createAppServerRuntime = async (options = {}) => {
|
|
|
318
340
|
});
|
|
319
341
|
}
|
|
320
342
|
|
|
343
|
+
isExternalApiKeyRequest = true;
|
|
344
|
+
appServerApiKey = requestApiKey;
|
|
345
|
+
|
|
321
346
|
if (isPreviewMode() && !requestAccessToken) {
|
|
322
347
|
previewAppServerApiKey = requestApiKey;
|
|
323
348
|
}
|
|
@@ -330,17 +355,24 @@ export const createAppServerRuntime = async (options = {}) => {
|
|
|
330
355
|
hasRequestApiKey: Boolean(requestApiKey),
|
|
331
356
|
hasPlatformInteropHeader: Boolean(platformInteropHeader),
|
|
332
357
|
hasSchedulerInteropHeader: Boolean(schedulerInteropHeader),
|
|
358
|
+
isExternalApiKeyRequest,
|
|
359
|
+
willForwardAppServerApiKey: Boolean(appServerApiKey && !requestAccessToken),
|
|
333
360
|
willForwardPreviewAppServerApiKey: Boolean(previewAppServerApiKey),
|
|
334
361
|
});
|
|
335
362
|
|
|
336
363
|
const appsClient = createRequestScopedAppsClient(env, {
|
|
337
364
|
accessToken: requestAccessToken || null,
|
|
365
|
+
appServerApiKey: requestAccessToken ? null : appServerApiKey,
|
|
338
366
|
previewAppServerApiKey,
|
|
367
|
+
enableAppServerServiceAuth: Boolean((isSchedulerRequest || isExternalApiKeyRequest) && !requestAccessToken),
|
|
339
368
|
});
|
|
340
369
|
console.log(`[AppServer Debug] scoped AppsClient api="${apiName}"`, {
|
|
341
370
|
hasAccessToken: Boolean(appsClient.getAccessToken()),
|
|
371
|
+
hasAppServerApiKey: Boolean(appsClient.config.appServerApiKey),
|
|
342
372
|
hasPreviewAppServerApiKey: Boolean(appsClient.config.previewAppServerApiKey),
|
|
343
373
|
hasInteropKey: Boolean(appsClient.config.tacoreServerInteropAppServerApiKey),
|
|
374
|
+
serviceAuthEnabled: appsClient.config.enableAppServerServiceAuth === true,
|
|
375
|
+
hasServiceToken: Boolean(appsClient.config.tacoreAppServerServiceToken),
|
|
344
376
|
});
|
|
345
377
|
const data = await appsClient.invokeAppServerAPI(apiName, payload, {
|
|
346
378
|
appsClient,
|
|
@@ -80,22 +80,11 @@ export const appsClientDataMethods = {
|
|
|
80
80
|
modelName: modelName,
|
|
81
81
|
};
|
|
82
82
|
|
|
83
|
-
if (typeof query
|
|
84
|
-
|
|
85
|
-
} else if (query && typeof query === "object") {
|
|
86
|
-
const { page = 1, pageSize = 20, fields, filter } = query;
|
|
87
|
-
params.page = Math.max(1, parseInt(page));
|
|
88
|
-
params.pageSize = parseInt(pageSize) || 1000;
|
|
89
|
-
console.log(params);
|
|
90
|
-
if (fields && Array.isArray(fields)) {
|
|
91
|
-
params.fields = fields;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (filter && typeof filter === "object") {
|
|
95
|
-
params.filter = filter;
|
|
96
|
-
}
|
|
83
|
+
if (typeof query !== "string" || !query) {
|
|
84
|
+
throw new Error("readData only supports single-record reads by wyID. Use invoke('data/list', ...) or invoke('data/query', ...) for list/query reads.");
|
|
97
85
|
}
|
|
98
86
|
|
|
87
|
+
params.wyID = query;
|
|
99
88
|
const result = await this._get("/apps/data/read", params);
|
|
100
89
|
|
|
101
90
|
if (result.data === null && params.wyID) {
|
|
@@ -152,7 +141,13 @@ export const appsClientDataMethods = {
|
|
|
152
141
|
async hasData(modelName) {
|
|
153
142
|
try {
|
|
154
143
|
if (!modelName) throw new Error("modelName is required for hasData");
|
|
155
|
-
const result = await this.
|
|
144
|
+
const result = await this.invoke("data/list", {
|
|
145
|
+
modelName,
|
|
146
|
+
query: {
|
|
147
|
+
page: 1,
|
|
148
|
+
pageSize: 1,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
156
151
|
return result && result.data && result.data.length > 0;
|
|
157
152
|
} catch (error) {
|
|
158
153
|
console.error(`检查应用数据失败 (model: ${modelName}):`, error);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export const appsClientIamMethods = {
|
|
2
|
+
async getIamContext(params = {}) {
|
|
3
|
+
return this._post("/apps/iam/me", params);
|
|
4
|
+
},
|
|
5
|
+
|
|
6
|
+
async bootstrapIam(params = {}) {
|
|
7
|
+
return this._post("/apps/iam/bootstrap", params);
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
async listIamOrgUnits(params = {}) {
|
|
11
|
+
return this._post("/apps/iam/org-units/list", params);
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
async createIamOrgUnit(params = {}) {
|
|
15
|
+
return this._post("/apps/iam/org-units", params);
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
async updateIamOrgUnit(params = {}) {
|
|
19
|
+
return this._post("/apps/iam/org-units/update", params);
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
async deleteIamOrgUnit(params = {}) {
|
|
23
|
+
return this._post("/apps/iam/org-units/delete", params);
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
async listIamRoles(params = {}) {
|
|
27
|
+
return this._post("/apps/iam/roles/list", params);
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
async createIamRole(params = {}) {
|
|
31
|
+
return this._post("/apps/iam/roles", params);
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
async updateIamRole(params = {}) {
|
|
35
|
+
return this._post("/apps/iam/roles/update", params);
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
async deleteIamRole(params = {}) {
|
|
39
|
+
return this._post("/apps/iam/roles/delete", params);
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
async listIamStaff(params = {}) {
|
|
43
|
+
return this._post("/apps/iam/staff/list", params);
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
async createIamStaff(params = {}) {
|
|
47
|
+
return this._post("/apps/iam/staff", params);
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
async updateIamStaff(params = {}) {
|
|
51
|
+
return this._post("/apps/iam/staff/update", params);
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
async deleteIamStaff(params = {}) {
|
|
55
|
+
return this._post("/apps/iam/staff/delete", params);
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
async listModelPolicies(params = {}) {
|
|
59
|
+
return this._post("/apps/data/model-policies/list", params);
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
async declareModelPolicy(params = {}) {
|
|
63
|
+
return this._post("/apps/data/model-policies/declare", params);
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
async writeAuditLog(params = {}) {
|
|
67
|
+
return this._post("/apps/audit/write", params);
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
async queryAuditLogs(params = {}) {
|
|
71
|
+
return this._post("/apps/audit/query", params);
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
async authorizeFileDownload(params = {}) {
|
|
75
|
+
return this._post("/apps/files/authorize-download", params);
|
|
76
|
+
},
|
|
77
|
+
};
|
|
@@ -62,4 +62,34 @@ export const appsClientUserMethods = {
|
|
|
62
62
|
password,
|
|
63
63
|
});
|
|
64
64
|
},
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 从当前应用移除用户访问权限,保留 Auth 账号。
|
|
68
|
+
* @param {Object} params { userId: string }
|
|
69
|
+
* @returns {Promise<{success: boolean, data: {userId: string, appId: string}}>}
|
|
70
|
+
*/
|
|
71
|
+
async removeAppUser({ userId }) {
|
|
72
|
+
if (!userId) {
|
|
73
|
+
throw new Error("userId is required.");
|
|
74
|
+
}
|
|
75
|
+
return this._post("/apps/users/remove", {
|
|
76
|
+
appId: this.appId,
|
|
77
|
+
targetUserId: userId,
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 彻底删除当前应用中的用户账号,使该邮箱可重新注册。
|
|
83
|
+
* @param {Object} params { userId: string }
|
|
84
|
+
* @returns {Promise<{success: boolean, data: {userId: string, appId: string, deletionMode: string, authDeleted: boolean}}>}
|
|
85
|
+
*/
|
|
86
|
+
async deleteAppUser({ userId }) {
|
|
87
|
+
if (!userId) {
|
|
88
|
+
throw new Error("userId is required.");
|
|
89
|
+
}
|
|
90
|
+
return this._post("/apps/users/delete", {
|
|
91
|
+
appId: this.appId,
|
|
92
|
+
targetUserId: userId,
|
|
93
|
+
});
|
|
94
|
+
},
|
|
65
95
|
};
|
|
@@ -18,6 +18,7 @@ import { appsClientToolsMethods } from "./AppsClient.Tools.js";
|
|
|
18
18
|
import { appsClientDebugMethods } from "./AppsClient.Debug.js";
|
|
19
19
|
import { appsClientCrawlerMethods } from "./AppsClient.Crawler.js";
|
|
20
20
|
import { appsClientWechatMethods } from "./AppsClient.Wechat.js";
|
|
21
|
+
import { appsClientIamMethods } from "./AppsClient.IAM.js";
|
|
21
22
|
|
|
22
23
|
const instanceMap = new Map();
|
|
23
24
|
/**
|
|
@@ -79,8 +80,7 @@ class AppsClient extends BaseAppsClient {
|
|
|
79
80
|
return this.readData(modelName, rest.wyID);
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
return this.readData(modelName, rest);
|
|
83
|
+
throw new Error("readData only supports single-record reads by wyID. Use invoke('data/list', ...) or invoke('data/query', ...) for list/query reads.");
|
|
84
84
|
},
|
|
85
85
|
|
|
86
86
|
"deleteData": ({ modelName, wyID, options }) =>
|
|
@@ -103,8 +103,31 @@ class AppsClient extends BaseAppsClient {
|
|
|
103
103
|
"createRole": params => this.createRole(params),
|
|
104
104
|
"updateUserRole": params => this.updateUserRole(params),
|
|
105
105
|
"users/update-password": params => this.updateUserPassword(params),
|
|
106
|
+
"users/remove": params => this.removeAppUser(params),
|
|
107
|
+
"users/delete": params => this.deleteAppUser(params),
|
|
106
108
|
"listAppUsers": params => this.listAppUsers(params),
|
|
107
109
|
|
|
110
|
+
// === 企业 IAM / 审计 / 文件鉴权 ===
|
|
111
|
+
"iam/me": params => this.getIamContext(params),
|
|
112
|
+
"iam/bootstrap": params => this.bootstrapIam(params),
|
|
113
|
+
"iam/org-units/list": params => this.listIamOrgUnits(params),
|
|
114
|
+
"iam/org-units": params => this.createIamOrgUnit(params),
|
|
115
|
+
"iam/org-units/update": params => this.updateIamOrgUnit(params),
|
|
116
|
+
"iam/org-units/delete": params => this.deleteIamOrgUnit(params),
|
|
117
|
+
"iam/roles/list": params => this.listIamRoles(params),
|
|
118
|
+
"iam/roles": params => this.createIamRole(params),
|
|
119
|
+
"iam/roles/update": params => this.updateIamRole(params),
|
|
120
|
+
"iam/roles/delete": params => this.deleteIamRole(params),
|
|
121
|
+
"iam/staff/list": params => this.listIamStaff(params),
|
|
122
|
+
"iam/staff": params => this.createIamStaff(params),
|
|
123
|
+
"iam/staff/update": params => this.updateIamStaff(params),
|
|
124
|
+
"iam/staff/delete": params => this.deleteIamStaff(params),
|
|
125
|
+
"data/model-policies/list": params => this.listModelPolicies(params),
|
|
126
|
+
"data/model-policies/declare": params => this.declareModelPolicy(params),
|
|
127
|
+
"audit/write": params => this.writeAuditLog(params),
|
|
128
|
+
"audit/query": params => this.queryAuditLogs(params),
|
|
129
|
+
"files/authorize-download": params => this.authorizeFileDownload(params),
|
|
130
|
+
|
|
108
131
|
// === 文件存储 ===
|
|
109
132
|
"uploadFile": ({ file }) => this.uploadFile(file),
|
|
110
133
|
"uploadToR2": ({ file }) => this.uploadToR2(file),
|
|
@@ -213,7 +236,8 @@ Object.assign(
|
|
|
213
236
|
appsClientToolsMethods,
|
|
214
237
|
appsClientDebugMethods,
|
|
215
238
|
appsClientCrawlerMethods,
|
|
216
|
-
appsClientWechatMethods
|
|
239
|
+
appsClientWechatMethods,
|
|
240
|
+
appsClientIamMethods
|
|
217
241
|
);
|
|
218
242
|
|
|
219
243
|
export { AppsClient };
|
|
@@ -4,8 +4,10 @@ import { BaseAppsClient } from "./BaseAppsClient.js";
|
|
|
4
4
|
* AI应用公共数据客户端 - 无需认证
|
|
5
5
|
*
|
|
6
6
|
* 核心理念:
|
|
7
|
-
* -
|
|
7
|
+
* - 通过继承的通用 invoke(path, params) 调用匿名公开接口。
|
|
8
|
+
* - 用于匿名读取模型策略 `publicRead` 明确开放的 CMS 数据。
|
|
8
9
|
* - 用于匿名提交数据到指定的、允许公开写入的模型。
|
|
10
|
+
* - 保留 `readConfiguredPublicData` 兼容旧 publicConfig 公开读。
|
|
9
11
|
* - 此客户端的所有操作都无需用户登录。
|
|
10
12
|
*/
|
|
11
13
|
export class AppsPublicClient extends BaseAppsClient {
|
|
@@ -2,8 +2,11 @@ import { getAppsApiBaseUrl, isBrowser, isBackend, isSSR, getGlobalOptions } from
|
|
|
2
2
|
|
|
3
3
|
const ACCESS_TOKEN_STORAGE_KEY = "tacoreai_apps_access_token";
|
|
4
4
|
const PREVIEW_APPSERVER_API_KEY_HEADER = "x-tacore-preview-app-server-api-key";
|
|
5
|
-
const
|
|
6
|
-
|
|
5
|
+
const APP_SERVER_API_KEY_HEADER = "x-tacore-app-server-api-key";
|
|
6
|
+
const APP_SERVER_SERVICE_TOKEN_HEADER = "x-tacore-app-server-service-token";
|
|
7
|
+
const APP_SERVER_OWNER_APP_ID_HEADER = "x-tacore-app-server-owner-app-id";
|
|
8
|
+
const APP_SERVER_SERVICE_CREDENTIAL_REQUIRED_ERROR =
|
|
9
|
+
"AppServer service credential is required for backend service auth. Provide TACORE_SERVER_INTEROP_APP_SERVER_API_KEY + TACORE_APP_SERVER_SERVICE_TOKEN, or forward a validated appServerApiKey.";
|
|
7
10
|
|
|
8
11
|
const normalizeAccessToken = (value) => {
|
|
9
12
|
if (typeof value !== "string") {
|
|
@@ -48,7 +51,17 @@ const writePersistedAccessToken = (token) => {
|
|
|
48
51
|
};
|
|
49
52
|
|
|
50
53
|
const isBackendPreviewMode = () => isBackend && process.env.TACORE_APPSERVER_PREVIEW_MODE === "true";
|
|
51
|
-
const
|
|
54
|
+
const hasAppServerServiceCredential = (config = {}) => {
|
|
55
|
+
if (config.tacoreServerInteropAppServerApiKey && config.tacoreAppServerServiceToken) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
if (config.appServerApiKey) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
return isBackendPreviewMode() && Boolean(config.previewAppServerApiKey);
|
|
62
|
+
};
|
|
63
|
+
const shouldRequireAppServerServiceCredential = (config = {}) =>
|
|
64
|
+
isBackend && !isSSR && config.enableAppServerServiceAuth === true;
|
|
52
65
|
|
|
53
66
|
/**
|
|
54
67
|
* Apps SDK 基类
|
|
@@ -85,9 +98,17 @@ export class BaseAppsClient {
|
|
|
85
98
|
if (isBackend && !isSSR && !this.config.tacoreServerInteropAppServerApiKey) {
|
|
86
99
|
this.config.tacoreServerInteropAppServerApiKey = process.env.TACORE_SERVER_INTEROP_APP_SERVER_API_KEY;
|
|
87
100
|
}
|
|
101
|
+
if (
|
|
102
|
+
isBackend &&
|
|
103
|
+
!isSSR &&
|
|
104
|
+
this.config.enableAppServerServiceAuth === true &&
|
|
105
|
+
!this.config.tacoreAppServerServiceToken
|
|
106
|
+
) {
|
|
107
|
+
this.config.tacoreAppServerServiceToken = process.env.TACORE_APP_SERVER_SERVICE_TOKEN;
|
|
108
|
+
}
|
|
88
109
|
|
|
89
|
-
if (
|
|
90
|
-
throw new Error(
|
|
110
|
+
if (shouldRequireAppServerServiceCredential(this.config) && !hasAppServerServiceCredential(this.config)) {
|
|
111
|
+
throw new Error(APP_SERVER_SERVICE_CREDENTIAL_REQUIRED_ERROR);
|
|
91
112
|
}
|
|
92
113
|
}
|
|
93
114
|
|
|
@@ -157,10 +178,6 @@ export class BaseAppsClient {
|
|
|
157
178
|
...additionalHeaders,
|
|
158
179
|
};
|
|
159
180
|
|
|
160
|
-
if (this.config.datasource) {
|
|
161
|
-
headers["x-datasource"] = this.config.datasource;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
181
|
if (this.config.supabaseBaseUrl) {
|
|
165
182
|
headers["x-supabase-base-url"] = this.config.supabaseBaseUrl;
|
|
166
183
|
}
|
|
@@ -186,8 +203,20 @@ export class BaseAppsClient {
|
|
|
186
203
|
headers["Authorization"] = `Bearer ${token}`;
|
|
187
204
|
}
|
|
188
205
|
|
|
189
|
-
if (isBackend && !isSSR && this.config.
|
|
190
|
-
|
|
206
|
+
if (isBackend && !isSSR && !token && this.config.enableAppServerServiceAuth === true) {
|
|
207
|
+
if (this.config.tacoreServerInteropAppServerApiKey && this.config.tacoreAppServerServiceToken) {
|
|
208
|
+
headers["x-tacore-server-interop-app-server-api-key"] = this.config.tacoreServerInteropAppServerApiKey;
|
|
209
|
+
headers[APP_SERVER_SERVICE_TOKEN_HEADER] = this.config.tacoreAppServerServiceToken;
|
|
210
|
+
headers[APP_SERVER_OWNER_APP_ID_HEADER] = this.config.appServerOwnerAppId || this.config.appId || process.env.APP_ID || this.appId;
|
|
211
|
+
} else if (this.config.appServerApiKey && !headers[APP_SERVER_API_KEY_HEADER]) {
|
|
212
|
+
headers[APP_SERVER_API_KEY_HEADER] = this.config.appServerApiKey;
|
|
213
|
+
} else if (
|
|
214
|
+
isBackendPreviewMode() &&
|
|
215
|
+
this.config.previewAppServerApiKey &&
|
|
216
|
+
!headers[PREVIEW_APPSERVER_API_KEY_HEADER]
|
|
217
|
+
) {
|
|
218
|
+
headers[PREVIEW_APPSERVER_API_KEY_HEADER] = this.config.previewAppServerApiKey;
|
|
219
|
+
}
|
|
191
220
|
} else if (
|
|
192
221
|
isBackendPreviewMode() &&
|
|
193
222
|
!token &&
|
|
@@ -304,6 +333,7 @@ export class BaseAppsClient {
|
|
|
304
333
|
getConfig() {
|
|
305
334
|
const config = { ...this.config };
|
|
306
335
|
delete config.accessToken;
|
|
336
|
+
delete config.appServerApiKey;
|
|
307
337
|
delete config.previewAppServerApiKey;
|
|
308
338
|
return config;
|
|
309
339
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tacoreai/web-sdk",
|
|
3
3
|
"description": "This file is for app server package, not the real npm package",
|
|
4
|
-
"version": "1.22.
|
|
4
|
+
"version": "1.22.1-beta.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"./app-server-runtime": "./database/core/apps/AppServerRuntime.js"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"release": "npm version minor && git commit -am \"web-sdk update version\" && npm publish"
|
|
16
|
+
"release": "npm version minor && git commit -am \"web-sdk update version\" && git push && npm publish",
|
|
17
|
+
"release:beta": "npm version prerelease --preid beta --no-git-tag-version && git commit -am \"web-sdk update beta version\" && npm publish --tag beta"
|
|
17
18
|
}
|
|
18
19
|
}
|
package/utils/index.js
CHANGED
|
@@ -54,11 +54,8 @@ export function getAppsApiBaseUrl() {
|
|
|
54
54
|
}
|
|
55
55
|
// 后端环境逻辑
|
|
56
56
|
if (isBackend) {
|
|
57
|
-
// stone 手动:
|
|
58
|
-
const baseUrl = process.env.TACORE_SERVER_INTRANET_BASE_URL;
|
|
59
|
-
if (!baseUrl) {
|
|
60
|
-
throw new Error("TACORE_SERVER_INTRANET_BASE_URL environment variable is not set in the backend environment.");
|
|
61
|
-
}
|
|
57
|
+
// stone 手动:tacore server 地址,优先使用内网地址,兜底走公网(比如平台外本地部署)
|
|
58
|
+
const baseUrl = process.env.TACORE_SERVER_INTRANET_BASE_URL || 'https://api.tacore.chat';
|
|
62
59
|
return baseUrl;
|
|
63
60
|
}
|
|
64
61
|
|