openxiangda 1.0.141 → 1.0.142
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 +52 -2
- package/lib/cli.js +89 -0
- package/lib/design-gates.js +29 -0
- package/openxiangda-skills/SKILL.md +3 -1
- package/package.json +1 -1
- package/packages/sdk/dist/{ProcessPreview-CsOTCTRT.d.mts → ProcessPreview-CUQFUDvw.d.mts} +5 -2
- package/packages/sdk/dist/{ProcessPreview-CsOTCTRT.d.ts → ProcessPreview-CUQFUDvw.d.ts} +5 -2
- package/packages/sdk/dist/components/index.cjs +59 -19
- package/packages/sdk/dist/components/index.cjs.map +1 -1
- package/packages/sdk/dist/components/index.d.mts +4 -4
- package/packages/sdk/dist/components/index.d.ts +4 -4
- package/packages/sdk/dist/components/index.mjs +59 -19
- package/packages/sdk/dist/components/index.mjs.map +1 -1
- package/packages/sdk/dist/{dataManagementApi-Cd1axl3v.d.ts → dataManagementApi-CwBEmnXg.d.ts} +1 -1
- package/packages/sdk/dist/{dataManagementApi-C1OKWpP7.d.mts → dataManagementApi-DQKInwWS.d.mts} +1 -1
- package/packages/sdk/dist/runtime/index.cjs +75 -20
- package/packages/sdk/dist/runtime/index.cjs.map +1 -1
- package/packages/sdk/dist/runtime/index.d.mts +3 -3
- package/packages/sdk/dist/runtime/index.d.ts +3 -3
- package/packages/sdk/dist/runtime/index.mjs +75 -20
- package/packages/sdk/dist/runtime/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/react.cjs +16 -1
- package/packages/sdk/dist/runtime/react.cjs.map +1 -1
- package/packages/sdk/dist/runtime/react.d.mts +17 -2
- package/packages/sdk/dist/runtime/react.d.ts +17 -2
- package/packages/sdk/dist/runtime/react.mjs +16 -1
- package/packages/sdk/dist/runtime/react.mjs.map +1 -1
- package/templates/openxiangda-react-spa/src/pages/defaults/FormRoutePage.tsx +9 -0
package/README.md
CHANGED
|
@@ -123,6 +123,8 @@ openxiangda permission audit --json
|
|
|
123
123
|
|
|
124
124
|
调用和测试类命令会检查 JSON envelope。HTTP 200 但 `code: "PUBLIC_GRANT_DENIED"`、`success: false` 或其他字符串业务错误码会被当作失败。
|
|
125
125
|
|
|
126
|
+
角色资源里的 `apiPermissionCodes` 依赖平台端已存在的 API 权限点。比如校区管理员需要维护应用角色时,manifest 应声明 `app:role:manage`;如果 `resource publish` 提示缺少这个权限点,说明当前平台租户的 `api_permissions` seed 不完整,通常是平台版本未包含 seed、服务启动 seed 没跑成功,或租户创建在 seed 之后。处理方式是升级/重启平台并补齐权限点 seed,或由平台管理员补入 `app:role:manage`、`app:page-permission-group:manage`、`app:form-permission-group:manage` 等应用级权限点;不要为了通过发布删除 manifest 里的权限声明。
|
|
127
|
+
|
|
126
128
|
Codex skills are installed separately from login/profile state. Run `openxiangda skill install` after installing or linking the CLI. It installs the `openxiangda` root skill plus the 8 top-level subskills into `${CODEX_HOME:-~/.codex}/skills` by default:
|
|
127
129
|
|
|
128
130
|
- `openxiangda`
|
|
@@ -228,10 +230,47 @@ const affiliatedDepartmentExternalId = user?.affiliatedDepartment?.externalId
|
|
|
228
230
|
|
|
229
231
|
工程化资源放在工作区 `src/resources/` 下,由 `openxiangda resource validate|plan|publish|pull` 管理。`workspace publish` 会先构建并注册 workspace 表单/页面,再执行非破坏性资源 upsert,这样菜单、权限组、流程和表单设置可以解析最新的 profile-local ID。需要删除平台中 manifest 未声明的资源时,显式传 `--prune`。连接器页面运行时通过 `sdk.connector.invoke()` / `sdk.connector.call("connector.api")` 调用平台运行时接口,第三方密钥只保存在后端连接器配置中。
|
|
230
232
|
|
|
231
|
-
React SPA 新应用的无需登录访问统一使用 `/view/:appType/public/*`。应用在 `src/resources/routes/*.json` 声明公开路由,在 `src/resources/public-access/*.json` 声明公开策略、外部角色和可访问资源 grant,页面用 `PublicAccessGate` 或 `createPublicAccessClient` 创建 scoped public session。React SPA 路由树必须在 `OpenXiangdaProvider` 内再包一层 `OpenXiangdaPageProvider`;页面只要调用 `usePageSdk()`、`usePageContext()`、`useDataSource()` 等 Page SDK hooks,就依赖这层 provider。公开 guest 的 form、dataView、function、connector 默认全部拒绝,只有 policy `grants` 明确列出的资源可访问,并且仍受对应表单权限组、dataView
|
|
233
|
+
React SPA 新应用的无需登录访问统一使用 `/view/:appType/public/*`。应用在 `src/resources/routes/*.json` 声明公开路由,在 `src/resources/public-access/*.json` 声明公开策略、外部角色和可访问资源 grant,页面用 `PublicAccessGate` 或 `createPublicAccessClient` 创建 scoped public session。React SPA 路由树必须在 `OpenXiangdaProvider` 内再包一层 `OpenXiangdaPageProvider`;页面只要调用 `usePageSdk()`、`usePageContext()`、`useDataSource()` 等 Page SDK hooks,就依赖这层 provider。公开 guest 的 form、dataView、function、connector、storage 默认全部拒绝,只有 policy `grants` 明确列出的资源可访问,并且仍受对应表单权限组、dataView 权限组、storage grant 等后端权限控制。
|
|
232
234
|
|
|
233
235
|
`mode: "ticket"` 的公开策略默认按单次 ticket 使用;只有明确配置 `ticketConfig.singleUse: false` 时才允许复用。新 public session 只返回 bearer token,SDK 会把 token 注入后续 runtime/bootstrap、dataView、function、connector 请求,不依赖认证 cookie。
|
|
234
236
|
|
|
237
|
+
公开页上传图片或附件时,不要放开游客全局 `/service/file/upload`。在 public-access policy 的 `grants.storage` 中声明 bucket、动作、MIME、扩展名、大小、visibility 和 `pathPrefix`,`ImageField` / `AttachmentField` 会通过 `OpenXiangdaProvider` 注入的 public token 调用上传接口。服务端会生成 objectName,前端不要传入路径;私有文件预览/下载通过 access ticket,不直接暴露对象地址。安全默认值是:公开报名、公开采集等场景上传到私有 bucket,按公开会话授权短时预览或下载。
|
|
238
|
+
|
|
239
|
+
```json
|
|
240
|
+
{
|
|
241
|
+
"grants": {
|
|
242
|
+
"storage": [
|
|
243
|
+
{
|
|
244
|
+
"bucketName": "images",
|
|
245
|
+
"actions": ["upload", "preview"],
|
|
246
|
+
"allowedMimeTypes": ["image/jpeg", "image/png", "image/webp"],
|
|
247
|
+
"allowedExtensions": ["jpg", "jpeg", "png", "webp"],
|
|
248
|
+
"maxSizeBytes": 15728640,
|
|
249
|
+
"visibility": "private",
|
|
250
|
+
"pathPrefix": "public/register/images/"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"bucketName": "attachments",
|
|
254
|
+
"actions": ["upload", "preview", "download"],
|
|
255
|
+
"allowedMimeTypes": [
|
|
256
|
+
"application/pdf",
|
|
257
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
258
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
259
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
260
|
+
"text/plain",
|
|
261
|
+
"text/csv",
|
|
262
|
+
"application/json"
|
|
263
|
+
],
|
|
264
|
+
"allowedExtensions": ["pdf", "docx", "xlsx", "pptx", "txt", "csv", "json"],
|
|
265
|
+
"maxSizeBytes": 31457280,
|
|
266
|
+
"visibility": "private",
|
|
267
|
+
"pathPrefix": "public/register/attachments/"
|
|
268
|
+
}
|
|
269
|
+
]
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
235
274
|
公开访问和测试脚本不能只判断 HTTP 状态。平台部分运行时接口可能返回 HTTP 200,同时在 JSON envelope 中返回业务错误码,例如 `code: "PUBLIC_GRANT_DENIED"`。只有 `code` 为 `200`、`"200"` 或兼容成功码 `0` 时才算成功;字符串错误码、`success: false` 或非 2xx HTTP 都必须当作失败。
|
|
236
275
|
|
|
237
276
|
旧 `?publicAccess=guest` 和表单 `settings/forms/*.json` 里的 `publicAccess` 只用于旧 `sy-lowcode-view` 兼容。新 React SPA 应用不要再设计或生成这种链接。
|
|
@@ -280,7 +319,18 @@ import { ImageField } from "openxiangda"
|
|
|
280
319
|
"forms": ["registration_form"],
|
|
281
320
|
"dataViews": ["public_registration_lookup"],
|
|
282
321
|
"functions": ["submit_public_registration"],
|
|
283
|
-
"connectors": ["sms.sendCode"]
|
|
322
|
+
"connectors": ["sms.sendCode"],
|
|
323
|
+
"storage": [
|
|
324
|
+
{
|
|
325
|
+
"bucketName": "images",
|
|
326
|
+
"actions": ["upload", "preview"],
|
|
327
|
+
"allowedMimeTypes": ["image/jpeg", "image/png", "image/webp"],
|
|
328
|
+
"allowedExtensions": ["jpg", "jpeg", "png", "webp"],
|
|
329
|
+
"maxSizeBytes": 15728640,
|
|
330
|
+
"visibility": "private",
|
|
331
|
+
"pathPrefix": "public/register/images/"
|
|
332
|
+
}
|
|
333
|
+
]
|
|
284
334
|
}
|
|
285
335
|
}
|
|
286
336
|
```
|
package/lib/cli.js
CHANGED
|
@@ -5861,17 +5861,64 @@ function buildPublicGrantChecks(bound, grants, flags = {}) {
|
|
|
5861
5861
|
for (const code of splitList(flags.connector || flags['connector-code'])) {
|
|
5862
5862
|
checks.push({ type: 'connector', code, allowed: (grants.connectors || []).includes(code) });
|
|
5863
5863
|
}
|
|
5864
|
+
const storageGrants = Array.isArray(grants.storage) ? grants.storage : [];
|
|
5865
|
+
for (const bucketName of splitList(flags['storage-bucket'] || flags.bucket)) {
|
|
5866
|
+
const action = String(flags['storage-action'] || flags.action || 'upload').trim();
|
|
5867
|
+
const extension = String(flags.extension || flags.ext || '').replace(/^\./, '').toLowerCase();
|
|
5868
|
+
const mimeType = String(flags.mime || flags['mime-type'] || '').trim().toLowerCase();
|
|
5869
|
+
const visibility = String(flags.visibility || '').trim();
|
|
5870
|
+
const size = Number(flags.size || flags['size-bytes'] || 0);
|
|
5871
|
+
checks.push({
|
|
5872
|
+
type: 'storage',
|
|
5873
|
+
bucketName,
|
|
5874
|
+
action,
|
|
5875
|
+
allowed: storageGrants.some(grant =>
|
|
5876
|
+
publicStorageGrantAllows(grant, {
|
|
5877
|
+
bucketName,
|
|
5878
|
+
action,
|
|
5879
|
+
extension,
|
|
5880
|
+
mimeType,
|
|
5881
|
+
visibility,
|
|
5882
|
+
size,
|
|
5883
|
+
})
|
|
5884
|
+
),
|
|
5885
|
+
});
|
|
5886
|
+
}
|
|
5864
5887
|
if (checks.length === 0) {
|
|
5865
5888
|
return [
|
|
5866
5889
|
{ type: 'forms', granted: grants.forms || [], allowed: true },
|
|
5867
5890
|
{ type: 'dataViews', granted: grants.dataViews || [], allowed: true },
|
|
5868
5891
|
{ type: 'functions', granted: grants.functions || [], allowed: true },
|
|
5869
5892
|
{ type: 'connectors', granted: grants.connectors || [], allowed: true },
|
|
5893
|
+
{ type: 'storage', granted: storageGrants, allowed: true },
|
|
5870
5894
|
];
|
|
5871
5895
|
}
|
|
5872
5896
|
return checks;
|
|
5873
5897
|
}
|
|
5874
5898
|
|
|
5899
|
+
function publicStorageGrantAllows(grant, input) {
|
|
5900
|
+
if (!grant || grant.bucketName !== input.bucketName) return false;
|
|
5901
|
+
const actions = Array.isArray(grant.actions) ? grant.actions : [];
|
|
5902
|
+
if (!actions.includes(input.action)) return false;
|
|
5903
|
+
if (input.visibility && String(grant.visibility || 'private') !== input.visibility) return false;
|
|
5904
|
+
if (input.size > 0 && Number(grant.maxSizeBytes || 0) > 0 && input.size > Number(grant.maxSizeBytes)) {
|
|
5905
|
+
return false;
|
|
5906
|
+
}
|
|
5907
|
+
if (input.extension) {
|
|
5908
|
+
const extensions = (grant.allowedExtensions || []).map(value =>
|
|
5909
|
+
String(value || '').replace(/^\./, '').toLowerCase()
|
|
5910
|
+
);
|
|
5911
|
+
if (!extensions.includes(input.extension)) return false;
|
|
5912
|
+
}
|
|
5913
|
+
if (input.mimeType) {
|
|
5914
|
+
const mimeTypes = (grant.allowedMimeTypes || []).map(value =>
|
|
5915
|
+
String(value || '').trim().toLowerCase()
|
|
5916
|
+
);
|
|
5917
|
+
if (!mimeTypes.includes(input.mimeType)) return false;
|
|
5918
|
+
}
|
|
5919
|
+
return true;
|
|
5920
|
+
}
|
|
5921
|
+
|
|
5875
5922
|
async function buildPermissionAudit(config, target, flags = {}) {
|
|
5876
5923
|
const manifest = loadWorkspaceResources();
|
|
5877
5924
|
const validation = validateWorkspaceResources(manifest);
|
|
@@ -10682,9 +10729,51 @@ function normalizePublicAccessGrants(bound, grants = {}) {
|
|
|
10682
10729
|
dataViews: normalizePermissionCodeArray(grants.dataViews),
|
|
10683
10730
|
functions: normalizePermissionCodeArray(grants.functions),
|
|
10684
10731
|
connectors: normalizePermissionCodeArray(grants.connectors),
|
|
10732
|
+
storage: normalizePublicStorageGrants(grants.storage),
|
|
10685
10733
|
});
|
|
10686
10734
|
}
|
|
10687
10735
|
|
|
10736
|
+
function normalizePublicStorageGrants(storage = []) {
|
|
10737
|
+
if (!Array.isArray(storage)) return [];
|
|
10738
|
+
return storage
|
|
10739
|
+
.map(item => {
|
|
10740
|
+
const bucketName = String(item?.bucketName || '').trim();
|
|
10741
|
+
if (!bucketName) return null;
|
|
10742
|
+
return stripUndefinedValues({
|
|
10743
|
+
bucketName,
|
|
10744
|
+
actions: normalizePublicStorageActions(item.actions),
|
|
10745
|
+
allowedMimeTypes: normalizePermissionCodeArray(item.allowedMimeTypes).map(value =>
|
|
10746
|
+
value.toLowerCase()
|
|
10747
|
+
),
|
|
10748
|
+
allowedExtensions: normalizePermissionCodeArray(item.allowedExtensions).map(value =>
|
|
10749
|
+
value.replace(/^\./, '').toLowerCase()
|
|
10750
|
+
),
|
|
10751
|
+
maxSizeBytes: normalizePublicStoragePositiveInteger(item.maxSizeBytes),
|
|
10752
|
+
visibility: String(item.visibility || '').trim() === 'public' ? 'public' : 'private',
|
|
10753
|
+
pathPrefix: normalizePublicStoragePathPrefix(item.pathPrefix),
|
|
10754
|
+
});
|
|
10755
|
+
})
|
|
10756
|
+
.filter(Boolean);
|
|
10757
|
+
}
|
|
10758
|
+
|
|
10759
|
+
function normalizePublicStorageActions(actions) {
|
|
10760
|
+
const allowed = new Set(['upload', 'preview', 'download']);
|
|
10761
|
+
const normalized = normalizePermissionCodeArray(actions).filter(action => allowed.has(action));
|
|
10762
|
+
return normalized.length > 0 ? unique(normalized) : ['upload'];
|
|
10763
|
+
}
|
|
10764
|
+
|
|
10765
|
+
function normalizePublicStoragePositiveInteger(value) {
|
|
10766
|
+
const numeric = Number(value);
|
|
10767
|
+
if (!Number.isFinite(numeric) || numeric <= 0) return undefined;
|
|
10768
|
+
return Math.floor(numeric);
|
|
10769
|
+
}
|
|
10770
|
+
|
|
10771
|
+
function normalizePublicStoragePathPrefix(value) {
|
|
10772
|
+
const normalized = String(value || '').trim().replace(/^\/+/, '');
|
|
10773
|
+
if (!normalized) return undefined;
|
|
10774
|
+
return normalized.endsWith('/') ? normalized : `${normalized}/`;
|
|
10775
|
+
}
|
|
10776
|
+
|
|
10688
10777
|
function normalizeAuthConfigManifest(authConfig) {
|
|
10689
10778
|
const code = authConfig.code || authConfig.resourceCode || 'default';
|
|
10690
10779
|
const configJson = clonePlainJson(
|
package/lib/design-gates.js
CHANGED
|
@@ -459,12 +459,41 @@ const RESOURCE_EXPLAINS = {
|
|
|
459
459
|
dataViews: ['public_lookup'],
|
|
460
460
|
functions: [],
|
|
461
461
|
connectors: [],
|
|
462
|
+
storage: [
|
|
463
|
+
{
|
|
464
|
+
bucketName: 'images',
|
|
465
|
+
actions: ['upload', 'preview'],
|
|
466
|
+
allowedMimeTypes: ['image/jpeg', 'image/png', 'image/webp'],
|
|
467
|
+
allowedExtensions: ['jpg', 'jpeg', 'png', 'webp'],
|
|
468
|
+
maxSizeBytes: 15728640,
|
|
469
|
+
visibility: 'private',
|
|
470
|
+
pathPrefix: 'public/register/images/',
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
bucketName: 'attachments',
|
|
474
|
+
actions: ['upload', 'preview', 'download'],
|
|
475
|
+
allowedMimeTypes: [
|
|
476
|
+
'application/pdf',
|
|
477
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
478
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
479
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
480
|
+
'text/plain',
|
|
481
|
+
'text/csv',
|
|
482
|
+
'application/json',
|
|
483
|
+
],
|
|
484
|
+
allowedExtensions: ['pdf', 'docx', 'xlsx', 'pptx', 'txt', 'csv', 'json'],
|
|
485
|
+
maxSizeBytes: 31457280,
|
|
486
|
+
visibility: 'private',
|
|
487
|
+
pathPrefix: 'public/register/attachments/',
|
|
488
|
+
},
|
|
489
|
+
],
|
|
462
490
|
},
|
|
463
491
|
},
|
|
464
492
|
commands: [
|
|
465
493
|
'openxiangda public-access upsert --json-file src/resources/public-access/public_register.json',
|
|
466
494
|
'openxiangda public-access session-test public_register --path /view/APP_XXX/public/register',
|
|
467
495
|
'openxiangda public-access grant-check public_register --form-code customer',
|
|
496
|
+
'公开页 ImageField/AttachmentField 上传走 public session bearer token;storage grant 按 bucket/action/MIME/ext/size/visibility/pathPrefix 限制。',
|
|
468
497
|
'React 页面侧使用 PublicAccessGate;如果使用 Page SDK hooks,放在 OpenXiangdaProvider + OpenXiangdaPageProvider 内',
|
|
469
498
|
],
|
|
470
499
|
},
|
|
@@ -50,7 +50,7 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
|
|
|
50
50
|
- ✅ Run `openxiangda update check --json` at the start of substantial work; if `updateAvailable`, run `openxiangda update install` and `openxiangda skill install --force`.
|
|
51
51
|
- ✅ For non-trivial app requirements, run the architecture design gate first: forms, fields, pages, permissions, data views, status/workflow, automation, notifications, and development tasks must be decided before coding.
|
|
52
52
|
- ✅ For app login/auth requirements, run the auth security gate before coding: enabled methods, registration policy, identity matching keys, provider boundary, default roles, rate limits, audit fields, and third-party ownership must be confirmed.
|
|
53
|
-
- ✅ For public/no-login access requirements, run the public access design gate before coding: public routes, external role codes, guest vs ticket, form/dataView/function/connector grants, backend permission groups, and visible loading/error fallback states must be confirmed. New React SPA apps use `/view/:appType/public/*`, `src/resources/routes/`, `src/resources/public-access/`, and `PublicAccessGate`; route children that use Page SDK hooks must be inside `OpenXiangdaProvider` + `OpenXiangdaPageProvider`.
|
|
53
|
+
- ✅ For public/no-login access requirements, run the public access design gate before coding: public routes, external role codes, guest vs ticket, form/dataView/function/connector/storage grants, backend permission groups, and visible loading/error fallback states must be confirmed. New React SPA apps use `/view/:appType/public/*`, `src/resources/routes/`, `src/resources/public-access/`, and `PublicAccessGate`; route children that use Page SDK hooks must be inside `OpenXiangdaProvider` + `OpenXiangdaPageProvider`.
|
|
54
54
|
- ✅ For account/role/permission/data-scope/query-param authorization requirements, run `openxiangda design gates --topic permissions --json` before coding. Pick one mode from `managed-platform-account`, `existing-platform-user-assignment`, `static-role-permission`, or `query-param-context`, write the permission matrix, and use roles/page-groups/form-groups/App Functions for real enforcement.
|
|
55
55
|
- ✅ Public/no-login validation must inspect the JSON envelope, not only HTTP status. HTTP 200 with `code: "PUBLIC_GRANT_DENIED"` is a denied request; success requires `code` `200`, `"200"`, or compatible `0`, and no `success: false`.
|
|
56
56
|
- ✅ Use first-class resource CLI commands for discovery, diagnosis, and small live fixes: `route`, `public-access`, `auth-config`, `function`, `connector`, `notification`, `organization`, `data-view`, `menu`, and `permission`. For multi-resource formal development, still prefer `src/resources/**` + `openxiangda resource validate|plan|publish`. If a direct CLI write is intentionally used, add `--write-manifest` when the repository should stay the source of truth.
|
|
@@ -151,6 +151,7 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
|
|
|
151
151
|
- Never store token data in the project directory. User tokens live in `~/.openxiangda/profiles.json`; project state lives in `.openxiangda/state.json` and stores only IDs and mappings.
|
|
152
152
|
- Shared workspace env values such as `APP_OSS_*` should live in `~/.openxiangda/.env` by default. Project `.env` files are only per-workspace overrides.
|
|
153
153
|
- For custom OSS image or attachment upload, declare `src/resources/storage/<code>.json`, then set `uploadProvider: "oss"` and `storageCode` on `AttachmentField` or `ImageField`. If `imageCompression` outputs `webp` or another explicit format, include that extension in storage `allowedExtensions`.
|
|
154
|
+
- For public/no-login image or attachment uploads, declare `grants.storage` in `src/resources/public-access/<code>.json`; do not open `/service/file/upload` globally. Use conservative private bucket grants by default, with explicit bucket, actions, MIME types, extensions, max size, visibility, and pathPrefix. `ImageField` / `AttachmentField` uploads inside `PublicAccessGate` use the public session bearer token injected by `OpenXiangdaProvider`.
|
|
154
155
|
- For suspected platform defects, bugs, missing capabilities, unclear design rules, repeated workarounds, AI uncertainty, or product optimization requests, proactively run `openxiangda feedback submit --yes` with a concise summary and detailed description. Include the command, error, relevant files, and logs/context files when available. If robot env is missing or submission fails, run/record `openxiangda feedback preview` and tell the user what could not be sent. After successful submission, tell the user what was reported and include the feedback fingerprint. The CLI redacts tokens, cookies, secrets, phone numbers, and emails before sending.
|
|
155
156
|
- For form pages, every visible field should have a concise user-facing `placeholder`. Use `tips` only for special constraints or non-obvious business rules; do not add tips to every field.
|
|
156
157
|
- Form pages should display only fields the user needs to see. Use `SelectField` / `RadioField` for enums. For data maintained by another form, such as class, college, customer, or project, use `SelectField` with `optionSource.type: "linkedForm"` so the runtime queries form data through the SDK and builds dropdown options; set `remoteSearch: true` and an explicit `searchFieldId` when the source data can be large. Do not make users maintain raw ID text fields, and do not use `AssociationFormField` for new form work.
|
|
@@ -165,6 +166,7 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
|
|
|
165
166
|
- For app-managed platform departments/accounts, use `sdk.organization.departments.*`, `sdk.organization.accounts.*`, `ctx.organization.departments.*`, or `ctx.organization.accounts.*`. The real current user/audit actor must hold `app:organization:manage` on the app; runtime service principals do not bypass this permission. Do not call legacy `/user` or `/department` write endpoints from apps. Do not include `password` in account updates; reset target accounts with `resetPassword`, and change the current user's password with `changeMyPassword({ oldPassword, newPassword })`.
|
|
166
167
|
- For account, role, data-scope, organization-account, RBAC, or query-param authorization designs, read `references/permission-design-patterns.md` and choose a permission mode before writing forms/pages/resources. Formal backends should use platform roles, page permission groups, form permission groups, public-access grants, and App Function role/scope checks; frontend hiding is display only.
|
|
167
168
|
- When a role such as 校区管理员 will create app roles, assign role members, grant role permissions, maintain permission groups, or manage organization accounts, its role manifest must declare the needed `apiPermissionCodes` such as `app:role:manage`, `app:page-permission-group:manage`, `app:form-permission-group:manage`, and `app:organization:manage`. If that administrator creates another administrator role, the new role must also declare its own `apiPermissionCodes`; permissions are not inherited from the creator.
|
|
169
|
+
- If `resource publish` reports a missing API permission point such as `app:role:manage`, treat it as a platform seed/data problem. The platform tenant must have the permission code in `api_permissions`; do not remove the role manifest permission just to make publish pass.
|
|
168
170
|
- For application login, declare auth resources in `src/resources/auth/<code>.json` and publish them with `openxiangda resource publish`. App Function auth providers may validate external credentials and return only an identity assertion; they must never issue tokens, set cookies, or write platform user/binding tables directly. The platform auth service decides create/bind/reject and issues tokens.
|
|
169
171
|
- For external/no-login pages in React SPA apps, declare `src/resources/routes/<code>.json` and `src/resources/public-access/<code>.json`, put the page under `/view/:appType/public/*`, and use `PublicAccessGate` or `createPublicAccessClient`. The route tree must also include `OpenXiangdaPageProvider` inside `OpenXiangdaProvider` before any `usePageSdk()` / `usePageContext()` / `useDataSource()` call; otherwise pages throw `usePageSdkStore 必须在 PageProvider 内使用`. Always provide `fallback` and `errorFallback` for public routes so slow networks, missing tickets, or expired tickets do not render blank pages. Public guest access to forms, dataViews, functions, and connectors is denied unless policy `grants` explicitly names the resource; backend permission groups still apply.
|
|
170
172
|
- When verifying public access, parse the response body and reject string business errors such as `PUBLIC_GRANT_DENIED` even if the HTTP status is 200. Do not use `response.ok` alone as the success condition.
|
package/package.json
CHANGED
|
@@ -248,9 +248,10 @@ interface RuntimeRequestConfig {
|
|
|
248
248
|
method?: string;
|
|
249
249
|
params?: Record<string, any>;
|
|
250
250
|
data?: any;
|
|
251
|
-
headers?:
|
|
251
|
+
headers?: HeadersInit;
|
|
252
252
|
responseType?: 'json' | 'blob';
|
|
253
253
|
}
|
|
254
|
+
type RuntimeAuthHeadersProvider = () => HeadersInit | undefined;
|
|
254
255
|
type RuntimeUploadProvider = 'platform' | 'oss' | 'builtin-oss';
|
|
255
256
|
interface RuntimeUploadOptions {
|
|
256
257
|
uploadProvider?: RuntimeUploadProvider;
|
|
@@ -293,6 +294,8 @@ interface FormRuntimeApi {
|
|
|
293
294
|
}
|
|
294
295
|
type FormRuntimeApiConfig = Partial<FormRuntimeApi> & {
|
|
295
296
|
baseUrl?: string;
|
|
297
|
+
fetchImpl?: typeof fetch;
|
|
298
|
+
getAuthHeaders?: RuntimeAuthHeadersProvider;
|
|
296
299
|
};
|
|
297
300
|
/** 表单引擎配置 */
|
|
298
301
|
interface FormEngineConfig {
|
|
@@ -1171,4 +1174,4 @@ interface ProcessPreviewProps {
|
|
|
1171
1174
|
}
|
|
1172
1175
|
declare const ProcessPreview: React__default.FC<ProcessPreviewProps>;
|
|
1173
1176
|
|
|
1174
|
-
export { type DepartmentSelectFieldProps as $, type ApprovalPermission as A, type ReturnParams as B, type ChangeRecordQueryParams as C, type SaveTaskParams as D, type TransferParams as E, type FieldDefinition as F, type TextFieldProps as G, type TextAreaFieldProps as H, type InitiatorSelectCandidate as I, type SelectFieldProps as J, type RadioFieldProps as K, type CheckboxFieldProps as L, type MultiSelectFieldProps as M, type NumberFieldProps as N, type OptionItem as O, type ProcessStatus as P, type DateFieldProps as Q, type RuntimeResponse as R, type StatusMeta as S, type TaskStatus as T, type CascadeDateFieldProps as U, type ValidationPreset as V, type WithdrawParams as W, type AttachmentFieldProps as X, type ImageFieldProps as Y, type SubFormFieldProps as Z, type UserSelectFieldProps as _, type FormRuntimeApi as a, ProcessPreview as a$, type CascadeSelectFieldProps as a0, type AddressFieldProps as a1, type AssociationFormFieldProps as a2, type EditorFieldProps as a3, type EditorChoiceOption as a4, type SerialNumberFieldProps as a5, type LocationFieldProps as a6, type DigitalSignatureFieldProps as a7, type JSONFieldProps as a8, type ProcessAction as a9, type FieldLayoutNode as aA, type FieldValueSyncConfig as aB, type FormEffectAction as aC, type FormEffectCondition as aD, type FormEffectConditionOperator as aE, type FormEngineMode as aF, type FormLayoutNode as aG, FormSection as aH, type FormSectionProps as aI, type FormSubmitBehavior as aJ, type FormTemplateConfig as aK, type GridLayoutCell as aL, type GridLayoutNode as aM, type ImageCompressionConfig as aN, type ImageCompressionVariantConfig as aO, type ImageVariant as aP, type InitiatorSelectScope as aQ, type LayoutVisibleWhen as aR, type LinkedFormOptionConfig as aS, type LocationValue as aT, type LowcodePageMeta as aU, type LowcodePageNode as aV, type LowcodePageNodeType as aW, type OptionSourceType as aX, type PeopleShortcutConfig as aY, type PeopleShortcutType as aZ, type ProcessNodeType as a_, type InitiatorSelectedApprovers as aa, type ReturnPolicy as ab, type ChangeRecord as ac, type StandardFormPageMode as ad, type LowcodePageSchema as ae, type AddressValue as af, type ApprovalActionType as ag, ApprovalTimeline as ah, type ApprovalTimelineProps as ai, type AssociationFormConfig as aj, type AssociationValue as ak, type AttachmentImageVariants as al, type AttachmentItem as am, type BaseFieldProps as an, type BaseLayoutNode as ao, type DataFilter as ap, type DataLinkageCondition as aq, type DataLinkageConfig as ar, type DateRangeRestriction as as, type DateRestrictionConfig as at, type DateShortcutConfig as au, type DateShortcutType as av, type DefaultValueLinkageConfig as aw, type DepartmentTreeNode as ax, type DigitalSignatureValue as ay, type EditorToolbarAction as az, type FormSchema as b, type ProcessPreviewProps as b0, type
|
|
1177
|
+
export { type DepartmentSelectFieldProps as $, type ApprovalPermission as A, type ReturnParams as B, type ChangeRecordQueryParams as C, type SaveTaskParams as D, type TransferParams as E, type FieldDefinition as F, type TextFieldProps as G, type TextAreaFieldProps as H, type InitiatorSelectCandidate as I, type SelectFieldProps as J, type RadioFieldProps as K, type CheckboxFieldProps as L, type MultiSelectFieldProps as M, type NumberFieldProps as N, type OptionItem as O, type ProcessStatus as P, type DateFieldProps as Q, type RuntimeResponse as R, type StatusMeta as S, type TaskStatus as T, type CascadeDateFieldProps as U, type ValidationPreset as V, type WithdrawParams as W, type AttachmentFieldProps as X, type ImageFieldProps as Y, type SubFormFieldProps as Z, type UserSelectFieldProps as _, type FormRuntimeApi as a, ProcessPreview as a$, type CascadeSelectFieldProps as a0, type AddressFieldProps as a1, type AssociationFormFieldProps as a2, type EditorFieldProps as a3, type EditorChoiceOption as a4, type SerialNumberFieldProps as a5, type LocationFieldProps as a6, type DigitalSignatureFieldProps as a7, type JSONFieldProps as a8, type ProcessAction as a9, type FieldLayoutNode as aA, type FieldValueSyncConfig as aB, type FormEffectAction as aC, type FormEffectCondition as aD, type FormEffectConditionOperator as aE, type FormEngineMode as aF, type FormLayoutNode as aG, FormSection as aH, type FormSectionProps as aI, type FormSubmitBehavior as aJ, type FormTemplateConfig as aK, type GridLayoutCell as aL, type GridLayoutNode as aM, type ImageCompressionConfig as aN, type ImageCompressionVariantConfig as aO, type ImageVariant as aP, type InitiatorSelectScope as aQ, type LayoutVisibleWhen as aR, type LinkedFormOptionConfig as aS, type LocationValue as aT, type LowcodePageMeta as aU, type LowcodePageNode as aV, type LowcodePageNodeType as aW, type OptionSourceType as aX, type PeopleShortcutConfig as aY, type PeopleShortcutType as aZ, type ProcessNodeType as a_, type InitiatorSelectedApprovers as aa, type ReturnPolicy as ab, type ChangeRecord as ac, type StandardFormPageMode as ad, type LowcodePageSchema as ae, type AddressValue as af, type ApprovalActionType as ag, ApprovalTimeline as ah, type ApprovalTimelineProps as ai, type AssociationFormConfig as aj, type AssociationValue as ak, type AttachmentImageVariants as al, type AttachmentItem as am, type BaseFieldProps as an, type BaseLayoutNode as ao, type DataFilter as ap, type DataLinkageCondition as aq, type DataLinkageConfig as ar, type DateRangeRestriction as as, type DateRestrictionConfig as at, type DateShortcutConfig as au, type DateShortcutType as av, type DefaultValueLinkageConfig as aw, type DepartmentTreeNode as ax, type DigitalSignatureValue as ay, type EditorToolbarAction as az, type FormSchema as b, type ProcessPreviewProps as b0, type RuntimeAuthHeadersProvider as b1, type RuntimeDataQueryParams as b2, type RuntimeDataQueryResult as b3, type RuntimeRequestConfig as b4, type RuntimeUploadOptions as b5, type RuntimeUploadProvider as b6, type SectionLayoutNode as b7, type SignaturePoint as b8, type StepLayoutItem as b9, type StepsLayoutNode as ba, type SubFormColumn as bb, type TabLayoutItem as bc, type TabsLayoutNode as bd, type TextShortcutConfig as be, type TextShortcutType as bf, type UserDisplayFormat as bg, type UserItem as bh, type FormRuntimeApiConfig as c, type FormEngineConfig as d, type FieldBehavior as e, type FormRuntimeConfig as f, type FormAppearanceConfig as g, type ValidationRule as h, type FormEffect as i, type OptionSourceConfig as j, type FormDataDeleteParams as k, type ChangeRecordListResponse as l, type FormDataQueryParams as m, type FormInstanceData as n, type InitiatorSelectRequirement as o, type ProcessBasicInfo as p, type ProcessDefinition as q, type ProcessTask as r, type ReturnableNodeResult as s, type ReturnableNode as t, type ViewPermissionQueryParams as u, type ViewPermissionSummary as v, type ApproveParams as w, type PreviewParams as x, type ProcessRoute as y, type ResubmitParams as z };
|
|
@@ -248,9 +248,10 @@ interface RuntimeRequestConfig {
|
|
|
248
248
|
method?: string;
|
|
249
249
|
params?: Record<string, any>;
|
|
250
250
|
data?: any;
|
|
251
|
-
headers?:
|
|
251
|
+
headers?: HeadersInit;
|
|
252
252
|
responseType?: 'json' | 'blob';
|
|
253
253
|
}
|
|
254
|
+
type RuntimeAuthHeadersProvider = () => HeadersInit | undefined;
|
|
254
255
|
type RuntimeUploadProvider = 'platform' | 'oss' | 'builtin-oss';
|
|
255
256
|
interface RuntimeUploadOptions {
|
|
256
257
|
uploadProvider?: RuntimeUploadProvider;
|
|
@@ -293,6 +294,8 @@ interface FormRuntimeApi {
|
|
|
293
294
|
}
|
|
294
295
|
type FormRuntimeApiConfig = Partial<FormRuntimeApi> & {
|
|
295
296
|
baseUrl?: string;
|
|
297
|
+
fetchImpl?: typeof fetch;
|
|
298
|
+
getAuthHeaders?: RuntimeAuthHeadersProvider;
|
|
296
299
|
};
|
|
297
300
|
/** 表单引擎配置 */
|
|
298
301
|
interface FormEngineConfig {
|
|
@@ -1171,4 +1174,4 @@ interface ProcessPreviewProps {
|
|
|
1171
1174
|
}
|
|
1172
1175
|
declare const ProcessPreview: React__default.FC<ProcessPreviewProps>;
|
|
1173
1176
|
|
|
1174
|
-
export { type DepartmentSelectFieldProps as $, type ApprovalPermission as A, type ReturnParams as B, type ChangeRecordQueryParams as C, type SaveTaskParams as D, type TransferParams as E, type FieldDefinition as F, type TextFieldProps as G, type TextAreaFieldProps as H, type InitiatorSelectCandidate as I, type SelectFieldProps as J, type RadioFieldProps as K, type CheckboxFieldProps as L, type MultiSelectFieldProps as M, type NumberFieldProps as N, type OptionItem as O, type ProcessStatus as P, type DateFieldProps as Q, type RuntimeResponse as R, type StatusMeta as S, type TaskStatus as T, type CascadeDateFieldProps as U, type ValidationPreset as V, type WithdrawParams as W, type AttachmentFieldProps as X, type ImageFieldProps as Y, type SubFormFieldProps as Z, type UserSelectFieldProps as _, type FormRuntimeApi as a, ProcessPreview as a$, type CascadeSelectFieldProps as a0, type AddressFieldProps as a1, type AssociationFormFieldProps as a2, type EditorFieldProps as a3, type EditorChoiceOption as a4, type SerialNumberFieldProps as a5, type LocationFieldProps as a6, type DigitalSignatureFieldProps as a7, type JSONFieldProps as a8, type ProcessAction as a9, type FieldLayoutNode as aA, type FieldValueSyncConfig as aB, type FormEffectAction as aC, type FormEffectCondition as aD, type FormEffectConditionOperator as aE, type FormEngineMode as aF, type FormLayoutNode as aG, FormSection as aH, type FormSectionProps as aI, type FormSubmitBehavior as aJ, type FormTemplateConfig as aK, type GridLayoutCell as aL, type GridLayoutNode as aM, type ImageCompressionConfig as aN, type ImageCompressionVariantConfig as aO, type ImageVariant as aP, type InitiatorSelectScope as aQ, type LayoutVisibleWhen as aR, type LinkedFormOptionConfig as aS, type LocationValue as aT, type LowcodePageMeta as aU, type LowcodePageNode as aV, type LowcodePageNodeType as aW, type OptionSourceType as aX, type PeopleShortcutConfig as aY, type PeopleShortcutType as aZ, type ProcessNodeType as a_, type InitiatorSelectedApprovers as aa, type ReturnPolicy as ab, type ChangeRecord as ac, type StandardFormPageMode as ad, type LowcodePageSchema as ae, type AddressValue as af, type ApprovalActionType as ag, ApprovalTimeline as ah, type ApprovalTimelineProps as ai, type AssociationFormConfig as aj, type AssociationValue as ak, type AttachmentImageVariants as al, type AttachmentItem as am, type BaseFieldProps as an, type BaseLayoutNode as ao, type DataFilter as ap, type DataLinkageCondition as aq, type DataLinkageConfig as ar, type DateRangeRestriction as as, type DateRestrictionConfig as at, type DateShortcutConfig as au, type DateShortcutType as av, type DefaultValueLinkageConfig as aw, type DepartmentTreeNode as ax, type DigitalSignatureValue as ay, type EditorToolbarAction as az, type FormSchema as b, type ProcessPreviewProps as b0, type
|
|
1177
|
+
export { type DepartmentSelectFieldProps as $, type ApprovalPermission as A, type ReturnParams as B, type ChangeRecordQueryParams as C, type SaveTaskParams as D, type TransferParams as E, type FieldDefinition as F, type TextFieldProps as G, type TextAreaFieldProps as H, type InitiatorSelectCandidate as I, type SelectFieldProps as J, type RadioFieldProps as K, type CheckboxFieldProps as L, type MultiSelectFieldProps as M, type NumberFieldProps as N, type OptionItem as O, type ProcessStatus as P, type DateFieldProps as Q, type RuntimeResponse as R, type StatusMeta as S, type TaskStatus as T, type CascadeDateFieldProps as U, type ValidationPreset as V, type WithdrawParams as W, type AttachmentFieldProps as X, type ImageFieldProps as Y, type SubFormFieldProps as Z, type UserSelectFieldProps as _, type FormRuntimeApi as a, ProcessPreview as a$, type CascadeSelectFieldProps as a0, type AddressFieldProps as a1, type AssociationFormFieldProps as a2, type EditorFieldProps as a3, type EditorChoiceOption as a4, type SerialNumberFieldProps as a5, type LocationFieldProps as a6, type DigitalSignatureFieldProps as a7, type JSONFieldProps as a8, type ProcessAction as a9, type FieldLayoutNode as aA, type FieldValueSyncConfig as aB, type FormEffectAction as aC, type FormEffectCondition as aD, type FormEffectConditionOperator as aE, type FormEngineMode as aF, type FormLayoutNode as aG, FormSection as aH, type FormSectionProps as aI, type FormSubmitBehavior as aJ, type FormTemplateConfig as aK, type GridLayoutCell as aL, type GridLayoutNode as aM, type ImageCompressionConfig as aN, type ImageCompressionVariantConfig as aO, type ImageVariant as aP, type InitiatorSelectScope as aQ, type LayoutVisibleWhen as aR, type LinkedFormOptionConfig as aS, type LocationValue as aT, type LowcodePageMeta as aU, type LowcodePageNode as aV, type LowcodePageNodeType as aW, type OptionSourceType as aX, type PeopleShortcutConfig as aY, type PeopleShortcutType as aZ, type ProcessNodeType as a_, type InitiatorSelectedApprovers as aa, type ReturnPolicy as ab, type ChangeRecord as ac, type StandardFormPageMode as ad, type LowcodePageSchema as ae, type AddressValue as af, type ApprovalActionType as ag, ApprovalTimeline as ah, type ApprovalTimelineProps as ai, type AssociationFormConfig as aj, type AssociationValue as ak, type AttachmentImageVariants as al, type AttachmentItem as am, type BaseFieldProps as an, type BaseLayoutNode as ao, type DataFilter as ap, type DataLinkageCondition as aq, type DataLinkageConfig as ar, type DateRangeRestriction as as, type DateRestrictionConfig as at, type DateShortcutConfig as au, type DateShortcutType as av, type DefaultValueLinkageConfig as aw, type DepartmentTreeNode as ax, type DigitalSignatureValue as ay, type EditorToolbarAction as az, type FormSchema as b, type ProcessPreviewProps as b0, type RuntimeAuthHeadersProvider as b1, type RuntimeDataQueryParams as b2, type RuntimeDataQueryResult as b3, type RuntimeRequestConfig as b4, type RuntimeUploadOptions as b5, type RuntimeUploadProvider as b6, type SectionLayoutNode as b7, type SignaturePoint as b8, type StepLayoutItem as b9, type StepsLayoutNode as ba, type SubFormColumn as bb, type TabLayoutItem as bc, type TabsLayoutNode as bd, type TextShortcutConfig as be, type TextShortcutType as bf, type UserDisplayFormat as bg, type UserItem as bh, type FormRuntimeApiConfig as c, type FormEngineConfig as d, type FieldBehavior as e, type FormRuntimeConfig as f, type FormAppearanceConfig as g, type ValidationRule as h, type FormEffect as i, type OptionSourceConfig as j, type FormDataDeleteParams as k, type ChangeRecordListResponse as l, type FormDataQueryParams as m, type FormInstanceData as n, type InitiatorSelectRequirement as o, type ProcessBasicInfo as p, type ProcessDefinition as q, type ProcessTask as r, type ReturnableNodeResult as s, type ReturnableNode as t, type ViewPermissionQueryParams as u, type ViewPermissionSummary as v, type ApproveParams as w, type PreviewParams as x, type ProcessRoute as y, type ResubmitParams as z };
|
|
@@ -10116,7 +10116,31 @@ var parseResponse = async (response) => {
|
|
|
10116
10116
|
message: response.statusText
|
|
10117
10117
|
};
|
|
10118
10118
|
};
|
|
10119
|
-
var
|
|
10119
|
+
var applyAuthHeaders = (headers, getAuthHeaders) => {
|
|
10120
|
+
const authHeaders = getAuthHeaders?.();
|
|
10121
|
+
if (authHeaders) {
|
|
10122
|
+
new Headers(authHeaders).forEach((value, key) => {
|
|
10123
|
+
if (!headers.has(key)) headers.set(key, value);
|
|
10124
|
+
});
|
|
10125
|
+
}
|
|
10126
|
+
const token = typeof window !== "undefined" ? window.localStorage?.getItem("token") : void 0;
|
|
10127
|
+
if (token && !headers.has("authorization")) {
|
|
10128
|
+
headers.set("authorization", token);
|
|
10129
|
+
}
|
|
10130
|
+
};
|
|
10131
|
+
var applyXhrAuthHeaders = (xhr, getAuthHeaders) => {
|
|
10132
|
+
let hasAuthorization = false;
|
|
10133
|
+
const authHeaders = getAuthHeaders?.();
|
|
10134
|
+
if (authHeaders) {
|
|
10135
|
+
new Headers(authHeaders).forEach((value, key) => {
|
|
10136
|
+
if (key.toLowerCase() === "authorization") hasAuthorization = true;
|
|
10137
|
+
xhr.setRequestHeader(key, value);
|
|
10138
|
+
});
|
|
10139
|
+
}
|
|
10140
|
+
const token = typeof window !== "undefined" ? window.localStorage?.getItem("token") : void 0;
|
|
10141
|
+
if (token && !hasAuthorization) xhr.setRequestHeader("authorization", token);
|
|
10142
|
+
};
|
|
10143
|
+
var createDefaultRequest = (baseUrl, fetchImpl = fetch, getAuthHeaders) => async (config) => {
|
|
10120
10144
|
const method = config.method ?? "get";
|
|
10121
10145
|
const url = appendQuery(joinUrl(baseUrl, config.url), config.params);
|
|
10122
10146
|
const headers = new Headers(config.headers);
|
|
@@ -10129,11 +10153,8 @@ var createDefaultRequest = (baseUrl) => async (config) => {
|
|
|
10129
10153
|
body = JSON.stringify(config.data);
|
|
10130
10154
|
}
|
|
10131
10155
|
}
|
|
10132
|
-
|
|
10133
|
-
|
|
10134
|
-
headers.set("authorization", token);
|
|
10135
|
-
}
|
|
10136
|
-
const response = await fetch(url, {
|
|
10156
|
+
applyAuthHeaders(headers, getAuthHeaders);
|
|
10157
|
+
const response = await fetchImpl(url, {
|
|
10137
10158
|
method,
|
|
10138
10159
|
headers,
|
|
10139
10160
|
body,
|
|
@@ -10201,7 +10222,7 @@ var normalizeUploadData = (data, file, bucketName, baseUrl = "") => {
|
|
|
10201
10222
|
variants
|
|
10202
10223
|
};
|
|
10203
10224
|
};
|
|
10204
|
-
var uploadWithXhr = (baseUrl, file, bucketName, onProgress, options = {}) => new Promise((resolve, reject) => {
|
|
10225
|
+
var uploadWithXhr = (baseUrl, file, bucketName, onProgress, options = {}, getAuthHeaders) => new Promise((resolve, reject) => {
|
|
10205
10226
|
if (globalThis.process?.env?.VITEST) {
|
|
10206
10227
|
onProgress?.(100);
|
|
10207
10228
|
resolve({
|
|
@@ -10231,8 +10252,7 @@ var uploadWithXhr = (baseUrl, file, bucketName, onProgress, options = {}) => new
|
|
|
10231
10252
|
)
|
|
10232
10253
|
);
|
|
10233
10254
|
xhr.withCredentials = true;
|
|
10234
|
-
|
|
10235
|
-
if (token) xhr.setRequestHeader("authorization", token);
|
|
10255
|
+
applyXhrAuthHeaders(xhr, getAuthHeaders);
|
|
10236
10256
|
xhr.upload.onprogress = (event) => {
|
|
10237
10257
|
if (event.lengthComputable) {
|
|
10238
10258
|
onProgress?.(Math.round(event.loaded / event.total * 100));
|
|
@@ -10336,7 +10356,8 @@ async function uploadChunkedFile(request, file, bucketName, baseUrl, onProgress,
|
|
|
10336
10356
|
fileSize: file.size,
|
|
10337
10357
|
chunkSize: DEFAULT_CHUNK_SIZE,
|
|
10338
10358
|
bucketName,
|
|
10339
|
-
visibility: options.visibility
|
|
10359
|
+
visibility: options.visibility,
|
|
10360
|
+
contentType: file.type || void 0
|
|
10340
10361
|
}
|
|
10341
10362
|
});
|
|
10342
10363
|
const uploadInfo = initiate.data || initiate.result;
|
|
@@ -10395,7 +10416,7 @@ var createSegmentProgress = (onProgress, start, end) => (percent) => {
|
|
|
10395
10416
|
onProgress?.(Math.round(next));
|
|
10396
10417
|
};
|
|
10397
10418
|
var getAttachmentUrl = (item) => item.publicUrl || item.previewUrl || item.downloadUrl || item.url || "";
|
|
10398
|
-
async function uploadSingleRuntimeFile(request, baseUrl, file, bucketName, onProgress, options) {
|
|
10419
|
+
async function uploadSingleRuntimeFile(request, baseUrl, file, bucketName, onProgress, options, getAuthHeaders) {
|
|
10399
10420
|
const singleOptions = stripImageCompressionOptions(options);
|
|
10400
10421
|
if (singleOptions.uploadProvider === "builtin-oss") {
|
|
10401
10422
|
return uploadBuiltinOssFile(request, file, bucketName, onProgress, singleOptions);
|
|
@@ -10406,7 +10427,7 @@ async function uploadSingleRuntimeFile(request, baseUrl, file, bucketName, onPro
|
|
|
10406
10427
|
if (file.size > CHUNK_UPLOAD_THRESHOLD) {
|
|
10407
10428
|
return uploadChunkedFile(request, file, bucketName, baseUrl, onProgress);
|
|
10408
10429
|
}
|
|
10409
|
-
return uploadWithXhr(baseUrl, file, bucketName, onProgress);
|
|
10430
|
+
return uploadWithXhr(baseUrl, file, bucketName, onProgress, {}, getAuthHeaders);
|
|
10410
10431
|
}
|
|
10411
10432
|
function toImageVariantMetadata(uploaded, variant) {
|
|
10412
10433
|
return {
|
|
@@ -10420,7 +10441,7 @@ function toImageVariantMetadata(uploaded, variant) {
|
|
|
10420
10441
|
quality: variant.quality
|
|
10421
10442
|
};
|
|
10422
10443
|
}
|
|
10423
|
-
async function uploadFileWithImageVariants(request, baseUrl, file, bucketName, onProgress, options) {
|
|
10444
|
+
async function uploadFileWithImageVariants(request, baseUrl, file, bucketName, onProgress, options, getAuthHeaders) {
|
|
10424
10445
|
let variants = [];
|
|
10425
10446
|
try {
|
|
10426
10447
|
variants = await createCompressedImageVariants(file, options.imageCompression);
|
|
@@ -10428,7 +10449,15 @@ async function uploadFileWithImageVariants(request, baseUrl, file, bucketName, o
|
|
|
10428
10449
|
variants = [];
|
|
10429
10450
|
}
|
|
10430
10451
|
if (variants.length === 0) {
|
|
10431
|
-
return uploadSingleRuntimeFile(
|
|
10452
|
+
return uploadSingleRuntimeFile(
|
|
10453
|
+
request,
|
|
10454
|
+
baseUrl,
|
|
10455
|
+
file,
|
|
10456
|
+
bucketName,
|
|
10457
|
+
onProgress,
|
|
10458
|
+
options,
|
|
10459
|
+
getAuthHeaders
|
|
10460
|
+
);
|
|
10432
10461
|
}
|
|
10433
10462
|
const originalProgressEnd = 70;
|
|
10434
10463
|
const uploaded = await uploadSingleRuntimeFile(
|
|
@@ -10437,7 +10466,8 @@ async function uploadFileWithImageVariants(request, baseUrl, file, bucketName, o
|
|
|
10437
10466
|
file,
|
|
10438
10467
|
bucketName,
|
|
10439
10468
|
createSegmentProgress(onProgress, 0, originalProgressEnd),
|
|
10440
|
-
options
|
|
10469
|
+
options,
|
|
10470
|
+
getAuthHeaders
|
|
10441
10471
|
);
|
|
10442
10472
|
const imageVariants = { ...uploaded.variants || {} };
|
|
10443
10473
|
const variantProgressStep = (100 - originalProgressEnd) / variants.length;
|
|
@@ -10452,7 +10482,8 @@ async function uploadFileWithImageVariants(request, baseUrl, file, bucketName, o
|
|
|
10452
10482
|
variant.file,
|
|
10453
10483
|
bucketName,
|
|
10454
10484
|
createSegmentProgress(onProgress, start, end),
|
|
10455
|
-
options
|
|
10485
|
+
options,
|
|
10486
|
+
getAuthHeaders
|
|
10456
10487
|
);
|
|
10457
10488
|
imageVariants[variant.kind] = toImageVariantMetadata(variantUpload, variant);
|
|
10458
10489
|
} catch {
|
|
@@ -10469,11 +10500,20 @@ async function uploadFileWithImageVariants(request, baseUrl, file, bucketName, o
|
|
|
10469
10500
|
}
|
|
10470
10501
|
function createFormRuntimeApi(config) {
|
|
10471
10502
|
const { baseUrl = getDefaultBaseUrl(), ...overrides } = config ?? {};
|
|
10472
|
-
const
|
|
10503
|
+
const { fetchImpl, getAuthHeaders } = overrides;
|
|
10504
|
+
const request = overrides.request ?? createDefaultRequest(baseUrl, fetchImpl, getAuthHeaders);
|
|
10473
10505
|
const defaults = {
|
|
10474
10506
|
request,
|
|
10475
10507
|
uploadFile: async (file, bucketName = "files", onProgress, options = {}) => {
|
|
10476
|
-
return uploadFileWithImageVariants(
|
|
10508
|
+
return uploadFileWithImageVariants(
|
|
10509
|
+
request,
|
|
10510
|
+
baseUrl,
|
|
10511
|
+
file,
|
|
10512
|
+
bucketName,
|
|
10513
|
+
onProgress,
|
|
10514
|
+
options,
|
|
10515
|
+
getAuthHeaders
|
|
10516
|
+
);
|
|
10477
10517
|
},
|
|
10478
10518
|
uploadPublicFile: async (file, bucketName = DEFAULT_PUBLIC_FILE_BUCKET, onProgress) => {
|
|
10479
10519
|
if (file.size > CHUNK_UPLOAD_THRESHOLD) {
|
|
@@ -10483,7 +10523,7 @@ function createFormRuntimeApi(config) {
|
|
|
10483
10523
|
}
|
|
10484
10524
|
return uploadWithXhr(baseUrl, file, bucketName, onProgress, {
|
|
10485
10525
|
visibility: "public"
|
|
10486
|
-
});
|
|
10526
|
+
}, getAuthHeaders);
|
|
10487
10527
|
},
|
|
10488
10528
|
deleteFile: async (objectName, bucketName = "files", options = {}) => {
|
|
10489
10529
|
if (options.uploadProvider === "builtin-oss" || options.storageScope === "platform") {
|