@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
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
/* eslint-disable test/prefer-lowercase-title -- 用例标题带端点路径,保留原样更易检索 */
|
|
15
15
|
import { afterEach, beforeAll, describe, expect, it } from "vitest";
|
|
16
16
|
|
|
17
|
-
import { invalidateUser } from "../src/
|
|
18
|
-
import notification from "../src/
|
|
19
|
-
import notificationDel from "../src/
|
|
20
|
-
import profile from "../src/personal-center/
|
|
21
|
-
import upload from "../src/personal-center/
|
|
17
|
+
import { invalidateUser } from "../src/app/api/_platform/_shared/service/infra/access";
|
|
18
|
+
import notification from "../src/app/api/notification/api";
|
|
19
|
+
import notificationDel from "../src/app/api/notification/del/api";
|
|
20
|
+
import profile from "../src/app/api/personal-center/profile/api";
|
|
21
|
+
import upload from "../src/app/api/personal-center/upload/api";
|
|
22
22
|
import { installDb } from "./fixture";
|
|
23
23
|
|
|
24
24
|
const g = globalThis as any;
|
|
@@ -80,14 +80,15 @@ describe("§5.7-6 读写自动按当前租户过滤", () => {
|
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
it("truncate 只清本租户(A 组表)", async () => {
|
|
83
|
+
// 用 home_pie 而非旧 demo 的 todos:后者已随示例模块删除,迁移里没有这张表。
|
|
83
84
|
asTenant("acme");
|
|
84
|
-
await db.table("
|
|
85
|
+
await db.table("home_pie").insert({ category: "acme-pie", value: 1 }).run();
|
|
85
86
|
asTenant("globex");
|
|
86
|
-
await db.table("
|
|
87
|
+
await db.table("home_pie").insert({ category: "globex-pie", value: 2 }).run();
|
|
87
88
|
|
|
88
89
|
asTenant("acme");
|
|
89
|
-
await db.table("
|
|
90
|
-
expect(rows("SELECT
|
|
90
|
+
await db.table("home_pie").truncate().run();
|
|
91
|
+
expect(rows("SELECT category FROM home_pie")).toEqual([{ category: "globex-pie" }]);
|
|
91
92
|
});
|
|
92
93
|
|
|
93
94
|
it("refresh token 换票:别家的 token_hash 在本租户查不到", async () => {
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
// · 端到端:改名被拒之后,真正的外部成员 JIT 登录拿到的是**新建**的那一行。
|
|
17
17
|
|
|
18
18
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
19
|
-
import { invalidateAllUsers } from "../src/
|
|
20
|
-
import { ensureWecomUser, externalUsername } from "../src/
|
|
21
|
-
import profile from "../src/personal-center/
|
|
19
|
+
import { invalidateAllUsers } from "../src/app/api/_platform/_shared/service/infra/access";
|
|
20
|
+
import { ensureWecomUser, externalUsername } from "../src/app/api/auth/_shared/wecom/login";
|
|
21
|
+
import profile from "../src/app/api/personal-center/profile/api";
|
|
22
22
|
import { installDb } from "./fixture";
|
|
23
23
|
|
|
24
24
|
const g = globalThis as any;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// BDD:/system/users 行级门禁——只有 superadmin 能改 superadmin 账号。
|
|
2
|
+
//
|
|
3
|
+
// 与 tests/rbac-users.spec.ts 的 assertCanManageTarget 用例互为两面:那边钉服务端 403,
|
|
4
|
+
// 这边钉「前端不给会 403 的按钮」(置灰)。两处都测同一个判定口径,改名必须一起改。
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
|
|
7
|
+
import { canManageUser, PLATFORM_ROLE } from "../src/app/web/system/pages/users/can-manage";
|
|
8
|
+
|
|
9
|
+
describe("用户管理页:superadmin 行对非超管置灰", () => {
|
|
10
|
+
it("admin 看普通用户 → 可操作", () => {
|
|
11
|
+
expect(canManageUser(["admin"], ["editor"])).toBe(true);
|
|
12
|
+
expect(canManageUser(["admin"], [])).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("admin 看 superadmin → 灰(口令/状态/成员资格一律禁用)", () => {
|
|
16
|
+
expect(canManageUser(["admin"], ["superadmin"])).toBe(false);
|
|
17
|
+
// 兼有 admin + editor 也不够:这条红线只认 superadmin
|
|
18
|
+
expect(canManageUser(["admin", "editor", "viewer"], ["superadmin", "editor"])).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("superadmin 看 superadmin → 可操作(红线是限定谁能改,不是禁止改)", () => {
|
|
22
|
+
expect(canManageUser(["superadmin"], ["superadmin"])).toBe(true);
|
|
23
|
+
expect(canManageUser(["superadmin"], ["admin"])).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("不看自己:本人行的禁用由页面另外用 uid 判,别混进来", () => {
|
|
27
|
+
// 空角色 + 空目标 = 可操作,说明本函数不承担 assertNotSelf 的职责
|
|
28
|
+
expect(canManageUser([], [])).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("角色名精确匹配:superadmin.readonly 不是平台超管", () => {
|
|
32
|
+
expect(canManageUser(["admin"], ["superadmin.readonly"])).toBe(true);
|
|
33
|
+
expect(canManageUser(["SuperAdmin"], ["superadmin"])).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("门禁常量与后端保留角色名同值(改名必须两处一起改)", () => {
|
|
37
|
+
expect(PLATFORM_ROLE).toBe("superadmin");
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
import { Buffer } from "node:buffer";
|
|
13
13
|
import { webcrypto } from "node:crypto";
|
|
14
14
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
15
|
-
import { assertSuperAdmin, invalidateAllUsers } from "../src/
|
|
16
|
-
import { listApps, setAppStatus } from "../src/
|
|
17
|
-
import { createWecomClient, runtimeCache, runtimeHttp } from "../src/
|
|
18
|
-
import { readSuiteConfig } from "../src/
|
|
15
|
+
import { assertSuperAdmin, invalidateAllUsers } from "../src/app/api/_platform/_shared/service/infra/access";
|
|
16
|
+
import { listApps, setAppStatus } from "../src/app/api/auth/_shared/wecom/admin";
|
|
17
|
+
import { createWecomClient, runtimeCache, runtimeHttp } from "../src/app/api/auth/_shared/wecom/client";
|
|
18
|
+
import { readSuiteConfig } from "../src/app/api/auth/_shared/wecom/config";
|
|
19
19
|
import { installDb } from "./fixture";
|
|
20
20
|
|
|
21
21
|
const g = globalThis as any;
|
|
@@ -124,11 +124,11 @@ const deps = { store: { encrypt: (p: string) => `gcm(${p})`, decrypt: (b: string
|
|
|
124
124
|
|
|
125
125
|
/** createInstall 的薄包装:省掉动态 import 的样板 */
|
|
126
126
|
async function createInstallForTest(cfg: ReturnType<typeof readSuiteConfig>, client: ReturnType<typeof createWecomClient>, state: string) {
|
|
127
|
-
const { createInstall } = await import("../src/
|
|
127
|
+
const { createInstall } = await import("../src/app/api/auth/_shared/wecom/admin");
|
|
128
128
|
return createInstall(cfg, client, state);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
const api = async () => (await import("../src/
|
|
131
|
+
const api = async () => (await import("../src/app/api/auth/wecom/admin/api")).default;
|
|
132
132
|
|
|
133
133
|
beforeAll(() => {
|
|
134
134
|
dbo = installDb();
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// 4. tasks 缺失 → 安全降级为「不超时」,快速请求正常返回。
|
|
12
12
|
|
|
13
13
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
14
|
-
import { runtimeHttp } from "../src/
|
|
14
|
+
import { runtimeHttp } from "../src/app/api/auth/_shared/wecom/client";
|
|
15
15
|
|
|
16
16
|
const g = globalThis as any;
|
|
17
17
|
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
// deps 全注入:store 用 `enc:` 前后缀假加密(Node 侧 crypto 只读,设计 §9.1),
|
|
11
11
|
// client 的 http/kv 用内存桩——真实网络与 AES 已由 wecom-crypt.spec / 运行时保证。
|
|
12
12
|
/* eslint-disable test/prefer-lowercase-title -- D/AC 编号是验收锚点,保留大写 */
|
|
13
|
-
import type { WecomCache, WecomHttp } from "../src/
|
|
14
|
-
import type { WecomConfig } from "../src/
|
|
15
|
-
import type { WecomStoreDeps } from "../src/
|
|
13
|
+
import type { WecomCache, WecomHttp } from "../src/app/api/auth/_shared/wecom/client";
|
|
14
|
+
import type { WecomConfig } from "../src/app/api/auth/_shared/wecom/config";
|
|
15
|
+
import type { WecomStoreDeps } from "../src/app/api/auth/_shared/wecom/store";
|
|
16
16
|
|
|
17
17
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
18
|
-
import { createWecomClient, runtimeCache } from "../src/
|
|
18
|
+
import { createWecomClient, runtimeCache } from "../src/app/api/auth/_shared/wecom/client";
|
|
19
19
|
import {
|
|
20
20
|
assertEncKey,
|
|
21
21
|
eventUrlOf,
|
|
@@ -23,8 +23,8 @@ import {
|
|
|
23
23
|
readCallbackConfig,
|
|
24
24
|
readSuiteConfig,
|
|
25
25
|
siteRootOf,
|
|
26
|
-
} from "../src/
|
|
27
|
-
import { envOfState, handleSuiteEvent } from "../src/
|
|
26
|
+
} from "../src/app/api/auth/_shared/wecom/config";
|
|
27
|
+
import { envOfState, handleSuiteEvent } from "../src/app/api/auth/_shared/wecom/events";
|
|
28
28
|
import {
|
|
29
29
|
assertUserActive,
|
|
30
30
|
buildAuthorizeUrl,
|
|
@@ -36,8 +36,8 @@ import {
|
|
|
36
36
|
loginLandingUrl,
|
|
37
37
|
putLoginState,
|
|
38
38
|
takeLoginState,
|
|
39
|
-
} from "../src/
|
|
40
|
-
import { callbackFields, field } from "../src/
|
|
39
|
+
} from "../src/app/api/auth/_shared/wecom/login";
|
|
40
|
+
import { callbackFields, field } from "../src/app/api/auth/_shared/wecom/payload";
|
|
41
41
|
import {
|
|
42
42
|
claimLoginTicket,
|
|
43
43
|
ensureTenant,
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
saveProfile,
|
|
49
49
|
setProfileStatus,
|
|
50
50
|
tenantOfCorp,
|
|
51
|
-
} from "../src/
|
|
51
|
+
} from "../src/app/api/auth/_shared/wecom/store";
|
|
52
52
|
import { installDb } from "./fixture";
|
|
53
53
|
|
|
54
54
|
let dbo: ReturnType<typeof installDb>;
|
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
//
|
|
16
16
|
// 夹具即真实库(真迁移 + 真 sqlite):0003__credential_kind 的默认值也一起被这层覆盖到。
|
|
17
17
|
/* eslint-disable test/prefer-lowercase-title -- AC 编号是验收锚点,保留大写 */
|
|
18
|
-
import type { WecomStoreDeps } from "../src/
|
|
18
|
+
import type { WecomStoreDeps } from "../src/app/api/auth/_shared/wecom/store";
|
|
19
19
|
import { Buffer } from "node:buffer";
|
|
20
20
|
import { createCipheriv, webcrypto } from "node:crypto";
|
|
21
21
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
22
|
-
import { connectCorp } from "../src/
|
|
23
|
-
import { createWecomClient, runtimeCache, runtimeHttp } from "../src/
|
|
24
|
-
import { callbackCreds, configuredKinds, corpIdentity, readBase, readCorpConfig, readSuiteConfig, wecomKinds } from "../src/
|
|
25
|
-
import { bytesToB64, utf8Bytes, wecomSignature } from "../src/
|
|
26
|
-
import { handleSuiteEvent } from "../src/
|
|
27
|
-
import { findCorpAuth, findProfile, probeProfile, saveProfile } from "../src/
|
|
22
|
+
import { connectCorp } from "../src/app/api/auth/_shared/wecom/admin";
|
|
23
|
+
import { createWecomClient, runtimeCache, runtimeHttp } from "../src/app/api/auth/_shared/wecom/client";
|
|
24
|
+
import { callbackCreds, configuredKinds, corpIdentity, readBase, readCorpConfig, readSuiteConfig, wecomKinds } from "../src/app/api/auth/_shared/wecom/config";
|
|
25
|
+
import { bytesToB64, utf8Bytes, wecomSignature } from "../src/app/api/auth/_shared/wecom/crypt";
|
|
26
|
+
import { handleSuiteEvent } from "../src/app/api/auth/_shared/wecom/events";
|
|
27
|
+
import { findCorpAuth, findProfile, probeProfile, saveProfile } from "../src/app/api/auth/_shared/wecom/store";
|
|
28
28
|
import { installDb } from "./fixture";
|
|
29
29
|
|
|
30
30
|
const g = globalThis as any;
|
|
@@ -209,8 +209,8 @@ function stubUpstream(opts: { depts?: Array<{ id: string, name?: string }>, memb
|
|
|
209
209
|
return { calls };
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
const wecomApi = async () => (await import("../src/
|
|
213
|
-
const adminApi = async () => (await import("../src/
|
|
212
|
+
const wecomApi = async () => (await import("../src/app/api/auth/wecom/api")).default;
|
|
213
|
+
const adminApi = async () => (await import("../src/app/api/auth/wecom/admin/api")).default;
|
|
214
214
|
|
|
215
215
|
/** 回调 timestamp 必须新鲜:重放守卫拒 ±5 分钟以外的报文,用固定值会让整段用例假红 */
|
|
216
216
|
const nowSecs = () => String(Math.floor(Date.now() / 1000));
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
unwrapWecomMessage,
|
|
18
18
|
utf8Bytes,
|
|
19
19
|
wecomSignature,
|
|
20
|
-
} from "../src/
|
|
20
|
+
} from "../src/app/api/auth/_shared/wecom/crypt";
|
|
21
21
|
|
|
22
22
|
const req = createRequire(import.meta.url);
|
|
23
23
|
const nodeCrypto = req("node:crypto") as typeof import("node:crypto");
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { Buffer } from "node:buffer";
|
|
12
12
|
import { createCipheriv, webcrypto } from "node:crypto";
|
|
13
13
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
14
|
-
import { bytesToB64, sha1Hex, utf8Bytes, wecomSignature } from "../src/
|
|
14
|
+
import { bytesToB64, sha1Hex, utf8Bytes, wecomSignature } from "../src/app/api/auth/_shared/wecom/crypt";
|
|
15
15
|
import { installDb } from "./fixture";
|
|
16
16
|
|
|
17
17
|
const g = globalThis as any;
|
|
@@ -147,7 +147,7 @@ function seedCorp(corpId = "ww_corp_a", agentId = "100001"): void {
|
|
|
147
147
|
.run(SUITE, corpId, agentId, corpId, `gcm(perm_${corpId})`);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
const api = async () => (await import("../src/
|
|
150
|
+
const api = async () => (await import("../src/app/api/auth/wecom/api")).default;
|
|
151
151
|
|
|
152
152
|
beforeAll(() => {
|
|
153
153
|
dbo = installDb();
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// · 参数要裁剪、要能容忍少了 tenant(此时沿用本地租户,不是登录失败);
|
|
6
6
|
// · 授权入口即使没填企业代码也要跳(后端会回 400 说明缺什么,比前端静默不动好排查)。
|
|
7
7
|
import { describe, expect, it } from "vitest";
|
|
8
|
-
import { parseWecomLanding, wecomAuthorizePath } from "../src/
|
|
8
|
+
import { parseWecomLanding, wecomAuthorizePath } from "../src/app/web/login/wecom";
|
|
9
9
|
|
|
10
10
|
describe("p2-A:回跳落地参数解析", () => {
|
|
11
11
|
it("带齐 ticket 与 tenant → 解析出两者", () => {
|
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
// · **登录自更新节流**:上万员工每天登录,不节流就是把 user/get 打成常驻流量;
|
|
10
10
|
// 但 `user/get` 拿不到(无权限/不在可见范围)时**不能**写节流键,否则这一整天都补不上资料。
|
|
11
11
|
// · **D10 不建占位部门**:登录路径若能顺手建部门,任何成员都能凭空造出组织节点。
|
|
12
|
-
import type { WecomCache, WecomHttp } from "../src/
|
|
13
|
-
import type { WecomStoreDeps } from "../src/
|
|
12
|
+
import type { WecomCache, WecomHttp } from "../src/app/api/auth/_shared/wecom/client";
|
|
13
|
+
import type { WecomStoreDeps } from "../src/app/api/auth/_shared/wecom/store";
|
|
14
14
|
|
|
15
15
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
16
|
-
import { upsertDepartment } from "../src/
|
|
17
|
-
import { createWecomClient, WecomApiError } from "../src/
|
|
18
|
-
import { handleSuiteEvent } from "../src/
|
|
19
|
-
import { SELF_UPDATE_TTL_SECS, selfUpdate } from "../src/
|
|
20
|
-
import { findProfile, probeProfile, putSuiteTicket, saveProfile, setProfileStatus } from "../src/
|
|
16
|
+
import { upsertDepartment } from "../src/app/api/_platform/_shared/service/domain/org";
|
|
17
|
+
import { createWecomClient, WecomApiError } from "../src/app/api/auth/_shared/wecom/client";
|
|
18
|
+
import { handleSuiteEvent } from "../src/app/api/auth/_shared/wecom/events";
|
|
19
|
+
import { SELF_UPDATE_TTL_SECS, selfUpdate } from "../src/app/api/auth/_shared/wecom/login";
|
|
20
|
+
import { findProfile, probeProfile, putSuiteTicket, saveProfile, setProfileStatus } from "../src/app/api/auth/_shared/wecom/store";
|
|
21
21
|
import { installDb } from "./fixture";
|
|
22
22
|
|
|
23
23
|
/* eslint-disable test/prefer-lowercase-title -- D/AC 编号是验收锚点,保留大写 */
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
// · 权限类错码是「跳过并计数」——一旦有跳过就**不许回收**(否则一次上游抖会被读成「部门被删」);
|
|
6
6
|
// · 限流/可恢复致命错要停在原部门(游标不前进),下一次重试同一部门;
|
|
7
7
|
// · 本地建的部门/成员一根毛都不碰(D13 归属权)。
|
|
8
|
-
import type { WecomClient, WecomMember } from "../src/
|
|
8
|
+
import type { WecomClient, WecomMember } from "../src/app/api/auth/_shared/wecom/client";
|
|
9
9
|
|
|
10
10
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
11
|
-
import { WecomApiError } from "../src/
|
|
12
|
-
import { attachProfile, cancelSync, emptyView, startSync, syncStatus } from "../src/
|
|
11
|
+
import { WecomApiError } from "../src/app/api/auth/_shared/wecom/client";
|
|
12
|
+
import { attachProfile, cancelSync, emptyView, startSync, syncStatus } from "../src/app/api/auth/_shared/wecom/sync";
|
|
13
13
|
import { installDb } from "./fixture";
|
|
14
14
|
|
|
15
15
|
/* eslint-disable test/prefer-lowercase-title -- D/AC 编号是验收锚点,保留大写 */
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
// 由 ojm 生成:ojm init / ojm build / ojm dev
|
|
1
|
+
// 由 ojm 生成:ojm init / ojm build / ojm dev 会按模块清单重写本文件,请勿手改。
|
|
2
2
|
//
|
|
3
|
-
// oj 别名 `#/<模块名>/…`
|
|
4
|
-
// paths 只能静态全局映射,且每条规则的替换值只允许一个 `*`(`./src/*/api/*`
|
|
5
|
-
// 以 TS5062 拒掉),故按模块逐条枚举。
|
|
3
|
+
// oj 别名 `#/<模块名>/…` 锚在目标模块根(src/<模块名>/api 或 src/app/api/<模块名>)。
|
|
4
|
+
// tsconfig 的 paths 只能静态全局映射,且每条规则的替换值只允许一个 `*`(`./src/*/api/*`
|
|
5
|
+
// 会被 TS 以 TS5062 拒掉),故按模块逐条枚举。
|
|
6
6
|
{
|
|
7
7
|
"compilerOptions": {
|
|
8
8
|
"paths": {
|
|
9
|
-
"#/_platform/*": ["./src/
|
|
10
|
-
"#/auth/*": ["./src/
|
|
11
|
-
"#/home/*": ["./src/
|
|
12
|
-
"#/notification/*": ["./src/
|
|
13
|
-
"#/personal-center/*": ["./src/personal-center
|
|
14
|
-
"#/system/*": ["./src/
|
|
15
|
-
"#/web/*": ["./src/
|
|
9
|
+
"#/_platform/*": ["./src/app/api/_platform/*"],
|
|
10
|
+
"#/auth/*": ["./src/app/api/auth/*"],
|
|
11
|
+
"#/home/*": ["./src/app/api/home/*"],
|
|
12
|
+
"#/notification/*": ["./src/app/api/notification/*"],
|
|
13
|
+
"#/personal-center/*": ["./src/app/api/personal-center/*"],
|
|
14
|
+
"#/system/*": ["./src/app/api/system/*"],
|
|
15
|
+
"#/web/*": ["./src/app/api/web/*"]
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -7,13 +7,13 @@ const srcDir = fileURLToPath(new URL("./src", import.meta.url));
|
|
|
7
7
|
|
|
8
8
|
export default defineConfig({
|
|
9
9
|
root: fileURLToPath(new URL(".", import.meta.url)),
|
|
10
|
-
// `#/<模块名>/…` 是 oj
|
|
10
|
+
// `#/<模块名>/…` 是 oj 的模块别名(锚在目标模块根,0.2.55 布局下即 `src/app/api/<模块名>`)。
|
|
11
11
|
// oj build / ojm dev 在打包器里解析它,vitest 走 vite 的 resolve,必须在此显式映射,
|
|
12
12
|
// 否则跨模块 import 的后端 spec 会以「Cannot find module」整文件跳过。
|
|
13
13
|
// 形如 `#x`(锚本模块根)与导入方相对,静态 alias 表达不了——模板后端不用这种形态,
|
|
14
14
|
// 一旦引入需先补 vitest 侧解析(或改写成 `#/<模块名>/…`)。
|
|
15
15
|
resolve: {
|
|
16
|
-
alias: [{ find: /^#\/([^/]+)\/(.*)$/, replacement: `${srcDir}
|
|
16
|
+
alias: [{ find: /^#\/([^/]+)\/(.*)$/, replacement: `${srcDir}/app/api/$1/$2` }],
|
|
17
17
|
},
|
|
18
18
|
test: {
|
|
19
19
|
include: ["tests/**/*.spec.ts"],
|
package/templates/web.config.ts
CHANGED
|
@@ -10,24 +10,17 @@
|
|
|
10
10
|
* 登记了不存在的前端入口才报错。
|
|
11
11
|
*
|
|
12
12
|
* 注意:这里的登记顺序只影响模块加载顺序,**不决定侧边栏导航顺序**——
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `home` 必须第一个登记(shell 的 "/" 重定向到它)。
|
|
13
|
+
* 菜单顺序由各根路由的 handle.order 决定(≥0 主菜单区升序;<0 底部固定区,
|
|
14
|
+
* pin 在侧边栏底部;同 order 按根 path 字母序)。
|
|
16
15
|
*/
|
|
17
16
|
const allModules = [
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
{ name: "
|
|
22
|
-
// 个人中心:runtime 用户菜单固定导航 /personal-center/my-profile,缺则点击落空
|
|
23
|
-
{ name: "personal-center", entry: "src/personal-center/web/entry.ts" },
|
|
24
|
-
// 通知:onInit 注册 notificationsApi provider(四方法全必填),
|
|
25
|
-
// 通知铃的互动(单条已读/全部已读/清空)由此生效;未登记则永远回落内置只读兜底
|
|
26
|
-
{ name: "notification", entry: "src/notification/web/entry.ts" },
|
|
27
|
-
// 系统管理:用户/成员资格/角色权限/操作日志/站点设置/登录方式(oj 后端 /api/system/*)
|
|
28
|
-
{ name: "system", entry: "src/system/web/entry.ts" },
|
|
17
|
+
// 框架内置页面:首页/登录/个人中心/通知/系统管理聚合成**一个多根模块**。
|
|
18
|
+
// 各根按 handle.order 排序(/home 1、/personal-center 110、/system -100 底部),
|
|
19
|
+
// 与拆分时的菜单逐项一致;/login、/change-password、/notification-center 走 hideInMenu。
|
|
20
|
+
{ name: "app", entry: "src/app/web/entry.ts" },
|
|
29
21
|
// 解决方案市场:第一方能力(消费 _platform 编排面 /api/_platform/market/*,
|
|
30
|
-
// R29
|
|
22
|
+
// R29:第三方方案不得仿照此调用面),默认随脚手架下发。保持独立模块——
|
|
23
|
+
// 可选能力,删目录即可下线。
|
|
31
24
|
{ name: "oj-market", entry: "src/oj-market/web/entry.ts" },
|
|
32
25
|
// #region demo-only
|
|
33
26
|
// 以下为模板内置的演示模块(about/access/exception/outside/route-nest),
|
|
@@ -49,4 +42,20 @@ export default {
|
|
|
49
42
|
/** 产物 URL 前缀,留空表示同源相对路径 */
|
|
50
43
|
baseUrl: "",
|
|
51
44
|
modules: allModules,
|
|
45
|
+
/**
|
|
46
|
+
* 生成 client 的归属模块:`api 前缀 → 承载其 __client 的 web 模块`。
|
|
47
|
+
*
|
|
48
|
+
* 框架内置后端全部收在 `src/app/api/<前缀>`(模块名即 URL 前缀),前端页面收在
|
|
49
|
+
* `src/app/web`;生成物统一落 `src/app/web/<api 前缀>/__client`,页面相对导入不变。
|
|
50
|
+
* 不配则回落同名模块(`src/<api 前缀>/web/__client`,历史行为)。
|
|
51
|
+
*/
|
|
52
|
+
clientOwners: {
|
|
53
|
+
"_platform": "app",
|
|
54
|
+
"auth": "app",
|
|
55
|
+
"home": "app",
|
|
56
|
+
"personal-center": "app",
|
|
57
|
+
"notification": "app",
|
|
58
|
+
"system": "app",
|
|
59
|
+
"web": "app",
|
|
60
|
+
},
|
|
52
61
|
};
|
|
@@ -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.56",
|
|
12
|
+
"@oj-module/runtime/contract": "0.2.56",
|
|
13
|
+
"@oj-module/runtime/contract/errors": "0.2.56",
|
|
14
14
|
"@plane/kit": "0.1.20",
|
|
15
15
|
"@plane/editor": "0.1.20",
|
|
16
16
|
"antd": "6.6.1",
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { HomeOutlined } from "@ant-design/icons";
|
|
2
|
-
import { defineModule } from "@oj-module/runtime";
|
|
3
|
-
import { createElement, lazy } from "react";
|
|
4
|
-
|
|
5
|
-
const Home = lazy(() => import("./pages/index"));
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* 首页模块:提供 `/home` 路由。
|
|
9
|
-
*
|
|
10
|
-
* 为什么必须有:shell 预构建产物把 `VITE_BASE_HOME_PATH` 定为 `/home`
|
|
11
|
-
* (登录成功回跳、logo 点击、tabbar 首页、403/404 回首页都指向它)。
|
|
12
|
-
* 工程缺 `/home` 路由时这些跳转会落到错误边界。
|
|
13
|
-
*
|
|
14
|
-
* 结构对齐 apps/playground-oj/web/src/home;页面用统计卡片 + 折线/柱/饼图
|
|
15
|
-
* 演示宿主共享依赖矩阵(echarts / echarts-for-react / react-countup / dayjs /
|
|
16
|
-
* antd / @ant-design/icons / react-i18next,均由宿主 importmap 提供单例)。
|
|
17
|
-
* 图表数据为静态演示值——真实业务走模块契约 + `ojm api` 生成的 client。
|
|
18
|
-
*/
|
|
19
|
-
export default defineModule({
|
|
20
|
-
name: "home",
|
|
21
|
-
description: "首页模块",
|
|
22
|
-
version: "0.1.0",
|
|
23
|
-
peerRuntime: ">=0.0.0",
|
|
24
|
-
routes: [
|
|
25
|
-
{
|
|
26
|
-
path: "/home",
|
|
27
|
-
handle: {
|
|
28
|
-
layout: "container",
|
|
29
|
-
order: 1,
|
|
30
|
-
title: "home:menu.home",
|
|
31
|
-
icon: createElement(HomeOutlined),
|
|
32
|
-
},
|
|
33
|
-
children: [
|
|
34
|
-
{
|
|
35
|
-
index: true,
|
|
36
|
-
Component: Home,
|
|
37
|
-
handle: {
|
|
38
|
-
title: "home:menu.home",
|
|
39
|
-
icon: createElement(HomeOutlined),
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
],
|
|
43
|
-
},
|
|
44
|
-
],
|
|
45
|
-
i18n: {
|
|
46
|
-
"zh-CN": () => import("./locales/zh-CN.json"),
|
|
47
|
-
"en-US": () => import("./locales/en-US.json"),
|
|
48
|
-
},
|
|
49
|
-
});
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { defineModule, useAuthStore, useTenantStore } from "@oj-module/runtime";
|
|
2
|
-
import { lazy } from "react";
|
|
3
|
-
import { authProvider } from "./auth-provider";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 登录模块:提供 `/login` 路由与内容区。
|
|
7
|
-
*
|
|
8
|
-
* shell 宿主只消费模块路由(`getRoutes()`),**不挂 runtime 的内置 baseRoutes**,
|
|
9
|
-
* 因此缺少本模块时 `/login` 无路由可跳(登出/回跳登录会落空)。本模块补上该路由:
|
|
10
|
-
* 登录链路仍回落到 runtime 默认链(`fetchLogin` → 后端内置 `auth/login` +
|
|
11
|
-
* 守卫 `getUserInfo` → `web/user-info`),模块只负责「内容区」。
|
|
12
|
-
*
|
|
13
|
-
* `handle.login: true` 让框架识别为登录页(`resolveLoginRoute` 校验路径必须是
|
|
14
|
-
* `/login`);`handle.layout: "fullscreen"` 由框架注入全屏外壳。
|
|
15
|
-
*/
|
|
16
|
-
export default defineModule({
|
|
17
|
-
name: "login",
|
|
18
|
-
description: "登录模块(内容区由模块提供,外壳由框架提供)",
|
|
19
|
-
version: "0.1.0",
|
|
20
|
-
peerRuntime: ">=0.0.0",
|
|
21
|
-
routes: [
|
|
22
|
-
{
|
|
23
|
-
path: "/login",
|
|
24
|
-
handle: {
|
|
25
|
-
layout: "fullscreen",
|
|
26
|
-
login: true,
|
|
27
|
-
hideInMenu: true,
|
|
28
|
-
title: "login:page.title",
|
|
29
|
-
},
|
|
30
|
-
children: [
|
|
31
|
-
{
|
|
32
|
-
index: true,
|
|
33
|
-
Component: lazy(() => import("./pages/login")),
|
|
34
|
-
handle: { title: "login:page.title" },
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
// 首登/重置后强制改密(must_change 悬挂期唯一可用写端);
|
|
40
|
-
// 管理面 403 msg=password change required 时也引导至此
|
|
41
|
-
path: "/change-password",
|
|
42
|
-
handle: {
|
|
43
|
-
layout: "fullscreen",
|
|
44
|
-
hideInMenu: true,
|
|
45
|
-
title: "login:page.changePwd.title",
|
|
46
|
-
},
|
|
47
|
-
children: [
|
|
48
|
-
{
|
|
49
|
-
index: true,
|
|
50
|
-
Component: lazy(() => import("./pages/change-password")),
|
|
51
|
-
handle: { title: "login:page.changePwd.title", hideInMenu: true },
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
i18n: {
|
|
57
|
-
"zh-CN": () => import("./locales/zh-CN.json"),
|
|
58
|
-
"en-US": () => import("./locales/en-US.json"),
|
|
59
|
-
},
|
|
60
|
-
lifecycle: {
|
|
61
|
-
/**
|
|
62
|
-
* 租户上下文(L2):请求头由**运行时**注入(`runtime/store/tenant.ts`),模块不再注册
|
|
63
|
-
* headerProvider——宿主 shell 的 hydrateUser / solution-routes 都在任何模块 onInit 之前
|
|
64
|
-
* 就发请求,模块注册必然晚一步(2026-10-30 冷启动 e2e 实测两个 400 missing tenant header)。
|
|
65
|
-
* 模块这里只保留「登出清租户」这一业务语义。
|
|
66
|
-
*/
|
|
67
|
-
async onInit(ctx) {
|
|
68
|
-
// 认证 provider:仅接管「企微票据换会话」,其余方式原样委托内置链(见 auth-provider.ts)。
|
|
69
|
-
// 注册时机必须在任何登录动作之前——onInit 先于路由渲染。
|
|
70
|
-
ctx.register.authProvider(authProvider);
|
|
71
|
-
// 登出必须清租户:否则下一个用户会带着上一个用户的租户头(跨租户请求 fail-closed 403,
|
|
72
|
-
// 但单租户部署会静默落到别人的 default 视图)。token 由非空转空即视为登出。
|
|
73
|
-
useAuthStore.subscribe((state, prev) => {
|
|
74
|
-
if (prev.token && !state.token)
|
|
75
|
-
useTenantStore.getState().clear();
|
|
76
|
-
});
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
});
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import type { AppRouteRecordRaw, NotificationsApiProvider } from "@oj-module/runtime";
|
|
2
|
-
|
|
3
|
-
import { BellOutlined } from "@ant-design/icons";
|
|
4
|
-
import { defineModule } from "@oj-module/runtime";
|
|
5
|
-
|
|
6
|
-
import { createElement, lazy } from "react";
|
|
7
|
-
|
|
8
|
-
import { createNotificationClient } from "./__client/api";
|
|
9
|
-
|
|
10
|
-
const NotificationCenter = lazy(() => import("./pages/center"));
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* 通知中心页(N3):全量消息 + 类型 Tab,铃铛弹层「查看全部」跳转至此。
|
|
14
|
-
* hideInMenu——入口在铃铛,不占侧边导航。
|
|
15
|
-
* 布局挂父路由 handle(resolveRouteLayouts 只给「无 Component 有 children」的
|
|
16
|
-
* 父路由注入 layout,页面组件写父级上布局会被忽略、裸渲染无 chrome)。
|
|
17
|
-
*/
|
|
18
|
-
const routes: AppRouteRecordRaw[] = [
|
|
19
|
-
{
|
|
20
|
-
path: "/notification-center",
|
|
21
|
-
handle: {
|
|
22
|
-
layout: "container",
|
|
23
|
-
title: "notification:menu.notificationCenter",
|
|
24
|
-
icon: createElement(BellOutlined),
|
|
25
|
-
hideInMenu: true,
|
|
26
|
-
},
|
|
27
|
-
children: [
|
|
28
|
-
{
|
|
29
|
-
index: true,
|
|
30
|
-
Component: NotificationCenter,
|
|
31
|
-
handle: {
|
|
32
|
-
title: "notification:menu.notificationCenter",
|
|
33
|
-
icon: createElement(BellOutlined),
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
},
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* notification 模块(G5 模板形态):通知聚合的唯一前端消费面。
|
|
42
|
-
*
|
|
43
|
-
* onInit 全量接管通知 API(D9 + G4):把通知读/写收敛到本模块 `/notification`
|
|
44
|
-
* 前缀下的 oj 后端,通知铃的互动(单条已读/全部已读/清空)由此生效;发射端
|
|
45
|
-
* 三方法(push/pushBroadcast/del)使本模块同时作为跨模块通知原语的接收端
|
|
46
|
-
* (前端 facade 委托至此)。
|
|
47
|
-
*/
|
|
48
|
-
export default defineModule({
|
|
49
|
-
name: "notification",
|
|
50
|
-
description: "通知模块(D9/G4 注入通知读写 + 通知中心页)",
|
|
51
|
-
version: "0.1.0",
|
|
52
|
-
peerRuntime: ">=0.0.0",
|
|
53
|
-
routes,
|
|
54
|
-
i18n: {
|
|
55
|
-
"zh-CN": () => import("./locales/zh-CN.json"),
|
|
56
|
-
"en-US": () => import("./locales/en-US.json"),
|
|
57
|
-
},
|
|
58
|
-
lifecycle: {
|
|
59
|
-
async onInit(ctx) {
|
|
60
|
-
// 构造即接线:createNotificationClient 登记契约前缀 /notification
|
|
61
|
-
// (唯一真源在 __client/api.ts 的 API_PREFIX)+ 绑定圈好前缀的 scoped
|
|
62
|
-
// request(越界/穿越发请求前即拒绝,前缀收敛 D11),返回的 client
|
|
63
|
-
// 方法都自动带 /api/notification/ 前缀。
|
|
64
|
-
const notificationClient = createNotificationClient(ctx);
|
|
65
|
-
const provider: NotificationsApiProvider = {
|
|
66
|
-
// 端点函数名由 oj operationId 派生(camelCase 子路径 + 方法),
|
|
67
|
-
// 见 __client/api.ts;provider 方法名(fetchNotifications/markRead…)不变。
|
|
68
|
-
fetchNotifications: () => notificationClient.notificationNotificationsGet(),
|
|
69
|
-
// id 为命名空间串("u:<id>" / "b:<id>"),原样透传
|
|
70
|
-
markRead: id => notificationClient.notificationNotificationsReadPost({ id: String(id) }).then(() => {}),
|
|
71
|
-
markAllRead: () => notificationClient.notificationNotificationsReadAllPost().then(() => {}),
|
|
72
|
-
clearAll: () => notificationClient.notificationNotificationsClearPost().then(() => {}),
|
|
73
|
-
push: input => notificationClient
|
|
74
|
-
.notificationPushPost({ userId: input.userId, title: input.title, message: input.message, link: input.link, avatar: input.avatar, category: input.category })
|
|
75
|
-
.then(r => String((r as { id: string }).id)),
|
|
76
|
-
pushBroadcast: input => notificationClient
|
|
77
|
-
.notificationPushBroadcastPost({ title: input.title, message: input.message, link: input.link, avatar: input.avatar, category: input.category })
|
|
78
|
-
.then(r => String((r as { id: string }).id)),
|
|
79
|
-
del: (id, userId) => notificationClient.notificationDelPost({ id: String(id), userId }).then(() => {}),
|
|
80
|
-
};
|
|
81
|
-
ctx.register.notificationsApi(provider);
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
});
|