@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.
Files changed (330) hide show
  1. package/devkit/SKILL.md +4 -4
  2. package/devkit/manual.md +79 -65
  3. package/package.json +2 -2
  4. package/shell-dist/assets/{index-e_Mp2mb5.js → index-CImwskAz.js} +24 -11
  5. package/shell-dist/assets/runtime.js +7 -4
  6. package/shell-dist/index.html +3 -3
  7. package/shell-dist/versions.json +3 -3
  8. package/src/__tests__/layout.test.ts +30 -1
  9. package/src/__tests__/templates-schema-columns.test.ts +99 -0
  10. package/src/__tests__/templates-tenant-scope.test.ts +3 -2
  11. package/src/alias-paths.ts +11 -12
  12. package/src/builtin.ts +10 -3
  13. package/src/config.ts +9 -0
  14. package/src/contract/__tests__/client-path.test.ts +30 -0
  15. package/src/contract/__tests__/emit-client.test.ts +20 -2
  16. package/src/contract/__tests__/handler-scan.test.ts +23 -0
  17. package/src/contract/__tests__/market-client.integration.test.ts +2 -2
  18. package/src/contract/__tests__/run-api.test.ts +2 -2
  19. package/src/contract/check.ts +11 -7
  20. package/src/contract/client-path.ts +30 -0
  21. package/src/contract/emit-client.ts +27 -6
  22. package/src/contract/handler-scan.ts +40 -3
  23. package/src/contract/run.ts +15 -5
  24. package/src/init.ts +29 -3
  25. package/src/layout.ts +72 -16
  26. package/src/lifecycle/__tests__/manifest-contract.test.ts +2 -2
  27. package/src/market/__tests__/bootstrap.test.ts +1 -1
  28. package/src/site.ts +1 -1
  29. package/templates/README.md +49 -43
  30. package/templates/init.include.yaml +31 -19
  31. package/templates/openapi.json +1488 -120
  32. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/users.ts +27 -0
  33. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/oplog.ts +4 -2
  34. package/templates/src/{_platform/api → app/api/_platform}/contacts/api.ts +29 -1
  35. package/templates/src/{_platform/api → app/api/_platform}/contacts/detail/api.ts +25 -1
  36. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.mysql.sql +7 -0
  37. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.pg.sql +4 -0
  38. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.sql +8 -0
  39. package/templates/src/{_platform/api → app/api/_platform}/schema.yaml +5 -0
  40. package/templates/src/{_platform/api → app/api/_platform}/solutions/api.ts +60 -1
  41. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/replay.ts +9 -3
  42. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/tenant-admin.ts +18 -27
  43. package/templates/src/{auth/api → app/api/auth}/api.ts +59 -0
  44. package/templates/src/{auth/api → app/api/auth}/ldap-login/api.ts +37 -1
  45. package/templates/src/{auth/api → app/api/auth}/wecom/api.ts +23 -0
  46. package/templates/src/{notification/api → app/api/notification}/api.ts +17 -0
  47. package/templates/src/{notification/api → app/api/notification}/del/api.ts +2 -0
  48. package/templates/src/{system/api → app/api/system}/oplog/api.ts +5 -3
  49. package/templates/src/{system/api → app/api/system}/roles/api.ts +3 -0
  50. package/templates/src/{system/api → app/api/system}/roles/bindings/api.ts +3 -0
  51. package/templates/src/{system/api → app/api/system}/settings/api.ts +3 -0
  52. package/templates/src/{system/api → app/api/system}/users/forbidden/api.ts +3 -0
  53. package/templates/src/{system/api → app/api/system}/users/password/api.ts +3 -0
  54. package/templates/src/{system/api → app/api/system}/users/roles/api.ts +3 -0
  55. package/templates/src/app/api/web/get-async-routes/api.ts +23 -0
  56. package/templates/src/app/api/web/hello/api.ts +22 -0
  57. package/templates/src/{web/api → app/api/web}/user-info/api.ts +21 -1
  58. package/templates/src/{_platform/web → app/web/_platform}/__client/api.schemas.ts +99 -35
  59. package/templates/src/{_platform/web/__client/api.ts → app/web/_platform/__client/index.ts} +108 -61
  60. package/templates/src/{auth/web → app/web/auth}/__client/api.schemas.ts +133 -13
  61. package/templates/src/{auth/web/__client/api.ts → app/web/auth/__client/index.ts} +134 -33
  62. package/templates/src/app/web/entry.ts +77 -0
  63. package/templates/src/{home/web/__client/api.ts → app/web/home/__client/index.ts} +5 -2
  64. package/templates/src/app/web/home/routes.ts +41 -0
  65. package/templates/src/app/web/login/routes.ts +71 -0
  66. package/templates/src/{notification/web → app/web/notification}/__client/api.schemas.ts +14 -0
  67. package/templates/src/{notification/web/__client/api.ts → app/web/notification/__client/index.ts} +67 -20
  68. package/templates/src/{notification/web → app/web/notification}/pages/center/index.tsx +1 -1
  69. package/templates/src/app/web/notification/routes.ts +70 -0
  70. package/templates/src/{personal-center/web/__client/api.ts → app/web/personal-center/__client/index.ts} +5 -2
  71. package/templates/src/{personal-center/web → app/web/personal-center}/pages/my-profile/index.tsx +1 -1
  72. package/templates/src/app/web/personal-center/routes.ts +49 -0
  73. package/templates/src/{system/web → app/web/system}/__client/api.schemas.ts +29 -22
  74. package/templates/src/{system/web/__client/api.ts → app/web/system/__client/index.ts} +103 -58
  75. package/templates/src/{system/web → app/web/system}/locales/en-US.json +2 -3
  76. package/templates/src/{system/web → app/web/system}/locales/zh-CN.json +2 -3
  77. package/templates/src/{system/web → app/web/system}/pages/departments/index.tsx +2 -2
  78. package/templates/src/{system/web → app/web/system}/pages/login-methods/index.tsx +1 -1
  79. package/templates/src/{system/web → app/web/system}/pages/oplog/index.tsx +44 -19
  80. package/templates/src/{system/web → app/web/system}/pages/roles/index.tsx +5 -3
  81. package/templates/src/app/web/system/pages/roles/visible-roles.ts +14 -0
  82. package/templates/src/{system/web → app/web/system}/pages/settings/index.tsx +2 -2
  83. package/templates/src/app/web/system/pages/users/can-manage.ts +21 -0
  84. package/templates/src/{system/web → app/web/system}/pages/users/index.tsx +23 -15
  85. package/templates/src/{system/web → app/web/system}/pages/users/roles.tsx +1 -1
  86. package/templates/src/{system/web → app/web/system}/pages/wecom/index.tsx +12 -3
  87. package/templates/src/{system/web/entry.ts → app/web/system/routes.ts} +25 -36
  88. package/templates/src/app/web/web/__client/api.schemas.ts +36 -0
  89. package/templates/src/{web/web/__client/api.ts → app/web/web/__client/index.ts} +38 -11
  90. package/templates/src/oj-market/web/api/defaultClient.ts +1 -1
  91. package/templates/tests/auth-ldap.spec.ts +2 -2
  92. package/templates/tests/auth-session.spec.ts +3 -3
  93. package/templates/tests/auth-throttle.spec.ts +1 -1
  94. package/templates/tests/fixture.ts +6 -4
  95. package/templates/tests/guard.spec.ts +1 -1
  96. package/templates/tests/lifecycle/atomic-state.spec.ts +3 -3
  97. package/templates/tests/lifecycle/bootstrap.spec.ts +3 -3
  98. package/templates/tests/lifecycle/deploy-smoke.spec.ts +8 -8
  99. package/templates/tests/lifecycle/installer.spec.ts +10 -10
  100. package/templates/tests/lifecycle/market-api.spec.ts +14 -14
  101. package/templates/tests/lifecycle/migrate.spec.ts +6 -6
  102. package/templates/tests/lifecycle/preflight.spec.ts +3 -3
  103. package/templates/tests/lifecycle/register.spec.ts +1 -1
  104. package/templates/tests/lifecycle/semver.spec.ts +1 -1
  105. package/templates/tests/lifecycle/sha384.spec.ts +1 -1
  106. package/templates/tests/lifecycle/solutions-api.spec.ts +1 -1
  107. package/templates/tests/lifecycle/source-seq.spec.ts +1 -1
  108. package/templates/tests/lifecycle/steps-fs.spec.ts +9 -9
  109. package/templates/tests/lifecycle/text-edit.spec.ts +1 -1
  110. package/templates/tests/lifecycle/tgz-guard.spec.ts +2 -2
  111. package/templates/tests/lifecycle/trust.spec.ts +5 -5
  112. package/templates/tests/lifecycle/uninstall.spec.ts +7 -7
  113. package/templates/tests/lifecycle/verify-checks.spec.ts +5 -5
  114. package/templates/tests/lifecycle/verify-report.spec.ts +4 -4
  115. package/templates/tests/org-departments.spec.ts +10 -6
  116. package/templates/tests/org-read.spec.ts +7 -7
  117. package/templates/tests/query-utils.spec.ts +1 -1
  118. package/templates/tests/rbac-audit.spec.ts +1 -1
  119. package/templates/tests/rbac-contacts.spec.ts +2 -2
  120. package/templates/tests/rbac-guard.spec.ts +1 -1
  121. package/templates/tests/rbac-roles.spec.ts +10 -2
  122. package/templates/tests/rbac-seed.spec.ts +1 -1
  123. package/templates/tests/rbac-settings.spec.ts +77 -7
  124. package/templates/tests/rbac-solution-perms.spec.ts +1 -1
  125. package/templates/tests/rbac-tables.spec.ts +29 -0
  126. package/templates/tests/rbac-users.spec.ts +55 -3
  127. package/templates/tests/roles-page.spec.ts +40 -0
  128. package/templates/tests/seed-role-accounts.spec.ts +1 -1
  129. package/templates/tests/tenant-access-cache.spec.ts +1 -1
  130. package/templates/tests/tenant-forgery.spec.ts +5 -5
  131. package/templates/tests/tenant-isolation.spec.ts +5 -4
  132. package/templates/tests/username-namespace.spec.ts +3 -3
  133. package/templates/tests/users-page.spec.ts +39 -0
  134. package/templates/tests/wecom-admin.spec.ts +6 -6
  135. package/templates/tests/wecom-client-timeout.spec.ts +1 -1
  136. package/templates/tests/wecom-connector.spec.ts +9 -9
  137. package/templates/tests/wecom-corp.spec.ts +9 -9
  138. package/templates/tests/wecom-crypt.spec.ts +1 -1
  139. package/templates/tests/wecom-endpoints.spec.ts +2 -2
  140. package/templates/tests/wecom-landing.spec.ts +1 -1
  141. package/templates/tests/wecom-org-sync.spec.ts +7 -7
  142. package/templates/tests/wecom-sync.spec.ts +3 -3
  143. package/templates/tsconfig.paths.json +11 -11
  144. package/templates/vitest.config.ts +2 -2
  145. package/templates/web.config.ts +24 -15
  146. package/vendor/host-versions.json +3 -3
  147. package/templates/src/home/web/entry.ts +0 -49
  148. package/templates/src/login/web/entry.ts +0 -79
  149. package/templates/src/notification/web/entry.ts +0 -84
  150. package/templates/src/personal-center/web/entry.ts +0 -60
  151. package/templates/src/web/api/get-async-routes/api.ts +0 -8
  152. package/templates/src/web/api/hello/api.ts +0 -10
  153. package/templates/src/web/web/__client/api.schemas.ts +0 -8
  154. package/templates/src/{_platform/api → app/api/_platform}/README.md +0 -0
  155. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/atomic.ts +0 -0
  156. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/history.ts +0 -0
  157. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/installer.ts +0 -0
  158. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/limits.ts +0 -0
  159. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/lock.ts +0 -0
  160. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/semver.ts +0 -0
  161. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/sha384.ts +0 -0
  162. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/source-seq.ts +0 -0
  163. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/state.ts +0 -0
  164. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/deploy-web.ts +0 -0
  165. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/download.ts +0 -0
  166. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/extract.ts +0 -0
  167. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/migrate.ts +0 -0
  168. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/preflight.ts +0 -0
  169. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/register.ts +0 -0
  170. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/reload.ts +0 -0
  171. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/smoke.ts +0 -0
  172. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/verify.ts +0 -0
  173. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tasks.ts +0 -0
  174. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/text-edit.ts +0 -0
  175. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tgz-guard.ts +0 -0
  176. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/trust.ts +0 -0
  177. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/types.ts +0 -0
  178. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/uninstall.ts +0 -0
  179. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-checks.ts +0 -0
  180. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-report.ts +0 -0
  181. package/templates/src/{_platform/api → app/api/_platform}/_shared/respond.ts +0 -0
  182. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/contacts.ts +0 -0
  183. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org-read.ts +0 -0
  184. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org.ts +0 -0
  185. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/roles.ts +0 -0
  186. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/settings.ts +0 -0
  187. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/solution-perms.ts +0 -0
  188. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/access.ts +0 -0
  189. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/audit.ts +0 -0
  190. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/guard.ts +0 -0
  191. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/query.ts +0 -0
  192. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/rate.ts +0 -0
  193. package/templates/src/{_platform/api → app/api/_platform}/_shared/site-facade.ts +0 -0
  194. package/templates/src/{_platform/api → app/api/_platform}/fixtures/l1-test-user.sql +0 -0
  195. package/templates/src/{_platform/api → app/api/_platform}/manifest.yaml +0 -0
  196. package/templates/src/{_platform/api → app/api/_platform}/market/api.ts +0 -0
  197. package/templates/src/{_platform/api → app/api/_platform}/market/bootstrap.ts +0 -0
  198. package/templates/src/{_platform/api → app/api/_platform}/market/index-source.ts +0 -0
  199. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.mysql.sql +0 -0
  200. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.pg.sql +0 -0
  201. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.sql +0 -0
  202. package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.mysql.sql +0 -0
  203. package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.sql +0 -0
  204. package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.mysql.sql +0 -0
  205. package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.sql +0 -0
  206. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.mysql.sql +0 -0
  207. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.pg.sql +0 -0
  208. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.sql +0 -0
  209. package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.mysql.sql +0 -0
  210. package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.sql +0 -0
  211. package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.mysql.sql +0 -0
  212. package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.sql +0 -0
  213. package/templates/src/{_platform/api → app/api/_platform}/migrations/0007__users_email.sql +0 -0
  214. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.mysql.sql +0 -0
  215. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.pg.sql +0 -0
  216. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.sql +0 -0
  217. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.mysql.sql +0 -0
  218. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.pg.sql +0 -0
  219. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.sql +0 -0
  220. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.mysql.sql +0 -0
  221. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.pg.sql +0 -0
  222. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.sql +0 -0
  223. package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.mysql.sql +0 -0
  224. package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.sql +0 -0
  225. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.mysql.sql +0 -0
  226. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.pg.sql +0 -0
  227. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.sql +0 -0
  228. package/templates/src/{_platform/api → app/api/_platform}/migrations/0013__users_profile.sql +0 -0
  229. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.mysql.sql +0 -0
  230. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.pg.sql +0 -0
  231. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.sql +0 -0
  232. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.mysql.sql +0 -0
  233. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.pg.sql +0 -0
  234. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.sql +0 -0
  235. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.mysql.sql +0 -0
  236. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.pg.sql +0 -0
  237. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.sql +0 -0
  238. package/templates/src/{_platform/api → app/api/_platform}/org/departments/api.ts +0 -0
  239. package/templates/src/{_platform/api → app/api/_platform}/org/is-member/api.ts +0 -0
  240. package/templates/src/{_platform/api → app/api/_platform}/org/members/api.ts +0 -0
  241. package/templates/src/{_platform/api → app/api/_platform}/org/picker/api.ts +0 -0
  242. package/templates/src/{_platform/api → app/api/_platform}/org/resolve/api.ts +0 -0
  243. package/templates/src/{_platform/api → app/api/_platform}/org/tree/api.ts +0 -0
  244. package/templates/src/{_platform/api → app/api/_platform}/org/user/api.ts +0 -0
  245. package/templates/src/{_platform/api → app/api/_platform}/seed.mysql.sql +0 -0
  246. package/templates/src/{_platform/api → app/api/_platform}/seed.pg.sql +0 -0
  247. package/templates/src/{_platform/api → app/api/_platform}/seed.sql +0 -0
  248. package/templates/src/{auth/api → app/api/auth}/_shared/ldap-login.ts +0 -0
  249. package/templates/src/{auth/api → app/api/auth}/_shared/ldap.ts +0 -0
  250. package/templates/src/{auth/api → app/api/auth}/_shared/login-throttle.ts +0 -0
  251. package/templates/src/{auth/api → app/api/auth}/_shared/session.ts +0 -0
  252. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/admin.ts +1 -1
  253. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/client.ts +0 -0
  254. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/config.ts +0 -0
  255. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/contacts.ts +0 -0
  256. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/crypt.ts +0 -0
  257. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/events.ts +1 -1
  258. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/login.ts +0 -0
  259. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/payload.ts +0 -0
  260. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/store.ts +0 -0
  261. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/sync.ts +0 -0
  262. package/templates/src/{auth/api → app/api/auth}/manifest.yaml +0 -0
  263. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.mysql.sql +0 -0
  264. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.pg.sql +0 -0
  265. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.sql +0 -0
  266. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.mysql.sql +0 -0
  267. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.pg.sql +0 -0
  268. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.sql +0 -0
  269. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.mysql.sql +0 -0
  270. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.pg.sql +0 -0
  271. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.sql +0 -0
  272. package/templates/src/{auth/api → app/api/auth}/schema.yaml +0 -0
  273. package/templates/src/{auth/api → app/api/auth}/wecom/admin/api.ts +0 -0
  274. package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/api.ts +1 -1
  275. package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/password/api.ts +1 -1
  276. /package/templates/src/{auth/api → app/api/auth}/wecom/sync/api.ts +0 -0
  277. /package/templates/src/{home/api → app/api/home}/line/api.ts +0 -0
  278. /package/templates/src/{home/api → app/api/home}/manifest.yaml +0 -0
  279. /package/templates/src/{home/api → app/api/home}/migrations/0001__create_home_tables.sql +0 -0
  280. /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.mysql.sql +0 -0
  281. /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.sql +0 -0
  282. /package/templates/src/{home/api → app/api/home}/pie/api.ts +0 -0
  283. /package/templates/src/{home/api → app/api/home}/schema.yaml +0 -0
  284. /package/templates/src/{home/api → app/api/home}/seed.sql +0 -0
  285. /package/templates/src/{notification/api → app/api/notification}/_shared/notify.ts +0 -0
  286. /package/templates/src/{notification/api → app/api/notification}/manifest.yaml +0 -0
  287. /package/templates/src/{notification/api → app/api/notification}/migrations/0001__create_notifications.sql +0 -0
  288. /package/templates/src/{notification/api → app/api/notification}/migrations/0002__notifications_user.sql +0 -0
  289. /package/templates/src/{notification/api → app/api/notification}/migrations/0003__notifications_broadcast.sql +0 -0
  290. /package/templates/src/{notification/api → app/api/notification}/migrations/0004__notifications_broadcast_hidden.sql +0 -0
  291. /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.mysql.sql +0 -0
  292. /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.sql +0 -0
  293. /package/templates/src/{notification/api → app/api/notification}/schema.yaml +0 -0
  294. /package/templates/src/{notification/api → app/api/notification}/seed.sql +0 -0
  295. /package/templates/src/{personal-center/api → app/api/personal-center}/manifest.yaml +0 -0
  296. /package/templates/src/{personal-center/api → app/api/personal-center}/profile/api.ts +0 -0
  297. /package/templates/src/{personal-center/api → app/api/personal-center}/upload/api.ts +0 -0
  298. /package/templates/src/{system/api → app/api/system}/departments/api.ts +0 -0
  299. /package/templates/src/{system/api → app/api/system}/departments/detach/api.ts +0 -0
  300. /package/templates/src/{system/api → app/api/system}/departments/members/api.ts +0 -0
  301. /package/templates/src/{system/api → app/api/system}/departments/move/api.ts +0 -0
  302. /package/templates/src/{system/api → app/api/system}/login-methods/api.ts +0 -0
  303. /package/templates/src/{system/api → app/api/system}/manifest.yaml +0 -0
  304. /package/templates/src/{system/api → app/api/system}/permissions/api.ts +0 -0
  305. /package/templates/src/{system/api → app/api/system}/users/api.ts +0 -0
  306. /package/templates/src/{system/api → app/api/system}/users/profile/api.ts +0 -0
  307. /package/templates/src/{web/api → app/api/web}/manifest.yaml +0 -0
  308. /package/templates/src/{home/web → app/web/home}/__client/api.schemas.ts +0 -0
  309. /package/templates/src/{home/web → app/web/home}/locales/en-US.json +0 -0
  310. /package/templates/src/{home/web → app/web/home}/locales/zh-CN.json +0 -0
  311. /package/templates/src/{home/web → app/web/home}/pages/components/bar-chart.tsx +0 -0
  312. /package/templates/src/{home/web → app/web/home}/pages/components/card-list.tsx +0 -0
  313. /package/templates/src/{home/web → app/web/home}/pages/components/line-chart.tsx +0 -0
  314. /package/templates/src/{home/web → app/web/home}/pages/components/pie-chart.tsx +0 -0
  315. /package/templates/src/{home/web → app/web/home}/pages/index.tsx +0 -0
  316. /package/templates/src/{login/web → app/web/login}/auth-provider.ts +0 -0
  317. /package/templates/src/{login/web → app/web/login}/locales/en-US.json +0 -0
  318. /package/templates/src/{login/web → app/web/login}/locales/zh-CN.json +0 -0
  319. /package/templates/src/{login/web → app/web/login}/pages/change-password.tsx +0 -0
  320. /package/templates/src/{login/web → app/web/login}/pages/login.tsx +0 -0
  321. /package/templates/src/{login/web → app/web/login}/wecom.ts +0 -0
  322. /package/templates/src/{notification/web → app/web/notification}/locales/en-US.json +0 -0
  323. /package/templates/src/{notification/web → app/web/notification}/locales/zh-CN.json +0 -0
  324. /package/templates/src/{personal-center/web → app/web/personal-center}/__client/api.schemas.ts +0 -0
  325. /package/templates/src/{personal-center/web → app/web/personal-center}/locales/en-US.json +0 -0
  326. /package/templates/src/{personal-center/web → app/web/personal-center}/locales/zh-CN.json +0 -0
  327. /package/templates/src/{system/web → app/web/system}/pages/departments/sync-bar.tsx +0 -0
  328. /package/templates/src/{system/web → app/web/system}/pages/departments/tree.ts +0 -0
  329. /package/templates/src/{system/web → app/web/system}/pages/errors.ts +0 -0
  330. /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/_platform/api/_shared/service/infra/access";
