@oj-module/cli 0.2.17 → 0.2.20
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/devkit/README.md +4 -4
- package/devkit/SKILL.md +41 -37
- package/devkit/framework-dev.md +6 -5
- package/devkit/manual.md +260 -162
- package/devkit/menu-rules.md +3 -4
- package/devkit/module-dev-and-cdn-playground.md +31 -22
- package/devkit/sql-builder-missing-capabilities-pr.md +11 -6
- package/package.json +2 -2
- package/shell-dist/assets/runtime.js +2 -2
- package/shell-dist/index.html +2 -2
- package/shell-dist/versions.json +3 -3
- package/src/__tests__/build-backend.test.ts +54 -0
- package/src/__tests__/layout.test.ts +176 -0
- package/src/__tests__/module-list.test.ts +66 -0
- package/src/__tests__/oj-caps.test.ts +114 -0
- package/src/alias-paths.ts +102 -0
- package/src/build.ts +41 -9
- package/src/bundle.ts +25 -13
- package/src/config.ts +82 -9
- package/src/contract/__tests__/market-client.integration.test.ts +2 -2
- package/src/contract/check.ts +56 -28
- package/src/contract/emit-stub.ts +21 -9
- package/src/contract/handler-scan.ts +19 -10
- package/src/contract/run.ts +14 -4
- package/src/dev.ts +22 -7
- package/src/index.ts +1 -1
- package/src/info.ts +20 -3
- package/src/init.ts +91 -21
- package/src/layout.ts +282 -24
- package/src/lifecycle/__tests__/bundle.test.ts +16 -1
- package/src/lifecycle/__tests__/manifest-contract.test.ts +17 -58
- package/src/manifest-contract.ts +2 -25
- package/src/market/__tests__/bootstrap.test.ts +17 -9
- package/src/market/bootstrap.ts +30 -17
- package/src/oj-caps.ts +133 -0
- package/src/oj.ts +27 -4
- package/src/preview-config.ts +1 -1
- package/src/preview.ts +2 -1
- package/src/site.ts +2 -2
- package/src/usage.ts +6 -6
- package/src/vendor.ts +5 -1
- package/templates/{api/config.yaml → config.yaml} +13 -8
- package/templates/global.d.ts +7 -6
- package/templates/init.include.yaml +41 -36
- package/templates/package.json +1 -1
- package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/installer.ts +4 -0
- package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/smoke.ts +21 -2
- package/templates/{api/src/_platform → src/_platform/api}/market/api.ts +25 -15
- package/templates/{api/src/_platform → src/_platform/api}/solutions/api.ts +7 -3
- package/templates/{api/src/auth → src/auth/api}/_shared/login-throttle.ts +2 -2
- package/templates/{api/src/auth → src/auth/api}/_shared/session.ts +2 -2
- package/templates/{api/src/auth → src/auth/api}/api.ts +1 -1
- package/templates/{api/src/auth → src/auth/api}/ldap-login/api.ts +1 -1
- package/templates/src/auth/api/manifest.yaml +6 -0
- package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/InstallDialog.test.tsx +53 -1
- package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/marketClient.fake.ts +6 -0
- package/templates/{web/src/oj-market → src/oj-market/web}/api/client.ts +5 -0
- package/templates/{web/src/oj-market → src/oj-market/web}/api/defaultClient.ts +7 -1
- package/templates/{web/src/oj-market → src/oj-market/web}/components/InstallDialog.tsx +80 -5
- package/templates/{web/src/oj-market → src/oj-market/web}/hooks/useMarket.ts +6 -1
- package/templates/{web/src/oj-market → src/oj-market/web}/locales/en-US.json +10 -1
- package/templates/{web/src/oj-market → src/oj-market/web}/locales/zh-CN.json +10 -1
- package/templates/{web/src/oj-market → src/oj-market/web}/types.ts +15 -2
- package/templates/{api/src/system → src/system/api}/login-methods/api.ts +2 -2
- package/templates/src/system/api/manifest.yaml +6 -0
- package/templates/{api/src/system → src/system/api}/oplog/api.ts +4 -4
- package/templates/{api/src/system → src/system/api}/permissions/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/roles/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/roles/bindings/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/settings/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/users/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/users/forbidden/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/users/password/api.ts +3 -3
- package/templates/{api/src/system → src/system/api}/users/roles/api.ts +3 -3
- package/templates/src/tsconfig.json +16 -0
- package/templates/{api/tests → tests}/auth-ldap.spec.ts +2 -2
- package/templates/{api/tests → tests}/auth-session.spec.ts +3 -3
- package/templates/{api/tests → tests}/auth-throttle.spec.ts +1 -1
- package/templates/tests/fixture.ts +434 -0
- package/templates/{api/tests → tests}/guard.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/atomic-state.spec.ts +3 -3
- package/templates/{api/tests → tests}/lifecycle/bootstrap.spec.ts +8 -4
- package/templates/{api/tests → tests}/lifecycle/deploy-smoke.spec.ts +31 -8
- package/templates/{api/tests → tests}/lifecycle/installer.spec.ts +15 -15
- package/templates/{api/tests → tests}/lifecycle/market-api.spec.ts +30 -29
- package/templates/{api/tests → tests}/lifecycle/migrate.spec.ts +6 -6
- package/templates/{api/tests → tests}/lifecycle/preflight.spec.ts +4 -4
- package/templates/{api/tests → tests}/lifecycle/register.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/semver.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/sha384.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/sig-fixtures.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/solutions-api.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/steps-fs.spec.ts +12 -12
- package/templates/{api/tests → tests}/lifecycle/text-edit.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/tgz-guard.spec.ts +1 -1
- package/templates/{api/tests → tests}/lifecycle/trust.spec.ts +5 -5
- package/templates/{api/tests → tests}/lifecycle/uninstall.spec.ts +7 -7
- package/templates/{api/tests → tests}/lifecycle/verify-checks.spec.ts +9 -6
- package/templates/{api/tests → tests}/lifecycle/verify-report.spec.ts +7 -5
- package/templates/{api/tests → tests}/query-utils.spec.ts +1 -1
- package/templates/{api/tests → tests}/rbac-audit.spec.ts +1 -1
- package/templates/{api/tests → tests}/rbac-contacts.spec.ts +2 -2
- package/templates/{api/tests → tests}/rbac-guard.spec.ts +1 -1
- package/templates/{api/tests → tests}/rbac-roles.spec.ts +2 -2
- package/templates/{api/tests → tests}/rbac-seed.spec.ts +1 -1
- package/templates/{api/tests → tests}/rbac-settings.spec.ts +2 -2
- package/templates/{api/tests → tests}/rbac-solution-perms.spec.ts +1 -1
- package/templates/{api/tests → tests}/rbac-users.spec.ts +3 -3
- package/templates/tsconfig.json +5 -1
- package/templates/tsconfig.paths.json +19 -0
- package/templates/types/oj-modules.d.ts +17 -0
- package/templates/vitest.config.ts +22 -0
- package/templates/web.config.ts +23 -13
- package/vendor/host-versions.json +3 -3
- package/templates/api/src/auth/manifest.yaml +0 -5
- package/templates/api/src/system/manifest.yaml +0 -5
- package/templates/api/tests/fixture.ts +0 -58
- package/templates/api/tsconfig.json +0 -8
- package/templates/api/vitest.config.ts +0 -12
- package/templates/logs/task-events.jsonl +0 -0
- /package/templates/{api/.ojm-api-exempt.json → .ojm-api-exempt.json} +0 -0
- /package/templates/{api/seed.mysql.sql → seed.mysql.sql} +0 -0
- /package/templates/{api/seed.pg.sql → seed.pg.sql} +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/README.md +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/atomic.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/history.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/lock.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/semver.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/sha384.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/state.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/deploy-web.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/download.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/extract.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/migrate.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/preflight.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/register.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/reload.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/verify.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/tasks.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/text-edit.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/tgz-guard.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/trust.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/types.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/uninstall.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/verify-checks.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/verify-report.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/respond.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/contacts.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/roles.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/settings.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/solution-perms.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/users.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/access.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/audit.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/guard.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/oplog.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/query.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/rate.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/_shared/site-facade.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/contacts/api.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/contacts/detail/api.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/fixtures/l1-test-user.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/manifest.yaml +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/market/bootstrap.ts +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0001__create_users.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0001__create_users.pg.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0001__create_users.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0002__add_users_avatar_base64.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0002__add_users_avatar_base64.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0003__users_source_display_name.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0003__users_source_display_name.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0004__sys_rbac.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0004__sys_rbac.pg.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0004__sys_rbac.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0005__users_must_change.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0005__users_must_change.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0006__users_is_forbidden.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0006__users_is_forbidden.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0007__users_email.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0008__sys_refresh_token.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0008__sys_refresh_token.pg.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0008__sys_refresh_token.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0009__sys_throttle.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0009__sys_throttle.pg.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0009__sys_throttle.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0010__sys_solution_route.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0010__sys_solution_route.pg.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0010__sys_solution_route.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0011__sys_setting_group_label.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0011__sys_setting_group_label.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0012__market_history.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0012__market_history.pg.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0012__market_history.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/migrations/0013__users_profile.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/schema.yaml +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/seed.mysql.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/seed.pg.sql +0 -0
- /package/templates/{api/src/_platform → src/_platform/api}/seed.sql +0 -0
- /package/templates/{web/src/_platform → src/_platform/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/_platform → src/_platform/web}/__client/api.ts +0 -0
- /package/templates/{web/src/about → src/about/web}/entry.ts +0 -0
- /package/templates/{web/src/about → src/about/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/about → src/about/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/about → src/about/web}/pages/constants.ts +0 -0
- /package/templates/{web/src/about → src/about/web}/pages/index.tsx +0 -0
- /package/templates/{web/src/access → src/access/web}/entry.ts +0 -0
- /package/templates/{web/src/access → src/access/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/access → src/access/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/access → src/access/web}/pages/access-mode/index.tsx +0 -0
- /package/templates/{web/src/access → src/access/web}/pages/admin-visible/index.tsx +0 -0
- /package/templates/{web/src/access → src/access/web}/pages/button-control/index.tsx +0 -0
- /package/templates/{web/src/access → src/access/web}/pages/common-visible/index.tsx +0 -0
- /package/templates/{web/src/access → src/access/web}/pages/page-control/index.tsx +0 -0
- /package/templates/{api/src/auth → src/auth/api}/_shared/ldap-login.ts +0 -0
- /package/templates/{api/src/auth → src/auth/api}/_shared/ldap.ts +0 -0
- /package/templates/{web/src/auth → src/auth/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/auth → src/auth/web}/__client/api.ts +0 -0
- /package/templates/{api/src/demo → src/demo/api}/manifest.yaml +0 -0
- /package/templates/{api/src/demo → src/demo/api}/migrations/0001__create_todos.sql +0 -0
- /package/templates/{api/src/demo → src/demo/api}/schema.yaml +0 -0
- /package/templates/{api/src/demo → src/demo/api}/seed.sql +0 -0
- /package/templates/{api/src/demo → src/demo/api}/todos/api.ts +0 -0
- /package/templates/{web/src/demo → src/demo/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/demo → src/demo/web}/__client/api.ts +0 -0
- /package/templates/{web/src/demo → src/demo/web}/entry.ts +0 -0
- /package/templates/{web/src/demo → src/demo/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/demo → src/demo/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/demo → src/demo/web}/pages/index.tsx +0 -0
- /package/templates/{web/src/exception → src/exception/web}/entry.ts +0 -0
- /package/templates/{web/src/exception → src/exception/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/exception → src/exception/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/exception → src/exception/web}/pages/403/index.tsx +0 -0
- /package/templates/{web/src/exception → src/exception/web}/pages/404/index.tsx +0 -0
- /package/templates/{web/src/exception → src/exception/web}/pages/500/index.tsx +0 -0
- /package/templates/{web/src/exception → src/exception/web}/pages/unknown-component/index.tsx +0 -0
- /package/templates/{api/src/home → src/home/api}/line/api.ts +0 -0
- /package/templates/{api/src/home → src/home/api}/manifest.yaml +0 -0
- /package/templates/{api/src/home → src/home/api}/migrations/0001__create_home_tables.sql +0 -0
- /package/templates/{api/src/home → src/home/api}/pie/api.ts +0 -0
- /package/templates/{api/src/home → src/home/api}/schema.yaml +0 -0
- /package/templates/{api/src/home → src/home/api}/seed.sql +0 -0
- /package/templates/{web/src/home → src/home/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/home → src/home/web}/__client/api.ts +0 -0
- /package/templates/{web/src/home → src/home/web}/entry.ts +0 -0
- /package/templates/{web/src/home → src/home/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/home → src/home/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/home → src/home/web}/pages/components/bar-chart.tsx +0 -0
- /package/templates/{web/src/home → src/home/web}/pages/components/card-list.tsx +0 -0
- /package/templates/{web/src/home → src/home/web}/pages/components/line-chart.tsx +0 -0
- /package/templates/{web/src/home → src/home/web}/pages/components/pie-chart.tsx +0 -0
- /package/templates/{web/src/home → src/home/web}/pages/index.tsx +0 -0
- /package/templates/{web/src/login → src/login/web}/entry.ts +0 -0
- /package/templates/{web/src/login → src/login/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/login → src/login/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/login → src/login/web}/pages/change-password.tsx +0 -0
- /package/templates/{web/src/login → src/login/web}/pages/login.tsx +0 -0
- /package/templates/{api/src/notification → src/notification/api}/_shared/notify.ts +0 -0
- /package/templates/{api/src/notification → src/notification/api}/api.ts +0 -0
- /package/templates/{api/src/notification → src/notification/api}/del/api.ts +0 -0
- /package/templates/{api/src/notification → src/notification/api}/manifest.yaml +0 -0
- /package/templates/{api/src/notification → src/notification/api}/migrations/0001__create_notifications.sql +0 -0
- /package/templates/{api/src/notification → src/notification/api}/migrations/0002__notifications_user.sql +0 -0
- /package/templates/{api/src/notification → src/notification/api}/migrations/0003__notifications_broadcast.sql +0 -0
- /package/templates/{api/src/notification → src/notification/api}/migrations/0004__notifications_broadcast_hidden.sql +0 -0
- /package/templates/{api/src/notification → src/notification/api}/schema.yaml +0 -0
- /package/templates/{api/src/notification → src/notification/api}/seed.sql +0 -0
- /package/templates/{web/src/notification → src/notification/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/notification → src/notification/web}/__client/api.ts +0 -0
- /package/templates/{web/src/notification → src/notification/web}/entry.ts +0 -0
- /package/templates/{web/src/notification → src/notification/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/notification → src/notification/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/notification → src/notification/web}/pages/center/index.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/CardGrid.test.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/HistoryDrawer.test.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/RollbackDialog.test.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/UninstallConfirm.test.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/poll.test.ts +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/render.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/runtime-stub.ts +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/semver.test.ts +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/api/MarketClientProvider.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/api/context.ts +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/components/CardGrid.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/components/CardItem.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/components/DetailDrawer.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/components/HistoryDrawer.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/components/RollbackDialog.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/components/UninstallConfirm.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/entry.ts +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/pages/MarketApp.tsx +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/utils/poll.ts +0 -0
- /package/templates/{web/src/oj-market → src/oj-market/web}/utils/semver.ts +0 -0
- /package/templates/{web/src/outside → src/outside/web}/entry.ts +0 -0
- /package/templates/{web/src/outside → src/outside/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/outside → src/outside/web}/locales/zh-CN.json +0 -0
- /package/templates/{api/src/personal-center → src/personal-center/api}/manifest.yaml +0 -0
- /package/templates/{api/src/personal-center → src/personal-center/api}/profile/api.ts +0 -0
- /package/templates/{api/src/personal-center → src/personal-center/api}/upload/api.ts +0 -0
- /package/templates/{web/src/personal-center → src/personal-center/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/personal-center → src/personal-center/web}/__client/api.ts +0 -0
- /package/templates/{web/src/personal-center → src/personal-center/web}/entry.ts +0 -0
- /package/templates/{web/src/personal-center → src/personal-center/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/personal-center → src/personal-center/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/personal-center → src/personal-center/web}/pages/my-profile/index.tsx +0 -0
- /package/templates/{web/src/route-nest → src/route-nest/web}/entry.ts +0 -0
- /package/templates/{web/src/route-nest → src/route-nest/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/route-nest → src/route-nest/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/route-nest → src/route-nest/web}/pages/menu1/menu1-1/index.tsx +0 -0
- /package/templates/{web/src/route-nest → src/route-nest/web}/pages/menu1/menu1-2/index.tsx +0 -0
- /package/templates/{web/src/route-nest → src/route-nest/web}/pages/menu2/index.tsx +0 -0
- /package/templates/{web/src → src}/styles.css +0 -0
- /package/templates/{web/src/system → src/system/web}/__client/api.schemas.ts +0 -0
- /package/templates/{web/src/system → src/system/web}/__client/api.ts +0 -0
- /package/templates/{web/src/system → src/system/web}/entry.ts +0 -0
- /package/templates/{web/src/system → src/system/web}/locales/en-US.json +0 -0
- /package/templates/{web/src/system → src/system/web}/locales/zh-CN.json +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/errors.ts +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/forbidden.tsx +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/login-methods/index.tsx +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/oplog/index.tsx +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/roles/index.tsx +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/settings/index.tsx +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/users/index.tsx +0 -0
- /package/templates/{web/src/system → src/system/web}/pages/users/roles.tsx +0 -0
- /package/templates/{api/src/web → src/web/api}/get-async-routes/api.ts +0 -0
- /package/templates/{api/src/web → src/web/api}/hello/api.ts +0 -0
- /package/templates/{api/src/web → src/web/api}/manifest.yaml +0 -0
- /package/templates/{api/src/web → src/web/api}/user-info/api.ts +0 -0
- /package/templates/{web/src → src/web}/web/__client/api.schemas.ts +0 -0
- /package/templates/{web/src → src/web}/web/__client/api.ts +0 -0
- /package/templates/{api/tests → tests}/l1-builder.test.ts +0 -0
- /package/templates/{api/tests → tests}/rbac-tables.spec.ts +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# 本文件由 ojm init 生成;config 内相对路径相对本文件所在目录。
|
|
3
3
|
# 更多能力见手册 §10:vars / broker / tasks / oidc / ws / html_meta / secrets。
|
|
4
4
|
#
|
|
5
|
-
# ---- 证书与密钥的唯一落点:`./config/`(=
|
|
5
|
+
# ---- 证书与密钥的唯一落点:`./config/`(= 工程根/config/,本文件同级的子目录)----
|
|
6
6
|
# 三件套由 `ojm init` 现场签发(dev 缺证时 `ojm dev` 也会补签),逐把的作用与后果:
|
|
7
7
|
#
|
|
8
8
|
# ① private.pem —— 证书签发私钥(RSA 2048)。只用来给 ③ 签名,**oj 不读它**,
|
|
@@ -25,19 +25,24 @@
|
|
|
25
25
|
# 站点挂载(oj v0.1.58 起顶层扁平表:一行=一个 URL 前缀+一个目录,api/web 恰好其一)。
|
|
26
26
|
# 被删除的 server.api_prefix / app_spa_fallback 已由 mounts 取代;
|
|
27
27
|
# ojm dev 的 SPA 深链回落由 ojm 自建静态层负责;ojm preview 为单服务形态,
|
|
28
|
-
# 现场生成派生 config
|
|
28
|
+
# 现场生成派生 config(.ojm-preview.yaml,注入下面的 web 挂载 + spa: true),无需手改。
|
|
29
|
+
#
|
|
30
|
+
# **这里没有 api 挂载是有意的**:本工程是 `src/<name>/{api,web}` 布局,模块分散在
|
|
31
|
+
# `src/<name>/api` 下,而 mounts 的 api 项按 `<目录>/<模块>/manifest.yaml` 单层解析、
|
|
32
|
+
# 不会递归(实测报 `module 'x' missing manifest.yaml`)。源码形态由 `ojm dev` 用
|
|
33
|
+
# `--module-root src` 供给,产物形态由 `ojm preview` 用 `--api-path api/dist` 供给;
|
|
34
|
+
# 两者与 mounts 的 api 项同时出现还会报「module declared in multiple places」。
|
|
35
|
+
# 手写部署(不经 ojm)时才需要自己加回一条 `- prefix: /api` + `api: dist`。
|
|
29
36
|
mounts:
|
|
30
|
-
- prefix: /api # api 挂载:/api/<module>/...(目录相对 config 所在目录 → api/src)
|
|
31
|
-
api: src
|
|
32
37
|
# 本地模拟 CDN:直出工程根 cdn/(内容见 cdn/README.md)。
|
|
33
38
|
# 红线:生产请删掉这一条挂载,并把 system 设置 framework.market_cdn_url 指向真 CDN。
|
|
34
39
|
# 不得给此挂载设 spa——缺文件必须返回 404,回落 index.html 会让市场解析崩。
|
|
35
40
|
- prefix: /cdn
|
|
36
|
-
web:
|
|
41
|
+
web: ./cdn
|
|
37
42
|
# 生产若由 oj 直出前端(前后端同进程,即 ojm preview 的生产形态),
|
|
38
43
|
# 取消下一行注释并先 build 出 web/dist(preview 会自动注入等价挂载,无需手改):
|
|
39
44
|
# - prefix: "/"
|
|
40
|
-
# web: "
|
|
45
|
+
# web: "./web/dist" # 相对 config.yaml → 工程根/web/dist
|
|
41
46
|
# spa: true
|
|
42
47
|
# 嵌套 handler 组(oj v0.1.64)无需配置开关,组织方式见 api.ts 顶部注释。
|
|
43
48
|
|
|
@@ -84,8 +89,8 @@ platform:
|
|
|
84
89
|
# 平台特权模块名单:方案安装器的 reloading 段调 server.reloadConfig 触发热更。
|
|
85
90
|
# 缺这条 → 装在最后一步报 NotCapable: module '_platform' is not platform-privileged。
|
|
86
91
|
modules: [_platform]
|
|
87
|
-
#
|
|
88
|
-
#
|
|
92
|
+
# 受控根 = 后端模块源码根(`src/<name>/api` 的父目录,即 src)。ojm preview 派生 config
|
|
93
|
+
# 时会改写为 dist,保持与 `--api-path` 同源;手写部署请按实际模块根填写。
|
|
89
94
|
root: src
|
|
90
95
|
# ---- U6 信任库加固(P3-1;P2-2 红线 spike 结论)----
|
|
91
96
|
# 威胁:devkit 0.1.75 语义下 fs: 段对全部模块生效(jail = fs.root),且 trusted_paths 只增不减——
|
package/templates/global.d.ts
CHANGED
|
@@ -840,11 +840,12 @@ interface OjRabbitClient {
|
|
|
840
840
|
metadata: () => Promise<any>
|
|
841
841
|
}
|
|
842
842
|
|
|
843
|
-
// oj 的导入别名 `#x`(本模块根)/
|
|
844
|
-
// tsconfig 的 `paths`
|
|
845
|
-
//
|
|
846
|
-
//
|
|
847
|
-
//
|
|
848
|
-
//
|
|
843
|
+
// oj 的导入别名 `#x`(本模块根)/ `#/<模块名>/x`(目标模块根)是「引用方所在模块」
|
|
844
|
+
// 相对的,tsconfig 的 `paths` 表达不了模块相对解析,只能按模块枚举
|
|
845
|
+
// (`tsconfig.paths.json`,由 `ojm init` / `ojm build` / `ojm dev` 按 `src/*/api/manifest.yaml`
|
|
846
|
+
// 生成,被 `src/tsconfig.json` extends),因此仅能命中**已存在**的文件。无法静态定位
|
|
847
|
+
// 的别名(如尚未创建的 `#_shared/view`)由 `types/oj-modules.d.ts`(非模块,真 ambient
|
|
848
|
+
// `declare module "#*"`)兜底为 `any` 而非报 TS2307;已存在、能被 `paths` 命中的别名
|
|
849
|
+
// 仍走真实文件类型。运行时由 oj 的 loader 解析,本文件与该兜底只影响编辑器 / tsc。
|
|
849
850
|
|
|
850
851
|
export {};
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# 2. 模板自运行专属文件——templates 本身也是一个可 `pnpm dev` 的工程,
|
|
9
9
|
# 它的 package.json / node_modules / bin / lock 绝不能泄漏进用户工程;
|
|
10
10
|
# package.json 由 init 现场生成(钉死真实版本,不带 workspace:/catalog:)。
|
|
11
|
-
# 3. 构建与运行期产物——api/dist、web/dist、
|
|
11
|
+
# 3. 构建与运行期产物——api/dist、web/dist、logs、签发的私钥与 sqlite;
|
|
12
12
|
# 模板跑过 build/dev 后会留在本地,既不进用户工程,更不该让所有工程
|
|
13
13
|
# 共享同一把私钥。
|
|
14
14
|
# 4. 模板自用元数据——本清单自身。
|
|
@@ -32,43 +32,48 @@ include:
|
|
|
32
32
|
- tsconfig.json
|
|
33
33
|
- global.d.ts
|
|
34
34
|
- env.d.ts
|
|
35
|
+
# 别名类型兜底(`declare module "#*"`,非模块文件)。`tsconfig.paths.json` 不在此列:
|
|
36
|
+
# 它由 init 按目标工程的模块清单**现场生成**(模板兼作 demo 工程,多出的模块不该带过去)
|
|
37
|
+
- types
|
|
35
38
|
- web.config.ts
|
|
36
39
|
- openapi.json
|
|
37
|
-
-
|
|
38
|
-
#
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
- api
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
40
|
+
- src/styles.css
|
|
41
|
+
# 后端 typecheck 工程(package.json 的 typecheck:api 指向它);只收后端、排除 web
|
|
42
|
+
- src/tsconfig.json
|
|
43
|
+
# —— 后端:配置 / 种子 / 测试 / 默认模块(模块源码在 src/<name>/api)——
|
|
44
|
+
- config.yaml
|
|
45
|
+
- .ojm-api-exempt.json
|
|
46
|
+
- seed.mysql.sql
|
|
47
|
+
- seed.pg.sql
|
|
48
|
+
- vitest.config.ts
|
|
49
|
+
- tests
|
|
50
|
+
- src/_platform/api
|
|
51
|
+
- src/auth/api
|
|
52
|
+
- src/notification/api
|
|
53
|
+
- src/personal-center/api
|
|
54
|
+
- src/system/api
|
|
55
|
+
- src/web/api
|
|
51
56
|
# —— web 前端模块(demo/home 只列页面源码,__client 见顶部说明)——
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
57
|
+
- src/_platform/web
|
|
58
|
+
- src/auth/web
|
|
59
|
+
- src/demo/web/entry.ts
|
|
60
|
+
- src/demo/web/locales
|
|
61
|
+
- src/demo/web/pages
|
|
62
|
+
- src/home/web/entry.ts
|
|
63
|
+
- src/home/web/locales
|
|
64
|
+
- src/home/web/pages
|
|
65
|
+
- src/login/web
|
|
66
|
+
- src/notification/web
|
|
62
67
|
# 解决方案市场:默认第一方能力。逐子路径收录——__tests__ 不进脚手架
|
|
63
68
|
# (用户工程无 web 侧 vitest 基建,拷了是死文件)
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
69
|
+
- src/oj-market/web/entry.ts
|
|
70
|
+
- src/oj-market/web/types.ts
|
|
71
|
+
- src/oj-market/web/api
|
|
72
|
+
- src/oj-market/web/components
|
|
73
|
+
- src/oj-market/web/hooks
|
|
74
|
+
- src/oj-market/web/locales
|
|
75
|
+
- src/oj-market/web/pages
|
|
76
|
+
- src/oj-market/web/utils
|
|
77
|
+
- src/personal-center/web
|
|
78
|
+
- src/system/web
|
|
79
|
+
- src/web/web
|
package/templates/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"preview": "ojm preview",
|
|
10
10
|
"info": "ojm info",
|
|
11
11
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
12
|
-
"typecheck:api": "tsc --noEmit -p
|
|
12
|
+
"typecheck:api": "tsc --noEmit -p src/tsconfig.json"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@ant-design/cssinjs": "catalog:",
|
|
@@ -89,6 +89,8 @@ export interface InstallRequest extends InstallContext {
|
|
|
89
89
|
expected?: string[]
|
|
90
90
|
/** 安装器内部通道(外部调用方忽略):终态审计读 status/failedStage/taskId + migrate 捕获 appliedSeq */
|
|
91
91
|
outcome?: { taskId?: string, status?: TaskStatus, failedStage?: InstallStage, appliedSeq?: number | null, cancelled?: boolean }
|
|
92
|
+
/** 安装器内部通道(reloading → smoke):本次新追加进 auth.anonymous_paths 的路径(restart_pending,探活对其 401 判待重启) */
|
|
93
|
+
deferredAnonymous?: string[]
|
|
92
94
|
/**
|
|
93
95
|
* 取消安装令牌(U5,P3-8):运行期外部置位(端点 `?cancel=<taskId>` 或任务状态 `cancelling` 驱动的闭包),
|
|
94
96
|
* 返回 true 即中止——PIPELINE 循环每段前探测 + migrating 段 spawn 流逐块探测;幂等、可重入
|
|
@@ -347,6 +349,8 @@ async function reloadStage(req: InstallRequest): Promise<StageResult> {
|
|
|
347
349
|
const anonymous = Array.isArray(api.anonymous) ? api.anonymous.filter((x): x is string => typeof x === "string") : [];
|
|
348
350
|
const patched = anonymous.length > 0 ? appendAnonymousPaths(bumped.content, anonymous) : { content: bumped.content, added: [] };
|
|
349
351
|
await atomicWriteText(path, patched.content);
|
|
352
|
+
// 交接给 smoke:本次真正新增的匿名路径在重启前恒 401(restart_pending)
|
|
353
|
+
req.deferredAnonymous = patched.added;
|
|
350
354
|
}
|
|
351
355
|
catch (e) {
|
|
352
356
|
return { stage: "reloading", ok: false, retryable: true, message: `热更失败:config.yaml 安装序号自增/匿名端点接线失败(${String(e)})——reload unchanged 判定基于 config 文档 sha,无变更则热更不可能生效(B-11,PRD E5 口径)` };
|
|
@@ -30,6 +30,12 @@ export interface SmokeContext extends InstallContext {
|
|
|
30
30
|
modulesJsonPath: string
|
|
31
31
|
/** 注册快照查询;缺省回落 queryRegistered(isolate db 全局;测试注入 stub) */
|
|
32
32
|
query?: (name: string) => Promise<SolutionSnapshot | null>
|
|
33
|
+
/**
|
|
34
|
+
* 本次安装**新追加**进 config.yaml `auth.anonymous_paths` 的路径(reloading 段注入,带前导 /)。
|
|
35
|
+
* 匿名接线属 restart_pending(reload 段已明示),本进程内尚未生效——探活对这些路径的
|
|
36
|
+
* 401 判「待重启」而非失败;已在配置里却仍 401 才是真失败(说明接线或守卫有问题)。
|
|
37
|
+
*/
|
|
38
|
+
deferredAnonymous?: string[]
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
export interface StageResult {
|
|
@@ -116,6 +122,7 @@ export async function smoke(ctx: SmokeContext): Promise<StageResult> {
|
|
|
116
122
|
const web = isObj(m.web) ? m.web : undefined;
|
|
117
123
|
|
|
118
124
|
// ① 探活:health 优先(2xx),缺省首个非匿名路由(连通即可)
|
|
125
|
+
const notes: string[] = [];
|
|
119
126
|
try {
|
|
120
127
|
const health = typeof m.health === "string" && m.health !== "" ? m.health : undefined;
|
|
121
128
|
const target = health ?? firstNonAnonymousRoute(m);
|
|
@@ -123,7 +130,18 @@ export async function smoke(ctx: SmokeContext): Promise<StageResult> {
|
|
|
123
130
|
const url = probeUrlOf(ctx.apiPrefix, target);
|
|
124
131
|
const res = await ctx.fetch(url);
|
|
125
132
|
if (health !== undefined && !res.ok) {
|
|
126
|
-
|
|
133
|
+
// 本次安装新增的匿名接线尚未生效(restart_pending):此处的 401 反映的是
|
|
134
|
+
// 「宿主还没重启」,不是包或站点的问题——判待重启并落人话提示,不判失败。
|
|
135
|
+
const rel = (s: string) => s.replace(/^\/+/, "");
|
|
136
|
+
const healthRel = rel(health);
|
|
137
|
+
const pending = (ctx.deferredAnonymous ?? []).some(a => healthRel === rel(a) || healthRel.startsWith(`${rel(a)}/`));
|
|
138
|
+
const deferred = res.status === 401 && pending;
|
|
139
|
+
if (deferred) {
|
|
140
|
+
notes.push(`探活 ${url} 返回 401:该端点是本次安装新增的匿名路径(auth.anonymous_paths),匿名接线属 restart_pending——重启宿主后生效,不计入失败`);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
failures.push({ item: "探活", retryable: true, reason: `health 端点 ${url} 返回 HTTP ${res.status}(C2-33 冒烟探针要求 2xx)` });
|
|
144
|
+
}
|
|
127
145
|
}
|
|
128
146
|
}
|
|
129
147
|
}
|
|
@@ -205,7 +223,8 @@ export async function smoke(ctx: SmokeContext): Promise<StageResult> {
|
|
|
205
223
|
// ④ entry 浏览器加载不在安装器验(设计 §4.1)——无判定,口径钉于成功 message。
|
|
206
224
|
|
|
207
225
|
if (failures.length === 0) {
|
|
208
|
-
|
|
226
|
+
const passed = "冒烟通过:探活 / integrity / 注册一致性 三项全过(entry 浏览器加载不在安装器验——设计 §4.1,宿主 error boundary + P2 深检兜底)";
|
|
227
|
+
return { stage: "smoke", ok: true, message: notes.length === 0 ? passed : `${passed}\n${notes.map(n => `- ${n}`).join("\n")}` };
|
|
209
228
|
}
|
|
210
229
|
return {
|
|
211
230
|
stage: "smoke",
|
|
@@ -52,9 +52,9 @@ export interface MarketEnv {
|
|
|
52
52
|
trustPath: string
|
|
53
53
|
/** sqlite 库文件路径(R14 备份源;模板 config.yaml `sqlite://db.sqlite` → 站点根 db.sqlite) */
|
|
54
54
|
dbPath: string
|
|
55
|
-
/** 站点 web
|
|
55
|
+
/** 站点 web 模块目录(站点产物根下的 modules/) */
|
|
56
56
|
siteModulesDir: string
|
|
57
|
-
/** 站点 modules.json 路径(与 siteModulesDir
|
|
57
|
+
/** 站点 modules.json 路径(与 siteModulesDir 同属站点产物根) */
|
|
58
58
|
modulesJsonPath: string
|
|
59
59
|
/** API 前缀(smoke 探针 URL 前缀) */
|
|
60
60
|
apiPrefix: string
|
|
@@ -78,17 +78,20 @@ export interface MarketEnv {
|
|
|
78
78
|
/** 迁移账本 head 真源(P1-16 接线①:缺省读 oj journal 表 `_oj_migrations` max(version),查询失败 → null 不猜) */
|
|
79
79
|
ledgerHeadOf?: (name: string) => Promise<number | bigint | null>
|
|
80
80
|
/**
|
|
81
|
-
* 宿主 versions.json(C2-09 peerRuntime 比对真源,键 = 包名)。缺省读
|
|
82
|
-
* (ojm
|
|
83
|
-
* peerRuntime 即预检 fail-closed 拒装(C2-09 语义保持)。
|
|
81
|
+
* 宿主 versions.json(C2-09 peerRuntime 比对真源,键 = 包名)。缺省读 `<sharedRoot>/versions.json`
|
|
82
|
+
* (ojm 宿主构建产物:与 manifests.yaml 同层,即 release 根,B-11 接线);不可得 → undefined,
|
|
83
|
+
* manifest 声明 peerRuntime 即预检 fail-closed 拒装(C2-09 语义保持)。
|
|
84
84
|
*/
|
|
85
85
|
hostVersions?: Record<string, string>
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
/**
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
/**
|
|
89
|
+
* 宿主 versions.json 布局推导(B-11):与 manifests.yaml 同层(release 根 = `sharedRoot`),
|
|
90
|
+
* 不是站点产物根——两处根在 `src/<name>/{api,web}` 布局下已分流(`api/dist` vs `web/dist`)。
|
|
91
|
+
*/
|
|
92
|
+
export function versionsJsonPathOf(releaseRoot: string): string {
|
|
93
|
+
const trimmed = releaseRoot.replace(/\/+$/, "");
|
|
94
|
+
return trimmed === "" ? "versions.json" : `${trimmed}/versions.json`;
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
/** 宿主 versions.json 解析(纯函数):{packageName: version} 全字符串表;任何违约 → undefined(fail-closed 不猜) */
|
|
@@ -113,30 +116,37 @@ export function parseVersionsJson(text: string): Record<string, string> | undefi
|
|
|
113
116
|
return out;
|
|
114
117
|
}
|
|
115
118
|
|
|
116
|
-
/** hostVersions 注入缺省:显式注入优先,否则读
|
|
119
|
+
/** hostVersions 注入缺省:显式注入优先,否则读 `<sharedRoot>/versions.json`;不可得 → undefined(C2-09 fail-closed) */
|
|
117
120
|
async function hostVersionsOf(env: MarketEnv): Promise<Record<string, string> | undefined> {
|
|
118
121
|
if (env.hostVersions !== undefined) {
|
|
119
122
|
return env.hostVersions;
|
|
120
123
|
}
|
|
121
124
|
try {
|
|
122
|
-
return parseVersionsJson(new TextDecoder().decode(await fsFace().readFile(versionsJsonPathOf(env.
|
|
125
|
+
return parseVersionsJson(new TextDecoder().decode(await fsFace().readFile(versionsJsonPathOf(env.sharedRoot))));
|
|
123
126
|
}
|
|
124
127
|
catch {
|
|
125
128
|
return undefined;
|
|
126
129
|
}
|
|
127
130
|
}
|
|
128
131
|
|
|
129
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* oj serve release 布局生产缺省(P1-16 B3/B4;全字段可覆盖)。
|
|
134
|
+
*
|
|
135
|
+
* 两处根在 `src/<name>/{api,web}` 布局下分流,且都相对 fs jail 根(= config.yaml 所在目录):
|
|
136
|
+
* release 根 `api/dist` —— 版本目录 / manifests.yaml / versions.json / migrations(后端产物)
|
|
137
|
+
* 站点根 `web/dist` —— modules/ + modules.json(前端产物,安装器 upsert 的落点)
|
|
138
|
+
* 暂存 / 任务 / 状态 / 信任库 / db 仍在 jail 根下:它们是安装器私有面,不属任何产物根。
|
|
139
|
+
*/
|
|
130
140
|
export function marketEnv(): MarketEnv {
|
|
131
141
|
return {
|
|
132
|
-
sharedRoot: "dist",
|
|
142
|
+
sharedRoot: "api/dist",
|
|
133
143
|
staging: "modules/.staging",
|
|
134
144
|
taskDir: "modules/.install-tasks",
|
|
135
145
|
statePath: "modules/.solutions/state.yaml",
|
|
136
146
|
trustPath: ".solutions/trusted-keys.yaml",
|
|
137
147
|
dbPath: "db.sqlite",
|
|
138
|
-
siteModulesDir: "dist/modules",
|
|
139
|
-
modulesJsonPath: "dist/modules.json",
|
|
148
|
+
siteModulesDir: "web/dist/modules",
|
|
149
|
+
modulesJsonPath: "web/dist/modules.json",
|
|
140
150
|
apiPrefix: "/api",
|
|
141
151
|
ledgerHeadOf: journalHeadOf,
|
|
142
152
|
};
|
|
@@ -30,12 +30,16 @@ import { journalHeadOf, siteOrigin } from "../_shared/site-facade";
|
|
|
30
30
|
/** 线格式行(与 packages/runtime/src/api/rbac/types.ts 的 SolutionRouteRow 对齐) */
|
|
31
31
|
export type { SolutionRouteRow } from "../_lifecycle/verify-checks";
|
|
32
32
|
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* oj serve release 布局生产缺省(与 marketEnv 同口径,见其注释):
|
|
35
|
+
* sharedRoot = 后端 release 根(版本目录 / migrations)
|
|
36
|
+
* modulesJsonPath = 站点产物根下的 modules.json(web 入口 integrity 真源)
|
|
37
|
+
*/
|
|
34
38
|
export function solutionsEnv(): SolutionsEnv {
|
|
35
39
|
return {
|
|
36
|
-
sharedRoot: "dist",
|
|
40
|
+
sharedRoot: "api/dist",
|
|
37
41
|
statePath: "modules/.solutions/state.yaml",
|
|
38
|
-
modulesJsonPath: "dist/modules.json",
|
|
42
|
+
modulesJsonPath: "web/dist/modules.json",
|
|
39
43
|
apiPrefix: "/api",
|
|
40
44
|
};
|
|
41
45
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 登录限速编排(B5/AC1-26):auth/login 与 auth/ldap-login 共用的 (IP, username) 双维度守卫 + 失败记账。
|
|
2
2
|
// 状态全在 DB(sys_login_throttle/sys_rate_window,迁移 0009)——oj 每请求重建模块,进程内方案恒失效(§9.3-3)。
|
|
3
|
-
import { auditOp } from "
|
|
4
|
-
import { loginBlocked, loginFailed, loginSucceeded, rateLimit } from "
|
|
3
|
+
import { auditOp } from "#/_platform/_shared/service/infra/audit";
|
|
4
|
+
import { loginBlocked, loginFailed, loginSucceeded, rateLimit } from "#/_platform/_shared/service/infra/rate";
|
|
5
5
|
|
|
6
6
|
/** 客户端 IP:优先 X-Forwarded-For(反代后),回落 X-Real-IP。 */
|
|
7
7
|
export async function clientIp(): Promise<string> {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// B4:access JWT 仅载 sub(roles 走登录响应/资料接口,不进 claims)。
|
|
5
5
|
// B3:refresh = 不透明随机串 + DB 存 SHA-256 哈希(sys_refresh_token,迁移 0008);
|
|
6
6
|
// 轮换作旧 + 滑动窗口(绝对 TTL 封顶 min(旧 exp, now+30d));reuse 检测全族吊销 + 审计。
|
|
7
|
-
import { invalidateUser } from "
|
|
8
|
-
import { recordOp } from "
|
|
7
|
+
import { invalidateUser } from "#/_platform/_shared/service/infra/access";
|
|
8
|
+
import { recordOp } from "#/_platform/_shared/service/infra/audit";
|
|
9
9
|
|
|
10
10
|
export interface SessionDeps {
|
|
11
11
|
randomHex: (bytes: number) => string
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { respond } from "
|
|
1
|
+
import { respond } from "#/_platform/_shared/respond";
|
|
2
2
|
// oj 嵌套 handler 组:组名 = 路径段(login → POST /api/auth/login,路由不变)。
|
|
3
3
|
// 阶段 B 登录链(D14/AC1-12/13/27):领域逻辑全在 _shared/session.ts(createSession 注入 deps),
|
|
4
4
|
// handler 只编排:取参 → 调 service → json 信封;B5 限速收敛在 _shared/login-throttle.ts(与 ldap-login 共用)。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// POST /api/auth/ldap-login —— LDAP 目录登录(D1/AC1-12;匿名端点,E6:anonymous_paths 写 /auth/ldap-login)。
|
|
2
2
|
// 编排:B5 限速守卫(与 auth/login 同维度)→ ldapLogin 九步 → respond 统一异常出口(§9.3-1)。
|
|
3
|
-
import { respond } from "
|
|
3
|
+
import { respond } from "#/_platform/_shared/respond";
|
|
4
4
|
import { createLdapLogin, runtimeLdapGateway } from "../_shared/ldap-login";
|
|
5
5
|
import { clientIp, guardLoginThrottle, noteLoginFailed, noteLoginSucceeded } from "../_shared/login-throttle";
|
|
6
6
|
import { createSession } from "../_shared/session";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { IndexRow, TaskRecord } from "../types";
|
|
2
|
-
import { screen, waitFor } from "@testing-library/react";
|
|
2
|
+
import { fireEvent, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import { useState } from "react";
|
|
3
4
|
import { describe, expect, it } from "vitest";
|
|
4
5
|
import { InstallDialog } from "../components/InstallDialog";
|
|
5
6
|
import { InMemoryMarketClient } from "./marketClient.fake";
|
|
@@ -69,4 +70,55 @@ describe("installDialog 安装进度(BDD)", () => {
|
|
|
69
70
|
expect(client.calls).toContain("install:alpha@1.1.0");
|
|
70
71
|
expect(screen.getByText("回滚中")).toBeTruthy();
|
|
71
72
|
});
|
|
73
|
+
|
|
74
|
+
it("given needs_trust 终态 When 打开 Then 渲染指纹与确认按钮,点击后入库并重试安装", async () => {
|
|
75
|
+
const pending: TaskRecord = {
|
|
76
|
+
id: "t1",
|
|
77
|
+
name: "alpha",
|
|
78
|
+
version: "1.2.0",
|
|
79
|
+
status: "needs_trust",
|
|
80
|
+
stage: "verifying",
|
|
81
|
+
progress: 18,
|
|
82
|
+
message: "待信任确认",
|
|
83
|
+
stages: [],
|
|
84
|
+
trust: { key_id: "alpha.author", public_key: "ab".repeat(32), fingerprint: "6afc".repeat(16) },
|
|
85
|
+
startedAt: 0,
|
|
86
|
+
};
|
|
87
|
+
const client = new InMemoryMarketClient({ taskRecord: pending, installResult: { task_id: "t1" } });
|
|
88
|
+
renderWithProviders(<InstallDialog row={row} open onClose={() => {}} />, client);
|
|
89
|
+
const button = await screen.findByText("指纹一致,信任并重试");
|
|
90
|
+
expect(screen.getByTestId("install-trust-fingerprint").textContent).toContain("6afc".repeat(16));
|
|
91
|
+
fireEvent.click(button);
|
|
92
|
+
await waitFor(() => expect(client.calls).toContain("trust:t1"));
|
|
93
|
+
// 确认入库后自动重试同一安装(staging 未清场,服务端续跑)
|
|
94
|
+
await waitFor(() => expect(client.calls.filter(c => c.startsWith("install:")).length).toBe(2));
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("given 终态 When 父级每次渲染都换 onDone 身份 Then 只通告一次(防 onDone→重渲染→onDone 死循环)", async () => {
|
|
98
|
+
const done: TaskRecord = {
|
|
99
|
+
id: "t1",
|
|
100
|
+
name: "alpha",
|
|
101
|
+
version: "1.2.0",
|
|
102
|
+
status: "done",
|
|
103
|
+
stage: "done",
|
|
104
|
+
progress: 100,
|
|
105
|
+
message: "ok",
|
|
106
|
+
stages: [],
|
|
107
|
+
startedAt: 0,
|
|
108
|
+
};
|
|
109
|
+
const client = new InMemoryMarketClient({ taskRecord: done, installResult: { task_id: "t1" } });
|
|
110
|
+
function Harness() {
|
|
111
|
+
const [n, setN] = useState(0);
|
|
112
|
+
return (
|
|
113
|
+
<>
|
|
114
|
+
<span data-testid="done-count">{n}</span>
|
|
115
|
+
<InstallDialog row={row} open onClose={() => {}} onDone={() => setN(v => v + 1)} />
|
|
116
|
+
</>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
renderWithProviders(<Harness />, client);
|
|
120
|
+
await waitFor(() => expect(screen.getByTestId("install-message").textContent).toBe("ok"), { timeout: 5000 });
|
|
121
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
122
|
+
expect(screen.getByTestId("done-count").textContent).toBe("1");
|
|
123
|
+
});
|
|
72
124
|
});
|
|
@@ -11,6 +11,7 @@ export interface FakeState {
|
|
|
11
11
|
enableResult?: { ok: boolean, message: string }
|
|
12
12
|
uninstallResult?: { ok: boolean, message: string }
|
|
13
13
|
installResult?: { task_id: string }
|
|
14
|
+
trustResult?: { result: string, key_id: string, fingerprint: string }
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export class InMemoryMarketClient implements MarketClient {
|
|
@@ -49,6 +50,11 @@ export class InMemoryMarketClient implements MarketClient {
|
|
|
49
50
|
} as TaskRecord);
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
async trust(taskId: string) {
|
|
54
|
+
this.calls.push(`trust:${taskId}`);
|
|
55
|
+
return this.state.trustResult ?? { result: "added", key_id: "k1", fingerprint: "f".repeat(64) };
|
|
56
|
+
}
|
|
57
|
+
|
|
52
58
|
async enable(input: EnableInput) {
|
|
53
59
|
this.calls.push(`enable:${input.name}:${input.enabled}`);
|
|
54
60
|
return this.state.enableResult ?? { ok: true, message: "ok" };
|
|
@@ -24,6 +24,11 @@ export interface MarketClient {
|
|
|
24
24
|
install: (input: InstallInput) => Promise<{ task_id: string }>
|
|
25
25
|
/** 任务轮询消费面(query 形态 ?id=) */
|
|
26
26
|
task: (id: string) => Promise<TaskRecord>
|
|
27
|
+
/**
|
|
28
|
+
* TOFU 确认:把 needs_trust 任务记录里的发布方 key 写入宿主信任库(服务端自任务记录重取
|
|
29
|
+
* 公钥复算指纹比对,防混淆代理)。确认成功后重试同一安装即可续跑(staging 未清场)。
|
|
30
|
+
*/
|
|
31
|
+
trust: (taskId: string) => Promise<{ result: string, key_id: string, fingerprint: string }>
|
|
27
32
|
/** 生命周期开关(同步 setEnabled,无任务模型;{ok,message} 200 渲染 ok 位) */
|
|
28
33
|
enable: (input: EnableInput) => Promise<{ ok: boolean, message: string }>
|
|
29
34
|
/** 市场操作历史(_platform 只读端点 /_platform/market/history) */
|
|
@@ -25,9 +25,10 @@ import {
|
|
|
25
25
|
platformMarketInstallPost,
|
|
26
26
|
platformMarketMetaGet,
|
|
27
27
|
platformMarketTaskGet,
|
|
28
|
+
platformMarketTrustPost,
|
|
28
29
|
platformMarketUninstallImpactGet,
|
|
29
30
|
platformMarketUninstallPost,
|
|
30
|
-
} from "
|
|
31
|
+
} from "../../../_platform/web/__client/api";
|
|
31
32
|
|
|
32
33
|
// 绑定运行时 request 到生成 client 的模块级单槽(生成 client 是静态单例,必须由某处
|
|
33
34
|
// bindRequest;此处兜底确保市场子应用无论挂载顺序都能拿到请求实例)。与 _platform 模块
|
|
@@ -96,6 +97,11 @@ export const httpMarketClient: MarketClient = {
|
|
|
96
97
|
// 运行时形状由服务端真源 + DEV zod 校验保证,TS 侧以领域类型为准。
|
|
97
98
|
return (await platformMarketTaskGet({ id })) as unknown as TaskRecord;
|
|
98
99
|
},
|
|
100
|
+
async trust(taskId: string) {
|
|
101
|
+
// 服务端自任务记录重取公钥复算指纹比对(防混淆代理),前端只传 task_id,不传公钥
|
|
102
|
+
const data = await platformMarketTrustPost({ task_id: taskId });
|
|
103
|
+
return data as { result: string, key_id: string, fingerprint: string };
|
|
104
|
+
},
|
|
99
105
|
async enable(input: EnableInput) {
|
|
100
106
|
const data = await platformMarketEnablePost({ name: input.name, enabled: input.enabled });
|
|
101
107
|
return data as { ok: boolean, message: string };
|