create-windy 0.2.33 → 0.3.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/dist/cli.js +173 -17
- package/package.json +1 -1
- package/template/.agents/skills/windy-business-module/SKILL.md +10 -1
- package/template/.windy-template.json +2 -2
- package/template/AGENTS.md +9 -0
- package/template/README.md +3 -1
- package/template/apps/agent-server/Dockerfile +1 -0
- package/template/apps/agent-server/README.md +28 -5
- package/template/apps/agent-server/adk_web_agents/__init__.py +1 -0
- package/template/apps/agent-server/adk_web_agents/safe_debug/__init__.py +3 -0
- package/template/apps/agent-server/adk_web_agents/safe_debug/agent.py +40 -0
- package/template/apps/agent-server/pyproject.toml +1 -1
- package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +191 -16
- package/template/apps/agent-server/src/southwind_agent_server/adk_failure.py +13 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_model.py +225 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_tools.py +76 -0
- package/template/apps/agent-server/src/southwind_agent_server/app.py +63 -3
- package/template/apps/agent-server/src/southwind_agent_server/contracts.py +35 -0
- package/template/apps/agent-server/src/southwind_agent_server/engine.py +2 -0
- package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +31 -3
- package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +22 -9
- package/template/apps/agent-server/src/southwind_agent_server/provider_audit.py +144 -0
- package/template/apps/agent-server/src/southwind_agent_server/provider_governance.py +227 -0
- package/template/apps/agent-server/src/southwind_agent_server/service.py +1 -0
- package/template/apps/agent-server/tests/test_adk_adapter.py +305 -0
- package/template/apps/agent-server/tests/test_adk_web_agent.py +12 -0
- package/template/apps/agent-server/tests/test_api.py +16 -1
- package/template/apps/agent-server/tests/test_openai_provider.py +79 -0
- package/template/apps/agent-server/tests/test_provider_governance.py +215 -0
- package/template/apps/server/package.json +4 -1
- package/template/apps/server/src/agent/provider-input-governance.test.ts +82 -0
- package/template/apps/server/src/agent/provider-input-governance.ts +140 -0
- package/template/apps/server/src/agent/remote-runtime.test.ts +22 -0
- package/template/apps/server/src/agent/remote-runtime.ts +1 -0
- package/template/apps/server/src/agent/run-contracts.ts +2 -0
- package/template/apps/server/src/agent/run-facade.test.ts +52 -2
- package/template/apps/server/src/agent/run-facade.ts +38 -9
- package/template/apps/server/src/agent/tool-registry.ts +2 -6
- package/template/apps/server/src/application-services.ts +50 -0
- package/template/apps/server/src/audit/drizzle-log-store.ts +25 -0
- package/template/apps/server/src/audit/search-summary.ts +2 -6
- package/template/apps/server/src/auth/credential-restriction.ts +1 -0
- package/template/apps/server/src/auth/password.ts +30 -21
- package/template/apps/server/src/auth/routes.test.ts +44 -3
- package/template/apps/server/src/auth/routes.ts +6 -1
- package/template/apps/server/src/auth/runtime.ts +4 -1
- package/template/apps/server/src/auth/service.ts +25 -7
- package/template/apps/server/src/configuration/bootstrap.ts +20 -1
- package/template/apps/server/src/configuration/definition.test.ts +71 -0
- package/template/apps/server/src/configuration/definition.ts +242 -12
- package/template/apps/server/src/configuration/drizzle-repository.ts +36 -2
- package/template/apps/server/src/configuration/repository.ts +27 -2
- package/template/apps/server/src/configuration/routes.ts +4 -1
- package/template/apps/server/src/configuration/service.test.ts +100 -0
- package/template/apps/server/src/configuration/service.ts +50 -10
- package/template/apps/server/src/data-access/scoped-repository.integration.test.ts +3 -2
- package/template/apps/server/src/data-governance/export/service.ts +2 -6
- package/template/apps/server/src/example-modules.ts +4 -1
- package/template/apps/server/src/guards.ts +18 -5
- package/template/apps/server/src/http/request-source.test.ts +85 -0
- package/template/apps/server/src/http/request-source.ts +90 -0
- package/template/apps/server/src/index.ts +45 -45
- package/template/apps/server/src/license/routes.ts +2 -0
- package/template/apps/server/src/license/runtime-service.ts +10 -11
- package/template/apps/server/src/module-bootstrap.ts +2 -2
- package/template/apps/server/src/module-host/initialize-contracts.ts +6 -0
- package/template/apps/server/src/module-host/initialize.ts +30 -44
- package/template/apps/server/src/module-host/request-context.ts +29 -23
- package/template/apps/server/src/module-host/resource-file-policies.test.ts +197 -0
- package/template/apps/server/src/module-host/resource-file-policies.ts +108 -0
- package/template/apps/server/src/module-host/route-declaration.ts +45 -0
- package/template/apps/server/src/module-host/route-installer.ts +27 -3
- package/template/apps/server/src/module-host/storage-port.test.ts +170 -1
- package/template/apps/server/src/module-host/storage-port.ts +134 -1
- package/template/apps/server/src/module-host/storage-scope.ts +89 -0
- package/template/apps/server/src/module-host/tool-handlers.test.ts +305 -0
- package/template/apps/server/src/module-host/tool-handlers.ts +38 -4
- package/template/apps/server/src/module-host/user-directory-composition.test.ts +126 -0
- package/template/apps/server/src/module-host/user-directory-port.test.ts +238 -0
- package/template/apps/server/src/module-host/user-directory-port.ts +139 -0
- package/template/apps/server/src/module-host.ts +4 -0
- package/template/apps/server/src/notification/channel-adapter.ts +46 -0
- package/template/apps/server/src/notification/delivery-policy.test.ts +59 -0
- package/template/apps/server/src/notification/delivery-policy.ts +37 -0
- package/template/apps/server/src/notification/delivery-repository.ts +198 -0
- package/template/apps/server/src/notification/delivery-worker.test.ts +87 -0
- package/template/apps/server/src/notification/delivery-worker.ts +88 -0
- package/template/apps/server/src/notification/drizzle-management.ts +180 -0
- package/template/apps/server/src/notification/drizzle-query.ts +160 -0
- package/template/apps/server/src/notification/drizzle-repository.integration.test.ts +301 -0
- package/template/apps/server/src/notification/drizzle-repository.ts +156 -90
- package/template/apps/server/src/notification/extension-routes.test.ts +119 -0
- package/template/apps/server/src/notification/extension-routes.ts +111 -0
- package/template/apps/server/src/notification/memory-delivery-repository.ts +137 -0
- package/template/apps/server/src/notification/memory-support.ts +72 -0
- package/template/apps/server/src/notification/preference-repository.ts +95 -0
- package/template/apps/server/src/notification/recipient-directory.ts +104 -0
- package/template/apps/server/src/notification/repository.test.ts +171 -0
- package/template/apps/server/src/notification/repository.ts +179 -32
- package/template/apps/server/src/notification/revision-routes.test.ts +186 -0
- package/template/apps/server/src/notification/route-support.ts +225 -0
- package/template/apps/server/src/notification/routes.test.ts +182 -0
- package/template/apps/server/src/notification/routes.ts +225 -132
- package/template/apps/server/src/notification/service.test.ts +104 -0
- package/template/apps/server/src/notification/service.ts +140 -0
- package/template/apps/server/src/notification/smtp-adapter.test.ts +65 -0
- package/template/apps/server/src/notification/smtp-adapter.ts +68 -0
- package/template/apps/server/src/persistence.integration.test.ts +41 -2
- package/template/apps/server/src/route-guards.ts +1 -0
- package/template/apps/server/src/runtime-audit-list.test.ts +39 -0
- package/template/apps/server/src/runtime-development.ts +2 -0
- package/template/apps/server/src/runtime.test.ts +23 -9
- package/template/apps/server/src/runtime.ts +39 -2
- package/template/apps/server/src/scheduler/audit.ts +2 -11
- package/template/apps/server/src/scheduler/definitions.test.ts +6 -0
- package/template/apps/server/src/scheduler/definitions.ts +7 -0
- package/template/apps/server/src/scheduler/recovery-service.ts +2 -11
- package/template/apps/server/src/scheduler/routes.ts +2 -7
- package/template/apps/server/src/search/opensearch-client.test.ts +45 -0
- package/template/apps/server/src/search/opensearch-client.ts +145 -0
- package/template/apps/server/src/search/service.ts +2 -6
- package/template/apps/server/src/settings/routes.ts +15 -3
- package/template/apps/server/src/settings/runtime.ts +10 -1
- package/template/apps/server/src/storage/runtime.ts +13 -0
- package/template/apps/server/src/system/audit-query.ts +33 -4
- package/template/apps/server/src/system/audit-routes.test.ts +58 -4
- package/template/apps/server/src/system/audit-routes.ts +6 -5
- package/template/apps/server/src/system/built-in-roles.ts +1 -0
- package/template/apps/server/src/system/department-organization.ts +320 -0
- package/template/apps/server/src/system/department-routes.test.ts +207 -48
- package/template/apps/server/src/system/department-routes.ts +114 -33
- package/template/apps/server/src/system/drizzle-repository.ts +15 -7
- package/template/apps/server/src/system/drizzle-scoped-repository.ts +5 -3
- package/template/apps/server/src/system/drizzle-system.ts +37 -9
- package/template/apps/server/src/system/drizzle-transaction-context.ts +14 -0
- package/template/apps/server/src/system/identity-route-config.ts +7 -2
- package/template/apps/server/src/system/mutation-runner.ts +23 -0
- package/template/apps/server/src/system/organization-postgres.integration.test.ts +176 -0
- package/template/apps/server/src/system/resource-access-response.ts +12 -6
- package/template/apps/server/src/system/role-department-scope-policy.ts +55 -0
- package/template/apps/server/src/system/route-helpers.ts +2 -5
- package/template/apps/server/src/system/route-types.ts +12 -3
- package/template/apps/server/src/system/routes-validation.test.ts +26 -0
- package/template/apps/server/src/system/routes.test.ts +10 -2
- package/template/apps/server/src/system/routes.ts +70 -36
- package/template/apps/server/src/system/seed.ts +7 -0
- package/template/apps/server/src/work-order/data-scope.integration.test.ts +9 -2
- package/template/apps/server/src/work-order/opensearch-index.test.ts +97 -0
- package/template/apps/server/src/work-order/opensearch-index.ts +313 -0
- package/template/apps/server/src/work-order/search-provider.ts +60 -1
- package/template/apps/server/src/work-order/service.ts +42 -10
- package/template/apps/web/src/app/error-pages.ts +9 -0
- package/template/apps/web/src/components/auth/PasswordChangeForm.vue +16 -11
- package/template/apps/web/src/components/auth/PasswordChangeForm.webtest.ts +14 -0
- package/template/apps/web/src/components/common/FormDialog.vue +16 -2
- package/template/apps/web/src/components/common/ModalLayout.webtest.ts +20 -1
- package/template/apps/web/src/composables/useGlobalSearch.ts +68 -0
- package/template/apps/web/src/composables/usePlatformSettings.ts +8 -7
- package/template/apps/web/src/layout/GlobalSearchResults.vue +58 -0
- package/template/apps/web/src/layout/NavigationSearchDialog.vue +42 -6
- package/template/apps/web/src/layout/NavigationSearchDialog.webtest.ts +71 -0
- package/template/apps/web/src/layout/NotificationMenu.vue +73 -31
- package/template/apps/web/src/layout/NotificationMenu.webtest.ts +44 -1
- package/template/apps/web/src/layout/NotificationMenuItem.vue +61 -0
- package/template/apps/web/src/layout/NotificationPreferencesDialog.vue +181 -0
- package/template/apps/web/src/layout/NotificationPreferencesDialog.webtest.ts +80 -0
- package/template/apps/web/src/lib/date-time.ts +22 -10
- package/template/apps/web/src/lib/date-time.webtest.ts +9 -1
- package/template/apps/web/src/main.ts +14 -1
- package/template/apps/web/src/pages/auth/ChangePasswordPage.vue +16 -1
- package/template/apps/web/src/pages/configuration/components/ModuleConfigVersionList.vue +1 -0
- package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.vue +10 -0
- package/template/apps/web/src/pages/configuration/components/ModuleConfigurationWorkbench.vue +37 -6
- package/template/apps/web/src/pages/errors/PlatformErrorPage.vue +115 -0
- package/template/apps/web/src/pages/errors/PlatformErrorPage.webtest.ts +44 -0
- package/template/apps/web/src/pages/errors/error-page-extension.ts +26 -0
- package/template/apps/web/src/pages/system/SystemNotificationsPage.vue +136 -68
- package/template/apps/web/src/pages/system/SystemNotificationsPage.webtest.ts +224 -12
- package/template/apps/web/src/pages/system/audit-log-presenter.ts +86 -0
- package/template/apps/web/src/pages/system/audit-time-range.ts +28 -0
- package/template/apps/web/src/pages/system/components/AuditActionCell.vue +10 -69
- package/template/apps/web/src/pages/system/components/AuditActionCell.webtest.ts +20 -27
- package/template/apps/web/src/pages/system/components/AuditLogDetailDialog.vue +134 -0
- package/template/apps/web/src/pages/system/components/AuditLogDetailDialog.webtest.ts +76 -0
- package/template/apps/web/src/pages/system/components/AuditLogFilters.vue +116 -52
- package/template/apps/web/src/pages/system/components/AuditLogFilters.webtest.ts +52 -15
- package/template/apps/web/src/pages/system/components/NotificationHistoryDialog.vue +129 -0
- package/template/apps/web/src/pages/system/components/NotificationManagementCard.vue +91 -0
- package/template/apps/web/src/pages/system/components/NotificationPublishDialog.vue +121 -0
- package/template/apps/web/src/pages/system/components/NotificationPublishForm.vue +162 -1
- package/template/apps/web/src/pages/system/components/SystemResourceActions.vue +68 -0
- package/template/apps/web/src/pages/system/components/SystemResourceEditor.vue +74 -0
- package/template/apps/web/src/pages/system/components/SystemResourceFilters.vue +3 -4
- package/template/apps/web/src/pages/system/components/SystemResourcePage.vue +91 -98
- package/template/apps/web/src/pages/system/components/SystemResourcePage.webtest.ts +68 -0
- package/template/apps/web/src/pages/system/components/SystemResourceTable.vue +20 -1
- package/template/apps/web/src/pages/system/components/SystemResourceTable.webtest.ts +28 -7
- package/template/apps/web/src/pages/system/components/department/DepartmentOrganizationForm.vue +210 -0
- package/template/apps/web/src/pages/system/components/department/DepartmentTransitionDialog.vue +112 -0
- package/template/apps/web/src/pages/system/components/user-account/UserAccountForm.vue +17 -4
- package/template/apps/web/src/pages/system/composables/useDepartmentTransitions.ts +79 -0
- package/template/apps/web/src/pages/system/composables/useNotificationPermissions.ts +14 -0
- package/template/apps/web/src/pages/system/composables/useSystemResource.ts +2 -0
- package/template/apps/web/src/pages/system/composables/useSystemResource.webtest.ts +15 -2
- package/template/apps/web/src/pages/system/resource-config.ts +12 -4
- package/template/apps/web/src/router/error-navigation.ts +50 -0
- package/template/apps/web/src/router/error-navigation.webtest.ts +106 -0
- package/template/apps/web/src/router/index.ts +71 -0
- package/template/apps/web/src/router/index.webtest.ts +29 -0
- package/template/apps/web/src/router/manifest-routes.webtest.ts +4 -0
- package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +12 -0
- package/template/apps/web/src/services/auth-api.ts +5 -0
- package/template/apps/web/src/services/http.ts +43 -6
- package/template/apps/web/src/services/http.webtest.ts +70 -0
- package/template/apps/web/src/services/notification-api.ts +85 -2
- package/template/apps/web/src/services/notification-api.webtest.ts +110 -0
- package/template/apps/web/src/services/search-api.ts +39 -0
- package/template/apps/web/src/services/system-api.ts +18 -0
- package/template/apps/web/vitest.config.ts +1 -0
- package/template/docker-compose.yml +68 -0
- package/template/docs/architecture/ai-egress.md +49 -7
- package/template/docs/architecture/ai-runtime.md +34 -6
- package/template/docs/architecture/data-scope-query-enforcement.md +4 -0
- package/template/docs/architecture/object-storage.md +22 -2
- package/template/docs/architecture/search-provider-protocol.md +38 -2
- package/template/docs/development/custom-error-pages.md +78 -0
- package/template/docs/development/custom-login-page.md +3 -3
- package/template/docs/platform/agent-runtime.md +76 -7
- package/template/docs/platform/audit-reliability.md +46 -2
- package/template/docs/platform/business-user-directory.md +82 -0
- package/template/docs/platform/module-configuration.md +13 -4
- package/template/docs/platform/notifications.md +67 -11
- package/template/docs/platform/organization-membership.md +17 -3
- package/template/docs/platform/system-crud-api.md +16 -0
- package/template/docs/platform/web-system-crud.md +6 -2
- package/template/package.json +4 -0
- package/template/packages/config/index.test.ts +45 -0
- package/template/packages/config/package.json +1 -1
- package/template/packages/config/src/platform.ts +169 -48
- package/template/packages/database/drizzle/0035_material_nightshade.sql +10 -0
- package/template/packages/database/drizzle/0036_hesitant_speed.sql +35 -0
- package/template/packages/database/drizzle/0037_absent_nick_fury.sql +2 -0
- package/template/packages/database/drizzle/0038_uneven_wasp.sql +38 -0
- package/template/packages/database/drizzle/meta/0035_snapshot.json +7331 -0
- package/template/packages/database/drizzle/meta/0036_snapshot.json +7436 -0
- package/template/packages/database/drizzle/meta/0037_snapshot.json +7457 -0
- package/template/packages/database/drizzle/meta/0038_snapshot.json +7749 -0
- package/template/packages/database/drizzle/meta/_journal.json +28 -0
- package/template/packages/database/package.json +1 -1
- package/template/packages/database/src/schema/identity.ts +7 -0
- package/template/packages/database/src/schema/notifications.ts +135 -0
- package/template/packages/database/src/schema-notification-delivery.test.ts +54 -0
- package/template/packages/database/src/schema-workflow-notification.test.ts +40 -1
- package/template/packages/modules/package.json +1 -1
- package/template/packages/modules/src/ai-operation-composition.test.ts +68 -0
- package/template/packages/modules/src/ai-operation-composition.ts +115 -0
- package/template/packages/modules/src/manifest.ts +26 -0
- package/template/packages/modules/src/provider-data-policy-composition.test.ts +74 -0
- package/template/packages/modules/src/system-api-permissions.ts +54 -0
- package/template/packages/modules/src/system-audit-actions.ts +15 -0
- package/template/packages/modules/src/system-features.ts +6 -1
- package/template/packages/modules/src/system-module-catalog.test.ts +16 -0
- package/template/packages/modules/src/system-module-catalog.ts +28 -4
- package/template/packages/modules/src/system-modules.test.ts +13 -1
- package/template/packages/modules/src/system-modules.ts +12 -0
- package/template/packages/modules/src/system-permissions.ts +11 -5
- package/template/packages/server-sdk/index.ts +11 -0
- package/template/packages/server-sdk/package.json +3 -2
- package/template/packages/server-sdk/src/ai-operation-registration.ts +2 -1
- package/template/packages/server-sdk/src/module-host.ts +8 -0
- package/template/packages/server-sdk/src/request-context.ts +2 -0
- package/template/packages/server-sdk/src/storage-port.ts +66 -0
- package/template/packages/server-sdk/src/tool-registration.ts +6 -0
- package/template/packages/server-sdk/src/user-directory-port.ts +42 -0
- package/template/packages/shared/package.json +1 -1
- package/template/packages/shared/src/audit.ts +25 -0
- package/template/packages/shared/src/module-configuration.ts +5 -0
- package/template/packages/shared/src/notification.ts +92 -1
- package/template/packages/shared/src/password.ts +7 -2
- package/template/packages/shared/src/platform-settings.ts +2 -0
- package/template/packages/storage/package.json +1 -1
- package/template/packages/storage/src/upload-service.test.ts +16 -0
- package/template/packages/storage/src/upload-service.ts +5 -1
- package/template/scripts/notification-postgres-regression.ts +27 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
favoriteNotification,
|
|
4
|
+
getNotificationAudienceOptions,
|
|
5
|
+
getNotificationPreference,
|
|
6
|
+
listNotificationRevisions,
|
|
7
|
+
listNotifications,
|
|
8
|
+
unfavoriteNotification,
|
|
9
|
+
updateNotificationPreference,
|
|
10
|
+
updateNotification,
|
|
11
|
+
} from "./notification-api";
|
|
12
|
+
|
|
13
|
+
const mocks = vi.hoisted(() => ({
|
|
14
|
+
request: vi.fn(async () => ({ items: [], total: 0, page: 1, pageSize: 10 })),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
vi.mock("./http", () => ({ apiRequest: mocks.request }));
|
|
18
|
+
|
|
19
|
+
describe("普通用户通知 API", () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
mocks.request.mockClear();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("旧分页调用保持原 URL 兼容", async () => {
|
|
25
|
+
await listNotifications(2, 20);
|
|
26
|
+
|
|
27
|
+
expect(mocks.request).toHaveBeenCalledWith(
|
|
28
|
+
"/notifications?page=2&pageSize=20",
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("关键词、分类、已读和收藏筛选统一编码到服务端查询", async () => {
|
|
33
|
+
await listNotifications(3, 15, {
|
|
34
|
+
q: "维护 窗口",
|
|
35
|
+
category: "系统/安全",
|
|
36
|
+
read: "unread",
|
|
37
|
+
favorite: false,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(mocks.request).toHaveBeenCalledWith(
|
|
41
|
+
"/notifications?page=3&pageSize=15&q=%E7%BB%B4%E6%8A%A4+%E7%AA%97%E5%8F%A3&category=%E7%B3%BB%E7%BB%9F%2F%E5%AE%89%E5%85%A8&read=unread&favorite=false",
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("收藏与取消收藏使用显式幂等方法", async () => {
|
|
46
|
+
await favoriteNotification("notice/1");
|
|
47
|
+
await unfavoriteNotification("notice/1");
|
|
48
|
+
|
|
49
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
50
|
+
1,
|
|
51
|
+
"/notifications/notice%2F1/favorite",
|
|
52
|
+
{ method: "PUT" },
|
|
53
|
+
);
|
|
54
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
55
|
+
2,
|
|
56
|
+
"/notifications/notice%2F1/favorite",
|
|
57
|
+
{ method: "DELETE" },
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("管理端修改公告并分页读取版本历史", async () => {
|
|
62
|
+
const input = {
|
|
63
|
+
title: "修改公告",
|
|
64
|
+
summary: "修改摘要",
|
|
65
|
+
content: "修改正文",
|
|
66
|
+
category: "系统",
|
|
67
|
+
};
|
|
68
|
+
await updateNotification("notice/1", input);
|
|
69
|
+
await listNotificationRevisions("notice/1", 2, 20);
|
|
70
|
+
|
|
71
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
72
|
+
1,
|
|
73
|
+
"/system/notifications/notice%2F1",
|
|
74
|
+
{ method: "PUT", body: JSON.stringify(input) },
|
|
75
|
+
);
|
|
76
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
77
|
+
2,
|
|
78
|
+
"/system/notifications/notice%2F1/revisions?page=2&pageSize=20",
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("偏好和管理端受众选项走受 Guard 保护的稳定端点", async () => {
|
|
83
|
+
await getNotificationPreference();
|
|
84
|
+
await updateNotificationPreference({
|
|
85
|
+
enabledChannels: ["email"],
|
|
86
|
+
disabledCategories: ["营销"],
|
|
87
|
+
});
|
|
88
|
+
await getNotificationAudienceOptions();
|
|
89
|
+
|
|
90
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
91
|
+
1,
|
|
92
|
+
"/notifications/preferences",
|
|
93
|
+
);
|
|
94
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
95
|
+
2,
|
|
96
|
+
"/notifications/preferences",
|
|
97
|
+
{
|
|
98
|
+
method: "PUT",
|
|
99
|
+
body: JSON.stringify({
|
|
100
|
+
enabledChannels: ["email"],
|
|
101
|
+
disabledCategories: ["营销"],
|
|
102
|
+
}),
|
|
103
|
+
},
|
|
104
|
+
);
|
|
105
|
+
expect(mocks.request).toHaveBeenNthCalledWith(
|
|
106
|
+
3,
|
|
107
|
+
"/system/notifications/audience-options",
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { apiRequest } from "./http";
|
|
2
|
+
|
|
3
|
+
export interface SearchHit {
|
|
4
|
+
id: string;
|
|
5
|
+
resourceType: string;
|
|
6
|
+
maskedTitle: string;
|
|
7
|
+
maskedExcerpt?: string;
|
|
8
|
+
score?: number;
|
|
9
|
+
providerKey: string;
|
|
10
|
+
deepLink: {
|
|
11
|
+
surface: "business";
|
|
12
|
+
routeTemplate: string;
|
|
13
|
+
params: Record<string, string>;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SearchResultGroup {
|
|
18
|
+
providerKey: string;
|
|
19
|
+
label: string;
|
|
20
|
+
hits: readonly SearchHit[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SearchResultPage {
|
|
24
|
+
groups: readonly SearchResultGroup[];
|
|
25
|
+
partial: boolean;
|
|
26
|
+
failures: Array<{
|
|
27
|
+
providerKey: string;
|
|
28
|
+
code: string;
|
|
29
|
+
message: string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function searchPlatform(query: string, signal?: AbortSignal) {
|
|
34
|
+
return apiRequest<SearchResultPage>("/search", {
|
|
35
|
+
method: "POST",
|
|
36
|
+
body: JSON.stringify({ query, limit: 8 }),
|
|
37
|
+
signal,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -154,3 +154,21 @@ export function quickCreateDepartment(payload: Record<string, unknown>) {
|
|
|
154
154
|
body: JSON.stringify(payload),
|
|
155
155
|
});
|
|
156
156
|
}
|
|
157
|
+
|
|
158
|
+
export function transitionDepartment(
|
|
159
|
+
departmentId: string,
|
|
160
|
+
payload: {
|
|
161
|
+
action: "disable" | "delete";
|
|
162
|
+
targetDepartmentId?: string;
|
|
163
|
+
},
|
|
164
|
+
) {
|
|
165
|
+
return apiRequest<{
|
|
166
|
+
department: SystemRecord;
|
|
167
|
+
movedChildren: number;
|
|
168
|
+
movedUsers: number;
|
|
169
|
+
updatedRoles: number;
|
|
170
|
+
}>(`/system/departments/${departmentId}/transition`, {
|
|
171
|
+
method: "POST",
|
|
172
|
+
body: JSON.stringify(payload),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
@@ -46,6 +46,10 @@ services:
|
|
|
46
46
|
AGENT_SERVER_URL: http://agent-server:8080
|
|
47
47
|
AGENT_TOOL_HOST_URL: http://server:9747
|
|
48
48
|
AGENT_SERVER_INTERNAL_TOKEN: ${AGENT_SERVER_INTERNAL_TOKEN:-}
|
|
49
|
+
OPENSEARCH_URL: ${OPENSEARCH_URL:-}
|
|
50
|
+
OPENSEARCH_USERNAME: ${OPENSEARCH_USERNAME:-}
|
|
51
|
+
OPENSEARCH_PASSWORD: ${OPENSEARCH_PASSWORD:-}
|
|
52
|
+
OPENSEARCH_WORK_ORDER_INDEX: ${OPENSEARCH_WORK_ORDER_INDEX:-windy-work-orders-v1}
|
|
49
53
|
ports:
|
|
50
54
|
- "9747:9747"
|
|
51
55
|
volumes:
|
|
@@ -56,6 +60,29 @@ services:
|
|
|
56
60
|
migrate:
|
|
57
61
|
condition: service_completed_successfully
|
|
58
62
|
|
|
63
|
+
opensearch:
|
|
64
|
+
profiles: ["search"]
|
|
65
|
+
image: opensearchproject/opensearch:2.19.3
|
|
66
|
+
environment:
|
|
67
|
+
discovery.type: single-node
|
|
68
|
+
DISABLE_INSTALL_DEMO_CONFIG: "true"
|
|
69
|
+
DISABLE_SECURITY_PLUGIN: "true"
|
|
70
|
+
OPENSEARCH_JAVA_OPTS: "-Xms512m -Xmx512m"
|
|
71
|
+
ports:
|
|
72
|
+
- "127.0.0.1:19200:9200"
|
|
73
|
+
volumes:
|
|
74
|
+
- opensearch-data:/usr/share/opensearch/data
|
|
75
|
+
healthcheck:
|
|
76
|
+
test:
|
|
77
|
+
[
|
|
78
|
+
"CMD-SHELL",
|
|
79
|
+
"curl -fsS http://127.0.0.1:9200/_cluster/health?wait_for_status=yellow",
|
|
80
|
+
]
|
|
81
|
+
interval: 10s
|
|
82
|
+
timeout: 5s
|
|
83
|
+
retries: 12
|
|
84
|
+
|
|
85
|
+
|
|
59
86
|
agent-server:
|
|
60
87
|
profiles: ["agent"]
|
|
61
88
|
build:
|
|
@@ -64,6 +91,8 @@ services:
|
|
|
64
91
|
environment:
|
|
65
92
|
AGENT_SERVER_INTERNAL_TOKEN: ${AGENT_SERVER_INTERNAL_TOKEN:-}
|
|
66
93
|
AGENT_RUN_STORE_PATH: /app/data/agent/runs.sqlite3
|
|
94
|
+
AI_PROVIDER_AUDIT_STORE_PATH: /app/data/agent/provider-audit.sqlite3
|
|
95
|
+
AI_PROVIDER_AUDIT_RETENTION_DAYS: ${AI_PROVIDER_AUDIT_RETENTION_DAYS:-90}
|
|
67
96
|
QWEN_API_URL: ${QWEN_API_URL:-}
|
|
68
97
|
QWEN_API_KEY: ${QWEN_API_KEY:-}
|
|
69
98
|
QWEN_MODEL: ${QWEN_MODEL:-}
|
|
@@ -77,6 +106,44 @@ services:
|
|
|
77
106
|
server:
|
|
78
107
|
condition: service_started
|
|
79
108
|
|
|
109
|
+
adk-web-debug:
|
|
110
|
+
profiles: ["agent-debug"]
|
|
111
|
+
build:
|
|
112
|
+
context: .
|
|
113
|
+
dockerfile: apps/agent-server/Dockerfile
|
|
114
|
+
command:
|
|
115
|
+
[
|
|
116
|
+
"adk",
|
|
117
|
+
"web",
|
|
118
|
+
"--host",
|
|
119
|
+
"0.0.0.0",
|
|
120
|
+
"--port",
|
|
121
|
+
"8000",
|
|
122
|
+
"--no-reload",
|
|
123
|
+
"--session_service_uri",
|
|
124
|
+
"memory://",
|
|
125
|
+
"--artifact_service_uri",
|
|
126
|
+
"memory://",
|
|
127
|
+
"--logo-text",
|
|
128
|
+
"Windy ADK 开发调试(无业务工具)",
|
|
129
|
+
"--logo-image-url",
|
|
130
|
+
"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%2F%3E",
|
|
131
|
+
"/app/apps/agent-server/adk_web_agents",
|
|
132
|
+
]
|
|
133
|
+
ports:
|
|
134
|
+
- "127.0.0.1:8000:8000"
|
|
135
|
+
healthcheck:
|
|
136
|
+
test:
|
|
137
|
+
[
|
|
138
|
+
"CMD",
|
|
139
|
+
"python",
|
|
140
|
+
"-c",
|
|
141
|
+
"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/', timeout=3)",
|
|
142
|
+
]
|
|
143
|
+
interval: 10s
|
|
144
|
+
timeout: 5s
|
|
145
|
+
retries: 5
|
|
146
|
+
|
|
80
147
|
web:
|
|
81
148
|
build:
|
|
82
149
|
context: .
|
|
@@ -111,3 +178,4 @@ volumes:
|
|
|
111
178
|
postgres-data:
|
|
112
179
|
server-audit-data:
|
|
113
180
|
agent-run-data:
|
|
181
|
+
opensearch-data:
|
|
@@ -23,6 +23,27 @@ Operation 分别声明 Provider egress 与 Tool execution network policy:
|
|
|
23
23
|
mode: "configured-endpoint",
|
|
24
24
|
deploymentKey: "ai.openai-compatible",
|
|
25
25
|
},
|
|
26
|
+
providerDataPolicy: {
|
|
27
|
+
inputFields: [
|
|
28
|
+
{
|
|
29
|
+
jsonPointer: "/question",
|
|
30
|
+
classification: "internal",
|
|
31
|
+
dlpAction: "redact",
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
envelopeFields: [
|
|
35
|
+
{
|
|
36
|
+
field: "instructions",
|
|
37
|
+
classification: "internal",
|
|
38
|
+
dlpAction: "reject",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
field: "user-prompt",
|
|
42
|
+
classification: "internal",
|
|
43
|
+
dlpAction: "redact",
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
26
47
|
toolNetworkPolicy: {
|
|
27
48
|
mode: "forbidden",
|
|
28
49
|
},
|
|
@@ -36,6 +57,14 @@ Operation 分别声明 Provider egress 与 Tool execution network policy:
|
|
|
36
57
|
不能携带 URL、Host 或 Secret。
|
|
37
58
|
- 当前 Tool 网络策略只支持 `forbidden`,缺省也是 `forbidden`。Provider egress 不会
|
|
38
59
|
继承给 Tool。
|
|
60
|
+
- `inputFields` 是业务 raw input 的 JSON Pointer allowlist。Server 先选择并执行 DLP,
|
|
61
|
+
再把最小输入交给业务 `prepare` Handler;未列字段不会进入 Handler、Agent Server
|
|
62
|
+
DTO 或 Provider 请求。
|
|
63
|
+
- `envelopeFields` 是 Provider outbound envelope 的语义字段 allowlist,当前包含
|
|
64
|
+
`instructions`、`user-prompt`、`tool-definitions`、`provider-output` 和
|
|
65
|
+
`tool-results`。每轮实际发送前重新检查,字段遗漏或未知角色 fail closed。
|
|
66
|
+
- `dlpAction=reject` 在命中后禁止发送;`redact` 使用固定占位符替换。内置检测覆盖
|
|
67
|
+
credential/token、Bearer、邮箱、手机号、身份证号和银行卡/长账号。
|
|
39
68
|
|
|
40
69
|
## Provider 强制执行
|
|
41
70
|
|
|
@@ -50,9 +79,25 @@ Key。Agent Server 在发出模型请求前核对:
|
|
|
50
79
|
2. Deployment Key 必须等于当前 Adapter 的配置 Key;
|
|
51
80
|
3. 启动时已验证配置 Endpoint 的 HTTPS Host allowlist;
|
|
52
81
|
4. URL 仍来自 Agent Server 配置,Key 仍由 `SecretRef` 最后时刻解析。
|
|
82
|
+
5. raw input 已按 JSON Pointer allowlist 最小化,outbound envelope 也命中显式字段规则;
|
|
83
|
+
6. DLP 已完成且本次 Provider attempt 审计成功落库。
|
|
53
84
|
|
|
54
85
|
浏览器 API、SSE、错误响应和 Web 环境均不包含 URL、Host allowlist 或 API Key。
|
|
55
86
|
|
|
87
|
+
## Provider attempt 审计与留存
|
|
88
|
+
|
|
89
|
+
OpenAI-compatible Adapter 不启用隐式 HTTP retry。每次
|
|
90
|
+
`POST /chat/completions` 都对应唯一 `(runId, attempt)`,在打开网络请求前追加一条
|
|
91
|
+
Provider 发送审计;审计失败即禁止该次请求。记录只包含 Deployment、Operation、
|
|
92
|
+
Run、Attempt、分类集合、实际序列化 HTTP request body 字节数、策略决定和时间,不
|
|
93
|
+
保存 prompt、body、Tool result、Provider response 或 Secret。
|
|
94
|
+
|
|
95
|
+
`AI_PROVIDER_AUDIT_STORE_PATH` 必须在配置 Provider 时存在;
|
|
96
|
+
`AI_PROVIDER_AUDIT_RETENTION_DAYS` 默认 90 天、范围 1 到 3650 天。记录在留存期内
|
|
97
|
+
append-only,只有 TTL 清理可以删除过期行,并保留最近/累计删除计数。需要内部 Token
|
|
98
|
+
的 `GET /health/provider-governance` 只返回聚合计数与时间,不返回 Run ID 或内容。
|
|
99
|
+
当前 SQLite Adapter 只支持单副本;共享多副本必须换成集中式 append-only Adapter。
|
|
100
|
+
|
|
56
101
|
## Tool 强制执行
|
|
57
102
|
|
|
58
103
|
短时 Execution Grant 保存宿主校验过的 Operation。每次 Tool 回调重新读取 Grant,并
|
|
@@ -65,16 +110,13 @@ Key。Agent Server 在发出模型请求前核对:
|
|
|
65
110
|
|
|
66
111
|
## 尚未覆盖
|
|
67
112
|
|
|
68
|
-
|
|
113
|
+
当前实现完成契约拆分、Provider 精确配置绑定、字段 allowlist、DLP、逐 attempt 审计、
|
|
114
|
+
TTL 与聚合观测,但
|
|
69
115
|
不把以下能力宣称为已完成:
|
|
70
116
|
|
|
71
|
-
- 现有 `dataClassification` 是 Operation 级分类,尚未提供字段级数据分类、发送字段
|
|
72
|
-
allowlist、脱敏或 DLP 检查;
|
|
73
|
-
- 现有 Run 与 Tool 审计不等于逐次 Provider 发送审计;尚未记录目标 Deployment、
|
|
74
|
-
数据分类、字节数与策略决定的独立 append-only 事实;
|
|
75
117
|
- 同进程 project-owned Tool Handler 不是 OS 网络沙箱。平台契约不授予网络 Port,
|
|
76
118
|
但无法拦截业务代码直接调用全局 `fetch`;生产 deny-egress 仍需容器网络策略或防火墙;
|
|
77
119
|
- DNS 解析钉扎、代理治理和多 Provider/多 Endpoint 动态路由尚未实现。
|
|
78
120
|
|
|
79
|
-
|
|
80
|
-
|
|
121
|
+
不能声明业务 Tool 已获得进程级强制断网。DLP 是保守的模式检测,不替代行业专用实体
|
|
122
|
+
识别、人工审批或部署侧代理/防火墙。
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Southwind AI Runtime 架构
|
|
2
2
|
|
|
3
|
-
更新时间:2026-07-
|
|
3
|
+
更新时间:2026-07-26。
|
|
4
4
|
|
|
5
5
|
状态:架构已确认,首个可消费的文本 Agent 切片已实现。当前包含 Module AI Operation
|
|
6
6
|
注册、Guarded Run facade、短时 Execution Grant、受限 Tool Host、OpenAI-compatible
|
|
7
7
|
Provider、canonical HTTP/SSE、取消、cursor 重放和单副本有界 SQLite Store。未配置
|
|
8
|
-
Provider
|
|
9
|
-
|
|
8
|
+
Provider 时仍安全拒绝。真实业务 Run 已在内部使用 ADK 2.5 Runner、Session、LlmAgent
|
|
9
|
+
和 Event,并把事件转换为既有 canonical signal。ADK Workflow、LiteLLM 多 Provider
|
|
10
|
+
路由、PostgreSQL 多副本 Store、Document AI 和 Speech 运行进程尚未实现,不能据此
|
|
11
|
+
宣称完整 AI Runtime 已交付。
|
|
10
12
|
|
|
11
13
|
## 目标
|
|
12
14
|
|
|
@@ -272,13 +274,32 @@ interface ModuleAiOperationDefinition {
|
|
|
272
274
|
mode: "forbidden" | "configured-endpoint";
|
|
273
275
|
deploymentKey?: string;
|
|
274
276
|
};
|
|
277
|
+
providerDataPolicy?: {
|
|
278
|
+
inputFields: readonly {
|
|
279
|
+
jsonPointer: string;
|
|
280
|
+
classification: "public" | "internal" | "sensitive";
|
|
281
|
+
dlpAction: "reject" | "redact";
|
|
282
|
+
}[];
|
|
283
|
+
envelopeFields: readonly {
|
|
284
|
+
field:
|
|
285
|
+
| "instructions"
|
|
286
|
+
| "user-prompt"
|
|
287
|
+
| "tool-definitions"
|
|
288
|
+
| "provider-output"
|
|
289
|
+
| "tool-results";
|
|
290
|
+
classification: "public" | "internal" | "sensitive";
|
|
291
|
+
dlpAction: "reject" | "redact";
|
|
292
|
+
}[];
|
|
293
|
+
};
|
|
275
294
|
toolNetworkPolicy?: {
|
|
276
295
|
mode: "forbidden";
|
|
277
296
|
};
|
|
278
297
|
}
|
|
279
298
|
```
|
|
280
299
|
|
|
281
|
-
|
|
300
|
+
Provider 与 Tool 网络策略缺省均 fail closed。configured Provider 还必须声明字段
|
|
301
|
+
allowlist;业务 raw input 会在 Handler 前按 JSON Pointer 裁剪,outbound envelope 在
|
|
302
|
+
每个 Provider attempt 前再次治理。Provider 的实际 Endpoint 与 HTTPS Host allowlist 只存在于
|
|
282
303
|
服务端 Deployment 配置,不由业务 Manifest、浏览器或模型提供。详见
|
|
283
304
|
[Agent Provider 与 Tool 网络边界](./ai-egress.md)。
|
|
284
305
|
|
|
@@ -624,6 +645,12 @@ type DictationEvent =
|
|
|
624
645
|
|
|
625
646
|
ADK Web 只在 developer profile 启动,绑定 localhost 或受控开发网络。它不是生产 Prompt 管理或生产 Debug 控制面。
|
|
626
647
|
|
|
648
|
+
当前 Compose 使用独立 `agent-debug` profile,宿主机只发布
|
|
649
|
+
`127.0.0.1:8000`。由于原生 ADK Web 没有 Southwind AI Server 签发的 Actor 与
|
|
650
|
+
Execution Grant,它只加载一个无 Provider、无业务 Tool 的确定性调试 Agent。真实业务
|
|
651
|
+
Operation 只能走 canonical HTTP/SSE → ADK Adapter → 受治理 Provider/Tool Host 链路;
|
|
652
|
+
不得把 ADK Web 的调试会话解释为生产授权或业务执行证据。
|
|
653
|
+
|
|
627
654
|
Prompt 和 Workflow 使用:
|
|
628
655
|
|
|
629
656
|
```text
|
|
@@ -709,8 +736,9 @@ LiteLLM 版本必须固定并通过供应链审查,客户现场禁止动态安
|
|
|
709
736
|
|
|
710
737
|
当前已完成第 1、2、4 项和第 5 项的受限 Tool Host 基础闭环,并交付可直接连接
|
|
711
738
|
OpenAI-compatible Provider 的文本 Agent Adapter。SQLite Store 提供单副本有界持久化,
|
|
712
|
-
重启后保留终态和 cursor,但不替代后续 PostgreSQL 多副本 Store
|
|
713
|
-
|
|
739
|
+
重启后保留终态和 cursor,但不替代后续 PostgreSQL 多副本 Store。配置 Provider 后,
|
|
740
|
+
文本与 Tool Run 由 ADK Runner/Session/Event 执行;未配置 Provider 时同一 Adapter
|
|
741
|
+
fail closed。开发 ADK Web 已作为 localhost-only 的受控调试入口交付,不是业务入口。
|
|
714
742
|
|
|
715
743
|
1. 定义 Agent、Document、Speech Operation 与 Module Manifest 组合校验;
|
|
716
744
|
2. 建立 Python Agent Server、ADK Adapter 和 deterministic test Adapter;
|
|
@@ -24,3 +24,7 @@ Core 只理解 ownership dimension 的稳定 key/value 契约,不知道下游
|
|
|
24
24
|
通用资源路由的 `dataAccess` 是必填判别联合:资源必须明确声明 `request` 并提供 scoped access,或声明 `platform`,不能因漏配可选字段静默回退裸 Repository。V1 异步 bulk handler 只接受显式 `platform` 资源,由服务端构造 nominal `all` context;需要继承申请人数据范围的异步资源必须先设计不可篡改的 context snapshot,不允许套用当前平台模式。工单运营快照同样使用服务端平台任务 Actor,不再暴露或调用裸 Repository。
|
|
25
25
|
|
|
26
26
|
Search Provider 只能在 D1–D3 全部闭环后接入,不得先用 Provider 内存裁剪冒充 Repository 级强制。
|
|
27
|
+
|
|
28
|
+
业务模块用户目录同样复用这条 Repository 强制链。V1 `tenantId=single-tenant` 是宿主
|
|
29
|
+
nominal `ScopeContext` 的明确固定租户,不是省略租户判断;业务调用方只提交分页与
|
|
30
|
+
搜索,不能提交 Actor、tenant、department 或 scope。
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## 模块与 seam
|
|
4
4
|
|
|
5
|
-
`@southwind-ai/storage`
|
|
5
|
+
`@southwind-ai/storage` 是平台文件能力的唯一底层模块。`BlobStore` 只供平台 Storage
|
|
6
|
+
实现使用:流式写入、流式打开和删除;文件名、MIME、上传会话、Owner、权限与审计不
|
|
7
|
+
进入这个底层接口,由上层上传模块统一处理。可安装业务模块只学习
|
|
8
|
+
`@southwind-ai/server-sdk` 的 `ModuleStoragePort`。
|
|
6
9
|
|
|
7
10
|
`ArtifactRef` 使用 `blob_` 加 128 位随机值,不包含目录、租户或内容摘要,不能由业务主键推算。`ContentHash` 当前固定为 SHA-256。调用方可以声明大小和摘要,本地 Adapter 会在写入过程中验证;任何不一致都不会发布 Blob。
|
|
8
11
|
|
|
@@ -60,14 +63,29 @@ Logo 使用 `branding-logo` 上传策略。匿名 `/api/platform/files/:id` 只
|
|
|
60
63
|
|
|
61
64
|
## 模块受限 Upload/BlobRead Port
|
|
62
65
|
|
|
63
|
-
业务模块不直接使用 `UploadService` / `FileService`,而是经
|
|
66
|
+
业务模块不直接使用 `UploadService` / `FileService`,而是经
|
|
67
|
+
`ModuleRequestContext.storage` 或 `ModuleToolContext.storage` 获得
|
|
68
|
+
`ModuleStoragePort`(宿主实现见 `apps/server/src/module-host/storage-port.ts`):
|
|
64
69
|
|
|
65
70
|
- `createUploadSession` 的 purpose 强制属于本模块经 Host 注册的 Upload Policy;跨模块用途和平台基础用途(如 `bulk-data`、`branding-logo`)一律以 `UPLOAD_FORBIDDEN` 拒绝。Owner 由宿主以当前 Actor 补齐,模块无法代他人创建会话。
|
|
66
71
|
- `openFile` 走 `FileService.open` 的 Owner 约束,并在返回前校验文件用途属于本模块——即使是本人文件,用途越界也拒绝。
|
|
67
72
|
- `openPublicFile` 走 `FileService.openPublic` 的 Purpose 约束,且只允许本模块已注册用途。
|
|
73
|
+
- `openResourceFile` 用于同一业务范围内的跨上传者读取。模块必须先通过
|
|
74
|
+
`registerResourceFileAccessPolicies()` 为精确 `purpose + resourceType` 注册
|
|
75
|
+
project-owned 授权 Resolver,并绑定同 Manifest 的 Feature、Permission 与 Audit
|
|
76
|
+
Action。Host 每次调用都使用当前请求或 Tool grant 的 Actor 与服务端 ScopeContext
|
|
77
|
+
重跑 Guard;Resolver 必须从业务 Repository 回查 `resourceId -> fileId` 绑定和案件/
|
|
78
|
+
项目成员资格,客户端传入的 ID 不是授权证据。
|
|
79
|
+
- 资源读取只返回 `id/purpose/filename/mimeType/byteSize/sha256/body`;不会返回
|
|
80
|
+
Owner、ArtifactRef、对象存储 Key 或 Repository。成功和拒绝都写稳定审计,仅记录
|
|
81
|
+
`resourceType/resourceId/fileId/purpose`,不记录文件名、路径或内容。
|
|
68
82
|
- 模块不直接接触 `BlobStore` 或 Upload Repository;宿主未挂载存储服务时,任何访问都以明确错误失败,不静默绕过。
|
|
69
83
|
|
|
70
84
|
用途唯一性仍在 Host 注册时全局校验:命名空间重叠的模块(如 `shop` 与 `shop.sub`)注册同一 purpose 会在启动时失败。
|
|
85
|
+
资源策略的 purpose 必须由当前模块或其 Manifest 显式依赖模块的 Upload Policy 注册;
|
|
86
|
+
这允许 `media -> evidence` 之类的明确消费者关系,但拒绝平台基础用途和未声明的跨模块
|
|
87
|
+
读取。Feature、Permission、Audit Action 和 resourceType 仍必须属于当前消费者模块,
|
|
88
|
+
任一未知或越界都会在监听前失败。
|
|
71
89
|
|
|
72
90
|
## 当前切片
|
|
73
91
|
|
|
@@ -75,3 +93,5 @@ Logo 使用 `branding-logo` 上传策略。匿名 `/api/platform/files/:id` 只
|
|
|
75
93
|
- F2:上传会话、分片续传、文件元数据、校验和清理已完成。
|
|
76
94
|
- F3:服务端授权下载、审计、bulk artifact 与 Logo 接入已完成。
|
|
77
95
|
- F4:模块受限 Upload/BlobRead Port 已完成,业务模块经 Host 注册用途并受命名空间约束。
|
|
96
|
+
- F5:Route 与 Tool 共用资源绑定读取 Port,支持受审计、受数据范围约束的跨上传者
|
|
97
|
+
业务文件流读取,不扩散 Storage/Admin Repository。
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
平台通过一个稳定的 `SearchService` 聚合业务搜索结果,但不读取业务模块 Repository,也不把用户输入转换成跨模块 SQL。业务模块通过 Manifest 声明 `kind: "search"` 的 Provider,并提供同 Key、同版本的 Handler。Composition Root 启动时要求声明与实现一一对应,未知 Handler、缺失 Handler、重复 Key、协议版本或 Provider 版本漂移都会 fail-fast。
|
|
6
6
|
|
|
7
|
-
默认部署不引入 OpenSearch、Elasticsearch 或其它外部搜索引擎。小数据量模块可以实现 Repository Provider
|
|
7
|
+
默认部署不引入 OpenSearch、Elasticsearch 或其它外部搜索引擎。小数据量模块可以实现 Repository Provider;需要外部索引时可以显式启动 OpenSearch 开发 profile。两种实现共用同一 Provider 协议、调用方与业务详情授权边界,业务数据库始终是事实源。
|
|
8
8
|
|
|
9
9
|
## 深模块边界
|
|
10
10
|
|
|
@@ -48,11 +48,47 @@ Provider 只负责在自身模块边界内执行搜索。它接收平台从已
|
|
|
48
48
|
- `SEARCH_PROVIDER_KEYS` 是逗号分隔的启动白名单;未配置时按 Manifest 的 `enabledByDefault` 启用,显式空字符串关闭全部 Provider,未知、重复或非法 Key 使启动失败。
|
|
49
49
|
- `SEARCH_PROVIDER_TIMEOUT_MS`、`SEARCH_HEALTH_TIMEOUT_MS` 允许 10-5000 毫秒;`SEARCH_MAX_REQUESTS_PER_MINUTE` 允许 1-300。非整数或越界配置使启动失败,不能被静默截断或钳制。
|
|
50
50
|
|
|
51
|
+
## Web 全局入口
|
|
52
|
+
|
|
53
|
+
Header 的 `Ctrl/⌘ K` 全局搜索同时展示两类结果:
|
|
54
|
+
|
|
55
|
+
- 平台导航在浏览器内只过滤 Admin Bootstrap 已下发且当前账号获准的菜单;
|
|
56
|
+
- 业务数据只通过 `POST /api/search` 获取,并按 Manifest Provider 分组展示脱敏标题、摘要和业务深链。
|
|
57
|
+
|
|
58
|
+
输入使用 250ms 延迟并在关键词变化或 Dialog 关闭时中止旧请求,避免迟到响应覆盖新结果。单个 Provider 超时或故障时显示“结果可能不完整”,其它 Provider 和菜单仍可使用。Web 不维护 Provider Key、目标权限或 Feature 的副本,也不接收 OpenSearch 地址和凭据。
|
|
59
|
+
|
|
60
|
+
## 可选 OpenSearch Adapter
|
|
61
|
+
|
|
62
|
+
根项目和包含 `system.search` 的 Starter 提供显式开发入口:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
bun run dev:search
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
该命令设置容器内 `OPENSEARCH_URL=http://opensearch:9200`,并启动 Compose 的 `search` profile。普通 `bun run dev` 不设置地址,也不会启动或连接 OpenSearch。开发 profile 使用固定镜像、持久卷和健康检查;宿主端口只绑定 `127.0.0.1:19200`。`system.search` 被物理裁剪时,生成物同时移除 OpenSearch service、Server 环境变量、volume 和启动脚本。
|
|
69
|
+
|
|
70
|
+
Server 可读取以下配置:
|
|
71
|
+
|
|
72
|
+
- `OPENSEARCH_URL`:不含凭据、路径、查询参数的 HTTP(S) 根地址;未配置即使用原 Repository Provider;
|
|
73
|
+
- `OPENSEARCH_USERNAME` / `OPENSEARCH_PASSWORD`:必须成对配置,只进入 Server 到 OpenSearch 的 Authorization Header;
|
|
74
|
+
- `OPENSEARCH_REQUEST_TIMEOUT_MS`:100-30000ms,默认 3000ms;
|
|
75
|
+
- `OPENSEARCH_WORK_ORDER_INDEX`:示例工单的可重建索引名,默认 `windy-work-orders-v1`。
|
|
76
|
+
|
|
77
|
+
示例工单 Adapter 展示了项目业务层的正确接法:
|
|
78
|
+
|
|
79
|
+
1. 第一次查询或索引失效后,从 PostgreSQL 事实源读取当前快照并重建索引;
|
|
80
|
+
2. 创建、更新和归档成功后增量更新索引;OpenSearch 写失败只标记投影失效,不回滚已提交的业务事实;
|
|
81
|
+
3. Provider 把服务端 `ScopeContext` 编译为 OpenSearch `term` / `terms` Filter,无授权分支使用 `match_none`;
|
|
82
|
+
4. 命中 ID 返回前再次调用业务 Service 读取目标资源,防止陈旧索引绕过详情权限;
|
|
83
|
+
5. 标题和摘要只从重新授权后的业务实体生成脱敏投影。
|
|
84
|
+
|
|
85
|
+
这个 Compose profile 用于本地接入与回归,不是生产拓扑。生产环境应启用 OpenSearch TLS、认证、最小网络可达性、容量和快照策略;大规模增量索引应由项目自有 Outbox/Durable Job 驱动,不能把索引成功当成业务事务提交条件,也不能直接把开发 profile 暴露到客户网络。
|
|
86
|
+
|
|
51
87
|
Composition Root 把 Server 实际注册的搜索 API 与 Provider Handler Key 一并交给 Module Composition 校验。没有业务 Provider 声明的项目允许 Core 以空 Registry 启动;只要 Manifest 与 Handler 不一致,启动就在监听端口前失败。
|
|
52
88
|
|
|
53
89
|
## 当前验证范围
|
|
54
90
|
|
|
55
|
-
协议单元测试覆盖声明/实现/版本 fail-fast、启停与健康发现、目标权限裁剪、客户端不能扩大服务端身份和组织范围、分组与游标、Manifest 深链、明文响应拒绝、故障/超时隔离、部分失败、按 Actor 限流,以及审计中不出现原始查询词。Server API 测试另行覆盖入口权限先行、目标权限二次裁剪、Feature 关闭、只接受 POST body、稳定错误状态和直接 API 伪造身份/部门不能扩大 scoped Repository 结果。
|
|
91
|
+
协议单元测试覆盖声明/实现/版本 fail-fast、启停与健康发现、目标权限裁剪、客户端不能扩大服务端身份和组织范围、分组与游标、Manifest 深链、明文响应拒绝、故障/超时隔离、部分失败、按 Actor 限流,以及审计中不出现原始查询词。Server API 测试另行覆盖入口权限先行、目标权限二次裁剪、Feature 关闭、只接受 POST body、稳定错误状态和直接 API 伪造身份/部门不能扩大 scoped Repository 结果。OpenSearch 测试覆盖默认关闭、配置拒绝、凭据只进入 Server 请求头、索引重建、范围 Filter 与无授权 `match_none`;Web 测试覆盖菜单检索、业务分组结果和深链跳转。
|
|
56
92
|
|
|
57
93
|
示例 Work Order 已注册 `work-order.ticket.search`,Provider 只调用 `WorkOrderService.list`,后者把查询、count、分页与服务端 dataScope 一起交给 scoped Repository;测试会让裸 `Repository.list` 主动抛错,并确认本人范围不会返回其他负责人的匹配工单。另一个实现相同 Key/版本的测试 Provider 可以在不修改 Core 的情况下替换工单 Adapter。
|
|
58
94
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# 业务项目定制错误页
|
|
2
|
+
|
|
3
|
+
Windy 为 Web 提供统一的 404、500、维护中和网络异常页面,同时允许业务项目替换任意
|
|
4
|
+
一种页面。业务选择入口是:
|
|
5
|
+
|
|
6
|
+
```text
|
|
7
|
+
apps/web/src/app/error-pages.ts
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
该文件在 `.windy/files.json` 中属于 `project-owned`。Windy 升级会给尚无此文件的
|
|
11
|
+
项目创建默认选择器;文件存在后,后续更新只保留业务选择。平台默认页面
|
|
12
|
+
`apps/web/src/pages/errors/PlatformErrorPage.vue` 属于 `windy-managed`,Router 和
|
|
13
|
+
全局错误导航属于 `shared-scaffold`,业务项目不应为换视觉而修改这些文件。
|
|
14
|
+
|
|
15
|
+
## 默认行为
|
|
16
|
+
|
|
17
|
+
默认选择器让四种错误都使用平台页面:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import PlatformErrorPage from "@/pages/errors/PlatformErrorPage.vue";
|
|
21
|
+
import { defineErrorPageComponents } from "@/pages/errors/error-page-extension";
|
|
22
|
+
|
|
23
|
+
export const errorPageComponents = defineErrorPageComponents({
|
|
24
|
+
notFound: PlatformErrorPage,
|
|
25
|
+
internal: PlatformErrorPage,
|
|
26
|
+
maintenance: PlatformErrorPage,
|
|
27
|
+
network: PlatformErrorPage,
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
错误来源与页面的稳定映射如下:
|
|
32
|
+
|
|
33
|
+
| 来源 | 页面 |
|
|
34
|
+
| --------------------------------------------------- | -------- |
|
|
35
|
+
| Router 没有匹配业务路由 | 404 |
|
|
36
|
+
| Vue 渲染异常、Router 加载异常、HTTP 5xx(503 除外) | 500 |
|
|
37
|
+
| HTTP 503 | 维护中 |
|
|
38
|
+
| `fetch` 无法建立连接或传输中断 | 网络异常 |
|
|
39
|
+
|
|
40
|
+
HTTP 5xx 响应中的 `x-request-id` 会进入错误页,用户可将它直接提供给维护人员。调用方若
|
|
41
|
+
要在当前交互内自行处理后台重试,可以给 `apiRequest` / `apiResponse` 传入
|
|
42
|
+
`{ suppressFailureHandler: true }`,避免跳转全局错误页;这不改变原 Promise 的失败
|
|
43
|
+
语义。
|
|
44
|
+
|
|
45
|
+
## 替换业务页面
|
|
46
|
+
|
|
47
|
+
把业务 SFC 放在 `apps/web/src/app/` 下,再只修改选择器。例如只替换网络异常页:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import NetworkErrorPage from "./NetworkErrorPage.vue";
|
|
51
|
+
import PlatformErrorPage from "@/pages/errors/PlatformErrorPage.vue";
|
|
52
|
+
import { defineErrorPageComponents } from "@/pages/errors/error-page-extension";
|
|
53
|
+
|
|
54
|
+
export const errorPageComponents = defineErrorPageComponents({
|
|
55
|
+
notFound: PlatformErrorPage,
|
|
56
|
+
internal: PlatformErrorPage,
|
|
57
|
+
maintenance: PlatformErrorPage,
|
|
58
|
+
network: NetworkErrorPage,
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
业务组件接收统一的只读 Props:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
interface ErrorPageProps {
|
|
66
|
+
kind: "not-found" | "internal" | "maintenance" | "network";
|
|
67
|
+
from: string;
|
|
68
|
+
requestId?: string;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`from` 已限制为站内绝对路径,错误页自身和外部 URL 会回退到 `/`。业务组件应使用
|
|
73
|
+
文本插值展示地址和 Request ID,不使用 `v-html`;重试只导航到 `from`,不得把查询
|
|
74
|
+
参数解释为外部链接。错误页是公共恢复入口,不应读取管理权限快照或把敏感信息写入
|
|
75
|
+
页面。
|
|
76
|
+
|
|
77
|
+
完整自定义也必须保留四个注册项。`defineErrorPageComponents` 会在 TypeScript 阶段
|
|
78
|
+
检查 Key 是否齐全;Router 只依赖这个稳定导出,不感知业务组件实现。
|
|
@@ -78,9 +78,9 @@ composable。
|
|
|
78
78
|
先提交当前业务改动并保持 Git 工作区干净,然后执行:
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
bunx --bun create-windy@0.
|
|
82
|
-
bunx --bun create-windy@0.
|
|
83
|
-
bunx --bun create-windy@0.
|
|
81
|
+
bunx --bun create-windy@0.3.0 update --check --cwd .
|
|
82
|
+
bunx --bun create-windy@0.3.0 update --dry-run --cwd .
|
|
83
|
+
bunx --bun create-windy@0.3.0 update --cwd .
|
|
84
84
|
bun install
|
|
85
85
|
```
|
|
86
86
|
|