create-windy 0.3.18 → 0.4.0
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 +28 -13
- package/dist/cli.js +788 -280
- package/package.json +1 -1
- package/template/.agents/skills/windy-business-module/SKILL.md +34 -10
- package/template/.windy-template.json +2 -2
- package/template/AGENTS.md +18 -8
- package/template/README.md +32 -22
- package/template/apps/agent-server/src/southwind_agent_server/adk_debug.py +38 -5
- package/template/apps/agent-server/tests/test_adk_debug_result_polling.py +139 -0
- package/template/apps/server/Dockerfile +1 -0
- package/template/apps/server/package.json +1 -0
- package/template/apps/server/scripts/validate-module-composition-once.ts +4 -0
- package/template/apps/server/scripts/validate-module-composition.ts +28 -0
- package/template/apps/server/src/agent/data-scope-routes.test.ts +20 -5
- package/template/apps/server/src/agent/routes.test.ts +20 -7
- package/template/apps/server/src/agent/routes.ts +14 -0
- package/template/apps/server/src/agent/runtime-bootstrap.ts +22 -6
- package/template/apps/server/src/agent/sandbox-worker.ts +2 -2
- package/template/apps/server/src/agent/unavailable-routes.ts +33 -0
- package/template/apps/server/src/app/server-composition.ts +56 -0
- package/template/apps/server/src/application-services.ts +2 -0
- package/template/apps/server/src/bulk-data/service.ts +14 -0
- package/template/apps/server/src/data-governance/export/authorization-port.ts +1 -1
- package/template/apps/server/src/data-governance/export/postgres-api-fixture.ts +18 -2
- package/template/apps/server/src/data-governance/export/postgres-recovery.integration.test.ts +10 -9
- package/template/apps/server/src/data-governance/export/routes-test-fixture.ts +6 -8
- package/template/apps/server/src/data-governance/user-plaintext-routes.test.ts +4 -2
- package/template/apps/server/src/feature/routes.authorization.test.ts +26 -1
- package/template/apps/server/src/feature/routes.ts +33 -15
- package/template/apps/server/src/foundation-activation.test.ts +60 -0
- package/template/apps/server/src/foundation.ts +64 -5
- package/template/apps/server/src/guards.test.ts +23 -0
- package/template/apps/server/src/guards.ts +12 -0
- package/template/apps/server/src/index.ts +28 -34
- package/template/apps/server/src/installed-business-modules.ts +13 -24
- package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +1 -0
- package/template/apps/server/src/jobs/legacy-adapter.test.ts +106 -0
- package/template/apps/server/src/jobs/legacy-service-runtime.ts +40 -0
- package/template/apps/server/src/license/activation.test.ts +30 -0
- package/template/apps/server/src/license/activation.ts +26 -0
- package/template/apps/server/src/license/restriction.test.ts +16 -0
- package/template/apps/server/src/license/routes-offline-code.test.ts +6 -2
- package/template/apps/server/src/license/routes.ts +20 -4
- package/template/apps/server/src/license/runtime-service.test.ts +10 -2
- package/template/apps/server/src/module-bootstrap.ts +14 -23
- package/template/apps/server/src/module-composition.test.ts +43 -2
- package/template/apps/server/src/module-composition.ts +29 -0
- package/template/apps/server/src/module-host/durable-context-guard.test.ts +172 -0
- package/template/apps/server/src/module-host/durable-context-unavailable.test.ts +50 -1
- package/template/apps/server/src/module-host/durable-context.test.ts +42 -25
- package/template/apps/server/src/module-host/durable-context.ts +42 -2
- package/template/apps/server/src/module-host/durable-handler-registration.ts +96 -5
- package/template/apps/server/src/module-host/implementation-completeness.test.ts +185 -0
- package/template/apps/server/src/module-host/implementation-completeness.ts +132 -0
- package/template/apps/server/src/module-host/initialize-contracts.ts +14 -0
- package/template/apps/server/src/module-host/initialize.test.ts +78 -2
- package/template/apps/server/src/module-host/initialize.ts +15 -4
- package/template/apps/server/src/module-host/project-attachments.test.ts +92 -0
- package/template/apps/server/src/module-host/project-attachments.ts +58 -0
- package/template/apps/server/src/module-host/project-composition-package.test.ts +186 -0
- package/template/apps/server/src/module-host/project-composition.test.ts +260 -0
- package/template/apps/server/src/module-host/project-composition.ts +311 -0
- package/template/apps/server/src/module-host/request-context.ts +3 -0
- package/template/apps/server/src/module-host/route-declaration.ts +15 -6
- package/template/apps/server/src/module-host/route-installer.test.ts +15 -2
- package/template/apps/server/src/module-host/route-installer.ts +12 -2
- package/template/apps/server/src/module-host/route-registration.test.ts +13 -4
- package/template/apps/server/src/module-host/runtime-composition-compatibility.test.ts +63 -0
- package/template/apps/server/src/module-host/runtime-composition-compatibility.ts +46 -0
- package/template/apps/server/src/module-host/runtime-composition.ts +34 -0
- package/template/apps/server/src/module-host/search-provider-registration.test.ts +64 -0
- package/template/apps/server/src/module-host/search-provider-registration.ts +35 -0
- package/template/apps/server/src/module-host/static-route-authorization.test.ts +99 -0
- package/template/apps/server/src/module-host/static-route-authorization.ts +90 -0
- package/template/apps/server/src/module-host/task-definitions.ts +1 -0
- package/template/apps/server/src/module-host/user-directory-composition.test.ts +4 -1
- package/template/apps/server/src/module-host.test.ts +2 -0
- package/template/apps/server/src/module-preflight.test.ts +289 -0
- package/template/apps/server/src/module-preflight.ts +96 -0
- package/template/apps/server/src/module-runtime-validation.ts +1 -0
- package/template/apps/server/src/platform-app-bootstrap.ts +50 -0
- package/template/apps/server/src/platform-http-runtime.ts +32 -7
- package/template/apps/server/src/route-guards.test.ts +22 -0
- package/template/apps/server/src/route-guards.ts +10 -3
- package/template/apps/server/src/runtime-audit-query.ts +50 -0
- package/template/apps/server/src/runtime-feature.test.ts +46 -0
- package/template/apps/server/src/runtime-feature.ts +5 -0
- package/template/apps/server/src/runtime.ts +10 -49
- package/template/apps/server/src/scheduler/audit-action.test.ts +49 -0
- package/template/apps/server/src/scheduler/audit.ts +1 -1
- package/template/apps/server/src/scheduler/execution-recorder.ts +10 -5
- package/template/apps/server/src/scheduler/recovery-service.test.ts +4 -3
- package/template/apps/server/src/scheduler/service-idle.test.ts +4 -3
- package/template/apps/server/src/scheduler/service.test.ts +1 -0
- package/template/apps/server/src/scheduler/service.ts +18 -21
- package/template/apps/server/src/scheduler/types.ts +5 -1
- package/template/apps/server/src/startup-composition-validation.test.ts +35 -0
- package/template/apps/server/src/startup-composition-validation.ts +54 -0
- package/template/apps/server/src/startup-preflight.test.ts +36 -0
- package/template/apps/server/src/startup-preflight.ts +37 -0
- package/template/apps/server/src/system/audit-routes.test.ts +9 -3
- package/template/apps/server/src/system/department-routes.test.ts +5 -3
- package/template/apps/server/src/system/feature-routes.test.ts +4 -3
- package/template/apps/server/src/system/interface-catalog.test.ts +5 -3
- package/template/apps/server/src/system/interface-catalog.ts +3 -0
- package/template/apps/server/src/system/menu-routes.test.ts +5 -3
- package/template/apps/server/src/system/route-test-runtime.ts +24 -0
- package/template/apps/server/src/system/routes-validation.test.ts +7 -3
- package/template/apps/server/src/system/routes.test.ts +21 -11
- package/template/apps/server/src/system/routes.ts +11 -2
- package/template/apps/server/src/work-order/foundation-modules.test.ts +6 -4
- package/template/apps/server/src/work-order/routes.test.ts +8 -0
- package/template/apps/server/src/work-order/task.test.ts +6 -1
- package/template/apps/web/Dockerfile +5 -0
- package/template/apps/web/package.json +1 -0
- package/template/apps/web/scripts/validate-admin-ui-composition.ts +106 -0
- package/template/apps/web/src/app/web-composition.ts +24 -0
- package/template/apps/web/src/app/web-composition.webtest.ts +15 -0
- package/template/apps/web/src/layout/AppSidebar.vue +8 -1
- package/template/apps/web/src/layout/NavigationSearchDialog.vue +8 -1
- package/template/apps/web/src/layout/navigation.ts +29 -4
- package/template/apps/web/src/layout/navigation.webtest.ts +51 -0
- package/template/apps/web/src/pages/dashboard/dashboard-presenter.ts +6 -1
- package/template/apps/web/src/pages/work-orders/WorkOrderListPage.vue +202 -0
- package/template/apps/web/src/pages/work-orders/WorkOrderListPage.webtest.ts +51 -0
- package/template/apps/web/src/router/admin-extension-loader.ts +44 -0
- package/template/apps/web/src/router/admin-extension-loader.webtest.ts +49 -0
- package/template/apps/web/src/router/admin-ui-composition-plugin.ts +33 -0
- package/template/apps/web/src/router/admin-ui-composition-plugin.webtest.ts +20 -0
- package/template/apps/web/src/router/admin-ui-composition.ts +313 -0
- package/template/apps/web/src/router/admin-ui-composition.webtest.ts +225 -0
- package/template/apps/web/src/router/admin-ui-module-registry-plugin.ts +55 -0
- package/template/apps/web/src/router/admin-ui-module-registry-plugin.webtest.ts +46 -0
- package/template/apps/web/src/router/business-routes.ts +32 -45
- package/template/apps/web/src/router/business-routes.webtest.ts +40 -44
- package/template/apps/web/src/router/index.ts +64 -54
- package/template/apps/web/src/router/manifest-routes.ts +27 -7
- package/template/apps/web/src/router/manifest-routes.webtest.ts +10 -2
- package/template/apps/web/src/router/platform-admin-route-bindings.ts +58 -0
- package/template/apps/web/src/router/reserved-route-paths.ts +14 -0
- package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +10 -4
- package/template/apps/web/src/router/web-composition-host.ts +20 -0
- package/template/apps/web/src/services/work-order-api.ts +16 -0
- package/template/apps/web/src/services/work-order-api.webtest.ts +21 -0
- package/template/apps/web/src/virtual-admin-ui-modules.d.ts +5 -0
- package/template/apps/web/vite.config.ts +22 -1
- package/template/docs/architecture/migration-bundle.md +10 -7
- package/template/docs/architecture/module-manifest-composition.md +85 -3
- package/template/docs/architecture/search-provider-protocol.md +6 -1
- package/template/docs/architecture/server-module-interface.md +70 -7
- package/template/docs/development/business-web-routes.md +53 -50
- package/template/docs/development/module-lifecycle.md +48 -54
- package/template/docs/platform/agent-runtime.md +2 -0
- package/template/docs/platform/navigation-registry.md +5 -4
- package/template/package.json +1 -1
- package/template/packages/config/package.json +1 -1
- package/template/packages/database/package.json +1 -1
- package/template/packages/example-work-order/package.json +6 -0
- package/template/packages/example-work-order/src/admin-page.ts +78 -0
- package/template/packages/example-work-order/src/admin.ts +35 -0
- package/template/packages/example-work-order/src/manifest.test.ts +62 -5
- package/template/packages/example-work-order/src/manifest.ts +54 -7
- package/template/packages/example-work-order/src/migration-bundle.ts +8 -0
- package/template/packages/example-work-order/src/server-module.ts +2 -2
- package/template/packages/jobs/package.json +1 -1
- package/template/packages/modules/index.ts +4 -0
- package/template/packages/modules/package.json +3 -2
- package/template/packages/modules/src/admin-ui-entry.ts +8 -0
- package/template/packages/modules/src/admin-ui-manifest.test.ts +52 -0
- package/template/packages/modules/src/admin-ui-manifest.ts +53 -0
- package/template/packages/modules/src/capability-audit-validation.test.ts +127 -0
- package/template/packages/modules/src/capability-audit-validation.ts +38 -0
- package/template/packages/modules/src/capability-guard-validation.ts +148 -0
- package/template/packages/modules/src/capability-key-validation.test.ts +150 -0
- package/template/packages/modules/src/composition-fixtures.test-support.ts +7 -5
- package/template/packages/modules/src/composition.test.ts +74 -9
- package/template/packages/modules/src/composition.ts +8 -0
- package/template/packages/modules/src/manifest.ts +1 -0
- package/template/packages/modules/src/migration-bundle-validation.test.ts +23 -0
- package/template/packages/modules/src/module-host-range.test.ts +57 -0
- package/template/packages/modules/src/module-host-range.ts +125 -0
- package/template/packages/modules/src/project-module-assembly.test.ts +133 -0
- package/template/packages/modules/src/project-module-assembly.ts +126 -0
- package/template/packages/modules/src/system-admin-routes.ts +6 -5
- package/template/packages/modules/src/system-admin-ui-catalog.ts +72 -0
- package/template/packages/modules/src/system-agent-tools.ts +2 -2
- package/template/packages/modules/src/system-api-binding.ts +43 -9
- package/template/packages/modules/src/system-api-control-plane.ts +202 -0
- package/template/packages/modules/src/system-api-data-governance.ts +4 -2
- package/template/packages/modules/src/system-api-operational.ts +57 -8
- package/template/packages/modules/src/system-api-permissions.ts +115 -21
- package/template/packages/modules/src/system-api-watermark.ts +3 -0
- package/template/packages/modules/src/system-audit-actions.ts +7 -0
- package/template/packages/modules/src/system-data-governance.ts +5 -2
- package/template/packages/modules/src/system-features.ts +31 -0
- package/template/packages/modules/src/system-governed-export-bindings.ts +8 -4
- package/template/packages/modules/src/system-module-catalog.test.ts +39 -4
- package/template/packages/modules/src/system-module-catalog.ts +68 -7
- package/template/packages/modules/src/system-module-tasks.ts +8 -1
- package/template/packages/modules/src/system-modules-access.test.ts +4 -0
- package/template/packages/modules/src/system-modules.test.ts +10 -2
- package/template/packages/modules/src/system-modules.ts +6 -5
- package/template/packages/modules/src/system-permissions.ts +4 -0
- package/template/packages/modules/user/module.ts +19 -2
- package/template/packages/server-sdk/index.ts +8 -0
- package/template/packages/server-sdk/package.json +2 -1
- package/template/packages/server-sdk/src/durable-handler.ts +16 -1
- package/template/packages/server-sdk/src/module-adapter.test.ts +44 -0
- package/template/packages/server-sdk/src/module-adapter.ts +59 -0
- package/template/packages/server-sdk/src/route-definition.ts +3 -2
- package/template/packages/shared/package.json +1 -1
- package/template/packages/shared/src/audit.test.ts +7 -0
- package/template/packages/shared/src/audit.ts +16 -0
- package/template/packages/shared/src/migration-bundle-validation.ts +9 -11
- package/template/packages/shared/src/rbac.ts +2 -0
- package/template/apps/web/src/app/business-routes.ts +0 -22
|
@@ -14,7 +14,10 @@ import type {
|
|
|
14
14
|
PermissionDefinition,
|
|
15
15
|
} from "@southwind-ai/shared";
|
|
16
16
|
import { resolveLicenseCatalog } from "@southwind-ai/shared";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
runtimeBusinessModules,
|
|
19
|
+
serverComposition,
|
|
20
|
+
} from "./module-host/runtime-composition.js";
|
|
18
21
|
|
|
19
22
|
export interface FoundationSnapshot {
|
|
20
23
|
platform: {
|
|
@@ -34,12 +37,21 @@ export interface FoundationSnapshot {
|
|
|
34
37
|
auditActions: string[];
|
|
35
38
|
auditActionCatalog: readonly AuditActionDefinition[];
|
|
36
39
|
licenseCatalog: readonly LicenseCatalogDefinition[];
|
|
40
|
+
disabledModuleNames: readonly string[];
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
export function loadFoundationModules(): ModuleManifest[] {
|
|
40
44
|
const registry = createModuleRegistry();
|
|
41
|
-
for (const factory of systemModuleFactories)
|
|
42
|
-
|
|
45
|
+
for (const factory of systemModuleFactories) {
|
|
46
|
+
const manifest = factory();
|
|
47
|
+
registry.register(() =>
|
|
48
|
+
applyPlatformModuleActivation(
|
|
49
|
+
manifest,
|
|
50
|
+
serverComposition.enabledModuleNames.has(manifest.name),
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
for (const module of runtimeBusinessModules) {
|
|
43
55
|
registry.register(() => module.manifest);
|
|
44
56
|
}
|
|
45
57
|
return registry.list();
|
|
@@ -47,10 +59,48 @@ export function loadFoundationModules(): ModuleManifest[] {
|
|
|
47
59
|
|
|
48
60
|
export function loadSystemModules(): ModuleManifest[] {
|
|
49
61
|
const registry = createModuleRegistry();
|
|
50
|
-
for (const factory of systemModuleFactories)
|
|
62
|
+
for (const factory of systemModuleFactories) {
|
|
63
|
+
const manifest = factory();
|
|
64
|
+
registry.register(() =>
|
|
65
|
+
applyPlatformModuleActivation(
|
|
66
|
+
manifest,
|
|
67
|
+
serverComposition.enabledModuleNames.has(manifest.name),
|
|
68
|
+
),
|
|
69
|
+
);
|
|
70
|
+
}
|
|
51
71
|
return registry.list();
|
|
52
72
|
}
|
|
53
73
|
|
|
74
|
+
export function applyPlatformModuleActivation(
|
|
75
|
+
manifest: ModuleManifest,
|
|
76
|
+
enabled: boolean,
|
|
77
|
+
): ModuleManifest {
|
|
78
|
+
if (enabled) return manifest;
|
|
79
|
+
return {
|
|
80
|
+
...manifest,
|
|
81
|
+
metadata: {
|
|
82
|
+
...manifest.metadata,
|
|
83
|
+
runtimeActivation: "disabled",
|
|
84
|
+
},
|
|
85
|
+
features: manifest.features.map((feature) => ({
|
|
86
|
+
...feature,
|
|
87
|
+
enabled: false,
|
|
88
|
+
visible: "hidden",
|
|
89
|
+
})),
|
|
90
|
+
menus: manifest.menus.map(hideMenu),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function hideMenu(menu: MenuNode): MenuNode {
|
|
95
|
+
return {
|
|
96
|
+
...menu,
|
|
97
|
+
visible: menu.type === "catalog" ? menu.visible : false,
|
|
98
|
+
...(menu.children
|
|
99
|
+
? { children: menu.children.map((child) => hideMenu(child)) }
|
|
100
|
+
: {}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
54
104
|
function collectMenus(modules: ModuleManifest[]): MenuNode[] {
|
|
55
105
|
return composeModuleMenus(modules);
|
|
56
106
|
}
|
|
@@ -89,7 +139,13 @@ export function createFoundationSnapshot(
|
|
|
89
139
|
name: config.name,
|
|
90
140
|
enableAI: config.enableAI,
|
|
91
141
|
databaseDialect: config.database.dialect,
|
|
92
|
-
licenseEnabled:
|
|
142
|
+
licenseEnabled:
|
|
143
|
+
config.license.enabled &&
|
|
144
|
+
modules.some(
|
|
145
|
+
({ name, metadata }) =>
|
|
146
|
+
name === "system.license" &&
|
|
147
|
+
metadata?.runtimeActivation !== "disabled",
|
|
148
|
+
),
|
|
93
149
|
auditEnabled: config.audit.enabled,
|
|
94
150
|
ruoyiCompatibilityEnabled: config.compatibility.ruoyiVue.enabled,
|
|
95
151
|
},
|
|
@@ -105,5 +161,8 @@ export function createFoundationSnapshot(
|
|
|
105
161
|
auditActions: collectAuditActions(modules),
|
|
106
162
|
auditActionCatalog: catalogs.auditActions,
|
|
107
163
|
licenseCatalog: catalogs.licenseVersions,
|
|
164
|
+
disabledModuleNames: modules
|
|
165
|
+
.filter(({ metadata }) => metadata?.runtimeActivation === "disabled")
|
|
166
|
+
.map(({ name }) => name),
|
|
108
167
|
};
|
|
109
168
|
}
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
evaluateEntitlementGuard,
|
|
10
10
|
evaluateFeatureGuard,
|
|
11
|
+
evaluateLicenseRestrictionGuard,
|
|
12
|
+
evaluateLicenseVersionGuard,
|
|
11
13
|
evaluatePermissionGuard,
|
|
12
14
|
type RequestGuardContext,
|
|
13
15
|
} from "./guards.js";
|
|
@@ -72,6 +74,27 @@ describe("guards", () => {
|
|
|
72
74
|
).toBe("entitlement-not-granted");
|
|
73
75
|
});
|
|
74
76
|
|
|
77
|
+
test("License 未启用时跳过版本、Entitlement 与限制模式", () => {
|
|
78
|
+
const unrestricted = {
|
|
79
|
+
...context,
|
|
80
|
+
licenseEnforced: false,
|
|
81
|
+
license: { restricted: true as const, status: "expired" as const },
|
|
82
|
+
};
|
|
83
|
+
const licensedFeature = {
|
|
84
|
+
...feature,
|
|
85
|
+
requiredEntitlement: entitlementKey("capability.audit"),
|
|
86
|
+
allowedLicenseVersions: [],
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
expect(
|
|
90
|
+
evaluateEntitlementGuard(unrestricted, licensedFeature).allowed,
|
|
91
|
+
).toBe(true);
|
|
92
|
+
expect(
|
|
93
|
+
evaluateLicenseVersionGuard(unrestricted, licensedFeature).allowed,
|
|
94
|
+
).toBe(true);
|
|
95
|
+
expect(evaluateLicenseRestrictionGuard(unrestricted).allowed).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
|
|
75
98
|
test("运行时解析出依赖缺失时拒绝并记录 security.denied", () => {
|
|
76
99
|
const denied = evaluateFeatureGuard(context, {
|
|
77
100
|
...feature,
|
|
@@ -58,6 +58,7 @@ export interface RequestGuardContext {
|
|
|
58
58
|
status?: "valid" | "expired" | "clock-rollback" | "invalid" | "missing";
|
|
59
59
|
restricted?: boolean;
|
|
60
60
|
};
|
|
61
|
+
licenseEnforced?: boolean;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
export type GuardDecisionReason =
|
|
@@ -84,6 +85,7 @@ export interface RequestGuardContextOptions {
|
|
|
84
85
|
resolveLicense?: (
|
|
85
86
|
request: Request,
|
|
86
87
|
) => RequestGuardContext["license"] | undefined;
|
|
88
|
+
licenseEnforced?: boolean;
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
export type PermissionGuardRequirement = Pick<
|
|
@@ -107,6 +109,7 @@ export function createRequestGuardContext(
|
|
|
107
109
|
clientIp: options.clientIp,
|
|
108
110
|
userAgent: options.userAgent,
|
|
109
111
|
license: options.resolveLicense?.(request),
|
|
112
|
+
licenseEnforced: options.licenseEnforced,
|
|
110
113
|
};
|
|
111
114
|
}
|
|
112
115
|
|
|
@@ -176,6 +179,9 @@ export function evaluateEntitlementGuard(
|
|
|
176
179
|
context: RequestGuardContext,
|
|
177
180
|
feature: FeatureFlagDefinition,
|
|
178
181
|
): GuardDecision {
|
|
182
|
+
if (context.licenseEnforced === false) {
|
|
183
|
+
return { allowed: true, reason: "allowed" };
|
|
184
|
+
}
|
|
179
185
|
if (
|
|
180
186
|
!feature.requiredEntitlement ||
|
|
181
187
|
context.license?.entitlements?.includes(feature.requiredEntitlement)
|
|
@@ -194,6 +200,9 @@ export function evaluateLicenseVersionGuard(
|
|
|
194
200
|
context: RequestGuardContext,
|
|
195
201
|
feature: FeatureFlagDefinition,
|
|
196
202
|
): GuardDecision {
|
|
203
|
+
if (context.licenseEnforced === false) {
|
|
204
|
+
return { allowed: true, reason: "allowed" };
|
|
205
|
+
}
|
|
197
206
|
if (isLicenseVersionAllowed(feature, context.license?.versionKey)) {
|
|
198
207
|
return { allowed: true, reason: "allowed" };
|
|
199
208
|
}
|
|
@@ -208,6 +217,9 @@ export function evaluateLicenseRestrictionGuard(
|
|
|
208
217
|
context: RequestGuardContext,
|
|
209
218
|
explicitlyAllowed = false,
|
|
210
219
|
): GuardDecision {
|
|
220
|
+
if (context.licenseEnforced === false) {
|
|
221
|
+
return { allowed: true, reason: "allowed" };
|
|
222
|
+
}
|
|
211
223
|
if (!context.license?.restricted || explicitlyAllowed) {
|
|
212
224
|
return { allowed: true, reason: "allowed" };
|
|
213
225
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { registerAdminBootstrapRoute } from "./admin/routes.js";
|
|
2
|
-
import { registerSessionContextRoute } from "./session/routes.js";
|
|
3
2
|
import { registerFoundationRoute } from "./foundation-route.js";
|
|
4
3
|
// @windy-module system.scheduler begin
|
|
5
4
|
import { createApplicationServices } from "./application-services.js";
|
|
@@ -21,12 +20,7 @@ import { resolveCurrentSystemActor } from "./system/actor.js";
|
|
|
21
20
|
// @windy-module system.agent end
|
|
22
21
|
import { createAuthRuntime } from "./auth/runtime.js";
|
|
23
22
|
// @windy-module system.license begin
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
createPlatformLicenseRuntimeOptions,
|
|
27
|
-
hasConfiguredLicenseKeys,
|
|
28
|
-
PlatformLicenseRuntime,
|
|
29
|
-
} from "./license/runtime-service.js";
|
|
23
|
+
import { createLicenseRuntimeActivation } from "./license/activation.js";
|
|
30
24
|
// @windy-module system.license end
|
|
31
25
|
// @windy-module system.operations begin
|
|
32
26
|
import { registerSystemOperationRoutes } from "./system/operations.js";
|
|
@@ -64,15 +58,15 @@ import { registerWorkflowRoutes } from "./workflow/routes.js";
|
|
|
64
58
|
import { bootstrapServerModules } from "./module-bootstrap.js";
|
|
65
59
|
import {
|
|
66
60
|
createHttpObservability,
|
|
67
|
-
createPlatformServerApp,
|
|
68
61
|
listenPlatformServer,
|
|
69
62
|
logServerStartup,
|
|
70
63
|
} from "./platform-http-runtime.js";
|
|
71
|
-
import {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
64
|
+
import { bootstrapPlatformApp } from "./platform-app-bootstrap.js";
|
|
65
|
+
import { createPreparedPlatformBootstrap } from "./startup-preflight.js";
|
|
66
|
+
import { validateCompositionBeforeStartup } from "./startup-composition-validation.js";
|
|
67
|
+
validateCompositionBeforeStartup();
|
|
68
|
+
const { logger, persistence, platformConfig, preparedModules } =
|
|
69
|
+
await createPreparedPlatformBootstrap();
|
|
76
70
|
const {
|
|
77
71
|
// @windy-module system.configuration begin
|
|
78
72
|
configurationRuntime,
|
|
@@ -82,16 +76,14 @@ const {
|
|
|
82
76
|
foundationSnapshot,
|
|
83
77
|
moduleHosts,
|
|
84
78
|
systemRepositories,
|
|
85
|
-
} = await bootstrapServerModules(platformConfig, persistence);
|
|
79
|
+
} = await bootstrapServerModules(platformConfig, persistence, preparedModules);
|
|
86
80
|
const features = foundationSnapshot.features;
|
|
87
81
|
// @windy-module system.license begin
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
const licenseActivation = createLicenseRuntimeActivation(
|
|
83
|
+
process.env,
|
|
84
|
+
foundationSnapshot.platform.licenseEnabled,
|
|
85
|
+
persistence?.licenseResolver,
|
|
91
86
|
);
|
|
92
|
-
const useInstalledLicense =
|
|
93
|
-
Boolean(platformLicenseOptions.enforceRestrictions) ||
|
|
94
|
-
hasConfiguredLicenseKeys(platformLicenseOptions);
|
|
95
87
|
// @windy-module system.license end
|
|
96
88
|
const runtime = createServerRuntime(process.env, {
|
|
97
89
|
actorResolver: persistence?.actorResolver,
|
|
@@ -100,12 +92,12 @@ const runtime = createServerRuntime(process.env, {
|
|
|
100
92
|
resolveCurrentSystemActor(systemRepositories, actorId),
|
|
101
93
|
// @windy-module system.agent end
|
|
102
94
|
// @windy-module system.license begin
|
|
103
|
-
licenseResolver:
|
|
104
|
-
|
|
105
|
-
: persistence?.licenseResolver,
|
|
95
|
+
licenseResolver: licenseActivation.resolver,
|
|
96
|
+
licenseEnforced: licenseActivation.enforced,
|
|
106
97
|
// @windy-module system.license end
|
|
107
98
|
featureResolver: persistence ? systemRepositories.features : undefined,
|
|
108
99
|
featureCatalog: features,
|
|
100
|
+
disabledModuleNames: new Set(foundationSnapshot.disabledModuleNames),
|
|
109
101
|
// @windy-module system.audit begin
|
|
110
102
|
auditReader: persistence?.auditReader,
|
|
111
103
|
auditWriter: persistence?.auditWriter,
|
|
@@ -113,7 +105,7 @@ const runtime = createServerRuntime(process.env, {
|
|
|
113
105
|
});
|
|
114
106
|
const observability = createHttpObservability(runtime, persistence, logger);
|
|
115
107
|
// @windy-module system.license begin
|
|
116
|
-
|
|
108
|
+
licenseActivation.runtime.setAuditSink(runtime.auditSink);
|
|
117
109
|
// @windy-module system.license end
|
|
118
110
|
const {
|
|
119
111
|
authRepository,
|
|
@@ -213,7 +205,7 @@ const schedulerWorker = new SchedulerWorker(
|
|
|
213
205
|
(result) => observability.metrics.observeSchedulerTick(result),
|
|
214
206
|
);
|
|
215
207
|
// @windy-module system.scheduler end
|
|
216
|
-
const app = await
|
|
208
|
+
const app = await bootstrapPlatformApp({
|
|
217
209
|
runtime,
|
|
218
210
|
persistence,
|
|
219
211
|
platformConfig,
|
|
@@ -223,10 +215,11 @@ const app = await createPlatformServerApp({
|
|
|
223
215
|
features,
|
|
224
216
|
logger,
|
|
225
217
|
observability,
|
|
218
|
+
modules: foundationModules,
|
|
219
|
+
// @windy-module system.license begin
|
|
220
|
+
licenseRuntime: licenseActivation.runtime,
|
|
221
|
+
// @windy-module system.license end
|
|
226
222
|
});
|
|
227
|
-
// @windy-module system.license begin
|
|
228
|
-
registerPlatformLicenseRoutes(app, platformLicenseRuntime);
|
|
229
|
-
// @windy-module system.license end
|
|
230
223
|
// @windy-module system.settings begin
|
|
231
224
|
platformSettings.register(app, runtime, features);
|
|
232
225
|
// @windy-module system.settings end
|
|
@@ -267,7 +260,6 @@ registerAdminBootstrapRoute(app, runtime, {
|
|
|
267
260
|
snapshot: foundationSnapshot,
|
|
268
261
|
loadMenus: () => systemRepositories.menuRegistry.tree(),
|
|
269
262
|
});
|
|
270
|
-
registerSessionContextRoute(app, runtime, features);
|
|
271
263
|
registerFoundationRoute(app, runtime, foundationSnapshot, systemRepositories);
|
|
272
264
|
// @windy-module system.data-governance begin
|
|
273
265
|
registerGovernedUserExportRoutes(app, governedExportService, runtime, features);
|
|
@@ -342,8 +334,10 @@ validateInstalledRuntime({
|
|
|
342
334
|
// @windy-module system.search end
|
|
343
335
|
hosts: moduleHosts,
|
|
344
336
|
});
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
337
|
+
if (!process.argv.includes("--validate-module-composition")) {
|
|
338
|
+
const port = listenPlatformServer(app);
|
|
339
|
+
// @windy-module system.scheduler begin
|
|
340
|
+
schedulerWorker.start();
|
|
341
|
+
// @windy-module system.scheduler end
|
|
342
|
+
logServerStartup(logger, featureSync, Boolean(persistence), port);
|
|
343
|
+
}
|
|
@@ -1,29 +1,18 @@
|
|
|
1
|
-
// @windy-module work-order begin
|
|
2
|
-
import { createWorkOrderModule } from "./example-modules.js";
|
|
3
|
-
// @windy-module work-order end
|
|
4
1
|
import type { ModuleManifest } from "@southwind-ai/modules";
|
|
5
2
|
import type { InstallableServerModule } from "@southwind-ai/server-sdk";
|
|
6
|
-
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
7
|
-
import type { ManagedDepartment } from "./system/entities.js";
|
|
8
|
-
import type { EntityRepository } from "./system/repository.js";
|
|
9
3
|
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
readonly departments: EntityRepository<ManagedDepartment>;
|
|
13
|
-
}
|
|
4
|
+
export type { ProjectBusinessModuleRuntime as BusinessModuleRuntime } from "./app/server-composition.js";
|
|
5
|
+
import type { ProjectBusinessModuleRuntime } from "./app/server-composition.js";
|
|
14
6
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
/**
|
|
8
|
+
* 0.3.17 起保留的 project-owned 兼容装配缝。
|
|
9
|
+
*
|
|
10
|
+
* 新项目使用 app/server-composition.ts;升级项目可以继续在这里维护旧模块,受管 Host
|
|
11
|
+
* 会合并两种来源且不会要求客户文件新增导出。
|
|
12
|
+
*/
|
|
13
|
+
export const installedBusinessModules: readonly InstallableServerModule<ModuleManifest>[] =
|
|
14
|
+
[];
|
|
18
15
|
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// @windy-module work-order end
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
export function attachInstalledBusinessModules(
|
|
26
|
-
runtime: BusinessModuleRuntime,
|
|
27
|
-
): void {
|
|
28
|
-
for (const module of installedBusinessModules) module.attach(runtime);
|
|
29
|
-
}
|
|
16
|
+
export async function attachInstalledBusinessModules(
|
|
17
|
+
_runtime: ProjectBusinessModuleRuntime,
|
|
18
|
+
): Promise<void> {}
|
|
@@ -73,6 +73,7 @@ describe("legacy durable adapters", () => {
|
|
|
73
73
|
() => now,
|
|
74
74
|
undefined,
|
|
75
75
|
adapter,
|
|
76
|
+
async () => undefined,
|
|
76
77
|
);
|
|
77
78
|
executor.attach(({ legacyId, generation }) =>
|
|
78
79
|
restarted.resumeDurable(legacyId, generation),
|
|
@@ -110,6 +111,44 @@ describe("legacy durable adapters", () => {
|
|
|
110
111
|
processedRows: 1,
|
|
111
112
|
});
|
|
112
113
|
});
|
|
114
|
+
|
|
115
|
+
test("Bulk durable 恢复会重新校验当前 Actor 与 Module 激活状态", async () => {
|
|
116
|
+
const now = new Date("2026-07-16T00:00:00.000Z");
|
|
117
|
+
const repository = new InMemoryBulkDataRepository();
|
|
118
|
+
let executions = 0;
|
|
119
|
+
const service = new BulkDataService(
|
|
120
|
+
repository,
|
|
121
|
+
[
|
|
122
|
+
{
|
|
123
|
+
...testBulkHandler(),
|
|
124
|
+
exportRows: async () => {
|
|
125
|
+
executions += 1;
|
|
126
|
+
return [{ id: "row-1" }];
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
{},
|
|
131
|
+
() => now,
|
|
132
|
+
undefined,
|
|
133
|
+
undefined,
|
|
134
|
+
async () => "feature-disabled",
|
|
135
|
+
);
|
|
136
|
+
await service.createExport(
|
|
137
|
+
"fixtures",
|
|
138
|
+
"durable:bulk:guard",
|
|
139
|
+
actor("user_bulk"),
|
|
140
|
+
);
|
|
141
|
+
const running = await repository.claimNext(now);
|
|
142
|
+
if (!running) throw new Error("应创建 running Bulk 任务");
|
|
143
|
+
|
|
144
|
+
await service.resumeDurable(running.id, running.attempt);
|
|
145
|
+
|
|
146
|
+
expect(executions).toBe(0);
|
|
147
|
+
expect(await repository.getJob(running.id)).toMatchObject({
|
|
148
|
+
status: "failed",
|
|
149
|
+
error: "feature-disabled",
|
|
150
|
+
});
|
|
151
|
+
});
|
|
113
152
|
// @windy-module system.bulk-data end
|
|
114
153
|
|
|
115
154
|
test("Scheduler 采用未关联 running 记录且重放已完成记录为空操作", async () => {
|
|
@@ -142,6 +181,7 @@ describe("legacy durable adapters", () => {
|
|
|
142
181
|
maxAttempts: 3,
|
|
143
182
|
retryDelaySeconds: 10,
|
|
144
183
|
permissionKey: "system.scheduler.manage",
|
|
184
|
+
auditAction: "task.execute",
|
|
145
185
|
feature,
|
|
146
186
|
handler: () => {
|
|
147
187
|
executions += 1;
|
|
@@ -152,6 +192,13 @@ describe("legacy durable adapters", () => {
|
|
|
152
192
|
createServerRuntime(),
|
|
153
193
|
clock,
|
|
154
194
|
adapter,
|
|
195
|
+
async (actorId) => ({
|
|
196
|
+
id: actorId,
|
|
197
|
+
type: "user",
|
|
198
|
+
name: actorId,
|
|
199
|
+
roleCodes: [],
|
|
200
|
+
permissionKeys: ["system.scheduler.manage"],
|
|
201
|
+
}),
|
|
155
202
|
);
|
|
156
203
|
executor.attach(({ legacyId, generation }) =>
|
|
157
204
|
scheduler.resumeDurable(legacyId, generation),
|
|
@@ -181,6 +228,65 @@ describe("legacy durable adapters", () => {
|
|
|
181
228
|
expect(durableJob?.id).toBe(completed.durableJobId);
|
|
182
229
|
expect((await durable.get(durableJob!.id))?.attempts).toHaveLength(1);
|
|
183
230
|
});
|
|
231
|
+
|
|
232
|
+
test("Scheduler durable 恢复会重新校验当前 Actor 与 Module 激活状态", async () => {
|
|
233
|
+
const now = new Date("2026-07-16T00:00:00.000Z");
|
|
234
|
+
const feature = createFoundationSnapshot().features.find(
|
|
235
|
+
({ key }) => key === "system.scheduler",
|
|
236
|
+
);
|
|
237
|
+
if (!feature) throw new Error("测试缺少 Scheduler Feature");
|
|
238
|
+
const repository = new InMemorySchedulerRepository();
|
|
239
|
+
let executions = 0;
|
|
240
|
+
const scheduler = new SchedulerService(
|
|
241
|
+
repository,
|
|
242
|
+
[
|
|
243
|
+
{
|
|
244
|
+
key: "test.durable-guard",
|
|
245
|
+
label: "持久执行恢复门禁",
|
|
246
|
+
intervalSeconds: 3_600,
|
|
247
|
+
maxAttempts: 3,
|
|
248
|
+
retryDelaySeconds: 10,
|
|
249
|
+
permissionKey: "system.scheduler.manage",
|
|
250
|
+
auditAction: "task.execute",
|
|
251
|
+
feature,
|
|
252
|
+
handler: () => {
|
|
253
|
+
executions += 1;
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
createServerRuntime(
|
|
258
|
+
{},
|
|
259
|
+
{
|
|
260
|
+
featureCatalog: [feature],
|
|
261
|
+
disabledModuleNames: new Set(["system.scheduler"]),
|
|
262
|
+
},
|
|
263
|
+
),
|
|
264
|
+
{ now: () => now },
|
|
265
|
+
undefined,
|
|
266
|
+
async (actorId) => ({
|
|
267
|
+
id: actorId,
|
|
268
|
+
type: "user",
|
|
269
|
+
name: actorId,
|
|
270
|
+
roleCodes: [],
|
|
271
|
+
permissionKeys: ["system.scheduler.manage"],
|
|
272
|
+
}),
|
|
273
|
+
);
|
|
274
|
+
await scheduler.initialize();
|
|
275
|
+
const run = await repository.claimManual(
|
|
276
|
+
"test.durable-guard",
|
|
277
|
+
"user_scheduler",
|
|
278
|
+
now,
|
|
279
|
+
);
|
|
280
|
+
if (!run) throw new Error("应创建 Scheduler run");
|
|
281
|
+
|
|
282
|
+
await scheduler.resumeDurable(run.id, run.attempt);
|
|
283
|
+
|
|
284
|
+
expect(executions).toBe(0);
|
|
285
|
+
expect(await repository.getRun(run.id)).toMatchObject({
|
|
286
|
+
status: "blocked",
|
|
287
|
+
error: "feature-disabled",
|
|
288
|
+
});
|
|
289
|
+
});
|
|
184
290
|
});
|
|
185
291
|
|
|
186
292
|
// @windy-module system.bulk-data begin
|
|
@@ -10,6 +10,10 @@ import type { BulkDataRepository } from "../bulk-data/repository.js";
|
|
|
10
10
|
import type { BulkResourceHandler } from "../bulk-data/resource-handler.js";
|
|
11
11
|
// @windy-module system.bulk-data end
|
|
12
12
|
import type { createServerRuntime } from "../runtime.js";
|
|
13
|
+
import type { RequestActor } from "../guards.js";
|
|
14
|
+
// @windy-module system.bulk-data begin
|
|
15
|
+
import { evaluateAccessGuards } from "../access-guards.js";
|
|
16
|
+
// @windy-module system.bulk-data end
|
|
13
17
|
import type { SchedulerRepository } from "../scheduler/repository.js";
|
|
14
18
|
import { SchedulerService } from "../scheduler/service.js";
|
|
15
19
|
import type { ScheduledTaskDefinition } from "../scheduler/types.js";
|
|
@@ -38,6 +42,7 @@ interface LegacyServiceRuntimeInput {
|
|
|
38
42
|
schedulerRepository: SchedulerRepository;
|
|
39
43
|
taskDefinitions: ScheduledTaskDefinition[];
|
|
40
44
|
runtime: ReturnType<typeof createServerRuntime>;
|
|
45
|
+
resolveActor?: (actorId: string) => Promise<RequestActor | undefined>;
|
|
41
46
|
// @windy-module system.workflow begin
|
|
42
47
|
workflow: WorkflowService;
|
|
43
48
|
// @windy-module system.workflow end
|
|
@@ -80,6 +85,7 @@ export function createLegacyServiceRuntime(input: LegacyServiceRuntimeInput) {
|
|
|
80
85
|
workerId: `server-${process.pid}-bulk`,
|
|
81
86
|
execute: (payload) => bulkExecutor.execute(payload),
|
|
82
87
|
}),
|
|
88
|
+
createBulkResumeAuthorization(input),
|
|
83
89
|
);
|
|
84
90
|
bulkExecutor.attach(({ legacyId, generation }) =>
|
|
85
91
|
bulkDataService.resumeDurable(legacyId, generation),
|
|
@@ -99,6 +105,7 @@ export function createLegacyServiceRuntime(input: LegacyServiceRuntimeInput) {
|
|
|
99
105
|
workerId: `server-${process.pid}-scheduler`,
|
|
100
106
|
execute: (payload) => schedulerExecutor.execute(payload),
|
|
101
107
|
}),
|
|
108
|
+
input.resolveActor,
|
|
102
109
|
);
|
|
103
110
|
schedulerExecutor.attach(({ legacyId, generation }) =>
|
|
104
111
|
scheduler.resumeDurable(legacyId, generation),
|
|
@@ -116,3 +123,36 @@ export function createLegacyServiceRuntime(input: LegacyServiceRuntimeInput) {
|
|
|
116
123
|
durable: { facts, service, registry, worker },
|
|
117
124
|
};
|
|
118
125
|
}
|
|
126
|
+
|
|
127
|
+
// @windy-module system.bulk-data begin
|
|
128
|
+
function createBulkResumeAuthorization(input: LegacyServiceRuntimeInput) {
|
|
129
|
+
const feature = input.taskDefinitions.find(
|
|
130
|
+
({ key }) => key === "system.bulk-data.dispatch",
|
|
131
|
+
)?.feature;
|
|
132
|
+
return async (job: { requestedBy: string }) => {
|
|
133
|
+
if (!feature) return "feature-definition-missing";
|
|
134
|
+
const actor =
|
|
135
|
+
(await input.resolveActor?.(job.requestedBy)) ??
|
|
136
|
+
deniedResumeActor(job.requestedBy);
|
|
137
|
+
const context = await input.runtime.createGuardContextForActor(
|
|
138
|
+
new Request("http://jobs.local/bulk-data/resume"),
|
|
139
|
+
actor,
|
|
140
|
+
);
|
|
141
|
+
const guarded = await evaluateAccessGuards(input.runtime, context, {
|
|
142
|
+
permission: { permissionKey: "system.bulk-data.manage" },
|
|
143
|
+
feature,
|
|
144
|
+
});
|
|
145
|
+
return guarded.decision.allowed ? undefined : guarded.decision.reason;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function deniedResumeActor(actorId: string): RequestActor {
|
|
150
|
+
return {
|
|
151
|
+
id: actorId,
|
|
152
|
+
type: "user",
|
|
153
|
+
name: actorId,
|
|
154
|
+
roleCodes: [],
|
|
155
|
+
permissionKeys: [],
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
// @windy-module system.bulk-data end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { createLicenseRuntimeActivation } from "./activation.js";
|
|
3
|
+
|
|
4
|
+
describe("License Module 激活", () => {
|
|
5
|
+
test("Module 未启用时不创建授权限制边界", () => {
|
|
6
|
+
const fallback = { resolve: () => ({ status: "valid" as const }) };
|
|
7
|
+
const activation = createLicenseRuntimeActivation(
|
|
8
|
+
{ NODE_ENV: "production" },
|
|
9
|
+
false,
|
|
10
|
+
fallback,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
expect(activation.enforced).toBe(false);
|
|
14
|
+
expect(activation.resolver).toBeUndefined();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("Module 启用后按环境选择官方或持久化 Resolver", () => {
|
|
18
|
+
const fallback = { resolve: () => ({ status: "valid" as const }) };
|
|
19
|
+
const production = createLicenseRuntimeActivation(
|
|
20
|
+
{ NODE_ENV: "production" },
|
|
21
|
+
true,
|
|
22
|
+
fallback,
|
|
23
|
+
);
|
|
24
|
+
const development = createLicenseRuntimeActivation({}, true, fallback);
|
|
25
|
+
|
|
26
|
+
expect(production.resolver).toBe(production.runtime);
|
|
27
|
+
expect(development.resolver).toBe(fallback);
|
|
28
|
+
expect(production.enforced).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { LicenseEntitlementResolver } from "../runtime-license.js";
|
|
2
|
+
import {
|
|
3
|
+
createPlatformLicenseRuntimeOptions,
|
|
4
|
+
hasConfiguredLicenseKeys,
|
|
5
|
+
PlatformLicenseRuntime,
|
|
6
|
+
} from "./runtime-service.js";
|
|
7
|
+
|
|
8
|
+
export function createLicenseRuntimeActivation(
|
|
9
|
+
env: Record<string, string | undefined>,
|
|
10
|
+
enabled: boolean,
|
|
11
|
+
fallbackResolver?: LicenseEntitlementResolver,
|
|
12
|
+
) {
|
|
13
|
+
const options = createPlatformLicenseRuntimeOptions(env);
|
|
14
|
+
const runtime = new PlatformLicenseRuntime(options);
|
|
15
|
+
const useOfficialRuntime =
|
|
16
|
+
Boolean(options.enforceRestrictions) || hasConfiguredLicenseKeys(options);
|
|
17
|
+
return {
|
|
18
|
+
enforced: enabled,
|
|
19
|
+
runtime,
|
|
20
|
+
resolver: enabled
|
|
21
|
+
? useOfficialRuntime
|
|
22
|
+
? runtime
|
|
23
|
+
: fallbackResolver
|
|
24
|
+
: undefined,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -51,4 +51,20 @@ describe("License 限制模式 HTTP 白名单", () => {
|
|
|
51
51
|
);
|
|
52
52
|
}
|
|
53
53
|
});
|
|
54
|
+
|
|
55
|
+
test("License Module 未启用时不进入全局限制模式", async () => {
|
|
56
|
+
const runtime = createServerRuntime(
|
|
57
|
+
{},
|
|
58
|
+
{
|
|
59
|
+
licenseEnforced: false,
|
|
60
|
+
licenseResolver: {
|
|
61
|
+
resolve: () => ({ status: "expired", restricted: true }),
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
const request = new Request("http://localhost/api/system/users");
|
|
66
|
+
const context = await runtime.createGuardContext(request);
|
|
67
|
+
|
|
68
|
+
expect(evaluateRestrictedHttpRequest(context, request).allowed).toBe(true);
|
|
69
|
+
});
|
|
54
70
|
});
|