18
- import notification from "../src/notification/api/api";
19
- import notificationDel from "../src/notification/api/del/api";
20
- import profile from "../src/personal-center/api/profile/api";
21
- import upload from "../src/personal-center/api/upload/api";
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("todos").insert({ title: "acme-todo" }).run();
85
+ await db.table("home_pie").insert({ category: "acme-pie", value: 1 }).run();
85
86
  asTenant("globex");
86
- await db.table("todos").insert({ title: "globex-todo" }).run();
87
+ await db.table("home_pie").insert({ category: "globex-pie", value: 2 }).run();
87
88
 
88
89
  asTenant("acme");
89
- await db.table("todos").truncate().run();
90
- expect(rows("SELECT title FROM todos")).toEqual([{ title: "globex-todo" }]);
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/_platform/api/_shared/service/infra/access";
20
- import { ensureWecomUser, externalUsername } from "../src/auth/api/_shared/wecom/login";
21
- import profile from "../src/personal-center/api/profile/api";
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/_platform/api/_shared/service/infra/access";
16
- import { listApps, setAppStatus } from "../src/auth/api/_shared/wecom/admin";
17
- import { createWecomClient, runtimeCache, runtimeHttp } from "../src/auth/api/_shared/wecom/client";
18
- import { readSuiteConfig } from "../src/auth/api/_shared/wecom/config";
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/auth/api/_shared/wecom/admin");
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/auth/api/wecom/admin/api")).default;
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/auth/api/_shared/wecom/client";
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/auth/api/_shared/wecom/client";
14
- import type { WecomConfig } from "../src/auth/api/_shared/wecom/config";
15
- import type { WecomStoreDeps } from "../src/auth/api/_shared/wecom/store";
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/auth/api/_shared/wecom/client";
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/auth/api/_shared/wecom/config";
27
- import { envOfState, handleSuiteEvent } from "../src/auth/api/_shared/wecom/events";
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/auth/api/_shared/wecom/login";
40
- import { callbackFields, field } from "../src/auth/api/_shared/wecom/payload";
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/auth/api/_shared/wecom/store";
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/auth/api/_shared/wecom/store";
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/auth/api/_shared/wecom/admin";
23
- import { createWecomClient, runtimeCache, runtimeHttp } from "../src/auth/api/_shared/wecom/client";
24
- import { callbackCreds, configuredKinds, corpIdentity, readBase, readCorpConfig, readSuiteConfig, wecomKinds } from "../src/auth/api/_shared/wecom/config";
25
- import { bytesToB64, utf8Bytes, wecomSignature } from "../src/auth/api/_shared/wecom/crypt";
26
- import { handleSuiteEvent } from "../src/auth/api/_shared/wecom/events";
27
- import { findCorpAuth, findProfile, probeProfile, saveProfile } from "../src/auth/api/_shared/wecom/store";
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/auth/api/wecom/api")).default;
213
- const adminApi = async () => (await import("../src/auth/api/wecom/admin/api")).default;
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/auth/api/_shared/wecom/crypt";
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/auth/api/_shared/wecom/crypt";
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/auth/api/wecom/api")).default;
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/login/web/wecom";
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/auth/api/_shared/wecom/client";
13
- import type { WecomStoreDeps } from "../src/auth/api/_shared/wecom/store";
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/_platform/api/_shared/service/domain/org";
17
- import { createWecomClient, WecomApiError } from "../src/auth/api/_shared/wecom/client";
18
- import { handleSuiteEvent } from "../src/auth/api/_shared/wecom/events";
19
- import { SELF_UPDATE_TTL_SECS, selfUpdate } from "../src/auth/api/_shared/wecom/login";
20
- import { findProfile, probeProfile, putSuiteTicket, saveProfile, setProfileStatus } from "../src/auth/api/_shared/wecom/store";
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/auth/api/_shared/wecom/client";
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/auth/api/_shared/wecom/client";
12
- import { attachProfile, cancelSync, emptyView, startSync, syncStatus } from "../src/auth/api/_shared/wecom/sync";
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 会按 src/*/api/manifest.yaml 重写本文件,请勿手改。
1
+ // 由 ojm 生成:ojm init / ojm build / ojm dev 会按模块清单重写本文件,请勿手改。
2
2
  //
3
- // oj 别名 `#/<模块名>/…` 锚在目标模块根(本布局下即 src/<模块名>/api)。tsconfig 的
4
- // paths 只能静态全局映射,且每条规则的替换值只允许一个 `*`(`./src/*/api/*` 会被 TS
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/_platform/api/*"],
10
- "#/auth/*": ["./src/auth/api/*"],
11
- "#/home/*": ["./src/home/api/*"],
12
- "#/notification/*": ["./src/notification/api/*"],
13
- "#/personal-center/*": ["./src/personal-center/api/*"],
14
- "#/system/*": ["./src/system/api/*"],
15
- "#/web/*": ["./src/web/api/*"]
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 的模块别名(锚在目标模块根,本布局下即 `src/<模块名>/api`)。
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}/$1/api/$2` }],
16
+ alias: [{ find: /^#\/([^/]+)\/(.*)$/, replacement: `${srcDir}/app/api/$1/$2` }],
17
17
  },
18
18
  test: {
19
19
  include: ["tests/**/*.spec.ts"],
