@oj-module/cli 0.2.53 → 0.2.56
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/SKILL.md +4 -4
- package/devkit/manual.md +79 -65
- package/package.json +2 -2
- package/shell-dist/assets/{index-e_Mp2mb5.js → index-CImwskAz.js} +24 -11
- package/shell-dist/assets/runtime.js +7 -4
- package/shell-dist/index.html +3 -3
- package/shell-dist/versions.json +3 -3
- package/src/__tests__/layout.test.ts +30 -1
- package/src/__tests__/templates-schema-columns.test.ts +99 -0
- package/src/__tests__/templates-tenant-scope.test.ts +3 -2
- package/src/alias-paths.ts +11 -12
- package/src/builtin.ts +10 -3
- package/src/config.ts +9 -0
- package/src/contract/__tests__/client-path.test.ts +30 -0
- package/src/contract/__tests__/emit-client.test.ts +20 -2
- package/src/contract/__tests__/handler-scan.test.ts +23 -0
- package/src/contract/__tests__/market-client.integration.test.ts +2 -2
- package/src/contract/__tests__/run-api.test.ts +2 -2
- package/src/contract/check.ts +11 -7
- package/src/contract/client-path.ts +30 -0
- package/src/contract/emit-client.ts +27 -6
- package/src/contract/handler-scan.ts +40 -3
- package/src/contract/run.ts +15 -5
- package/src/init.ts +29 -3
- package/src/layout.ts +72 -16
- package/src/lifecycle/__tests__/manifest-contract.test.ts +2 -2
- package/src/market/__tests__/bootstrap.test.ts +1 -1
- package/src/site.ts +1 -1
- package/templates/README.md +49 -43
- package/templates/init.include.yaml +31 -19
- package/templates/openapi.json +1488 -120
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/users.ts +27 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/oplog.ts +4 -2
- package/templates/src/{_platform/api → app/api/_platform}/contacts/api.ts +29 -1
- package/templates/src/{_platform/api → app/api/_platform}/contacts/detail/api.ts +25 -1
- package/templates/src/app/api/_platform/migrations/0017__users_employee_no.mysql.sql +7 -0
- package/templates/src/app/api/_platform/migrations/0017__users_employee_no.pg.sql +4 -0
- package/templates/src/app/api/_platform/migrations/0017__users_employee_no.sql +8 -0
- package/templates/src/{_platform/api → app/api/_platform}/schema.yaml +5 -0
- package/templates/src/{_platform/api → app/api/_platform}/solutions/api.ts +60 -1
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/replay.ts +9 -3
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/tenant-admin.ts +18 -27
- package/templates/src/{auth/api → app/api/auth}/api.ts +59 -0
- package/templates/src/{auth/api → app/api/auth}/ldap-login/api.ts +37 -1
- package/templates/src/{auth/api → app/api/auth}/wecom/api.ts +23 -0
- package/templates/src/{notification/api → app/api/notification}/api.ts +17 -0
- package/templates/src/{notification/api → app/api/notification}/del/api.ts +2 -0
- package/templates/src/{system/api → app/api/system}/oplog/api.ts +5 -3
- package/templates/src/{system/api → app/api/system}/roles/api.ts +3 -0
- package/templates/src/{system/api → app/api/system}/roles/bindings/api.ts +3 -0
- package/templates/src/{system/api → app/api/system}/settings/api.ts +3 -0
- package/templates/src/{system/api → app/api/system}/users/forbidden/api.ts +3 -0
- package/templates/src/{system/api → app/api/system}/users/password/api.ts +3 -0
- package/templates/src/{system/api → app/api/system}/users/roles/api.ts +3 -0
- package/templates/src/app/api/web/get-async-routes/api.ts +23 -0
- package/templates/src/app/api/web/hello/api.ts +22 -0
- package/templates/src/{web/api → app/api/web}/user-info/api.ts +21 -1
- package/templates/src/{_platform/web → app/web/_platform}/__client/api.schemas.ts +99 -35
- package/templates/src/{_platform/web/__client/api.ts → app/web/_platform/__client/index.ts} +108 -61
- package/templates/src/{auth/web → app/web/auth}/__client/api.schemas.ts +133 -13
- package/templates/src/{auth/web/__client/api.ts → app/web/auth/__client/index.ts} +134 -33
- package/templates/src/app/web/entry.ts +77 -0
- package/templates/src/{home/web/__client/api.ts → app/web/home/__client/index.ts} +5 -2
- package/templates/src/app/web/home/routes.ts +41 -0
- package/templates/src/app/web/login/routes.ts +71 -0
- package/templates/src/{notification/web → app/web/notification}/__client/api.schemas.ts +14 -0
- package/templates/src/{notification/web/__client/api.ts → app/web/notification/__client/index.ts} +67 -20
- package/templates/src/{notification/web → app/web/notification}/pages/center/index.tsx +1 -1
- package/templates/src/app/web/notification/routes.ts +70 -0
- package/templates/src/{personal-center/web/__client/api.ts → app/web/personal-center/__client/index.ts} +5 -2
- package/templates/src/{personal-center/web → app/web/personal-center}/pages/my-profile/index.tsx +1 -1
- package/templates/src/app/web/personal-center/routes.ts +49 -0
- package/templates/src/{system/web → app/web/system}/__client/api.schemas.ts +29 -22
- package/templates/src/{system/web/__client/api.ts → app/web/system/__client/index.ts} +103 -58
- package/templates/src/{system/web → app/web/system}/locales/en-US.json +2 -3
- package/templates/src/{system/web → app/web/system}/locales/zh-CN.json +2 -3
- package/templates/src/{system/web → app/web/system}/pages/departments/index.tsx +2 -2
- package/templates/src/{system/web → app/web/system}/pages/login-methods/index.tsx +1 -1
- package/templates/src/{system/web → app/web/system}/pages/oplog/index.tsx +44 -19
- package/templates/src/{system/web → app/web/system}/pages/roles/index.tsx +5 -3
- package/templates/src/app/web/system/pages/roles/visible-roles.ts +14 -0
- package/templates/src/{system/web → app/web/system}/pages/settings/index.tsx +2 -2
- package/templates/src/app/web/system/pages/users/can-manage.ts +21 -0
- package/templates/src/{system/web → app/web/system}/pages/users/index.tsx +23 -15
- package/templates/src/{system/web → app/web/system}/pages/users/roles.tsx +1 -1
- package/templates/src/{system/web → app/web/system}/pages/wecom/index.tsx +12 -3
- package/templates/src/{system/web/entry.ts → app/web/system/routes.ts} +25 -36
- package/templates/src/app/web/web/__client/api.schemas.ts +36 -0
- package/templates/src/{web/web/__client/api.ts → app/web/web/__client/index.ts} +38 -11
- package/templates/src/oj-market/web/api/defaultClient.ts +1 -1
- package/templates/tests/auth-ldap.spec.ts +2 -2
- package/templates/tests/auth-session.spec.ts +3 -3
- package/templates/tests/auth-throttle.spec.ts +1 -1
- package/templates/tests/fixture.ts +6 -4
- package/templates/tests/guard.spec.ts +1 -1
- package/templates/tests/lifecycle/atomic-state.spec.ts +3 -3
- package/templates/tests/lifecycle/bootstrap.spec.ts +3 -3
- package/templates/tests/lifecycle/deploy-smoke.spec.ts +8 -8
- package/templates/tests/lifecycle/installer.spec.ts +10 -10
- package/templates/tests/lifecycle/market-api.spec.ts +14 -14
- package/templates/tests/lifecycle/migrate.spec.ts +6 -6
- package/templates/tests/lifecycle/preflight.spec.ts +3 -3
- package/templates/tests/lifecycle/register.spec.ts +1 -1
- package/templates/tests/lifecycle/semver.spec.ts +1 -1
- package/templates/tests/lifecycle/sha384.spec.ts +1 -1
- package/templates/tests/lifecycle/solutions-api.spec.ts +1 -1
- package/templates/tests/lifecycle/source-seq.spec.ts +1 -1
- package/templates/tests/lifecycle/steps-fs.spec.ts +9 -9
- package/templates/tests/lifecycle/text-edit.spec.ts +1 -1
- package/templates/tests/lifecycle/tgz-guard.spec.ts +2 -2
- package/templates/tests/lifecycle/trust.spec.ts +5 -5
- package/templates/tests/lifecycle/uninstall.spec.ts +7 -7
- package/templates/tests/lifecycle/verify-checks.spec.ts +5 -5
- package/templates/tests/lifecycle/verify-report.spec.ts +4 -4
- package/templates/tests/org-departments.spec.ts +10 -6
- package/templates/tests/org-read.spec.ts +7 -7
- package/templates/tests/query-utils.spec.ts +1 -1
- package/templates/tests/rbac-audit.spec.ts +1 -1
- package/templates/tests/rbac-contacts.spec.ts +2 -2
- package/templates/tests/rbac-guard.spec.ts +1 -1
- package/templates/tests/rbac-roles.spec.ts +10 -2
- package/templates/tests/rbac-seed.spec.ts +1 -1
- package/templates/tests/rbac-settings.spec.ts +77 -7
- package/templates/tests/rbac-solution-perms.spec.ts +1 -1
- package/templates/tests/rbac-tables.spec.ts +29 -0
- package/templates/tests/rbac-users.spec.ts +55 -3
- package/templates/tests/roles-page.spec.ts +40 -0
- package/templates/tests/seed-role-accounts.spec.ts +1 -1
- package/templates/tests/tenant-access-cache.spec.ts +1 -1
- package/templates/tests/tenant-forgery.spec.ts +5 -5
- package/templates/tests/tenant-isolation.spec.ts +5 -4
- package/templates/tests/username-namespace.spec.ts +3 -3
- package/templates/tests/users-page.spec.ts +39 -0
- package/templates/tests/wecom-admin.spec.ts +6 -6
- package/templates/tests/wecom-client-timeout.spec.ts +1 -1
- package/templates/tests/wecom-connector.spec.ts +9 -9
- package/templates/tests/wecom-corp.spec.ts +9 -9
- package/templates/tests/wecom-crypt.spec.ts +1 -1
- package/templates/tests/wecom-endpoints.spec.ts +2 -2
- package/templates/tests/wecom-landing.spec.ts +1 -1
- package/templates/tests/wecom-org-sync.spec.ts +7 -7
- package/templates/tests/wecom-sync.spec.ts +3 -3
- package/templates/tsconfig.paths.json +11 -11
- package/templates/vitest.config.ts +2 -2
- package/templates/web.config.ts +24 -15
- package/vendor/host-versions.json +3 -3
- package/templates/src/home/web/entry.ts +0 -49
- package/templates/src/login/web/entry.ts +0 -79
- package/templates/src/notification/web/entry.ts +0 -84
- package/templates/src/personal-center/web/entry.ts +0 -60
- package/templates/src/web/api/get-async-routes/api.ts +0 -8
- package/templates/src/web/api/hello/api.ts +0 -10
- package/templates/src/web/web/__client/api.schemas.ts +0 -8
- package/templates/src/{_platform/api → app/api/_platform}/README.md +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/atomic.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/history.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/installer.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/limits.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/lock.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/semver.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/sha384.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/source-seq.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/state.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/deploy-web.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/download.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/extract.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/migrate.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/preflight.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/register.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/reload.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/smoke.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/verify.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tasks.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/text-edit.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tgz-guard.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/trust.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/types.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/uninstall.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-checks.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-report.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/respond.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/contacts.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org-read.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/roles.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/settings.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/solution-perms.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/access.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/audit.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/guard.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/query.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/rate.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/_shared/site-facade.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/fixtures/l1-test-user.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/manifest.yaml +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/market/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/market/bootstrap.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/market/index-source.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0007__users_email.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0013__users_profile.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/departments/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/is-member/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/members/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/picker/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/resolve/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/tree/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/org/user/api.ts +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/seed.mysql.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/seed.pg.sql +0 -0
- package/templates/src/{_platform/api → app/api/_platform}/seed.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/ldap-login.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/ldap.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/login-throttle.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/session.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/admin.ts +1 -1
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/client.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/config.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/contacts.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/crypt.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/events.ts +1 -1
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/login.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/payload.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/store.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/_shared/wecom/sync.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/manifest.yaml +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.mysql.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.pg.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.mysql.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.pg.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.mysql.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.pg.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.sql +0 -0
- package/templates/src/{auth/api → app/api/auth}/schema.yaml +0 -0
- package/templates/src/{auth/api → app/api/auth}/wecom/admin/api.ts +0 -0
- package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/api.ts +1 -1
- package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/password/api.ts +1 -1
- /package/templates/src/{auth/api → app/api/auth}/wecom/sync/api.ts +0 -0
- /package/templates/src/{home/api → app/api/home}/line/api.ts +0 -0
- /package/templates/src/{home/api → app/api/home}/manifest.yaml +0 -0
- /package/templates/src/{home/api → app/api/home}/migrations/0001__create_home_tables.sql +0 -0
- /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.mysql.sql +0 -0
- /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.sql +0 -0
- /package/templates/src/{home/api → app/api/home}/pie/api.ts +0 -0
- /package/templates/src/{home/api → app/api/home}/schema.yaml +0 -0
- /package/templates/src/{home/api → app/api/home}/seed.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/_shared/notify.ts +0 -0
- /package/templates/src/{notification/api → app/api/notification}/manifest.yaml +0 -0
- /package/templates/src/{notification/api → app/api/notification}/migrations/0001__create_notifications.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/migrations/0002__notifications_user.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/migrations/0003__notifications_broadcast.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/migrations/0004__notifications_broadcast_hidden.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.mysql.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.sql +0 -0
- /package/templates/src/{notification/api → app/api/notification}/schema.yaml +0 -0
- /package/templates/src/{notification/api → app/api/notification}/seed.sql +0 -0
- /package/templates/src/{personal-center/api → app/api/personal-center}/manifest.yaml +0 -0
- /package/templates/src/{personal-center/api → app/api/personal-center}/profile/api.ts +0 -0
- /package/templates/src/{personal-center/api → app/api/personal-center}/upload/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/departments/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/departments/detach/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/departments/members/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/departments/move/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/login-methods/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/manifest.yaml +0 -0
- /package/templates/src/{system/api → app/api/system}/permissions/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/users/api.ts +0 -0
- /package/templates/src/{system/api → app/api/system}/users/profile/api.ts +0 -0
- /package/templates/src/{web/api → app/api/web}/manifest.yaml +0 -0
- /package/templates/src/{home/web → app/web/home}/__client/api.schemas.ts +0 -0
- /package/templates/src/{home/web → app/web/home}/locales/en-US.json +0 -0
- /package/templates/src/{home/web → app/web/home}/locales/zh-CN.json +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/components/bar-chart.tsx +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/components/card-list.tsx +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/components/line-chart.tsx +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/components/pie-chart.tsx +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/index.tsx +0 -0
- /package/templates/src/{login/web → app/web/login}/auth-provider.ts +0 -0
- /package/templates/src/{login/web → app/web/login}/locales/en-US.json +0 -0
- /package/templates/src/{login/web → app/web/login}/locales/zh-CN.json +0 -0
- /package/templates/src/{login/web → app/web/login}/pages/change-password.tsx +0 -0
- /package/templates/src/{login/web → app/web/login}/pages/login.tsx +0 -0
- /package/templates/src/{login/web → app/web/login}/wecom.ts +0 -0
- /package/templates/src/{notification/web → app/web/notification}/locales/en-US.json +0 -0
- /package/templates/src/{notification/web → app/web/notification}/locales/zh-CN.json +0 -0
- /package/templates/src/{personal-center/web → app/web/personal-center}/__client/api.schemas.ts +0 -0
- /package/templates/src/{personal-center/web → app/web/personal-center}/locales/en-US.json +0 -0
- /package/templates/src/{personal-center/web → app/web/personal-center}/locales/zh-CN.json +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/departments/sync-bar.tsx +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/departments/tree.ts +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/errors.ts +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/forbidden.tsx +0 -0
package/devkit/SKILL.md
CHANGED
|
@@ -54,7 +54,7 @@ description: 用 ojm 开发前后端一体工程时使用——新增/修改前
|
|
|
54
54
|
- **注册时机**:一切 `ctx.register.*` 必须在 `lifecycle.onInit` 内、且在 onInit 返回前完成。
|
|
55
55
|
拖到 `onActivate` 或某个异步回调里注册不保证生效。`onInit` 签名是 **async**(返回
|
|
56
56
|
`Promise<void>`)——漏写 `async` 会被 `tsc` 以 TS2322 拦下,范本见模板
|
|
57
|
-
`src/
|
|
57
|
+
`src/app/web/notification/entry.ts`。
|
|
58
58
|
- **一模块一 client**:`create<Module>Client(ctx)` 只在 onInit 里创建一次。
|
|
59
59
|
页面/组件/事件回调里**不要**再创建——会覆盖 request 单槽。页面里直接 import 裸函数调用。
|
|
60
60
|
- **只创建自己模块的 client**:A 模块的 onInit 里创建 B 模块的 client 会 DEV 告警。要别家接口,
|
|
@@ -103,7 +103,7 @@ description: 用 ojm 开发前后端一体工程时使用——新增/修改前
|
|
|
103
103
|
|
|
104
104
|
## 企业微信(接通真实套件 / 自建应用)
|
|
105
105
|
|
|
106
|
-
模板已内置企微登录(`src/
|
|
106
|
+
模板已内置企微登录(`src/app/api/auth/wecom/` + 登录页「企业微信」页签 + `/system/wecom` 后台)。
|
|
107
107
|
两种来源、两本手册,**按客户形态选**:
|
|
108
108
|
|
|
109
109
|
| 客户形态 | 读 | 凭据 |
|
|
@@ -132,7 +132,7 @@ description: 用 ojm 开发前后端一体工程时使用——新增/修改前
|
|
|
132
132
|
- **整页跳转拼 API base**:`window.location.assign(resolveApiBase() + wecomAuthorizePath(corp))`;
|
|
133
133
|
拼站点 base 会被 SPA 兜底页吞掉(页面原地重载、一个请求都不发,控制台无报错)。
|
|
134
134
|
- **通讯录同步是「组织架构底座 + 企微适配器」,不是企微专属功能**:模板的
|
|
135
|
-
`src/
|
|
135
|
+
`src/app/api/auth/wecom/{sync,contacts}.ts` 只负责「取快照 → 喂给领域层」,模型与读面在 `_platform`
|
|
136
136
|
(手册 §14)。要按部门算人、要选人、要给组织架构接 LDAP,一律读 §14,不要在企微代码里加分支。
|
|
137
137
|
同步/事件增量与回调解密是**两条独立纪律**:回调必须回 `success`,而事件处理失败**也照样回 `success`**
|
|
138
138
|
(重推补不回已判死的失败,兜底是下一轮全量同步)。
|
|
@@ -187,7 +187,7 @@ description: 用 ojm 开发前后端一体工程时使用——新增/修改前
|
|
|
187
187
|
| 登录 401 且 msg 不是 `invalid credentials` | `/auth/*` 不在 `anonymous_paths` | 加进工程根 `config.yaml`,重启 |
|
|
188
188
|
| WS 连接 401(「文件在却连不上」) | 模块写了 `ws.ts`,但其 `/ws` 路由不在 `anonymous_paths`(oj v0.1.30+ WS 握手也过鉴权) | 匿名 WS 加进 `anonymous_paths`;受保护 WS 带 Bearer/Cookie 是正当用法。`ojm api --check` 会对此 warn |
|
|
189
189
|
| 登出/回登录页落空,或登录后落错误边界 | 工程缺 `login` / `home` 模块 | 在 `web.config.ts` 保留两者 |
|
|
190
|
-
| 通知铃 404 `no route matched`(`/api/notifications`) | 前端 `notification` 模块没加载 / 没注册 `notificationsApi`,回落到内置只读兜底;脚手架不再提供 root 级 `/api/notifications` | 保留 `src/
|
|
190
|
+
| 通知铃 404 `no route matched`(`/api/notifications`) | 前端 `notification` 模块没加载 / 没注册 `notificationsApi`,回落到内置只读兜底;脚手架不再提供 root 级 `/api/notifications` | 保留 `src/app/web/notification/entry.ts` 的登记 + onInit 注册四方法;后端由 `src/app/api/notification` 出 `/api/notification/*` |
|
|
191
191
|
| `oj serve` 启动即退,报 `config uses removed server.* site keys (v0.1.58): api_prefix` | 用了 oj v0.1.58 起删除的旧站点键(`api_prefix`/`app_path`/`app_prefix`/`static_sites`/`app_spa_fallback`) | 改 `server.api_prefix: /api` 为顶层 `mounts:`(module-root 写 `{prefix: "/api", api: "api/dist"}`;历史布局写 `api: "src"`、源码交给 `-d`);迁移完整 YAML 由报错给出 |
|
|
192
192
|
| 改完 `mounts:` 仍全 404 | `api:` 指向目录不对(相对 config 所在目录) | module-root 指向产物目录 `"api/dist"`、历史布局指向源码树 `"src"`;`-c` 指向工程根 `config.yaml`(ojm dev/preview 已内置)。module-root 模板**故意不写 api 挂载**(源码交给 `--module-root src`、产物交给 `--api-path api/dist`,同时出现会报 module declared in multiple places) |
|
|
193
193
|
| `oj serve` 启动即退,报 `module '_shared' missing manifest.yaml` | 把共享后端代码放到了扫描根顶层(`src/_shared/`)——oj 把扫描根下的顶层目录都当模块;历史布局是 `api/src/_shared/` | 嵌进所属模块:`src/<模块>/api/_shared/`(历史布局 `api/src/<模块>/_shared/`),用 `#<本模块>/...` 别名 |
|
package/devkit/manual.md
CHANGED
|
@@ -42,16 +42,27 @@
|
|
|
42
42
|
│ ├── tsconfig.json # 后端 typecheck 工程(extends 上两者,exclude */web)
|
|
43
43
|
│ ├── .ojm-builtin.json # 框架自带方案名单(init 派生,可手改/删除;打包排除 + info 折叠,见 §11)
|
|
44
44
|
│ ├── styles.css
|
|
45
|
-
│ ├──
|
|
46
|
-
│ │ ├── api/ #
|
|
47
|
-
│ │
|
|
48
|
-
│ ├── auth/
|
|
49
|
-
│ ├── system/
|
|
50
|
-
│ ├── notification/
|
|
51
|
-
│ ├── personal-center/
|
|
52
|
-
│ └── web/
|
|
53
|
-
|
|
54
|
-
├──
|
|
45
|
+
│ ├── app/ # 整个应用(后端模块 + 前端聚合模块)
|
|
46
|
+
│ │ ├── api/ # 后端模块(模块名 = URL 前缀,各自仍是独立 oj 模块)
|
|
47
|
+
│ │ │ ├── _platform/ # users 表 / rbac / 市场编排 / 生命周期 + _shared/
|
|
48
|
+
│ │ │ ├── auth/ # login / refresh / logout / ldap-login
|
|
49
|
+
│ │ │ ├── system/ # 用户 / 角色 / 权限 / 设置 / oplog / login-methods
|
|
50
|
+
│ │ │ ├── notification/ # 通知互动(表 + push + del)
|
|
51
|
+
│ │ │ ├── personal-center/ # 头像上传
|
|
52
|
+
│ │ │ └── web/ # root 级 /api/web(hello / user-info / get-async-routes)
|
|
53
|
+
│ │ └── web/ # 前端**多根聚合模块**(一个 entry 多个根)
|
|
54
|
+
│ │ ├── entry.ts # routes 聚合 + i18nNamespaces + 生命周期
|
|
55
|
+
│ │ ├── home/ # /home(routes.ts + pages + locales + __client)
|
|
56
|
+
│ │ ├── login/ # /login、/change-password
|
|
57
|
+
│ │ ├── personal-center/ # /personal-center/my-profile
|
|
58
|
+
│ │ ├── notification/ # /notification-center
|
|
59
|
+
│ │ ├── system/ # /system/*(根上 handle.roles = 系统管理员)
|
|
60
|
+
│ │ ├── auth/ # 仅 __client(无页面)
|
|
61
|
+
│ │ ├── _platform/ # 仅 __client(oj-market 引用)
|
|
62
|
+
│ │ └── web/ # 仅 __client
|
|
63
|
+
│ ├── oj-market/web/ # 解决方案市场(独立模块,删目录即下线)
|
|
64
|
+
├── dist/api/ # 后端产物(release 根:<name>-<ver>/ + manifests.yaml + versions.json)—构建后才有
|
|
65
|
+
├── dist/web/ # 站点产物(shell + modules/ + modules.json)—构建后才有
|
|
55
66
|
├── bin/ # oj 二进制 + plugins/ + devkit/(不入库)
|
|
56
67
|
└── .claude/skills/ # ojm-module-dev(本 devkit)+ oj-api-dev(随 oj 二进制)
|
|
57
68
|
```
|
|
@@ -62,13 +73,15 @@
|
|
|
62
73
|
所在目录**,所有 `fs.*` 相对路径、`db.default`、`platform.root`、`mounts[].web`
|
|
63
74
|
都相对它解析;证书与密钥落在同级的 `config/`(`ojm` 侧由 `resolveConfigPath()` /
|
|
64
75
|
`resolveCertDir()` 单点派生,你不需要自己拼路径)。
|
|
65
|
-
- **产物有两个根**:后端 `api
|
|
66
|
-
`manifests.yaml` 同层),站点 `web
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
- **产物有两个根**:后端 `dist/api`(oj 扁平 release,`<name>-<ver>/` 与
|
|
77
|
+
`manifests.yaml` 同层),站点 `dist/web`(`modules/` + `modules.json`)。
|
|
78
|
+
- **`src/` 下的顶层目录必须是一个模块**(`api/manifest.yaml` 或 `web/entry.ts`):
|
|
79
|
+
脚本 / 共享库请放在 `src/` 之外。应用容器 `src/app` 是唯一例外——它的 `api/<name>/`
|
|
80
|
+
子目录各自是独立模块(模块名 = 子目录名 = URL 前缀,oj 的 `--module-root` 递归发现)。
|
|
81
|
+
- **前端 client 入口是 `__client/index.ts`**(0.2.54 起,旧名 `api.ts` 会被 oj dev
|
|
82
|
+
当路由扫描)。页面导入写 `from "../__client"`。
|
|
83
|
+
|
|
84
|
+
> 模板里还有一批 demo-only 模块(`src/app/api/home`、`src/{about,access,exception,
|
|
72
85
|
> outside,route-nest}/web`)仅供模板自演示,**不进脚手架**;其 `web.config.ts` 条目带
|
|
73
86
|
> `// #region demo-only` 标记,init 拷贝时整段剥离。新布局的清单是「扫描 + 覆盖」并集,
|
|
74
87
|
> 注释掉不会让模块消失——要「不构建」必须写 `enabled: false`。
|
|
@@ -93,7 +106,7 @@
|
|
|
93
106
|
> **更早建好的 dev 库里仍是旧口令**——拿 `aA123456` 登不上时先怀疑这个,别去查登录链路。
|
|
94
107
|
> 要统一:删 `db.sqlite` 重跑 `oj migrate`(seed 随服务启动重放),或直接
|
|
95
108
|
> `UPDATE users SET password_hash = '<aA123456 的哈希>' WHERE username IN ('superadmin','admin','editor','viewer');`。
|
|
96
|
-
> 哈希现成值从 `src/
|
|
109
|
+
> 哈希现成值从 `src/app/api/_platform/seed.sql` 里 grep `$2y$10$` 即可。
|
|
97
110
|
|
|
98
111
|
端口:前端 devServer **5174**,oj 后端 **9778**
|
|
99
112
|
(以工程根 `config.yaml` 的 `server.port` 为准)。
|
|
@@ -147,9 +160,9 @@
|
|
|
147
160
|
src/<模块>/api/<路径...>/api.ts → /api/<模块>/<路径...>
|
|
148
161
|
```
|
|
149
162
|
|
|
150
|
-
`src/
|
|
163
|
+
`src/app/api/web/hello/api.ts` → `GET|POST /api/web/hello`。
|
|
151
164
|
|
|
152
|
-
注意中间的 `api` 是**布局分隔符,不是路由段**(`src/
|
|
165
|
+
注意中间的 `api` 是**布局分隔符,不是路由段**(`src/app/api/web/hello` ↔ `/api/web/hello`,
|
|
153
166
|
不是 `/api/web/api/hello`)——`ojm` 的契约扫描按「模块的 api 侧目录」逐模块进行,
|
|
154
167
|
不会把 `src` 整棵当扫描根(历史布局同理:`api/src/web/hello/api.ts`)。
|
|
155
168
|
|
|
@@ -210,13 +223,14 @@ src/<模块>/api/<路径...>/api.ts → /api/<模块>/<路径...>
|
|
|
210
223
|
|
|
211
224
|
| 文件位置 | 到 `_platform/_shared` 的写法 |
|
|
212
225
|
|---|---|
|
|
213
|
-
| `src/
|
|
214
|
-
| `src/
|
|
215
|
-
| `src/
|
|
216
|
-
| `src/
|
|
217
|
-
| `src/
|
|
218
|
-
|
|
219
|
-
> 历史布局把 `src/<m>/api` 换成 `api/src/<m>`
|
|
226
|
+
| `src/app/api/system/<x>/api.ts` | `../../_platform/_shared/...` |
|
|
227
|
+
| `src/app/api/system/<x>/<y>/api.ts` | `../../../_platform/_shared/...` |
|
|
228
|
+
| `src/app/api/auth/<z>/api.ts` | `../../_platform/_shared/...` |
|
|
229
|
+
| `src/app/api/_platform/<x>/api.ts` | `../_shared/...` |
|
|
230
|
+
| `src/app/api/_platform/<x>/<y>/api.ts` | `../../_shared/...` |
|
|
231
|
+
|
|
232
|
+
> 历史布局把 `src/<m>/api` 换成 `api/src/<m>` 后同构,深度不变;内置模块收进
|
|
233
|
+
> `src/app/api/<m>` 后比原 `src/<m>/api` **少一层**(上表已按新布局校准)。
|
|
220
234
|
3. **模块级可变状态不跨请求存活。** oj **每请求重建 handler 模块实例**(实测:模块级
|
|
221
235
|
计数器连打三次恒为初值)——`let` / `Map` / `Set` 级状态一律视为**单请求作用域**。
|
|
222
236
|
需要跨请求的状态(限速、节流、去重窗口、判权缓存)**必须落库**或走 oj 持久面。
|
|
@@ -280,7 +294,7 @@ src/<模块名>/web/
|
|
|
280
294
|
> 就是忘了登记,结果模块安静地没加载、按钮安静地灰着,**全程没有一个报错**。
|
|
281
295
|
|
|
282
296
|
**建档顺序很重要**(有接口的模块):先写 handler 的 `.schema` → 跑 `oj openapi` + `ojm api` → 再写 `entry.ts`。
|
|
283
|
-
生成前 `import { createXxxClient } from "./__client
|
|
297
|
+
生成前 `import { createXxxClient } from "./__client"` 必然 ts 报红,
|
|
284
298
|
那不是你写错了,是产物还没生成。完整流水线见 §3.4。
|
|
285
299
|
|
|
286
300
|
```ts
|
|
@@ -289,14 +303,14 @@ export default {
|
|
|
289
303
|
baseUrl: "", // 模块产物 URL 前缀(可填 CDN 绝对域);与站点部署位置无关
|
|
290
304
|
// siteBase: "/oj", // 站点挂在 https://host/oj/ 下时才需要(默认根路径),见 §13 的路径空间一节
|
|
291
305
|
modules: [
|
|
292
|
-
// home
|
|
293
|
-
//
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
// 缺它 `/login` 无路由可跳(登出会落空)。
|
|
297
|
-
{ name: "login", entry: "src/login/web/entry.ts", enabled: true },
|
|
306
|
+
// 框架内置页面聚合为一个**多根模块**(/home /login /personal-center
|
|
307
|
+
// /notification /system):一个 entry 多个根,各根按 handle.order 排菜单。
|
|
308
|
+
// 缺 /home 会让登录回跳 / 点 logo 落错误边界;缺 /login 则登出无路由可跳。
|
|
309
|
+
{ name: "app", entry: "src/app/web/entry.ts", enabled: true },
|
|
298
310
|
{ name: "books", entry: "src/books/web/entry.ts", enabled: true },
|
|
299
311
|
],
|
|
312
|
+
// 多根聚合模块的 client 归属(生成物落 src/app/web/<api 前缀>/__client):
|
|
313
|
+
// clientOwners: { home: "app", "personal-center": "app", notification: "app", system: "app" },
|
|
300
314
|
};
|
|
301
315
|
```
|
|
302
316
|
|
|
@@ -309,7 +323,7 @@ import { BookOutlined } from "@ant-design/icons";
|
|
|
309
323
|
import { defineModule } from "@oj-module/runtime";
|
|
310
324
|
import { createElement, lazy } from "react";
|
|
311
325
|
|
|
312
|
-
import { createBooksClient } from "./__client
|
|
326
|
+
import { createBooksClient } from "./__client";
|
|
313
327
|
|
|
314
328
|
const Books = lazy(() => import("./pages/index"));
|
|
315
329
|
|
|
@@ -351,7 +365,7 @@ export default defineModule({
|
|
|
351
365
|
`defineModule` 字段:`name`(= 目录名)、`description`、`version`、
|
|
352
366
|
`peerRuntime`、`routes`、`i18n?`、`lifecycle?`。
|
|
353
367
|
|
|
354
|
-
> 页面组件用**静态 import 或 `lazy()` 都行**:模板自带模块(`src/
|
|
368
|
+
> 页面组件用**静态 import 或 `lazy()` 都行**:模板自带模块(`src/app/web/home/entry.ts` 等)
|
|
355
369
|
> 用静态 import,多页面模块可对非首屏页面用 `lazy(() => import("./pages/x"))` 做代码分割。
|
|
356
370
|
> 单个页面就用静态 import,少一层间接。
|
|
357
371
|
|
|
@@ -476,7 +490,7 @@ const roles = useUserStore(s => s.roles);
|
|
|
476
490
|
|
|
477
491
|
**写(改密/改角色/改设置)→ 走契约 API client,不碰框架内部**
|
|
478
492
|
|
|
479
|
-
- 框架已把 `auth/*`、`system/*`、`web/*` 等端点经 OpenAPI 契约定义,并从 `@oj-module/runtime` 导出类型化 fetch(如 `api/rbac` 的 `fetchChangePassword`、`fetchRbacUpdateUserRoles`,`api/user` 的 `fetchUserInfo`)。要调框架内置写接口,直接复用这些导出。**但更推荐模块 own 自己的契约**:在**自己模块的 handler 动词**上写 `.schema`,跑 `oj openapi` + `ojm api` 生成 `src/<模块>/web/__client/
|
|
493
|
+
- 框架已把 `auth/*`、`system/*`、`web/*` 等端点经 OpenAPI 契约定义,并从 `@oj-module/runtime` 导出类型化 fetch(如 `api/rbac` 的 `fetchChangePassword`、`fetchRbacUpdateUserRoles`,`api/user` 的 `fetchUserInfo`)。要调框架内置写接口,直接复用这些导出。**但更推荐模块 own 自己的契约**:在**自己模块的 handler 动词**上写 `.schema`,跑 `oj openapi` + `ojm api` 生成 `src/<模块>/web/__client/index.ts`,模块经自己的 client 调框架端点——接口由契约(versioned OpenAPI)对齐,而非把框架源文件打进来。
|
|
480
494
|
- 底层都走同一个 `request` 单例 + 契约 URL;模块侧请求被 `defineModule` 的 `ctx` 收敛到登记过的 `apiPrefix`,越界 / `../` 穿越 / 逐请求 prefix 覆盖均被拒(请求边界见 §2.6 + `tests/runtime/scoped-request.test.ts`)。
|
|
481
495
|
|
|
482
496
|
**场景:模块记录自己的操作日志(写端)**
|
|
@@ -530,7 +544,7 @@ oj openapi -c config.yaml --module-root src -b /api -o openapi.json → OpenA
|
|
|
530
544
|
ojm api → irFromOpenapi → emitZodFromJsonSchema / emitClient
|
|
531
545
|
│
|
|
532
546
|
▼
|
|
533
|
-
src/<模块>/web/__client/{
|
|
547
|
+
src/<模块>/web/__client/{index.ts, api.schemas.ts}(+ stub / mock)
|
|
534
548
|
```
|
|
535
549
|
|
|
536
550
|
`openapi.json` 即权威文档源,`ojm api --docs` 直接聚合它渲染文档站。`.schema` / `.response`
|
|
@@ -549,7 +563,7 @@ src/<模块>/web/__client/{api.ts, api.schemas.ts}(+ stub / mock)
|
|
|
549
563
|
|
|
550
564
|
| 工程形态 | 权威源 | 产物流向 |
|
|
551
565
|
|---|---|---|
|
|
552
|
-
| **module-root**(`ojm init` 产物,推荐) | 后端 `src/<模块>/api/**/api.ts` 上的 `.schema` | `src/<模块>/web/__client/{
|
|
566
|
+
| **module-root**(`ojm init` 产物,推荐) | 后端 `src/<模块>/api/**/api.ts` 上的 `.schema` | `src/<模块>/web/__client/{index.ts, api.schemas.ts}`(由 `ojm api` 消费 `openapi.json` 生成);stub → `src/<模块>/api/<路径>/api.ts`(未实现 handler 时补骨架) |
|
|
553
567
|
| **uni-dev**(历史布局) | 后端 `api/src/<模块>/**/api.ts` 上的 `.schema` | `web/src/<模块>/__client/…`;stub → `api/src/<模块>/<路径>/api.ts` |
|
|
554
568
|
| 纯前端 | 仅消费他人 `openapi.json` | `__client/` 全部落前端侧,无 stub |
|
|
555
569
|
|
|
@@ -562,7 +576,7 @@ src/<模块>/web/__client/{api.ts, api.schemas.ts}(+ stub / mock)
|
|
|
562
576
|
以目录名作 `MODULE_PREFIX`(跨模块认领护栏的对比基准),只改一边会让 dev 控制台报「正在认领
|
|
563
577
|
别人的 client」。与旧 `apiPrefix` 字面相等约束同源,只是不再手写字段。
|
|
564
578
|
|
|
565
|
-
> 工程里有两个 "web":前端侧 `src/web/web/`,与后端 `src/
|
|
579
|
+
> 工程里有两个 "web":前端侧 `src/app/web/web/`,与后端 `src/app/api/web/`(提供 user-info 等内置接口)。
|
|
566
580
|
> 豁免清单里的 `"modules": ["web"]` 指的是**后者**。
|
|
567
581
|
|
|
568
582
|
### 3.3 handler `.schema`(入参)+ `.response`(出参)写法(JSON Schema 子集)
|
|
@@ -629,7 +643,7 @@ minLength / maxLength / pattern / enum / nullable / minItems / maxItems / defaul
|
|
|
629
643
|
**上传文件(multipart/form-data)**:当前 `ojm api` 的 openapi 流水线**不生成** form 通道的
|
|
630
644
|
前端 client(文件部件是二进制,不进 JSON Schema)。惯例走 antd `Upload` **直连**后端
|
|
631
645
|
(不经 JSON client),handler 用 `http.files` / `http.file(i)` 读字节,仅平级声明
|
|
632
|
-
`.response`(见 `src/personal-center/
|
|
646
|
+
`.response`(见 `src/app/api/personal-center/upload/api.ts` 范本)。
|
|
633
647
|
|
|
634
648
|
**不要手写 `content-type`**:运行时(ky)见到 `FormData` 会自己补 `multipart/form-data;
|
|
635
649
|
boundary=…`,手写反而丢 boundary。
|
|
@@ -639,7 +653,7 @@ boundary=…`,手写反而丢 boundary。
|
|
|
639
653
|
|
|
640
654
|
### 3.4 生成物使用契约
|
|
641
655
|
|
|
642
|
-
生成的 `src/<模块>/web/__client/
|
|
656
|
+
生成的 `src/<模块>/web/__client/index.ts` 导出:
|
|
643
657
|
|
|
644
658
|
- 裸请求函数(`fetchNotifications`、`markRead`…)——**直接 import 调用**;
|
|
645
659
|
- `API_PREFIX` 常量——契约前缀的唯一真源;
|
|
@@ -665,7 +679,7 @@ const booksClient = createBooksClient(ctx);
|
|
|
665
679
|
> 目录却不在」只有一种解释:那个模块的 `__client/` 从没生成过(模板里的
|
|
666
680
|
> personal-center 上传走 antd Upload 直连,不经过 JSON client,故没人 import 它的 `__client`)。
|
|
667
681
|
> 你在自己工程跑一次 `ojm api`,它就会出现。
|
|
668
|
-
> 判断某模块**要不要用** client 的标准只有一个:代码里有没有人 import `./__client
|
|
682
|
+
> 判断某模块**要不要用** client 的标准只有一个:代码里有没有人 import `./__client`。
|
|
669
683
|
> 有就在 onInit 里 `create<Module>Client(ctx)` 一次;没有就什么都不写,
|
|
670
684
|
> 生成了但没人 import 也不报错。
|
|
671
685
|
|
|
@@ -676,7 +690,7 @@ const booksClient = createBooksClient(ctx);
|
|
|
676
690
|
|
|
677
691
|
### 3.5 生成物勿手改
|
|
678
692
|
|
|
679
|
-
`__client/
|
|
693
|
+
`__client/index.ts`、`__client/api.schemas.ts` **重跑即覆盖**(eslint 已忽略这些路径)。
|
|
680
694
|
改需求 = 改 handler 的 `.schema`(或路由),重跑 `oj openapi` + `ojm api`。
|
|
681
695
|
|
|
682
696
|
**唯一例外:stub。** `src/<模块>/api/**/api.ts` 的 stub 文件头带指纹
|
|
@@ -825,7 +839,7 @@ export default { get };
|
|
|
825
839
|
3. **生成规范**:`oj openapi -c config.yaml --module-root src -b /api -o openapi.json`
|
|
826
840
|
(历史布局用 `-d api/src`;项目级单文件、含全部模块;组级 `.schema`/`.response` 已由
|
|
827
841
|
oj 内省期合并)。
|
|
828
|
-
4. **生成 client**:`ojm api` → `src/<模块>/web/__client/{
|
|
842
|
+
4. **生成 client**:`ojm api` → `src/<模块>/web/__client/{index.ts, api.schemas.ts}`
|
|
829
843
|
(历史布局 `web/src/<模块>/__client/`)。
|
|
830
844
|
5. **改前端消费点**:函数名从旧人类命名(`fetchNotifications`)改为
|
|
831
845
|
`camelCase(operationId)`(`notificationNotificationsGet`)——用「逐字节比对」识别,
|
|
@@ -897,9 +911,9 @@ Provider 就是:「这块别用框架自带的了,换成我的实现。」
|
|
|
897
911
|
|
|
898
912
|
### 4.4 案例:接管通知功能(标准四文件)
|
|
899
913
|
|
|
900
|
-
**① handler `.schema`**(`src/
|
|
914
|
+
**① handler `.schema`**(`src/app/api/notification/api.ts` 上各动词挂的 `.schema`)——见 §3.3 与 §3.4。
|
|
901
915
|
|
|
902
|
-
**② handler**(`src/
|
|
916
|
+
**② handler**(`src/app/api/notification/api.ts`,模块根嵌套组——组名 = 路径段,oj v0.1.64):
|
|
903
917
|
|
|
904
918
|
```ts
|
|
905
919
|
const notifications = {
|
|
@@ -932,7 +946,7 @@ export default { notifications, /* push, "push-broadcast" 等同理 */ };
|
|
|
932
946
|
```ts
|
|
933
947
|
import type { NotificationsApiProvider } from "@oj-module/runtime";
|
|
934
948
|
import { defineModule } from "@oj-module/runtime";
|
|
935
|
-
import { createNotificationClient } from "./__client
|
|
949
|
+
import { createNotificationClient } from "./__client";
|
|
936
950
|
|
|
937
951
|
export default defineModule({
|
|
938
952
|
name: "notification",
|
|
@@ -957,7 +971,7 @@ export default defineModule({
|
|
|
957
971
|
**⑤ 跨模块发射原语(任何后端模块都能推通知)**
|
|
958
972
|
|
|
959
973
|
通知不是「只有通知模块自己用」的功能——下单、审核、@我等其它模块也需要给某个人推一条提醒。
|
|
960
|
-
把发射逻辑收敛成 `src/
|
|
974
|
+
把发射逻辑收敛成 `src/app/api/notification/_shared/notify.ts` 里的纯函数原语,其它模块**别名导入
|
|
961
975
|
直接调用,无需走 HTTP**:
|
|
962
976
|
|
|
963
977
|
```ts
|
|
@@ -990,7 +1004,7 @@ export default {
|
|
|
990
1004
|
不带 `node_modules`——dev 能跑、release 或装到站点后必炸。后端共享代码放本模块
|
|
991
1005
|
`api/_shared/`,跨模块走 `#/<模块名>/_shared/...` 别名;类型与 runtime 的
|
|
992
1006
|
`@oj-module/runtime/notify` 保持同形即可(共享契约类型单一真源在 runtime)。
|
|
993
|
-
- **`_shared` 必须嵌进所属模块的 api 侧**(`src/
|
|
1007
|
+
- **`_shared` 必须嵌进所属模块的 api 侧**(`src/app/api/notification/_shared/`):
|
|
994
1008
|
`src/` 下每个目录都得是模块(`api/manifest.yaml` 或 `web/entry.ts`),`src/_shared` 会被
|
|
995
1009
|
`ojm dev/build` 判成坏目录;放进模块内则是「无 `api.ts` 的纯工具目录」,不产生路由。
|
|
996
1010
|
(历史布局同理:`api/src/notification/_shared/`,而 `api/src/_shared` 会被 oj 当成缺
|
|
@@ -1349,7 +1363,7 @@ useEffect(() => {
|
|
|
1349
1363
|
复用展示不同内容(列表页/`index` 路由),离开时必须 `resetTableTitle(tabKey)`,否则再进来看到
|
|
1350
1364
|
上一个内容的标题。
|
|
1351
1365
|
|
|
1352
|
-
推荐形态(照 `src/
|
|
1366
|
+
推荐形态(照 `src/app/web/home/pages/`、`src/app/web/personal-center/pages/my-profile/` 抄):
|
|
1353
1367
|
`BasicContent` 包一层 → 搜索区 + `BasicTable` → 新增/编辑用 antd `Modal` + `Form`
|
|
1354
1368
|
→ 提交后重新拉列表。数据来自 §3.4 的生成函数,**页面里直接 import 裸函数调用**。
|
|
1355
1369
|
|
|
@@ -1402,7 +1416,7 @@ useEffect(() => {
|
|
|
1402
1416
|
`useEffect` 重新注册(上面的形态天然满足)。
|
|
1403
1417
|
- 同一 key 关闭流程进行中再次触发被忽略(per-key in-flight 去重),双击不会弹两个框。
|
|
1404
1418
|
|
|
1405
|
-
|
|
1419
|
+
可运行示例见模板工程 `src/app/web/*/pages/` 下的页面(layouts/权限门同源);
|
|
1406
1420
|
模板侧对应 `src/<模块>/web/pages/`,表单 dirty → 关页签弹确认)。
|
|
1407
1421
|
|
|
1408
1422
|
### 6.5 两份 tsconfig:`typecheck` 与 `typecheck:api`
|
|
@@ -1556,7 +1570,7 @@ export default async (path: string, html: string) => {
|
|
|
1556
1570
|
| 登录 401 且 msg 不是 `invalid credentials` | `/auth/*` 不在 `anonymous_paths` | 加进工程根 `config.yaml`,重启 |
|
|
1557
1571
|
| 登出/回登录页空白或 404 | 工程缺 `login` 模块(shell 无内置登录回落) | 在 `web.config.ts` 保留 `login` |
|
|
1558
1572
|
| 登录后 / 点 logo 落 React Router 错误边界 | 工程缺 `home` 模块 | 在 `web.config.ts` 保留 `home`(尽量靠前) |
|
|
1559
|
-
| 通知铃 404 `no route matched`(`/api/notifications`) | 前端 `notification` 模块没加载 / 没注册 `notificationsApi`,回落到内置只读兜底——而脚手架不再提供 root 级 `/api/notifications` 模块 | 保留 `web.config.ts` 的 `notification` 登记,onInit 里注册四方法(模板 `src/
|
|
1573
|
+
| 通知铃 404 `no route matched`(`/api/notifications`) | 前端 `notification` 模块没加载 / 没注册 `notificationsApi`,回落到内置只读兜底——而脚手架不再提供 root 级 `/api/notifications` 模块 | 保留 `web.config.ts` 的 `notification` 登记,onInit 里注册四方法(模板 `src/app/web/notification/entry.ts`);后端由 `src/app/api/notification` 提供 `/api/notification/*` |
|
|
1560
1574
|
| `@types/react` / `typescript` 装成 `*` | 宿主 versions.json 未列 | 安装后按实际版本钉死(`*` 不可复现) |
|
|
1561
1575
|
| 迁移账本落后、服务拒绝启动 | release `verify` 闸 | 先 `ojm migrate -d api/dist`(透传自动补 `-c`) |
|
|
1562
1576
|
| `seed.sql` 没生效 / 语法错 | 按 `;` 朴素切分,注释里有分号 | 去掉注释里的分号字面量 |
|
|
@@ -1579,15 +1593,15 @@ export default async (path: string, html: string) => {
|
|
|
1579
1593
|
|
|
1580
1594
|
| 想抄什么 | 抄这里 |
|
|
1581
1595
|
|---|---|
|
|
1582
|
-
| 带页面的模块(路由 / i18n / order) | `src/
|
|
1583
|
-
| 接管通知 provider(四方法全量) | `src/
|
|
1584
|
-
| 上传 provider(action + headers 方法) | `src/personal-center/
|
|
1585
|
-
| 无页面模块(`routes: []`) | 同 `src/
|
|
1586
|
-
| handler `.schema` 入参 + 平级 `.response` 出参写法(GET + POST、参数段) | `src/
|
|
1587
|
-
| handler 写法(嵌套组列表 / 写入 / 403 归属) | `src/
|
|
1588
|
-
| 统一异常出口 `respond()`(service 抛 `Error & {status}` → 信封 `json.fail`) | 模板 `src/
|
|
1589
|
-
| 生成物长什么样(含工厂) | `src/
|
|
1590
|
-
| 建表三件套 | `src/
|
|
1596
|
+
| 带页面的模块(路由 / i18n / order) | `src/app/web/home/entry.ts`、`src/app/web/entry.ts` |
|
|
1597
|
+
| 接管通知 provider(四方法全量) | `src/app/web/notification/entry.ts` |
|
|
1598
|
+
| 上传 provider(action + headers 方法) | `src/app/web/personal-center/entry.ts` |
|
|
1599
|
+
| 无页面模块(`routes: []`) | 同 `src/app/web/notification/entry.ts` |
|
|
1600
|
+
| handler `.schema` 入参 + 平级 `.response` 出参写法(GET + POST、参数段) | `src/app/api/notification/api.ts` |
|
|
1601
|
+
| handler 写法(嵌套组列表 / 写入 / 403 归属) | `src/app/api/notification/api.ts`(模块根单文件,`read`/`read-all`/`clear` 子组) |
|
|
1602
|
+
| 统一异常出口 `respond()`(service 抛 `Error & {status}` → 信封 `json.fail`) | 模板 `src/app/api/_platform/_shared/respond.ts` + 各 handler 用法(§1.7-1) |
|
|
1603
|
+
| 生成物长什么样(含工厂) | `src/app/web/notification/__client/index.ts` |
|
|
1604
|
+
| 建表三件套 | `src/app/api/notification/{schema.yaml,manifest.yaml,migrations/}` |
|
|
1591
1605
|
| 全局类型(`window.$message` 等) | 工程根 `env.d.ts` |
|
|
1592
1606
|
|
|
1593
1607
|
另有:
|
|
@@ -2009,7 +2023,7 @@ ojm trust revoke <key_id> # 吊销:阻断新装/升级,不动已装
|
|
|
2009
2023
|
|
|
2010
2024
|
### 12.3 第一方 bootstrap key(首装免 TOFU)
|
|
2011
2025
|
|
|
2012
|
-
平台模板内置第一方发布方**公钥**(`src/
|
|
2026
|
+
平台模板内置第一方发布方**公钥**(`src/app/api/_platform/market/bootstrap.ts` 的
|
|
2013
2027
|
`BOOTSTRAP_KEY`):信任库文件首次初始化时自动播种一次——**market 方案首装免 TOFU 直接过
|
|
2014
2028
|
验签**。发布时序(平台发行人执行):`ojm keygen` → 签 oj-market 首发包 → 公钥 + key_id
|
|
2015
2029
|
回填 `BOOTSTRAP_KEY` 随模板发布。播种纪律:库已初始化(含空库)不播种——运维 `revoke`
|
|
@@ -2225,11 +2239,11 @@ async function post() {
|
|
|
2225
2239
|
所以前端消费组织架构的唯一正路是「借壳」:
|
|
2226
2240
|
|
|
2227
2241
|
1. 在**自己模块**的 handler 里调 `resolveReadFace` + 需要的那几个读函数(§14.2);
|
|
2228
|
-
2. 跑 `oj openapi` + `ojm api`,生成本模块的 `src/<模块>/web/__client/
|
|
2242
|
+
2. 跑 `oj openapi` + `ojm api`,生成本模块的 `src/<模块>/web/__client/index.ts`;
|
|
2229
2243
|
3. 页面用本模块 client 调。
|
|
2230
2244
|
|
|
2231
2245
|
多一层 handler 换来三件事:接口形状由**你自己的契约**决定(只出页面要的字段)、框架内部实现不进你的
|
|
2232
|
-
bundle、契约变更走你自己的版本号。仓库里 `src/
|
|
2246
|
+
bundle、契约变更走你自己的版本号。仓库里 `src/app/web/system/pages/departments` 的组织架构页就是这条路的
|
|
2233
2247
|
另一个方向(它消费的是管理端 `/api/system/departments`,见 §14.4)。
|
|
2234
2248
|
|
|
2235
2249
|
### 14.4 管理端 CRUD:只有「管组织架构」的模块才该碰
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oj-module/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.56",
|
|
5
5
|
"description": "React Antd 模块工程命令行工具(含预构建宿主 shell-dist)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
],
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@faker-js/faker": "^10.4.0",
|
|
52
|
-
"@oj-module/runtime": "0.2.
|
|
52
|
+
"@oj-module/runtime": "0.2.56",
|
|
53
53
|
"@redocly/cli": "^2.51.0",
|
|
54
54
|
"@tailwindcss/vite": "^4.3.0",
|
|
55
55
|
"cac": "^6.7.14",
|
|
@@ -276,9 +276,30 @@ function buildShellChildren() {
|
|
|
276
276
|
* 路由表;随后 SPA 登录(不刷新页面)菜单回来了,但 /system/* 仍不在路由表里,
|
|
277
277
|
* 直达恒落 catch-all 404「返回首页」,须硬刷新才恢复。
|
|
278
278
|
*/
|
|
279
|
+
/**
|
|
280
|
+
* 宿主路由根(Boot 建表与登录后重登记共用同一形状,children 按当前角色快照生成)。
|
|
281
|
+
*
|
|
282
|
+
* 路由根只渲染容器(Outlet),整站 chrome 由模块自带的 ContainerLayout 提供;
|
|
283
|
+
* 宿主不叠加自己的 Layout chrome,避免「宿主侧栏 + 模块侧栏」双层嵌套。
|
|
284
|
+
* element 里同时挂 LayoutEffects(暗色类/动态标题/NProgress,与 App 链同源,偏差 4)
|
|
285
|
+
* 与页面级 ErrorBoundary(模块路由不经 runtime LayoutRoot,缺它时渲染抛错整页白屏)。
|
|
286
|
+
*/
|
|
287
|
+
function buildShellRoot() {
|
|
288
|
+
return {
|
|
289
|
+
path: "/",
|
|
290
|
+
id: SHELL_ROOT_ID,
|
|
291
|
+
element: /* @__PURE__ */ jsxs(ErrorBoundary, {
|
|
292
|
+
FallbackComponent: PageError,
|
|
293
|
+
children: [/* @__PURE__ */ jsx(LayoutEffects, {}), /* @__PURE__ */ jsx(Outlet, {})]
|
|
294
|
+
}),
|
|
295
|
+
children: buildShellChildren()
|
|
296
|
+
};
|
|
297
|
+
}
|
|
279
298
|
function repatchShellRoutes() {
|
|
280
299
|
if (!shellRouter) return;
|
|
281
|
-
shellRouter.
|
|
300
|
+
const internal = shellRouter._internalSetRoutes;
|
|
301
|
+
if (internal) internal.call(shellRouter, [buildShellRoot()]);
|
|
302
|
+
else shellRouter.patchRoutes(SHELL_ROOT_ID, buildShellChildren());
|
|
282
303
|
const { pathname, search } = shellRouter.state.location;
|
|
283
304
|
shellRouter.navigate(`${pathname}${search}`, { replace: true }).catch(() => {});
|
|
284
305
|
}
|
|
@@ -313,7 +334,7 @@ function Boot() {
|
|
|
313
334
|
(async () => {
|
|
314
335
|
try {
|
|
315
336
|
if (typeof navigator !== "undefined" && /wxwork/i.test(navigator.userAgent) && location.pathname.endsWith("/login") && !new URLSearchParams(location.search).get("wecom_ticket")) {
|
|
316
|
-
window.location.assign(resolveApiBase()
|
|
337
|
+
window.location.assign(`${resolveApiBase()}/auth/wecom/login`);
|
|
317
338
|
return;
|
|
318
339
|
}
|
|
319
340
|
ensureI18n();
|
|
@@ -348,15 +369,7 @@ function Boot() {
|
|
|
348
369
|
await hydrateUser();
|
|
349
370
|
await loadAll(manifest);
|
|
350
371
|
if (cancelled) return;
|
|
351
|
-
shellRouter = createBrowserRouter([{
|
|
352
|
-
path: "/",
|
|
353
|
-
id: SHELL_ROOT_ID,
|
|
354
|
-
element: /* @__PURE__ */ jsxs(ErrorBoundary, {
|
|
355
|
-
FallbackComponent: PageError,
|
|
356
|
-
children: [/* @__PURE__ */ jsx(LayoutEffects, {}), /* @__PURE__ */ jsx(Outlet, {})]
|
|
357
|
-
}),
|
|
358
|
-
children: buildShellChildren()
|
|
359
|
-
}], {
|
|
372
|
+
shellRouter = createBrowserRouter([buildShellRoot()], {
|
|
360
373
|
/**
|
|
361
374
|
* 子路径部署(oj `mounts: [{ prefix: /oj, web: dist, spa: true }]`):
|
|
362
375
|
* 不设 basename 时 react-router 看到的是含前缀的 `/oj/wiki/...`,
|
|
@@ -304,10 +304,10 @@ function getAppInfo() {
|
|
|
304
304
|
return {
|
|
305
305
|
"pkg": {
|
|
306
306
|
"name": "@oj-module/runtime",
|
|
307
|
-
"version": "0.2.
|
|
307
|
+
"version": "0.2.56",
|
|
308
308
|
"license": "MIT"
|
|
309
309
|
},
|
|
310
|
-
"lastBuildTime": "2026-11-02 15:
|
|
310
|
+
"lastBuildTime": "2026-11-02 23:15:22"
|
|
311
311
|
};
|
|
312
312
|
}
|
|
313
313
|
var init_get_app_info = __esmMin((() => {}));
|
|
@@ -7500,11 +7500,14 @@ function topologicalSort(entries, definitions) {
|
|
|
7500
7500
|
return sorted;
|
|
7501
7501
|
}
|
|
7502
7502
|
async function mergeI18nResources(definition) {
|
|
7503
|
-
if (
|
|
7504
|
-
for (const [locale, loader] of Object.entries(definition.i18n)) {
|
|
7503
|
+
if (definition.i18n) for (const [locale, loader] of Object.entries(definition.i18n)) {
|
|
7505
7504
|
const resources = await loader();
|
|
7506
7505
|
i18next.addResourceBundle(locale, definition.name, resources.default || resources);
|
|
7507
7506
|
}
|
|
7507
|
+
for (const [namespace, bundle] of Object.entries(definition.i18nNamespaces ?? {})) for (const [locale, loader] of Object.entries(bundle)) {
|
|
7508
|
+
const resources = await loader();
|
|
7509
|
+
i18next.addResourceBundle(locale, namespace, resources.default || resources);
|
|
7510
|
+
}
|
|
7508
7511
|
}
|
|
7509
7512
|
async function loadAll(manifest) {
|
|
7510
7513
|
const enabledEntries = manifest.modules.filter((m) => m.enabled !== false);
|
package/shell-dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<link rel="icon" href="/favicon.ico" />
|
|
7
7
|
<title>React Antd Admin · Shell</title>
|
|
8
|
-
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' https://modules.cdn.example.com 'wasm-unsafe-eval' 'nonce-
|
|
9
|
-
<script type="importmap" nonce="
|
|
8
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' https://modules.cdn.example.com 'wasm-unsafe-eval' 'nonce-oa6e5O+MEhBiegxvbtPppw=='; script-src-attr 'none'; connect-src 'self' data: https://modules.cdn.example.com; style-src 'self' https://modules.cdn.example.com 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-src https://ant.design https://react.dev https://condorheroblog.github.io; form-action 'none'; base-uri 'none'; object-src 'none'; upgrade-insecure-requests" />
|
|
9
|
+
<script type="importmap" nonce="oa6e5O+MEhBiegxvbtPppw==">{
|
|
10
10
|
"imports": {
|
|
11
11
|
"react": "/assets/react.js",
|
|
12
12
|
"react/jsx-runtime": "/assets/jsx-runtime.js",
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
.boot-skeleton:after{content:"";width:18px;height:18px;margin-left:8px;border:2px solid #d9d9d9;border-top-color:#1677ff;border-radius:50%;animation:bs-spin .8s linear infinite}
|
|
131
131
|
@keyframes bs-spin{to{transform:rotate(360deg)}}
|
|
132
132
|
</style>
|
|
133
|
-
<script nonce="
|
|
133
|
+
<script nonce="oa6e5O+MEhBiegxvbtPppw==" data-ojm-entry="/assets/index-CImwskAz.js">(function(){try{var ua=(navigator.userAgent||"").toLowerCase(),p=location.pathname;while(p.length>1&&p.charAt(p.length-1)==="/")p=p.slice(0,-1);if(ua.indexOf("wxwork")>=0&&p.slice(-6)==="/login"&&!new URLSearchParams(location.search).get("wecom_ticket")){var a=(typeof window.__OJ_API_BASE__==="string"&&window.__OJ_API_BASE__.trim())||"/api";while(a.length>1&&a.charAt(a.length-1)==="/")a=a.slice(0,-1);location.replace(a+"/auth/wecom/login");return}}catch(e){}var s=document.createElement("script");s.type="module";s.src=document.currentScript.getAttribute("data-ojm-entry");document.head.appendChild(s);})();</script>
|
|
134
134
|
</head>
|
|
135
135
|
<body>
|
|
136
136
|
<div id="root"><div class="boot-skeleton">加载中…</div></div>
|
package/shell-dist/versions.json
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
"react-router": "7.18.2",
|
|
8
8
|
"react-router/dom": "7.18.2",
|
|
9
9
|
"@tanstack/react-query": "5.102.8",
|
|
10
|
-
"@oj-module/runtime": "0.2.
|
|
11
|
-
"@oj-module/runtime/contract": "0.2.
|
|
12
|
-
"@oj-module/runtime/contract/errors": "0.2.
|
|
10
|
+
"@oj-module/runtime": "0.2.56",
|
|
11
|
+
"@oj-module/runtime/contract": "0.2.56",
|
|
12
|
+
"@oj-module/runtime/contract/errors": "0.2.56",
|
|
13
13
|
"@plane/kit": "0.1.20",
|
|
14
14
|
"@plane/editor": "0.1.20",
|
|
15
15
|
"antd": "6.6.1",
|
|
@@ -111,7 +111,7 @@ describe("resolveLayout / 历史布局", () => {
|
|
|
111
111
|
it("web/<name>/entry.ts 识别为 flat,web 产物落 build/(框架根仓约定不变),api 产物归 dist/api", () => {
|
|
112
112
|
const root = newRoot();
|
|
113
113
|
writeFile(root, "web/home/entry.ts", "export default {};\n");
|
|
114
|
-
writeFile(root, "web/home/__client/
|
|
114
|
+
writeFile(root, "web/home/__client/index.ts", "export {};\n");
|
|
115
115
|
|
|
116
116
|
const layout = resolveLayout(root);
|
|
117
117
|
expect(layout.kind).toBe("flat");
|
|
@@ -177,3 +177,32 @@ describe("路径取值函数", () => {
|
|
|
177
177
|
expect(webSrcOf(layout, "wiki")).toBe(join(layout.modulesSrc, "wiki", "web"));
|
|
178
178
|
});
|
|
179
179
|
});
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* 聚合容器:src/app 同时有 web 侧(多根前端模块)与 api 下多个后端模块。
|
|
183
|
+
* 模块身份取 api 下那层目录名(= URL 前缀),与平铺 src/<name>/api 并存且互不干扰。
|
|
184
|
+
*/
|
|
185
|
+
describe("resolveLayout / 聚合容器(src/app/api/<name>)", () => {
|
|
186
|
+
it("api 子目录各自成模块,web 侧仍是 app 模块", () => {
|
|
187
|
+
const root = newRoot();
|
|
188
|
+
writeFile(root, "src/app/api/system/manifest.yaml", "name: system\nversion: 0.1.0\n");
|
|
189
|
+
writeFile(root, "src/app/api/system/users/api.ts", "export default { get() {} };\n");
|
|
190
|
+
writeFile(root, "src/app/api/notification/manifest.yaml", "name: notification\nversion: 0.1.0\n");
|
|
191
|
+
writeFile(root, "src/app/web/entry.ts", "export default {};\n");
|
|
192
|
+
const layout = resolveLayout(root);
|
|
193
|
+
const mods = scanModules(layout);
|
|
194
|
+
|
|
195
|
+
expect(mods.map(m => m.name)).toEqual(["app", "notification", "system"]);
|
|
196
|
+
expect(mods.find(m => m.name === "system")?.apiSrc).toBe(join(root, "src/app/api/system"));
|
|
197
|
+
expect(mods.find(m => m.name === "system")?.webSrc).toBeUndefined();
|
|
198
|
+
expect(apiSrcOf(layout, "system")).toBe(join(root, "src/app/api/system"));
|
|
199
|
+
expect(webSrcOf(layout, "app")).toBe(join(root, "src/app/web"));
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("同名后端模块重复(旧 src/system/api + 新 src/app/api/system)→ 明确报错", () => {
|
|
203
|
+
const root = newRoot();
|
|
204
|
+
writeFile(root, "src/system/api/manifest.yaml", "name: system\nversion: 0.1.0\n");
|
|
205
|
+
writeFile(root, "src/app/api/system/manifest.yaml", "name: system\nversion: 0.1.0\n");
|
|
206
|
+
expect(() => resolveLayout(root)).toThrowError(/模块名重复:system/);
|
|
207
|
+
});
|
|
208
|
+
});
|