@oj-module/cli 0.2.18 → 0.2.23
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 +352 -168
- package/devkit/menu-rules.md +3 -3
- 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 +59 -34
- 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__/init-packagejson.test.ts +29 -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/args.ts +54 -4
- package/src/build.ts +41 -9
- package/src/builtin.ts +93 -0
- package/src/bundle.ts +254 -38
- package/src/config.ts +82 -9
- package/src/contract/__tests__/market-client.integration.test.ts +14 -14
- 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 +80 -9
- package/src/index.ts +27 -7
- package/src/info.ts +69 -10
- package/src/init.ts +114 -28
- package/src/layout.ts +282 -24
- package/src/lifecycle/__tests__/args.test.ts +20 -0
- package/src/lifecycle/__tests__/builtin.test.ts +97 -0
- package/src/lifecycle/__tests__/bundle.test.ts +184 -6
- package/src/lifecycle/__tests__/manifest-contract.test.ts +2 -2
- package/src/lifecycle/__tests__/new.test.ts +129 -0
- package/src/lifecycle/__tests__/smoke.test.ts +2 -1
- package/src/manifest-contract.ts +16 -3
- package/src/market/__tests__/bootstrap.test.ts +17 -9
- package/src/market/bootstrap.ts +30 -17
- package/src/market/publish.ts +10 -2
- package/src/new.ts +208 -0
- 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 +24 -12
- 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 +3 -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}/fixtures/l1-test-user.sql +4 -5
- package/templates/{api/src/_platform → src/_platform/api}/market/api.ts +25 -15
- package/templates/src/_platform/api/seed.mysql.sql +16 -0
- package/templates/src/_platform/api/seed.pg.sql +16 -0
- package/templates/{api/src/_platform → src/_platform/api}/seed.sql +16 -5
- 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/context.ts +2 -2
- package/templates/{web/src/oj-market → src/oj-market/web}/api/defaultClient.ts +7 -1
- package/templates/src/oj-market/web/components/InstallDialog.tsx +194 -0
- package/templates/{web/src/oj-market → src/oj-market/web}/components/RollbackDialog.tsx +7 -3
- package/templates/{web/src/oj-market → src/oj-market/web}/components/UninstallConfirm.tsx +18 -7
- package/templates/{web/src/oj-market → src/oj-market/web}/hooks/useMarket.ts +29 -12
- 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/{web/src/system → src/system/web}/locales/en-US.json +1 -1
- package/templates/{web/src/system → src/system/web}/locales/zh-CN.json +1 -1
- 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}/l1-builder.test.ts +2 -2
- 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 +13 -11
- 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/tests/seed-role-accounts.spec.ts +87 -0
- 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/_platform/seed.mysql.sql +0 -8
- package/templates/api/src/_platform/seed.pg.sql +0 -8
- 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/web/src/oj-market/components/InstallDialog.tsx +0 -112
- /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}/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/{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}/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}/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}/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}/rbac-tables.spec.ts +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// BDD:脚手架 seed 内置角色开发账号——账号名=角色名一一对应(superadmin/admin/editor/viewer),
|
|
2
|
+
// 口令均 123456,must_change=0 免首登改密(纯开发便利,生产部署须改密/删除)。
|
|
3
|
+
// 旧库收敛:历史 seed 把 admin 绑在 superadmin 且 must_change=1,seed 幂等重放即收敛
|
|
4
|
+
// (DELETE 旧绑定 + UPDATE must_change=0),无需手工迁移。
|
|
5
|
+
// 直接重放 _platform 迁移 + seed.sql 到内存 sqlite(installDb 不 replay seed,故自给自足)。
|
|
6
|
+
// 方言对齐:seed.mysql.sql / seed.pg.sql 与基线同构,人工对齐不受本 spec 钉守——
|
|
7
|
+
// 首条用例对三份文件做 INSERT 语法冒烟,防「INSERT IGNORE 错位」类事故回归。
|
|
8
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
9
|
+
|
|
10
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
11
|
+
import { createRequire } from "node:module";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { beforeAll, describe, expect, it } from "vitest";
|
|
15
|
+
|
|
16
|
+
const req = createRequire(import.meta.url);
|
|
17
|
+
const { DatabaseSync: Db } = req("node:sqlite") as { DatabaseSync: typeof DatabaseSync };
|
|
18
|
+
|
|
19
|
+
const PLATFORM_API = join(dirname(fileURLToPath(import.meta.url)), "..", "src", "_platform", "api");
|
|
20
|
+
|
|
21
|
+
let dbo: InstanceType<typeof DatabaseSync>;
|
|
22
|
+
|
|
23
|
+
/** 重放迁移 + seed(seed 按英文分号朴素切分,同 oj 运行时口径) */
|
|
24
|
+
function replaySeed(): void {
|
|
25
|
+
for (const stmt of readFileSync(join(PLATFORM_API, "seed.sql"), "utf8").split(";")) {
|
|
26
|
+
const sql = stmt.trim();
|
|
27
|
+
if (sql)
|
|
28
|
+
dbo.exec(sql);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
beforeAll(() => {
|
|
33
|
+
dbo = new Db(":memory:");
|
|
34
|
+
for (const f of readdirSync(join(PLATFORM_API, "migrations")).filter(f => f.endsWith(".sql") && !/\.(?:mysql|pg)\.sql$/.test(f)).sort())
|
|
35
|
+
dbo.exec(readFileSync(join(PLATFORM_API, "migrations", f), "utf8"));
|
|
36
|
+
replaySeed();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("seed 内置角色开发账号", () => {
|
|
40
|
+
it("方言文件 INSERT 语法冒烟:mysql 为 INSERT IGNORE 前缀、pg 为 ON CONFLICT 后缀", () => {
|
|
41
|
+
const mysql = readFileSync(join(PLATFORM_API, "seed.mysql.sql"), "utf8");
|
|
42
|
+
const pg = readFileSync(join(PLATFORM_API, "seed.pg.sql"), "utf8");
|
|
43
|
+
expect(mysql).not.toMatch(/INSERT INTO .*INSERT IGNORE/);
|
|
44
|
+
expect(mysql.match(/INSERT IGNORE INTO/g)!.length).toBeGreaterThanOrEqual(12);
|
|
45
|
+
expect(pg).not.toMatch(/INSERT IGNORE/);
|
|
46
|
+
expect(pg.match(/ON CONFLICT DO NOTHING/g)!.length).toBeGreaterThanOrEqual(12);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it.each(["superadmin", "admin", "editor", "viewer"])("账号 %s:must_change=0 且只绑定同名角色", (username) => {
|
|
50
|
+
const row = dbo.prepare("SELECT must_change FROM users WHERE username = ?").get(username) as { must_change: number };
|
|
51
|
+
expect(row).toBeDefined();
|
|
52
|
+
expect(Number(row.must_change)).toBe(0);
|
|
53
|
+
const roles = dbo.prepare("SELECT role_name FROM sys_user_role WHERE user_id = (SELECT id FROM users WHERE username = ?)").all(username) as Array<{ role_name: string }>;
|
|
54
|
+
expect(roles.map(r => r.role_name)).toEqual([username]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("四个角色账号口令一致(123456 的 bcrypt,复用 seed 既有哈希)", () => {
|
|
58
|
+
const hashes = dbo.prepare("SELECT username, password_hash h FROM users WHERE username IN ('superadmin','admin','editor','viewer')").all() as Array<{ username: string, h: string }>;
|
|
59
|
+
expect(new Set(hashes.map(r => r.h)).size).toBe(1);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("旧库收敛:legacy admin(superadmin 绑定 + must_change=1) 经重放收敛为账号名=角色名", () => {
|
|
63
|
+
// 构造历史库状态:admin 绑 superadmin 且 must_change=1(旧 seed 口径),无 superadmin 账号
|
|
64
|
+
dbo.exec("DELETE FROM sys_user_role");
|
|
65
|
+
dbo.exec("DELETE FROM users");
|
|
66
|
+
dbo.prepare("INSERT INTO users (username, password_hash, must_change) VALUES ('admin', 'x', 1)").run();
|
|
67
|
+
dbo.prepare("INSERT INTO sys_user_role (user_id, role_name) SELECT id, 'superadmin' FROM users WHERE username = 'admin'").run();
|
|
68
|
+
|
|
69
|
+
replaySeed();
|
|
70
|
+
|
|
71
|
+
expect(Number((dbo.prepare("SELECT must_change m FROM users WHERE username = 'admin'").get() as { m: number }).m)).toBe(0);
|
|
72
|
+
const adminRoles = (dbo.prepare("SELECT role_name r FROM sys_user_role WHERE user_id = (SELECT id FROM users WHERE username = 'admin')").all() as Array<{ r: string }>).map(x => x.r);
|
|
73
|
+
expect(adminRoles).toEqual(["admin"]);
|
|
74
|
+
const superRoles = (dbo.prepare("SELECT role_name r FROM sys_user_role WHERE user_id = (SELECT id FROM users WHERE username = 'superadmin')").all() as Array<{ r: string }>).map(x => x.r);
|
|
75
|
+
expect(superRoles).toEqual(["superadmin"]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("幂等:重放两遍结果不变", () => {
|
|
79
|
+
const snap = () => JSON.stringify([
|
|
80
|
+
dbo.prepare("SELECT username, must_change FROM users ORDER BY username").all(),
|
|
81
|
+
dbo.prepare("SELECT user_id, role_name FROM sys_user_role ORDER BY user_id, role_name").all(),
|
|
82
|
+
]);
|
|
83
|
+
const before = snap();
|
|
84
|
+
replaySeed();
|
|
85
|
+
expect(snap()).toBe(before);
|
|
86
|
+
});
|
|
87
|
+
});
|
package/templates/tsconfig.json
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// 由 ojm 生成:ojm init / ojm build / ojm dev 会按 src/*/api/manifest.yaml 重写本文件,请勿手改。
|
|
2
|
+
//
|
|
3
|
+
// oj 别名 `#/<模块名>/…` 锚在目标模块根(本布局下即 src/<模块名>/api)。tsconfig 的
|
|
4
|
+
// paths 只能静态全局映射,且每条规则的替换值只允许一个 `*`(`./src/*/api/*` 会被 TS
|
|
5
|
+
// 以 TS5062 拒掉),故按模块逐条枚举。
|
|
6
|
+
{
|
|
7
|
+
"compilerOptions": {
|
|
8
|
+
"paths": {
|
|
9
|
+
"#/_platform/*": ["./src/_platform/api/*"],
|
|
10
|
+
"#/auth/*": ["./src/auth/api/*"],
|
|
11
|
+
"#/demo/*": ["./src/demo/api/*"],
|
|
12
|
+
"#/home/*": ["./src/home/api/*"],
|
|
13
|
+
"#/notification/*": ["./src/notification/api/*"],
|
|
14
|
+
"#/personal-center/*": ["./src/personal-center/api/*"],
|
|
15
|
+
"#/system/*": ["./src/system/api/*"],
|
|
16
|
+
"#/web/*": ["./src/web/api/*"]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// oj 导入别名(`#` 开头)的类型兜底。
|
|
2
|
+
//
|
|
3
|
+
// 背景:oj 的 `#x`(**本模块根**,本布局下即 `src/<name>/api`)/ `#/<模块名>/x`
|
|
4
|
+
// (**目标模块根**)是「引用方所在模块」相对的,而 tsconfig 的 `paths` 只能做**静态
|
|
5
|
+
// 全局映射**。`tsconfig.paths.json` 因此按模块枚举了 `"#/<模块名>/*"`,仅能命中
|
|
6
|
+
// **已存在**的文件;跨模块别名的模块名写错、或用了尚未创建的 `#_shared/view` 这类
|
|
7
|
+
// 模块内别名,编辑器会报 TS2307「Cannot find module」。
|
|
8
|
+
//
|
|
9
|
+
// 本文件是**非模块**(无顶层 import/export),其中的 `declare module` 才是真正的
|
|
10
|
+
// ambient 声明——放在模块化的 `global.d.ts` 里会被当作 module augmentation 而不生效。
|
|
11
|
+
// 通配 ambient 作为 `paths` 解析失败后的兜底:无法静态定位的 `#` 导入降级为 `any`
|
|
12
|
+
// 且不再报错;**已存在、能被 `paths` 命中的别名仍走其真实文件类型**(解析优先)。
|
|
13
|
+
//
|
|
14
|
+
// 运行时始终由 oj 的 loader(`module_loader.rs::resolve_alias`)解析,本文件只影响
|
|
15
|
+
// 编辑器 / tsc。模块名写错这类事故由 `oj build` 的 S008(别名无法解析)在构建期拦下,
|
|
16
|
+
// 不会拖到运行期。业务项目若自定义了全局或别名,可仿此在源码根另建 `.d.ts`。
|
|
17
|
+
declare module "#*";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { defineConfig } from "vitest/config";
|
|
3
|
+
|
|
4
|
+
// 后端模块测试(BDD):node:sqlite 内存库 + 真实迁移建表(见 tests/fixture.ts)。
|
|
5
|
+
// root 锚定本目录,保证从工程根跑 vitest 时 include 相对本目录解析。
|
|
6
|
+
const srcDir = fileURLToPath(new URL("./src", import.meta.url));
|
|
7
|
+
|
|
8
|
+
export default defineConfig({
|
|
9
|
+
root: fileURLToPath(new URL(".", import.meta.url)),
|
|
10
|
+
// `#/<模块名>/…` 是 oj 的模块别名(锚在目标模块根,本布局下即 `src/<模块名>/api`)。
|
|
11
|
+
// oj build / ojm dev 在打包器里解析它,vitest 走 vite 的 resolve,必须在此显式映射,
|
|
12
|
+
// 否则跨模块 import 的后端 spec 会以「Cannot find module」整文件跳过。
|
|
13
|
+
// 形如 `#x`(锚本模块根)与导入方相对,静态 alias 表达不了——模板后端不用这种形态,
|
|
14
|
+
// 一旦引入需先补 vitest 侧解析(或改写成 `#/<模块名>/…`)。
|
|
15
|
+
resolve: {
|
|
16
|
+
alias: [{ find: /^#\/([^/]+)\/(.*)$/, replacement: `${srcDir}/$1/api/$2` }],
|
|
17
|
+
},
|
|
18
|
+
test: {
|
|
19
|
+
include: ["tests/**/*.spec.ts"],
|
|
20
|
+
environment: "node",
|
|
21
|
+
},
|
|
22
|
+
});
|
package/templates/web.config.ts
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
* 模块工程配置:共享依赖一律不进 dependencies(宿主 importmap 提供),
|
|
3
3
|
* 这里只登记模块清单(entry 相对本工程根)。
|
|
4
4
|
*
|
|
5
|
+
* 布局:`src/<name>/{api,web}`——模块的前后端同处一个目录,`web/entry.ts` 是前端入口、
|
|
6
|
+
* `api/manifest.yaml` 是后端模块标记(两者其一即可成模块;纯共享库目录两边都没有)。
|
|
7
|
+
*
|
|
8
|
+
* 新布局下本清单退化为**可选覆盖**:`ojm build/dev` 以 `src/<name>/web/entry.ts` 扫描结果
|
|
9
|
+
* 为真源(字典序),这里登记的条目按声明顺序排前、其余按扫描顺序追加——忘登记不会漏构建。
|
|
10
|
+
* 登记了不存在的前端入口才报错。
|
|
11
|
+
*
|
|
5
12
|
* 注意:这里的登记顺序只影响模块加载顺序,**不决定侧边栏导航顺序**——
|
|
6
13
|
* 菜单顺序由各模块 entry 路由的 handle.order 决定(≥0 主菜单区升序;
|
|
7
14
|
* <0 底部固定区,pin 在侧边栏底部;同 order 按根 path 字母序)。
|
|
@@ -9,30 +16,33 @@
|
|
|
9
16
|
*/
|
|
10
17
|
const allModules = [
|
|
11
18
|
// /home 必须是首个模块:shell 的 "/" → VITE_BASE_HOME_PATH(=/home)重定向
|
|
12
|
-
{ name: "home", entry: "
|
|
13
|
-
// { name: "demo", entry: "web/src/demo/entry.ts" },
|
|
19
|
+
{ name: "home", entry: "src/home/web/entry.ts" },
|
|
14
20
|
// /login 路由由模块提供:shell 宿主不挂 runtime 内置 baseRoutes
|
|
15
|
-
{ name: "login", entry: "
|
|
21
|
+
{ name: "login", entry: "src/login/web/entry.ts" },
|
|
16
22
|
// 个人中心:runtime 用户菜单固定导航 /personal-center/my-profile,缺则点击落空
|
|
17
|
-
{ name: "personal-center", entry: "
|
|
23
|
+
{ name: "personal-center", entry: "src/personal-center/web/entry.ts" },
|
|
18
24
|
// 通知:onInit 注册 notificationsApi provider(四方法全必填),
|
|
19
25
|
// 通知铃的互动(单条已读/全部已读/清空)由此生效;未登记则永远回落内置只读兜底
|
|
20
|
-
{ name: "notification", entry: "
|
|
26
|
+
{ name: "notification", entry: "src/notification/web/entry.ts" },
|
|
21
27
|
// 系统管理:用户/成员资格/角色权限/操作日志/站点设置/登录方式(oj 后端 /api/system/*)
|
|
22
|
-
{ name: "system", entry: "
|
|
28
|
+
{ name: "system", entry: "src/system/web/entry.ts" },
|
|
23
29
|
// 解决方案市场:第一方能力(消费 _platform 编排面 /api/_platform/market/*,
|
|
24
30
|
// R29:第三方方案不得仿照此调用面),默认随脚手架下发
|
|
25
|
-
{ name: "oj-market", entry: "
|
|
31
|
+
{ name: "oj-market", entry: "src/oj-market/web/entry.ts" },
|
|
26
32
|
// #region demo-only
|
|
27
|
-
// 以下为模板内置的演示模块(about/access/exception/outside/route-nest),
|
|
33
|
+
// 以下为模板内置的演示模块(demo/about/access/exception/outside/route-nest),
|
|
28
34
|
// 用于把 templates 当可运行 demo 直接跑;其文件不在 `ojm init` 的
|
|
29
35
|
// init.include.yaml 白名单里,不拷入新工程,故本区间连同这些条目会被
|
|
30
36
|
// init 一并剥离,脚手架产出的配置不会残留指向缺失文件的死条目。
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
//
|
|
38
|
+
// 注意:新布局的清单是「扫描 + 覆盖」并集——注释掉不会让它们消失,
|
|
39
|
+
// 扫描仍会捡到 `src/<name>/web/entry.ts`。要「不构建」必须写 `enabled: false`。
|
|
40
|
+
{ name: "demo", entry: "src/demo/web/entry.ts", enabled: false },
|
|
41
|
+
{ name: "about", entry: "src/about/web/entry.ts", enabled: false },
|
|
42
|
+
{ name: "access", entry: "src/access/web/entry.ts", enabled: false },
|
|
43
|
+
{ name: "exception", entry: "src/exception/web/entry.ts", enabled: false },
|
|
44
|
+
{ name: "outside", entry: "src/outside/web/entry.ts", enabled: false },
|
|
45
|
+
{ name: "route-nest", entry: "src/route-nest/web/entry.ts", enabled: false },
|
|
36
46
|
// #endregion demo-only
|
|
37
47
|
];
|
|
38
48
|
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
"react-router": "7.18.2",
|
|
9
9
|
"react-router/dom": "7.18.2",
|
|
10
10
|
"@tanstack/react-query": "5.102.8",
|
|
11
|
-
"@oj-module/runtime": "0.2.
|
|
12
|
-
"@oj-module/runtime/contract": "0.2.
|
|
13
|
-
"@oj-module/runtime/contract/errors": "0.2.
|
|
11
|
+
"@oj-module/runtime": "0.2.23",
|
|
12
|
+
"@oj-module/runtime/contract": "0.2.23",
|
|
13
|
+
"@oj-module/runtime/contract/errors": "0.2.23",
|
|
14
14
|
"@plane/kit": "0.1.20",
|
|
15
15
|
"@plane/editor": "0.1.20",
|
|
16
16
|
"antd": "6.6.1",
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
-- _platform 种子数据 mysql 方言覆盖(方言覆盖文件必胜;INSERT OR IGNORE → INSERT IGNORE)。
|
|
2
|
-
-- 语句分号切分与注释纪律同基线 seed.sql。
|
|
3
|
-
INSERT IGNORE INTO users (username, password_hash, must_change) VALUES ('admin', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 1);
|
|
4
|
-
INSERT IGNORE INTO sys_role (name, module, title, builtin) VALUES ('superadmin', 'framework', '超级管理员', 1);
|
|
5
|
-
INSERT IGNORE INTO sys_role (name, module, title, builtin) VALUES ('admin', 'framework', '系统管理员', 1);
|
|
6
|
-
INSERT IGNORE INTO sys_role (name, module, title, builtin) VALUES ('editor', 'framework', '编辑', 1);
|
|
7
|
-
INSERT IGNORE INTO sys_role (name, module, title, builtin) VALUES ('viewer', 'framework', '访客', 1);
|
|
8
|
-
INSERT IGNORE INTO sys_user_role (user_id, role_name) SELECT id, 'superadmin' FROM users WHERE username = 'admin';
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
-- _platform 种子数据 pg 方言覆盖(方言覆盖文件必胜;INSERT OR IGNORE → ON CONFLICT DO NOTHING)。
|
|
2
|
-
-- 语句分号切分与注释纪律同基线 seed.sql。
|
|
3
|
-
INSERT INTO users (username, password_hash, must_change) VALUES ('admin', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 1) ON CONFLICT DO NOTHING;
|
|
4
|
-
INSERT INTO sys_role (name, module, title, builtin) VALUES ('superadmin', 'framework', '超级管理员', 1) ON CONFLICT DO NOTHING;
|
|
5
|
-
INSERT INTO sys_role (name, module, title, builtin) VALUES ('admin', 'framework', '系统管理员', 1) ON CONFLICT DO NOTHING;
|
|
6
|
-
INSERT INTO sys_role (name, module, title, builtin) VALUES ('editor', 'framework', '编辑', 1) ON CONFLICT DO NOTHING;
|
|
7
|
-
INSERT INTO sys_role (name, module, title, builtin) VALUES ('viewer', 'framework', '访客', 1) ON CONFLICT DO NOTHING;
|
|
8
|
-
INSERT INTO sys_user_role (user_id, role_name) SELECT id, 'superadmin' FROM users WHERE username = 'admin' ON CONFLICT DO NOTHING;
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
// 后端 BDD 夹具:node:sqlite 内存库 + **真实迁移文件**建表,装到 globalThis.db——
|
|
2
|
-
// 与 oj 运行时注入的全局同名同面(query/exec/tx/dialect),service 代码不改一行即可被测。
|
|
3
|
-
// 夹具直接读 api/src/<模块>/migrations/*.sql,与迁移不可能漂移(沿用 oj-wiki 先例)。
|
|
4
|
-
//
|
|
5
|
-
// **构造器边界(架构定论,勿在此夹具里补 table())**:db.table()/listTables() 等构造器是
|
|
6
|
-
// oj 二进制内的纯 Rust 实现,无 JS 模块可导出,Node 侧垫不出来——本夹具刻意只给
|
|
7
|
-
// {query, exec, tx, dialect}。两层测试的分工因此定型:
|
|
8
|
-
// · L2(本目录 *.spec.ts):构造器面用**记录型假 builder**(记 where/orderBy/limit 调用
|
|
9
|
-
// 序列做编排断言);真 SQL 执行断言走 db.query 直查。
|
|
10
|
-
// · L1(同目录 *.test.ts,`oj test` 收集):真 v8 + 真构造器闭环,守 SQL 渲染/过滤/转义
|
|
11
|
-
// 的真执行面(样板见 tests/l1-builder.test.ts;方法论见 devkit oj-test-guide.md)。
|
|
12
|
-
import { createRequire } from "node:module";
|
|
13
|
-
|
|
14
|
-
const req = createRequire(import.meta.url);
|
|
15
|
-
const { DatabaseSync } = req("node:sqlite");
|
|
16
|
-
const { readdirSync, readFileSync, existsSync } = req("node:fs");
|
|
17
|
-
const { join, dirname } = req("node:path");
|
|
18
|
-
const { fileURLToPath } = req("node:url");
|
|
19
|
-
|
|
20
|
-
const SRC = join(dirname(fileURLToPath(import.meta.url)), "..", "src");
|
|
21
|
-
|
|
22
|
-
export const ADMIN = 1;
|
|
23
|
-
|
|
24
|
-
/** 建内存库 + 注入全局;返回 DatabaseSync 供用例直插数据/断言。 */
|
|
25
|
-
export function installDb(): any {
|
|
26
|
-
const dbo = new DatabaseSync(":memory:");
|
|
27
|
-
// 重放 src 下全部模块的迁移(方言专属文件除外)
|
|
28
|
-
for (const mod of readdirSync(SRC)) {
|
|
29
|
-
const dir = join(SRC, mod, "migrations");
|
|
30
|
-
if (!existsSync(dir))
|
|
31
|
-
continue;
|
|
32
|
-
for (const f of readdirSync(dir).filter((f: string) => f.endsWith(".sql") && !/\.(?:mysql|pg)\.sql$/.test(f)).sort())
|
|
33
|
-
dbo.exec(readFileSync(join(dir, f), "utf8"));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
dbo.prepare("INSERT INTO users (id, username, password_hash) VALUES (?, ?, 'x')").run(ADMIN, "admin");
|
|
37
|
-
|
|
38
|
-
const query = async (sql: string, params: unknown[] = []) => dbo.prepare(sql).all(...(params as never[]));
|
|
39
|
-
const exec = async (sql: string, params: unknown[] = []) => Number(dbo.prepare(sql).run(...(params as never[])).changes);
|
|
40
|
-
const txInstance = { query, exec, isTx: true };
|
|
41
|
-
// db.tx 镜像运行时面:BEGIN/COMMIT/ROLLBACK 包裹回调(versions.ts 注释同语义)
|
|
42
|
-
const tx = async (fn: (t: typeof txInstance) => unknown) => {
|
|
43
|
-
dbo.exec("BEGIN");
|
|
44
|
-
try {
|
|
45
|
-
const r = await fn(txInstance);
|
|
46
|
-
dbo.exec("COMMIT");
|
|
47
|
-
return r;
|
|
48
|
-
}
|
|
49
|
-
catch (e) {
|
|
50
|
-
dbo.exec("ROLLBACK");
|
|
51
|
-
throw e;
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const g = globalThis as any;
|
|
56
|
-
g.db = { query, exec, tx, dialect: () => "sqlite" };
|
|
57
|
-
return dbo;
|
|
58
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath } from "node:url";
|
|
2
|
-
import { defineConfig } from "vitest/config";
|
|
3
|
-
|
|
4
|
-
// 后端模块测试(BDD):node:sqlite 内存库 + 真实迁移建表(见 tests/fixture.ts)。
|
|
5
|
-
// root 锚定本目录,保证从工程根跑 vitest 时 include 相对本目录解析。
|
|
6
|
-
export default defineConfig({
|
|
7
|
-
root: fileURLToPath(new URL(".", import.meta.url)),
|
|
8
|
-
test: {
|
|
9
|
-
include: ["tests/**/*.spec.ts"],
|
|
10
|
-
environment: "node",
|
|
11
|
-
},
|
|
12
|
-
});
|
|
File without changes
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import type { IndexRow, TaskRecord } from "../types";
|
|
2
|
-
import { Alert, Button, Modal, Progress, Space, Typography } from "antd";
|
|
3
|
-
import React, { useEffect, useState } from "react";
|
|
4
|
-
import { useTranslation } from "react-i18next";
|
|
5
|
-
import { useMarketClient } from "../api/context";
|
|
6
|
-
import { useInstallTask } from "../hooks/useMarket";
|
|
7
|
-
|
|
8
|
-
export interface InstallDialogProps {
|
|
9
|
-
row: IndexRow | null
|
|
10
|
-
open: boolean
|
|
11
|
-
onClose: () => void
|
|
12
|
-
onDone?: (record: TaskRecord) => void
|
|
13
|
-
/** 显式目标版本(回滚用);缺省取 row.latest(安装/升级) */
|
|
14
|
-
version?: string | null
|
|
15
|
-
/** 动作模式:安装/升级/回滚(仅影响标题文案;回滚 = 对旧版降级安装) */
|
|
16
|
-
mode?: "install" | "upgrade" | "rollback"
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const isTerminal = (r: TaskRecord | null) => r != null && r.status !== "running";
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* 安装/升级/回滚对话框:触发 POST install → 800ms 轮询 TaskRecord 终态渲染(reason+retryable+hintKey 双语)。
|
|
23
|
-
* 回滚 = 对已装旧版降级安装(显式 version);降版迁移差异校验(R12)的可读失败由终态 error 渲染。
|
|
24
|
-
*/
|
|
25
|
-
export function InstallDialog({ row, open, onClose, onDone, version, mode }: InstallDialogProps) {
|
|
26
|
-
const { t } = useTranslation();
|
|
27
|
-
const client = useMarketClient();
|
|
28
|
-
const [taskId, setTaskId] = useState<string | null>(null);
|
|
29
|
-
const [started, setStarted] = useState(false);
|
|
30
|
-
const { record, done } = useInstallTask(taskId);
|
|
31
|
-
|
|
32
|
-
const targetVersion = version ?? row?.latest ?? null;
|
|
33
|
-
const title
|
|
34
|
-
= mode === "rollback"
|
|
35
|
-
? t("oj-market:rollback:running")
|
|
36
|
-
: row?.installed
|
|
37
|
-
? t("oj-market:card:upgrade")
|
|
38
|
-
: t("oj-market:task:installing");
|
|
39
|
-
|
|
40
|
-
// 打开即触发安装(version 缺省取 latest;已装视为升级;显式 version = 回滚降级安装)
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
if (open && row && !started) {
|
|
43
|
-
setStarted(true);
|
|
44
|
-
client
|
|
45
|
-
.install({ name: row.name, version: targetVersion, source: "cdn" })
|
|
46
|
-
.then(res => setTaskId(res.task_id))
|
|
47
|
-
.catch(e => setTaskId(`__error__${e instanceof Error ? e.message : String(e)}`));
|
|
48
|
-
}
|
|
49
|
-
if (!open) {
|
|
50
|
-
setTaskId(null);
|
|
51
|
-
setStarted(false);
|
|
52
|
-
}
|
|
53
|
-
}, [open, row, started, client, targetVersion]);
|
|
54
|
-
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
if (record && isTerminal(record) && done)
|
|
57
|
-
onDone?.(record);
|
|
58
|
-
}, [record, done, onDone]);
|
|
59
|
-
|
|
60
|
-
const terminal = isTerminal(record);
|
|
61
|
-
const failed = record?.status === "failed" || record?.status === "partial";
|
|
62
|
-
const errorTask = typeof taskId === "string" && taskId.startsWith("__error__");
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<Modal
|
|
66
|
-
title={title}
|
|
67
|
-
open={open}
|
|
68
|
-
onCancel={onClose}
|
|
69
|
-
footer={terminal ? <Button onClick={onClose}>{t("oj-market:task:close")}</Button> : null}
|
|
70
|
-
data-testid="install-dialog"
|
|
71
|
-
>
|
|
72
|
-
{errorTask && <Alert type="error" message={taskId!.slice("__error__".length)} />}
|
|
73
|
-
{!errorTask && !record && (
|
|
74
|
-
<Typography.Text>
|
|
75
|
-
{t("oj-market:task:installing")}
|
|
76
|
-
…
|
|
77
|
-
</Typography.Text>
|
|
78
|
-
)}
|
|
79
|
-
{record && (
|
|
80
|
-
<>
|
|
81
|
-
<Progress percent={record.progress} status={terminal ? (failed ? "exception" : "success") : "active"} />
|
|
82
|
-
<div data-testid="install-stage">{record.stage ? t(`oj-market:task:stage.${record.stage}`, record.stage) : record.stage}</div>
|
|
83
|
-
<div data-testid="install-message">{record.message}</div>
|
|
84
|
-
{failed && record.error && (
|
|
85
|
-
<Alert
|
|
86
|
-
type="error"
|
|
87
|
-
showIcon
|
|
88
|
-
message={record.error.reason}
|
|
89
|
-
description={(
|
|
90
|
-
<Space direction="vertical">
|
|
91
|
-
<span>{record.error.retryable ? t("oj-market:task:retryable") : t("oj-market:task:notRetryable")}</span>
|
|
92
|
-
{record.error.hintKey && <span data-testid="install-hint">{t(record.error.hintKey, record.error.hintKey)}</span>}
|
|
93
|
-
</Space>
|
|
94
|
-
)}
|
|
95
|
-
/>
|
|
96
|
-
)}
|
|
97
|
-
{failed && (
|
|
98
|
-
<Button
|
|
99
|
-
style={{ marginTop: 12 }}
|
|
100
|
-
onClick={() => {
|
|
101
|
-
setTaskId(null);
|
|
102
|
-
setStarted(false);
|
|
103
|
-
}}
|
|
104
|
-
>
|
|
105
|
-
{t("oj-market:card:retry")}
|
|
106
|
-
</Button>
|
|
107
|
-
)}
|
|
108
|
-
</>
|
|
109
|
-
)}
|
|
110
|
-
</Modal>
|
|
111
|
-
);
|
|
112
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/contacts.ts
RENAMED
|
File without changes
|
|
File without changes
|
/package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/settings.ts
RENAMED
|
File without changes
|
/package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/solution-perms.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/templates/{api/src/_platform → src/_platform/api}/migrations/0001__create_users.mysql.sql
RENAMED
|
File without changes
|
/package/templates/{api/src/_platform → src/_platform/api}/migrations/0001__create_users.pg.sql
RENAMED
|
File without changes
|
/package/templates/{api/src/_platform → src/_platform/api}/migrations/0001__create_users.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/templates/{api/src/_platform → src/_platform/api}/migrations/0004__sys_rbac.mysql.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/templates/{api/src/_platform → src/_platform/api}/migrations/0005__users_must_change.sql
RENAMED
|
File without changes
|
|
File without changes
|
/package/templates/{api/src/_platform → src/_platform/api}/migrations/0006__users_is_forbidden.sql
RENAMED
|
File without changes
|
|
File without changes
|