@@ -10,24 +10,17 @@
10
10
  * 登记了不存在的前端入口才报错。
11
11
  *
12
12
  * 注意:这里的登记顺序只影响模块加载顺序,**不决定侧边栏导航顺序**——
13
- * 菜单顺序由各模块 entry 路由的 handle.order 决定(≥0 主菜单区升序;
14
- * <0 底部固定区,pin 在侧边栏底部;同 order 按根 path 字母序)。
15
- * `home` 必须第一个登记(shell 的 "/" 重定向到它)。
13
+ * 菜单顺序由各根路由的 handle.order 决定(≥0 主菜单区升序;<0 底部固定区,
14
+ * pin 在侧边栏底部;同 order 按根 path 字母序)。
16
15
  */
17
16
  const allModules = [
18
- // /home 必须是首个模块:shell 的 "/" → VITE_BASE_HOME_PATH(=/home)重定向
19
- { name: "home", entry: "src/home/web/entry.ts" },
20
- // /login 路由由模块提供:shell 宿主不挂 runtime 内置 baseRoutes
21
- { name: "login", entry: "src/login/web/entry.ts" },
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.53",
12
- "@oj-module/runtime/contract": "0.2.53",
13
- "@oj-module/runtime/contract/errors": "0.2.53",
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
- });