openxiangda 1.0.128 → 1.0.130
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
CHANGED
|
@@ -108,6 +108,7 @@ openxiangda auth-config methods --json
|
|
|
108
108
|
openxiangda function invoke submit_public_registration --body-json '{"input":{}}'
|
|
109
109
|
openxiangda connector invoke sms.sendCode --body-json '{"body":{"phone":"13800000000"}}'
|
|
110
110
|
openxiangda notification preview public_register_notice --body-json '{"payload":{"title":"测试"}}'
|
|
111
|
+
openxiangda permission snapshot --form-codes customer,orders --json
|
|
111
112
|
openxiangda permission audit --json
|
|
112
113
|
```
|
|
113
114
|
|
package/lib/cli.js
CHANGED
|
@@ -150,12 +150,12 @@ Usage:
|
|
|
150
150
|
openxiangda function list|get|create|update|upsert|delete|invoke [--json-file file]
|
|
151
151
|
openxiangda connector list|get|create|update|upsert|delete|invoke|download-test [--json-file file]
|
|
152
152
|
openxiangda notification template-list|template-get|template-upsert|template-delete|type-list|type-get|type-upsert|type-delete|preview|send|batch-send
|
|
153
|
-
openxiangda permission role-list|role-create|role-update|role-delete|role-bind|audit
|
|
153
|
+
openxiangda permission role-list|role-create|role-update|role-delete|role-bind|snapshot|audit
|
|
154
154
|
openxiangda permission page-group-list|page-group-create|page-group-update|page-group-delete|page-group-bind
|
|
155
155
|
openxiangda permission form-group-list|form-group-create|form-group-update|form-group-delete|form-group-bind
|
|
156
156
|
openxiangda settings get|save|indexes|indexes-save|data-management|data-management-save|public-access
|
|
157
157
|
openxiangda resource validate|plan|publish|pull|typegen|explain [--profile name] [--json] [--dry-run]
|
|
158
|
-
openxiangda runtime deploy [--profile name] [--dist dist] [--build-id id] [--upload-mode staged|legacy-json] [--upload-timeout-ms ms] [--no-build] [--no-activate] [--json]
|
|
158
|
+
openxiangda runtime deploy [--profile name] [--dist dist] [--build-id id] [--upload-mode auto|staged|oss-direct|legacy-json] [--upload-timeout-ms ms] [--no-build] [--no-activate] [--json]
|
|
159
159
|
openxiangda runtime releases [--profile name] [--json]
|
|
160
160
|
openxiangda runtime activate <releaseId> [--profile name] [--json]
|
|
161
161
|
openxiangda inspect app|form|workflow|automation|permissions
|
|
@@ -3140,7 +3140,7 @@ async function permission(args) {
|
|
|
3140
3140
|
const { flags, positional } = parseArgs(rest);
|
|
3141
3141
|
if (wantsSubcommandHelp(subcommand, flags)) {
|
|
3142
3142
|
print(
|
|
3143
|
-
'用法: openxiangda permission role-list|role-create|role-update|role-delete|role-bind|role-users|role-add-users|page-group-list|page-group-create|page-group-update|page-group-delete|page-group-bind|form-group-list|form-group-create|form-group-update|form-group-delete|form-group-bind|form-summary|menu-permissions|audit'
|
|
3143
|
+
'用法: openxiangda permission role-list|role-create|role-update|role-delete|role-bind|role-users|role-add-users|page-group-list|page-group-create|page-group-update|page-group-delete|page-group-bind|form-group-list|form-group-create|form-group-update|form-group-delete|form-group-bind|form-summary|menu-permissions|snapshot|audit'
|
|
3144
3144
|
);
|
|
3145
3145
|
return;
|
|
3146
3146
|
}
|
|
@@ -3579,6 +3579,36 @@ async function permission(args) {
|
|
|
3579
3579
|
return;
|
|
3580
3580
|
}
|
|
3581
3581
|
|
|
3582
|
+
if (subcommand === 'snapshot') {
|
|
3583
|
+
const target = getWorkspaceTarget(config, profileName, flags);
|
|
3584
|
+
const formUuids = resolvePermissionSnapshotFormUuids(target.bound, flags);
|
|
3585
|
+
const data = await requestWithAuth(
|
|
3586
|
+
config,
|
|
3587
|
+
target.profileName,
|
|
3588
|
+
`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/permissions/snapshot`,
|
|
3589
|
+
{
|
|
3590
|
+
method: 'POST',
|
|
3591
|
+
body: {
|
|
3592
|
+
formUuids,
|
|
3593
|
+
includeRoles: flags['no-roles'] ? false : undefined,
|
|
3594
|
+
includePageGroups: flags['no-page-groups'] ? false : undefined,
|
|
3595
|
+
includeFormGroups: flags['no-form-groups'] ? false : undefined,
|
|
3596
|
+
},
|
|
3597
|
+
}
|
|
3598
|
+
);
|
|
3599
|
+
if (flags.json) return writeJson(data);
|
|
3600
|
+
const formGroups = data.formPermissionGroups || {};
|
|
3601
|
+
print(
|
|
3602
|
+
[
|
|
3603
|
+
`Permission snapshot appType=${data.appType || target.appType}`,
|
|
3604
|
+
`roles=${Array.isArray(data.roles) ? data.roles.length : 0}`,
|
|
3605
|
+
`pageGroups=${Array.isArray(data.pagePermissionGroups) ? data.pagePermissionGroups.length : 0}`,
|
|
3606
|
+
`formGroups=${Number(formGroups.total || 0)}`,
|
|
3607
|
+
].join(' ')
|
|
3608
|
+
);
|
|
3609
|
+
return;
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3582
3612
|
if (subcommand === 'audit') {
|
|
3583
3613
|
const target = getWorkspaceTarget(config, profileName, flags);
|
|
3584
3614
|
const result = await buildPermissionAudit(config, target, flags);
|
|
@@ -3589,7 +3619,7 @@ async function permission(args) {
|
|
|
3589
3619
|
}
|
|
3590
3620
|
|
|
3591
3621
|
fail(
|
|
3592
|
-
'用法: openxiangda permission role-list|role-create|role-update|role-delete|role-bind|role-users|role-add-users|page-group-list|page-group-create|page-group-update|page-group-delete|page-group-bind|form-group-list|form-group-create|form-group-update|form-group-delete|form-group-bind|form-summary|menu-permissions|audit'
|
|
3622
|
+
'用法: openxiangda permission role-list|role-create|role-update|role-delete|role-bind|role-users|role-add-users|page-group-list|page-group-create|page-group-update|page-group-delete|page-group-bind|form-group-list|form-group-create|form-group-update|form-group-delete|form-group-bind|form-summary|menu-permissions|snapshot|audit'
|
|
3593
3623
|
);
|
|
3594
3624
|
}
|
|
3595
3625
|
|
|
@@ -3902,10 +3932,42 @@ async function runtime(args) {
|
|
|
3902
3932
|
if (subcommand === 'deploy') {
|
|
3903
3933
|
const buildId = normalizeRuntimeBuildId(flags['build-id'] || createRuntimeBuildId());
|
|
3904
3934
|
const distDir = path.resolve(process.cwd(), flags.dist || 'dist');
|
|
3905
|
-
const assetBaseUrl = buildRuntimeAssetBaseUrl(target.appType, buildId);
|
|
3906
3935
|
const uploadMode = normalizeRuntimeUploadMode(flags['upload-mode']);
|
|
3907
3936
|
const uploadTimeoutMs = normalizeRuntimeUploadTimeoutMs(flags['upload-timeout-ms']);
|
|
3908
3937
|
const traceId = createRuntimeTraceId();
|
|
3938
|
+
const gatewayAssetBaseUrl = buildRuntimeAssetBaseUrl(target.appType, buildId);
|
|
3939
|
+
let assetBaseUrl = gatewayAssetBaseUrl;
|
|
3940
|
+
let effectiveUploadMode = uploadMode;
|
|
3941
|
+
if (uploadMode === 'auto' || uploadMode === 'oss-direct') {
|
|
3942
|
+
try {
|
|
3943
|
+
const plan = await createRuntimeOssUploadBasePlan({
|
|
3944
|
+
config,
|
|
3945
|
+
profileName: target.profileName,
|
|
3946
|
+
appType: target.appType,
|
|
3947
|
+
buildId,
|
|
3948
|
+
traceId,
|
|
3949
|
+
timeoutMs: uploadTimeoutMs,
|
|
3950
|
+
});
|
|
3951
|
+
if (plan?.assetBaseUrl) {
|
|
3952
|
+
assetBaseUrl = plan.assetBaseUrl;
|
|
3953
|
+
if (uploadMode === 'auto') {
|
|
3954
|
+
effectiveUploadMode = 'oss-direct';
|
|
3955
|
+
printRuntimeProgress(
|
|
3956
|
+
`runtime builtin OSS available; using OSS direct upload traceId=${traceId}`
|
|
3957
|
+
);
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
} catch (error) {
|
|
3961
|
+
if (uploadMode === 'oss-direct') {
|
|
3962
|
+
throw error;
|
|
3963
|
+
}
|
|
3964
|
+
effectiveUploadMode = 'auto';
|
|
3965
|
+
assetBaseUrl = gatewayAssetBaseUrl;
|
|
3966
|
+
printRuntimeProgress(
|
|
3967
|
+
`runtime builtin OSS preflight unavailable; using staged upload first traceId=${traceId}`
|
|
3968
|
+
);
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3909
3971
|
if (!flags['no-build']) {
|
|
3910
3972
|
runRuntimeBuild({
|
|
3911
3973
|
buildId,
|
|
@@ -3920,9 +3982,8 @@ async function runtime(args) {
|
|
|
3920
3982
|
});
|
|
3921
3983
|
const totalBytes = files.reduce((sum, file) => sum + file.size, 0);
|
|
3922
3984
|
printRuntimeProgress(
|
|
3923
|
-
`runtime release upload: mode=${
|
|
3985
|
+
`runtime release upload: mode=${effectiveUploadMode} traceId=${traceId} files=${files.length} size=${formatBytes(totalBytes)} timeout=${uploadTimeoutMs}ms`
|
|
3924
3986
|
);
|
|
3925
|
-
let effectiveUploadMode = uploadMode;
|
|
3926
3987
|
let releaseFiles;
|
|
3927
3988
|
if (uploadMode === 'legacy-json') {
|
|
3928
3989
|
releaseFiles = files.map(file => ({
|
|
@@ -3932,7 +3993,8 @@ async function runtime(args) {
|
|
|
3932
3993
|
contentType: file.contentType,
|
|
3933
3994
|
contentBase64: file.buffer.toString('base64'),
|
|
3934
3995
|
}));
|
|
3935
|
-
|
|
3996
|
+
effectiveUploadMode = 'legacy-json';
|
|
3997
|
+
} else if (effectiveUploadMode === 'oss-direct') {
|
|
3936
3998
|
releaseFiles = await uploadRuntimeDistFilesOssDirect({
|
|
3937
3999
|
config,
|
|
3938
4000
|
profileName: target.profileName,
|
|
@@ -3990,6 +4052,7 @@ async function runtime(args) {
|
|
|
3990
4052
|
}
|
|
3991
4053
|
);
|
|
3992
4054
|
saveRuntimeReleaseState(target, data);
|
|
4055
|
+
const releaseAssetBaseUrl = data?.assetBaseUrl || assetBaseUrl;
|
|
3993
4056
|
const result = {
|
|
3994
4057
|
appType: target.appType,
|
|
3995
4058
|
buildId,
|
|
@@ -3997,7 +4060,7 @@ async function runtime(args) {
|
|
|
3997
4060
|
distDir,
|
|
3998
4061
|
fileCount: files.length,
|
|
3999
4062
|
totalBytes,
|
|
4000
|
-
assetBaseUrl,
|
|
4063
|
+
assetBaseUrl: releaseAssetBaseUrl,
|
|
4001
4064
|
activated: !flags['no-activate'],
|
|
4002
4065
|
uploadMode: effectiveUploadMode,
|
|
4003
4066
|
traceId,
|
|
@@ -4010,7 +4073,7 @@ async function runtime(args) {
|
|
|
4010
4073
|
`files: ${files.length} (${formatBytes(totalBytes)})`,
|
|
4011
4074
|
`upload: ${effectiveUploadMode}`,
|
|
4012
4075
|
`traceId: ${traceId}`,
|
|
4013
|
-
`assetBase: ${
|
|
4076
|
+
`assetBase: ${releaseAssetBaseUrl}`,
|
|
4014
4077
|
`active: ${result.activated ? 'yes' : 'no'}`,
|
|
4015
4078
|
].join('\n')
|
|
4016
4079
|
);
|
|
@@ -4020,6 +4083,24 @@ async function runtime(args) {
|
|
|
4020
4083
|
fail('用法: openxiangda runtime deploy|releases|activate [--profile name]');
|
|
4021
4084
|
}
|
|
4022
4085
|
|
|
4086
|
+
async function createRuntimeOssUploadBasePlan(options) {
|
|
4087
|
+
return await requestWithAuth(
|
|
4088
|
+
options.config,
|
|
4089
|
+
options.profileName,
|
|
4090
|
+
`/openxiangda-api/v1/apps/${encodeURIComponent(options.appType)}/runtime/releases/oss-upload-plan`,
|
|
4091
|
+
{
|
|
4092
|
+
method: 'POST',
|
|
4093
|
+
timeoutMs: options.timeoutMs,
|
|
4094
|
+
headers: { 'x-openxiangda-trace-id': options.traceId },
|
|
4095
|
+
body: {
|
|
4096
|
+
buildId: options.buildId,
|
|
4097
|
+
planOnly: true,
|
|
4098
|
+
files: [],
|
|
4099
|
+
},
|
|
4100
|
+
}
|
|
4101
|
+
);
|
|
4102
|
+
}
|
|
4103
|
+
|
|
4023
4104
|
function runRuntimeBuild(options) {
|
|
4024
4105
|
const command = options.command || defaultRuntimeBuildCommand();
|
|
4025
4106
|
if (options.jsonOutput) {
|
|
@@ -4475,7 +4556,7 @@ async function commands(args) {
|
|
|
4475
4556
|
'function list|get|create|update|upsert|delete|invoke',
|
|
4476
4557
|
'connector list|get|create|update|upsert|delete|invoke|download-test',
|
|
4477
4558
|
'notification template-list|template-get|template-upsert|template-delete|type-list|type-get|type-upsert|type-delete|preview|send|batch-send',
|
|
4478
|
-
'permission role-list|role-create|role-update|role-delete|role-bind|role-users|role-add-users|audit',
|
|
4559
|
+
'permission role-list|role-create|role-update|role-delete|role-bind|role-users|role-add-users|snapshot|audit',
|
|
4479
4560
|
'permission page-group-list|page-group-create|page-group-update|page-group-delete|page-group-bind',
|
|
4480
4561
|
'permission form-group-list|form-group-create|form-group-update|form-group-delete|form-group-bind|form-summary|menu-permissions',
|
|
4481
4562
|
'settings get|save|indexes|indexes-save|data-management|data-management-save|public-access|public-access-save|public-access-delete',
|
|
@@ -5024,10 +5105,21 @@ async function buildPermissionAudit(config, target, flags = {}) {
|
|
|
5024
5105
|
warnings,
|
|
5025
5106
|
};
|
|
5026
5107
|
if (flags.live) {
|
|
5027
|
-
result.live =
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5108
|
+
result.live = await requestWithAuth(
|
|
5109
|
+
config,
|
|
5110
|
+
target.profileName,
|
|
5111
|
+
`/openxiangda-api/v1/apps/${encodeURIComponent(target.appType)}/permissions/snapshot`,
|
|
5112
|
+
{
|
|
5113
|
+
method: 'POST',
|
|
5114
|
+
body: {
|
|
5115
|
+
formUuids: unique(
|
|
5116
|
+
(manifest.formPermissionGroups || [])
|
|
5117
|
+
.map(group => resolveManifestFormUuid(target.bound, group))
|
|
5118
|
+
.filter(Boolean)
|
|
5119
|
+
),
|
|
5120
|
+
},
|
|
5121
|
+
}
|
|
5122
|
+
);
|
|
5031
5123
|
}
|
|
5032
5124
|
return result;
|
|
5033
5125
|
}
|
|
@@ -5066,6 +5158,25 @@ function resolveOptionalFormUuid(bound, formCode) {
|
|
|
5066
5158
|
return bound.resources?.forms?.[formCode]?.formUuid || formCode;
|
|
5067
5159
|
}
|
|
5068
5160
|
|
|
5161
|
+
function resolvePermissionSnapshotFormUuids(bound, flags = {}) {
|
|
5162
|
+
const directFormUuids = [
|
|
5163
|
+
...splitList(flags['form-uuids']),
|
|
5164
|
+
...splitList(flags['form-uuid']),
|
|
5165
|
+
];
|
|
5166
|
+
const mappedFormUuids = [
|
|
5167
|
+
...splitList(flags['form-codes']),
|
|
5168
|
+
...splitList(flags['form-code']),
|
|
5169
|
+
]
|
|
5170
|
+
.map(formCode => resolveOptionalFormUuid(bound, formCode))
|
|
5171
|
+
.filter(Boolean);
|
|
5172
|
+
const allFormUuids = flags['all-forms']
|
|
5173
|
+
? Object.values(bound.resources?.forms || {})
|
|
5174
|
+
.map(item => item?.formUuid)
|
|
5175
|
+
.filter(Boolean)
|
|
5176
|
+
: [];
|
|
5177
|
+
return unique([...directFormUuids, ...mappedFormUuids, ...allFormUuids]);
|
|
5178
|
+
}
|
|
5179
|
+
|
|
5069
5180
|
function resolveOptionalPageId(bound, pageCode) {
|
|
5070
5181
|
if (!pageCode) return undefined;
|
|
5071
5182
|
return bound.resources?.pages?.[pageCode]?.pageId || pageCode;
|
|
@@ -127,8 +127,14 @@ for real backend read/write field access (`edit`, `readonly`, `hidden`) with
|
|
|
127
127
|
```bash
|
|
128
128
|
openxiangda permission form-summary --form-code customer --profile dev --json
|
|
129
129
|
openxiangda permission menu-permissions --profile dev --json
|
|
130
|
+
openxiangda permission snapshot --profile dev --form-codes customer,orders --json
|
|
130
131
|
```
|
|
131
132
|
|
|
133
|
+
When auditing many roles or permission groups, prefer `openxiangda permission
|
|
134
|
+
snapshot` over looping `role-list`, `page-group-list`, or `form-group-list`.
|
|
135
|
+
The snapshot command uses the platform batch endpoint and returns roles, page
|
|
136
|
+
permission groups, and form permission groups in one request.
|
|
137
|
+
|
|
132
138
|
## Form Settings
|
|
133
139
|
|
|
134
140
|
Use settings commands for form runtime settings, field indexes, data management pages, and public access:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openxiangda",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.130",
|
|
4
4
|
"description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"bin": {
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"test:resource-plan": "node scripts/resource-plan-smoke.mjs",
|
|
68
68
|
"test:design-gates": "node scripts/design-gates-smoke.mjs",
|
|
69
69
|
"test:resource-cli": "node scripts/resource-cli-smoke.mjs",
|
|
70
|
+
"test:permission-snapshot": "node scripts/permission-snapshot-smoke.mjs",
|
|
70
71
|
"test:sdk-bundle": "node scripts/sdk-browser-bundle-smoke.mjs",
|
|
71
72
|
"test:api-contract": "node scripts/openxiangda-api-contract-smoke.mjs",
|
|
72
73
|
"test:help-no-side-effects": "node scripts/help-no-side-effects-smoke.mjs",
|