@oj-module/cli 0.2.54 → 0.2.57

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 (307) hide show
  1. package/devkit/README.md +7 -4
  2. package/devkit/SKILL.md +7 -4
  3. package/devkit/manual.md +63 -54
  4. package/devkit/module-dev-and-cdn-playground.md +7 -0
  5. package/devkit/publish-walkthrough.md +199 -0
  6. package/package.json +2 -2
  7. package/shell-dist/assets/{index-e_Mp2mb5.js → index-CImwskAz.js} +24 -11
  8. package/shell-dist/assets/runtime.js +5 -5
  9. package/shell-dist/index.html +3 -3
  10. package/shell-dist/versions.json +3 -3
  11. package/src/__tests__/layout.test.ts +30 -1
  12. package/src/__tests__/templates-schema-columns.test.ts +99 -0
  13. package/src/__tests__/vendor-devkit.test.ts +87 -0
  14. package/src/alias-paths.ts +11 -12
  15. package/src/build/backend.ts +131 -0
  16. package/src/build/module-build.ts +364 -0
  17. package/src/build/module-def.ts +381 -0
  18. package/src/build/site-merge.ts +33 -0
  19. package/src/build.ts +16 -885
  20. package/src/builtin.ts +10 -3
  21. package/src/bundle.ts +61 -0
  22. package/src/cli.ts +4 -1
  23. package/src/contract/__tests__/emit-client.test.ts +4 -4
  24. package/src/contract/__tests__/market-client.integration.test.ts +2 -2
  25. package/src/contract/__tests__/run-api.test.ts +2 -2
  26. package/src/contract/check.ts +6 -2
  27. package/src/contract/emit-client.ts +10 -3
  28. package/src/contract/run.ts +10 -2
  29. package/src/init.ts +31 -46
  30. package/src/layout.ts +72 -16
  31. package/src/lifecycle/__tests__/bundle-summary.test.ts +106 -0
  32. package/src/lifecycle/__tests__/bundle.test.ts +6 -0
  33. package/src/lifecycle/__tests__/manifest-contract.test.ts +2 -2
  34. package/src/market/__tests__/bootstrap.test.ts +1 -1
  35. package/src/site.ts +1 -1
  36. package/src/vendor.ts +34 -0
  37. package/templates/README.md +30 -20
  38. package/templates/init.include.yaml +19 -11
  39. package/templates/openapi.json +102 -86
  40. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/installer.ts +8 -3
  41. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/register.ts +53 -0
  42. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/roles.ts +4 -1
  43. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/solution-perms.ts +7 -3
  44. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/users.ts +27 -0
  45. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/oplog.ts +4 -2
  46. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.mysql.sql +7 -0
  47. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.pg.sql +4 -0
  48. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.sql +8 -0
  49. package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.mysql.sql +3 -0
  50. package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.pg.sql +3 -0
  51. package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.sql +8 -0
  52. package/templates/src/{_platform/api → app/api/_platform}/schema.yaml +7 -0
  53. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/replay.ts +9 -3
  54. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/tenant-admin.ts +18 -27
  55. package/templates/src/{system/api → app/api/system}/oplog/api.ts +5 -3
  56. package/templates/src/{system/api → app/api/system}/roles/api.ts +2 -1
  57. package/templates/src/{_platform/web/__client/api.ts → app/web/_platform/__client/index.ts} +5 -2
  58. package/templates/src/{auth/web/__client/api.ts → app/web/auth/__client/index.ts} +5 -2
  59. package/templates/src/app/web/notification/pages/center/index.tsx +1 -1
  60. package/templates/src/app/web/notification/routes.ts +1 -1
  61. package/templates/src/app/web/personal-center/pages/my-profile/index.tsx +1 -1
  62. package/templates/src/app/web/personal-center/routes.ts +1 -1
  63. package/templates/src/app/web/system/__client/api.schemas.ts +2 -0
  64. package/templates/src/app/web/system/locales/en-US.json +2 -3
  65. package/templates/src/app/web/system/locales/zh-CN.json +2 -3
  66. package/templates/src/app/web/system/pages/departments/index.tsx +2 -2
  67. package/templates/src/app/web/system/pages/login-methods/index.tsx +1 -1
  68. package/templates/src/app/web/system/pages/oplog/index.tsx +44 -19
  69. package/templates/src/app/web/system/pages/roles/index.tsx +12 -4
  70. package/templates/src/app/web/system/pages/roles/visible-roles.ts +14 -0
  71. package/templates/src/app/web/system/pages/settings/index.tsx +2 -2
  72. package/templates/src/app/web/system/pages/users/can-manage.ts +21 -0
  73. package/templates/src/app/web/system/pages/users/index.tsx +25 -17
  74. package/templates/src/app/web/system/pages/users/roles.tsx +1 -1
  75. package/templates/src/app/web/system/routes.ts +1 -1
  76. package/templates/src/{web/web/__client/api.ts → app/web/web/__client/index.ts} +5 -2
  77. package/templates/src/oj-market/web/api/defaultClient.ts +1 -1
  78. package/templates/tests/auth-ldap.spec.ts +2 -2
  79. package/templates/tests/auth-session.spec.ts +3 -3
  80. package/templates/tests/auth-throttle.spec.ts +1 -1
  81. package/templates/tests/fixture.ts +6 -4
  82. package/templates/tests/guard.spec.ts +1 -1
  83. package/templates/tests/lifecycle/atomic-state.spec.ts +3 -3
  84. package/templates/tests/lifecycle/bootstrap.spec.ts +3 -3
  85. package/templates/tests/lifecycle/deploy-smoke.spec.ts +8 -8
  86. package/templates/tests/lifecycle/installer.spec.ts +10 -10
  87. package/templates/tests/lifecycle/market-api.spec.ts +14 -14
  88. package/templates/tests/lifecycle/migrate.spec.ts +6 -6
  89. package/templates/tests/lifecycle/preflight.spec.ts +3 -3
  90. package/templates/tests/lifecycle/register.spec.ts +1 -1
  91. package/templates/tests/lifecycle/role-title-translate.spec.ts +171 -0
  92. package/templates/tests/lifecycle/semver.spec.ts +1 -1
  93. package/templates/tests/lifecycle/sha384.spec.ts +1 -1
  94. package/templates/tests/lifecycle/solutions-api.spec.ts +1 -1
  95. package/templates/tests/lifecycle/source-seq.spec.ts +1 -1
  96. package/templates/tests/lifecycle/steps-fs.spec.ts +9 -9
  97. package/templates/tests/lifecycle/text-edit.spec.ts +1 -1
  98. package/templates/tests/lifecycle/tgz-guard.spec.ts +2 -2
  99. package/templates/tests/lifecycle/trust.spec.ts +5 -5
  100. package/templates/tests/lifecycle/uninstall.spec.ts +7 -7
  101. package/templates/tests/lifecycle/verify-checks.spec.ts +5 -5
  102. package/templates/tests/lifecycle/verify-report.spec.ts +4 -4
  103. package/templates/tests/org-departments.spec.ts +10 -6
  104. package/templates/tests/org-read.spec.ts +7 -7
  105. package/templates/tests/query-utils.spec.ts +1 -1
  106. package/templates/tests/rbac-audit.spec.ts +1 -1
  107. package/templates/tests/rbac-contacts.spec.ts +2 -2
  108. package/templates/tests/rbac-guard.spec.ts +1 -1
  109. package/templates/tests/rbac-roles.spec.ts +19 -2
  110. package/templates/tests/rbac-seed.spec.ts +1 -1
  111. package/templates/tests/rbac-settings.spec.ts +77 -7
  112. package/templates/tests/rbac-solution-perms.spec.ts +1 -1
  113. package/templates/tests/rbac-tables.spec.ts +29 -0
  114. package/templates/tests/rbac-users.spec.ts +55 -3
  115. package/templates/tests/roles-page.spec.ts +40 -0
  116. package/templates/tests/seed-role-accounts.spec.ts +1 -1
  117. package/templates/tests/tenant-access-cache.spec.ts +1 -1
  118. package/templates/tests/tenant-forgery.spec.ts +5 -5
  119. package/templates/tests/tenant-isolation.spec.ts +5 -4
  120. package/templates/tests/username-namespace.spec.ts +3 -3
  121. package/templates/tests/users-page.spec.ts +39 -0
  122. package/templates/tests/wecom-admin.spec.ts +6 -6
  123. package/templates/tests/wecom-client-timeout.spec.ts +1 -1
  124. package/templates/tests/wecom-connector.spec.ts +9 -9
  125. package/templates/tests/wecom-corp.spec.ts +9 -9
  126. package/templates/tests/wecom-crypt.spec.ts +1 -1
  127. package/templates/tests/wecom-endpoints.spec.ts +2 -2
  128. package/templates/tests/wecom-landing.spec.ts +1 -1
  129. package/templates/tests/wecom-org-sync.spec.ts +7 -7
  130. package/templates/tests/wecom-sync.spec.ts +3 -3
  131. package/templates/tsconfig.paths.json +11 -11
  132. package/templates/vitest.config.ts +2 -2
  133. package/templates/web.config.ts +5 -2
  134. package/vendor/host-versions.json +3 -3
  135. package/templates/src/{_platform/api → app/api/_platform}/README.md +0 -0
  136. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/atomic.ts +0 -0
  137. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/history.ts +0 -0
  138. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/limits.ts +0 -0
  139. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/lock.ts +0 -0
  140. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/semver.ts +0 -0
  141. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/sha384.ts +0 -0
  142. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/source-seq.ts +0 -0
  143. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/state.ts +0 -0
  144. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/deploy-web.ts +0 -0
  145. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/download.ts +0 -0
  146. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/extract.ts +0 -0
  147. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/migrate.ts +0 -0
  148. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/preflight.ts +0 -0
  149. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/reload.ts +0 -0
  150. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/smoke.ts +0 -0
  151. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/steps/verify.ts +0 -0
  152. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tasks.ts +0 -0
  153. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/text-edit.ts +0 -0
  154. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/tgz-guard.ts +0 -0
  155. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/trust.ts +0 -0
  156. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/types.ts +0 -0
  157. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/uninstall.ts +0 -0
  158. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-checks.ts +0 -0
  159. package/templates/src/{_platform/api → app/api/_platform}/_lifecycle/verify-report.ts +0 -0
  160. package/templates/src/{_platform/api → app/api/_platform}/_shared/respond.ts +0 -0
  161. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/contacts.ts +0 -0
  162. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org-read.ts +0 -0
  163. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/org.ts +0 -0
  164. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/domain/settings.ts +0 -0
  165. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/access.ts +0 -0
  166. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/audit.ts +0 -0
  167. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/guard.ts +0 -0
  168. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/query.ts +0 -0
  169. package/templates/src/{_platform/api → app/api/_platform}/_shared/service/infra/rate.ts +0 -0
  170. package/templates/src/{_platform/api → app/api/_platform}/_shared/site-facade.ts +0 -0
  171. package/templates/src/{_platform/api → app/api/_platform}/contacts/api.ts +0 -0
  172. package/templates/src/{_platform/api → app/api/_platform}/contacts/detail/api.ts +0 -0
  173. package/templates/src/{_platform/api → app/api/_platform}/fixtures/l1-test-user.sql +0 -0
  174. package/templates/src/{_platform/api → app/api/_platform}/manifest.yaml +0 -0
  175. package/templates/src/{_platform/api → app/api/_platform}/market/api.ts +0 -0
  176. package/templates/src/{_platform/api → app/api/_platform}/market/bootstrap.ts +0 -0
  177. package/templates/src/{_platform/api → app/api/_platform}/market/index-source.ts +0 -0
  178. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.mysql.sql +0 -0
  179. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.pg.sql +0 -0
  180. package/templates/src/{_platform/api → app/api/_platform}/migrations/0001__create_users.sql +0 -0
  181. package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.mysql.sql +0 -0
  182. package/templates/src/{_platform/api → app/api/_platform}/migrations/0002__add_users_avatar_base64.sql +0 -0
  183. package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.mysql.sql +0 -0
  184. package/templates/src/{_platform/api → app/api/_platform}/migrations/0003__users_source_display_name.sql +0 -0
  185. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.mysql.sql +0 -0
  186. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.pg.sql +0 -0
  187. package/templates/src/{_platform/api → app/api/_platform}/migrations/0004__sys_rbac.sql +0 -0
  188. package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.mysql.sql +0 -0
  189. package/templates/src/{_platform/api → app/api/_platform}/migrations/0005__users_must_change.sql +0 -0
  190. package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.mysql.sql +0 -0
  191. package/templates/src/{_platform/api → app/api/_platform}/migrations/0006__users_is_forbidden.sql +0 -0
  192. package/templates/src/{_platform/api → app/api/_platform}/migrations/0007__users_email.sql +0 -0
  193. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.mysql.sql +0 -0
  194. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.pg.sql +0 -0
  195. package/templates/src/{_platform/api → app/api/_platform}/migrations/0008__sys_refresh_token.sql +0 -0
  196. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.mysql.sql +0 -0
  197. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.pg.sql +0 -0
  198. package/templates/src/{_platform/api → app/api/_platform}/migrations/0009__sys_throttle.sql +0 -0
  199. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.mysql.sql +0 -0
  200. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.pg.sql +0 -0
  201. package/templates/src/{_platform/api → app/api/_platform}/migrations/0010__sys_solution_route.sql +0 -0
  202. package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.mysql.sql +0 -0
  203. package/templates/src/{_platform/api → app/api/_platform}/migrations/0011__sys_setting_group_label.sql +0 -0
  204. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.mysql.sql +0 -0
  205. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.pg.sql +0 -0
  206. package/templates/src/{_platform/api → app/api/_platform}/migrations/0012__market_history.sql +0 -0
  207. package/templates/src/{_platform/api → app/api/_platform}/migrations/0013__users_profile.sql +0 -0
  208. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.mysql.sql +0 -0
  209. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.pg.sql +0 -0
  210. package/templates/src/{_platform/api → app/api/_platform}/migrations/0014__reg_source_seq.sql +0 -0
  211. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.mysql.sql +0 -0
  212. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.pg.sql +0 -0
  213. package/templates/src/{_platform/api → app/api/_platform}/migrations/0015__tenant.sql +0 -0
  214. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.mysql.sql +0 -0
  215. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.pg.sql +0 -0
  216. package/templates/src/{_platform/api → app/api/_platform}/migrations/0016__org.sql +0 -0
  217. package/templates/src/{_platform/api → app/api/_platform}/org/departments/api.ts +0 -0
  218. package/templates/src/{_platform/api → app/api/_platform}/org/is-member/api.ts +0 -0
  219. package/templates/src/{_platform/api → app/api/_platform}/org/members/api.ts +0 -0
  220. package/templates/src/{_platform/api → app/api/_platform}/org/picker/api.ts +0 -0
  221. package/templates/src/{_platform/api → app/api/_platform}/org/resolve/api.ts +0 -0
  222. package/templates/src/{_platform/api → app/api/_platform}/org/tree/api.ts +0 -0
  223. package/templates/src/{_platform/api → app/api/_platform}/org/user/api.ts +0 -0
  224. package/templates/src/{_platform/api → app/api/_platform}/seed.mysql.sql +0 -0
  225. package/templates/src/{_platform/api → app/api/_platform}/seed.pg.sql +0 -0
  226. package/templates/src/{_platform/api → app/api/_platform}/seed.sql +0 -0
  227. package/templates/src/{_platform/api → app/api/_platform}/solutions/api.ts +0 -0
  228. package/templates/src/{auth/api → app/api/auth}/_shared/ldap-login.ts +0 -0
  229. package/templates/src/{auth/api → app/api/auth}/_shared/ldap.ts +0 -0
  230. package/templates/src/{auth/api → app/api/auth}/_shared/login-throttle.ts +0 -0
  231. package/templates/src/{auth/api → app/api/auth}/_shared/session.ts +0 -0
  232. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/admin.ts +1 -1
  233. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/client.ts +0 -0
  234. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/config.ts +0 -0
  235. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/contacts.ts +0 -0
  236. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/crypt.ts +0 -0
  237. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/events.ts +1 -1
  238. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/login.ts +0 -0
  239. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/payload.ts +0 -0
  240. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/store.ts +0 -0
  241. package/templates/src/{auth/api → app/api/auth}/_shared/wecom/sync.ts +0 -0
  242. package/templates/src/{auth/api → app/api/auth}/api.ts +0 -0
  243. package/templates/src/{auth/api → app/api/auth}/ldap-login/api.ts +0 -0
  244. package/templates/src/{auth/api → app/api/auth}/manifest.yaml +0 -0
  245. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.mysql.sql +0 -0
  246. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.pg.sql +0 -0
  247. package/templates/src/{auth/api → app/api/auth}/migrations/0001__wecom.sql +0 -0
  248. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.mysql.sql +0 -0
  249. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.pg.sql +0 -0
  250. package/templates/src/{auth/api → app/api/auth}/migrations/0002__org_sync.sql +0 -0
  251. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.mysql.sql +0 -0
  252. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.pg.sql +0 -0
  253. package/templates/src/{auth/api → app/api/auth}/migrations/0003__credential_kind.sql +0 -0
  254. package/templates/src/{auth/api → app/api/auth}/schema.yaml +0 -0
  255. package/templates/src/{auth/api → app/api/auth}/wecom/admin/api.ts +0 -0
  256. package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/api.ts +1 -1
  257. package/templates/src/{auth/api → app/api/auth}/wecom/admin/tenant-admin/password/api.ts +1 -1
  258. /package/templates/src/{auth/api → app/api/auth}/wecom/api.ts +0 -0
  259. /package/templates/src/{auth/api → app/api/auth}/wecom/sync/api.ts +0 -0
  260. /package/templates/src/{home/api → app/api/home}/line/api.ts +0 -0
  261. /package/templates/src/{home/api → app/api/home}/manifest.yaml +0 -0
  262. /package/templates/src/{home/api → app/api/home}/migrations/0001__create_home_tables.sql +0 -0
  263. /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.mysql.sql +0 -0
  264. /package/templates/src/{home/api → app/api/home}/migrations/0002__tenant.sql +0 -0
  265. /package/templates/src/{home/api → app/api/home}/pie/api.ts +0 -0
  266. /package/templates/src/{home/api → app/api/home}/schema.yaml +0 -0
  267. /package/templates/src/{home/api → app/api/home}/seed.sql +0 -0
  268. /package/templates/src/{notification/api → app/api/notification}/_shared/notify.ts +0 -0
  269. /package/templates/src/{notification/api → app/api/notification}/api.ts +0 -0
  270. /package/templates/src/{notification/api → app/api/notification}/del/api.ts +0 -0
  271. /package/templates/src/{notification/api → app/api/notification}/manifest.yaml +0 -0
  272. /package/templates/src/{notification/api → app/api/notification}/migrations/0001__create_notifications.sql +0 -0
  273. /package/templates/src/{notification/api → app/api/notification}/migrations/0002__notifications_user.sql +0 -0
  274. /package/templates/src/{notification/api → app/api/notification}/migrations/0003__notifications_broadcast.sql +0 -0
  275. /package/templates/src/{notification/api → app/api/notification}/migrations/0004__notifications_broadcast_hidden.sql +0 -0
  276. /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.mysql.sql +0 -0
  277. /package/templates/src/{notification/api → app/api/notification}/migrations/0005__tenant.sql +0 -0
  278. /package/templates/src/{notification/api → app/api/notification}/schema.yaml +0 -0
  279. /package/templates/src/{notification/api → app/api/notification}/seed.sql +0 -0
  280. /package/templates/src/{personal-center/api → app/api/personal-center}/manifest.yaml +0 -0
  281. /package/templates/src/{personal-center/api → app/api/personal-center}/profile/api.ts +0 -0
  282. /package/templates/src/{personal-center/api → app/api/personal-center}/upload/api.ts +0 -0
  283. /package/templates/src/{system/api → app/api/system}/departments/api.ts +0 -0
  284. /package/templates/src/{system/api → app/api/system}/departments/detach/api.ts +0 -0
  285. /package/templates/src/{system/api → app/api/system}/departments/members/api.ts +0 -0
  286. /package/templates/src/{system/api → app/api/system}/departments/move/api.ts +0 -0
  287. /package/templates/src/{system/api → app/api/system}/login-methods/api.ts +0 -0
  288. /package/templates/src/{system/api → app/api/system}/manifest.yaml +0 -0
  289. /package/templates/src/{system/api → app/api/system}/permissions/api.ts +0 -0
  290. /package/templates/src/{system/api → app/api/system}/roles/bindings/api.ts +0 -0
  291. /package/templates/src/{system/api → app/api/system}/settings/api.ts +0 -0
  292. /package/templates/src/{system/api → app/api/system}/users/api.ts +0 -0
  293. /package/templates/src/{system/api → app/api/system}/users/forbidden/api.ts +0 -0
  294. /package/templates/src/{system/api → app/api/system}/users/password/api.ts +0 -0
  295. /package/templates/src/{system/api → app/api/system}/users/profile/api.ts +0 -0
  296. /package/templates/src/{system/api → app/api/system}/users/roles/api.ts +0 -0
  297. /package/templates/src/{web/api → app/api/web}/get-async-routes/api.ts +0 -0
  298. /package/templates/src/{web/api → app/api/web}/hello/api.ts +0 -0
  299. /package/templates/src/{web/api → app/api/web}/manifest.yaml +0 -0
  300. /package/templates/src/{web/api → app/api/web}/user-info/api.ts +0 -0
  301. /package/templates/src/{_platform/web → app/web/_platform}/__client/api.schemas.ts +0 -0
  302. /package/templates/src/{auth/web → app/web/auth}/__client/api.schemas.ts +0 -0
  303. /package/templates/src/app/web/home/__client/{api.ts → index.ts} +0 -0
  304. /package/templates/src/app/web/notification/__client/{api.ts → index.ts} +0 -0
  305. /package/templates/src/app/web/personal-center/__client/{api.ts → index.ts} +0 -0
  306. /package/templates/src/app/web/system/__client/{api.ts → index.ts} +0 -0
  307. /package/templates/src/{web → app/web}/web/__client/api.schemas.ts +0 -0
@@ -1,10 +1,10 @@
1
1
  import { useQuery } from "@tanstack/react-query";
2
- import { Alert, Button, Form, Input, InputNumber, Space, Table, Tooltip } from "antd";
2
+ import { Button, Form, Input, Space, Table, Tooltip } from "antd";
3
3
  import dayjs from "dayjs";
4
4
  import { useState } from "react";
5
5
  import { useTranslation } from "react-i18next";
6
6
 
7
- import { systemOplogGet } from "../../__client/api";
7
+ import { systemOplogGet } from "../../__client";
8
8
  import { isForbidden } from "../errors";
9
9
  import { ForbiddenPanel } from "../forbidden";
10
10
 
@@ -12,20 +12,26 @@ interface OplogFilters {
12
12
  action?: string
13
13
  username?: string
14
14
  module?: string
15
- limit?: number
16
15
  }
17
16
 
17
+ const DEFAULT_PAGE_SIZE = 20;
18
+ const PAGE_SIZE_OPTIONS = [20, 50, 100];
19
+
18
20
  /**
19
- * 操作日志(AC1-29/D13):action 精确 / username 前缀 / module 精确,limit 钳 200。
20
- * truncated=true 表示结果被截断(服务端 paged),顶部提示缩小过滤范围。
21
+ * 操作日志(AC1-29/D13):action 精确 / username 包含 / module 精确。
22
+ *
23
+ * 分页交给服务端:`page` + `limit`(每页条数,钳 200),响应带 `total`,页面按 total 出页码。
24
+ * 不再有「结果被截断」的横幅——记录再多也能通过页码翻到,不必靠缩小过滤范围。
21
25
  */
22
26
  export default function OplogPage() {
23
27
  const { t } = useTranslation();
24
28
  const [filters, setFilters] = useState<OplogFilters>({});
29
+ const [page, setPage] = useState(1);
30
+ const [size, setSize] = useState(DEFAULT_PAGE_SIZE);
25
31
 
26
32
  const query = useQuery({
27
- queryKey: ["system", "oplog", filters],
28
- queryFn: () => systemOplogGet(filters),
33
+ queryKey: ["system", "oplog", filters, page, size],
34
+ queryFn: () => systemOplogGet({ ...filters, page, limit: size }),
29
35
  });
30
36
 
31
37
  if (query.error && isForbidden(query.error))
@@ -50,12 +56,14 @@ export default function OplogPage() {
50
56
  <Space direction="vertical" style={{ width: "100%", paddingTop: 16 }}>
51
57
  <Form
52
58
  layout="inline"
53
- onFinish={values => setFilters({
54
- action: values.action || undefined,
55
- username: values.username || undefined,
56
- module: values.module || undefined,
57
- limit: values.limit || undefined,
58
- })}
59
+ onFinish={(values) => {
60
+ setFilters({
61
+ action: values.action || undefined,
62
+ username: values.username || undefined,
63
+ module: values.module || undefined,
64
+ });
65
+ setPage(1);
66
+ }}
59
67
  >
60
68
  <Form.Item name="action" label={t("system:oplog.filterAction")}>
61
69
  <Input allowClear />
@@ -66,25 +74,42 @@ export default function OplogPage() {
66
74
  <Form.Item name="module" label={t("system:oplog.filterModule")}>
67
75
  <Input allowClear />
68
76
  </Form.Item>
69
- <Form.Item name="limit" label={t("system:oplog.filterLimit")} initialValue={50}>
70
- <InputNumber min={1} max={200} />
71
- </Form.Item>
72
77
  <Form.Item>
73
78
  <Space>
74
79
  <Button type="primary" htmlType="submit">{t("system:common.search")}</Button>
75
- <Button onClick={() => { setFilters({}); }}>
80
+ <Button
81
+ onClick={() => {
82
+ setFilters({});
83
+ setPage(1);
84
+ }}
85
+ >
76
86
  {t("system:common.reset")}
77
87
  </Button>
78
88
  </Space>
79
89
  </Form.Item>
80
90
  </Form>
81
- {query.data?.truncated ? <Alert type="warning" showIcon message={t("system:oplog.truncated")} /> : null}
82
91
  <Table
83
92
  rowKey="id"
84
93
  loading={query.isLoading}
85
94
  dataSource={query.data?.list ?? []}
86
95
  columns={columns}
87
- pagination={{ hideOnSinglePage: true, pageSize: 50 }}
96
+ pagination={{
97
+ current: page,
98
+ pageSize: size,
99
+ total: query.data?.total ?? 0,
100
+ showSizeChanger: true,
101
+ pageSizeOptions: PAGE_SIZE_OPTIONS,
102
+ showTotal: total => t("system:oplog.total", { total }),
103
+ onChange: (nextPage, nextSize) => {
104
+ // 改每页条数后原页码可能越界:统一回第 1 页
105
+ if (nextSize !== size) {
106
+ setSize(nextSize);
107
+ setPage(1);
108
+ return;
109
+ }
110
+ setPage(nextPage);
111
+ },
112
+ }}
88
113
  />
89
114
  </Space>
90
115
  );
@@ -1,12 +1,13 @@
1
- import type { SystemPermissionsGetData, SystemRolesGetData } from "../../__client/api";
1
+ import type { SystemPermissionsGetData, SystemRolesGetData } from "../../__client";
2
2
 
3
3
  import { useQuery } from "@tanstack/react-query";
4
4
  import { Alert, Space, Table, Tabs, Tag } from "antd";
5
5
  import { useTranslation } from "react-i18next";
6
6
 
7
- import { systemPermissionsGet, systemRolesGet } from "../../__client/api";
7
+ import { systemPermissionsGet, systemRolesGet } from "../../__client";
8
8
  import { isForbidden } from "../errors";
9
9
  import { ForbiddenPanel } from "../forbidden";
10
+ import { visibleRoles } from "./visible-roles";
10
11
 
11
12
  type RoleRow = SystemRolesGetData[number];
12
13
  type PermRow = SystemPermissionsGetData[number];
@@ -19,6 +20,7 @@ export default function RolesPage() {
19
20
  const { t } = useTranslation();
20
21
  const rolesQuery = useQuery({ queryKey: ["system", "roles"], queryFn: systemRolesGet });
21
22
  const permsQuery = useQuery({ queryKey: ["system", "permissions"], queryFn: systemPermissionsGet });
23
+ const rows = visibleRoles(rolesQuery.data ?? []);
22
24
 
23
25
  if ((rolesQuery.error && isForbidden(rolesQuery.error)) || (permsQuery.error && isForbidden(permsQuery.error)))
24
26
  return <ForbiddenPanel t={t} error={rolesQuery.error ?? permsQuery.error} />;
@@ -36,7 +38,13 @@ export default function RolesPage() {
36
38
  ),
37
39
  },
38
40
  { title: "module", dataIndex: "module" },
39
- { title: "title", dataIndex: "title" },
41
+ {
42
+ // R2:solution 角色带 title_key → 按当前语言实时翻译,title(安装期解析文本)兜底;
43
+ // custom/builtin 无 key 原样显示 title(对齐下方权限点 title_key 的渲染先例)
44
+ title: "title",
45
+ dataIndex: "title",
46
+ render: (_: string, row: RoleRow) => (row.title_key ? t(row.title_key, { defaultValue: row.title ?? "" }) : row.title),
47
+ },
40
48
  { title: t("system:rolesPage.holders"), dataIndex: "holders" },
41
49
  {
42
50
  title: t("system:rolesPage.permissions"),
@@ -75,7 +83,7 @@ export default function RolesPage() {
75
83
  children: (
76
84
  <>
77
85
  <Alert style={{ marginBottom: 12 }} type="info" showIcon message={t("system:rolesPage.readonlyHint")} />
78
- <Table rowKey="name" loading={rolesQuery.isLoading} dataSource={rolesQuery.data ?? []} columns={roleColumns} pagination={{ hideOnSinglePage: true }} />
86
+ <Table rowKey="name" loading={rolesQuery.isLoading} dataSource={rows} columns={roleColumns} pagination={{ hideOnSinglePage: true }} />
79
87
  </>
80
88
  ),
81
89
  },
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 角色展示页的可见范围(纯函数,便于用例直接钉住,不必起 React)。
3
+ *
4
+ * superadmin 是平台级角色(跨租户),不属于「站点可分配的角色目录」;列在角色页只会让人
5
+ * 误以为能改它的绑定,故展示层隐去。
6
+ *
7
+ * **只在这一层过滤**:后端 listRoles() 必须原样返回 superadmin——成员资格页
8
+ * (users/roles.tsx)还要拿它作为可授予项(仅 superadmin 可勾选,见那里的 disabled 逻辑)。
9
+ */
10
+ export const HIDDEN_ROLE = "superadmin";
11
+
12
+ export function visibleRoles<T extends { name: string }>(roles: readonly T[]): T[] {
13
+ return roles.filter(r => r.name !== HIDDEN_ROLE);
14
+ }
@@ -1,13 +1,13 @@
1
1
  import type { TFunction } from "i18next";
2
2
 
3
- import type { SystemSettingsGetData } from "../../__client/api";
3
+ import type { SystemSettingsGetData } from "../../__client";
4
4
 
5
5
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
6
6
  import { App, Button, Card, Input, Space, Switch, Tooltip } from "antd";
7
7
  import { useState } from "react";
8
8
  import { useTranslation } from "react-i18next";
9
9
 
10
- import { systemSettingsGet, systemSettingsPut } from "../../__client/api";
10
+ import { systemSettingsGet, systemSettingsPut } from "../../__client";
11
11
  import { explainError, isForbidden } from "../errors";
12
12
  import { ForbiddenPanel } from "../forbidden";
13
13
 
@@ -0,0 +1,21 @@
1
+ /**
2
+ * /system/users 行级操作门禁:只有 superadmin 能改 superadmin 账号。
3
+ *
4
+ * 与后端 `domain/users.ts::assertCanManageTarget` 同一条红线(只有 superadmin 能改
5
+ * superadmin 的口令/状态/资料/角色)。前端这层只为「不给会 403 的按钮」——灰色可点却不
6
+ * 生效比报错更糟——真正的防线在服务端:admin 直打接口同样会被 403 挡住。
7
+ *
8
+ * 单独成文件是为了可测:vitest 跑在 node 环境,没有 jsdom,整页 render 不了。
9
+ */
10
+ export const PLATFORM_ROLE = "superadmin";
11
+
12
+ /**
13
+ * @param myRoles 当前登录者的角色
14
+ * @param targetRoles 行上用户的角色
15
+ * @returns 是否允许对目标账号执行管理操作(改密/启停/改资料/成员资格)
16
+ */
17
+ export function canManageUser(myRoles: readonly string[], targetRoles: readonly string[]): boolean {
18
+ if (!targetRoles.includes(PLATFORM_ROLE))
19
+ return true;
20
+ return myRoles.includes(PLATFORM_ROLE);
21
+ }
@@ -1,4 +1,4 @@
1
- import type { SystemUsersGetData } from "../../__client/api";
1
+ import type { SystemUsersGetData } from "../../__client";
2
2
 
3
3
  import { PlusOutlined } from "@ant-design/icons";
4
4
  import { useUserStore } from "@oj-module/runtime";
@@ -9,16 +9,19 @@ import { useTranslation } from "react-i18next";
9
9
 
10
10
  import { useNavigate } from "react-router";
11
11
 
12
- import { systemDepartmentsGet, systemUsersForbiddenPut, systemUsersGet, systemUsersPasswordPut, systemUsersPost } from "../../__client/api";
12
+ import { systemDepartmentsGet, systemUsersForbiddenPut, systemUsersGet, systemUsersPasswordPut, systemUsersPost } from "../../__client";
13
13
  import { buildTreeData, subtreeIds } from "../departments/tree";
14
14
  import { explainError, isForbidden } from "../errors";
15
15
  import { ForbiddenPanel } from "../forbidden";
16
+ import { canManageUser } from "./can-manage";
16
17
 
17
18
  type UserRow = SystemUsersGetData[number];
18
19
 
19
20
  /**
20
21
  * 用户管理(C1):GET /system/users 列表 + 建号 / 禁用切换 / 重置口令。
21
22
  * 本人行禁用一切自操作(后端 assertNotSelf 400 兜底,前端不给会失败的按钮,R3);
23
+ * superadmin 行也禁用一切操作,除非当前登录者是 superadmin(红线:只有 superadmin 能改
24
+ * superadmin——后端 assertCanManageTarget 403 兜底,前端把按钮置灰);
22
25
  * 成员资格入口:superadmin 与 **本租户 admin** 均可见(授予 admin 已下放到租户内,
23
26
  * 但授予 superadmin 仍只许 superadmin——见 domain/users.ts::assertMembershipAllowed)。
24
27
  * 部门列标出主部门,顶部可按部门筛选(含子部门)——筛选在前端做,名单本来就是一页全量。
@@ -117,8 +120,10 @@ export default function UsersPage() {
117
120
  title={t("system:users.forbidden")}
118
121
  description={`${row.username} → ${forbidden ? "false" : "true"}`}
119
122
  onConfirm={() => forbiddenMut.mutate({ uid: row.id, forbidden: !forbidden })}
123
+ // 置灰的 Switch 不会触发 Popconfirm,这里只是把「为什么点不了」挡在确认框之前
124
+ disabled={String(row.id) === me || !canManageUser(myRoles, row.roles)}
120
125
  >
121
- <Switch size="small" checked={forbidden} disabled={String(row.id) === me} />
126
+ <Switch size="small" checked={forbidden} disabled={String(row.id) === me || !canManageUser(myRoles, row.roles)} />
122
127
  </Popconfirm>
123
128
  {row.must_change ? <Tag color="orange">{t("system:users.firstLoginHint")}</Tag> : null}
124
129
  </Space>
@@ -127,20 +132,23 @@ export default function UsersPage() {
127
132
  {
128
133
  title: t("system:users.columns.actions"),
129
134
  key: "actions",
130
- render: (_: unknown, row: UserRow) => (
131
- <Space>
132
- <Button size="small" disabled={String(row.id) === me} onClick={() => setPwdTarget(row)}>
133
- {t("system:users.resetPwd")}
134
- </Button>
135
- {canManageMembership
136
- ? (
137
- <Button size="small" type="link" onClick={() => navigate(`/system/users/${row.id}/roles`)}>
138
- {t("system:users.membership")}
139
- </Button>
140
- )
141
- : null}
142
- </Space>
143
- ),
135
+ render: (_: unknown, row: UserRow) => {
136
+ const manageable = canManageUser(myRoles, row.roles);
137
+ return (
138
+ <Space>
139
+ <Button size="small" disabled={String(row.id) === me || !manageable} onClick={() => setPwdTarget(row)}>
140
+ {t("system:users.resetPwd")}
141
+ </Button>
142
+ {canManageMembership
143
+ ? (
144
+ <Button size="small" type="link" disabled={!manageable} onClick={() => navigate(`/system/users/${row.id}/roles`)}>
145
+ {t("system:users.membership")}
146
+ </Button>
147
+ )
148
+ : null}
149
+ </Space>
150
+ );
151
+ },
144
152
  },
145
153
  ];
146
154
 
@@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next";
6
6
 
7
7
  import { useNavigate, useParams } from "react-router";
8
8
 
9
- import { systemRolesGet, systemUsersGet, systemUsersRolesPut } from "../../__client/api";
9
+ import { systemRolesGet, systemUsersGet, systemUsersRolesPut } from "../../__client";
10
10
  import { explainError, isForbidden } from "../errors";
11
11
  import { ForbiddenPanel } from "../forbidden";
12
12
 
@@ -4,7 +4,7 @@ import { SettingOutlined } from "@ant-design/icons";
4
4
 
5
5
  import { createElement, lazy } from "react";
6
6
 
7
- import { createSystemClient } from "./__client/api";
7
+ import { createSystemClient } from "./__client";
8
8
 
9
9
  const Users = lazy(() => import("./pages/users"));
10
10
  const UserRoles = lazy(() => import("./pages/users/roles"));
@@ -17,6 +17,9 @@ export const API_PREFIX = "/web";
17
17
  /** 认领身份前缀(契约 apiPrefix,字面等于模块目录名):跨模块认领护栏对比基准 */
18
18
  export const MODULE_PREFIX = "/web";
19
19
 
20
+ /** 认领方模块(web.config clientOwners 指定):跨模块认领护栏对比基准 */
21
+ export const MODULE_OWNER = "/app";
22
+
20
23
  /**
21
24
  * 模块 entry 的 onInit 里一行创建(构造即 install):完成 apiPrefix 登记 +
22
25
  * scoped request 绑定,返回绑好前缀的端点函数集合。一模块一 client;
@@ -28,8 +31,8 @@ export function createWebClient(ctx: ModuleContext) {
28
31
  // 单槽、卸载互相耦合;跨模块需求应由本模块创建后经 provider 暴露。
29
32
  // 注意对比基准是 MODULE_PREFIX(目录名)而非 API_PREFIX——D-M8 映射时线上前缀
30
33
  // 属于别的命名空间,认领身份仍按目录名。
31
- if (import.meta.env.DEV && ctx.module.name !== MODULE_PREFIX.slice(1)) {
32
- console.warn(`[ojm-api] 模块 "${ctx.module.name}" 正在认领 ${MODULE_PREFIX} 的 client——跨模块认领会共享 request 单槽、卸载互相耦合;应由 "${MODULE_PREFIX.slice(1)}" 模块在自身 onInit 创建,跨模块需求经 provider 暴露。`);
34
+ if (import.meta.env.DEV && ctx.module.name !== MODULE_OWNER.slice(1)) {
35
+ console.warn(`[ojm-api] 模块 "${ctx.module.name}" 正在认领 ${MODULE_PREFIX} 的 client——跨模块认领会共享 request 单槽、卸载互相耦合;应由 "${MODULE_OWNER.slice(1)}" 模块在自身 onInit 创建,跨模块需求经 provider 暴露。`);
33
36
  }
34
37
  ctx.register.apiPrefix(API_PREFIX);
35
38
  bindRequest(ctx.utils.request);
@@ -28,7 +28,7 @@ import {
28
28
  platformMarketTrustPost,
29
29
  platformMarketUninstallImpactGet,
30
30
  platformMarketUninstallPost,
31
- } from "../../../_platform/web/__client/api";
31
+ } from "../../../app/web/_platform/__client";
32
32
 
33
33
  // 绑定运行时 request 到生成 client 的模块级单槽(生成 client 是静态单例,必须由某处
34
34
  // bindRequest;此处兜底确保市场子应用无论挂载顺序都能拿到请求实例)。与 _platform 模块
@@ -4,8 +4,8 @@
4
4
  /* eslint-disable test/prefer-lowercase-title -- AC 编号是验收锚点,保留大写 */
5
5
  import { afterEach, beforeAll, describe, expect, it } from "vitest";
6
6
 
7
- import { esc, firstAttr } from "../src/auth/api/_shared/ldap";
8
- import { createLdapLogin } from "../src/auth/api/_shared/ldap-login";
7
+ import { esc, firstAttr } from "../src/app/api/auth/_shared/ldap";
8
+ import { createLdapLogin } from "../src/app/api/auth/_shared/ldap-login";
9
9
  import { installDb } from "./fixture";
10
10
 
11
11
  let dbo: ReturnType<typeof installDb>;
@@ -1,11 +1,11 @@
1
1
  // BDD:登录链——B1 登录顺序(D14 统一 401)、B2 首登改密(AC1-27)、B3 refresh 轮换/reuse 检测(AC1-13)、B4 JWT 仅 sub。
2
2
  // deps 全注入(Node 侧 crypto 只读不可 stub,设计 §9.1);fake bcrypt 以 `hashed:<pw>` 为口令哈希约定。
3
3
  /* eslint-disable test/prefer-lowercase-title -- AC 编号是验收锚点,保留大写 */
4
- import type { SessionDeps } from "../src/auth/api/_shared/session";
4
+ import type { SessionDeps } from "../src/app/api/auth/_shared/session";
5
5
 
6
6
  import { afterEach, beforeAll, describe, expect, it } from "vitest";
7
- import { assertPermission } from "../src/_platform/api/_shared/service/infra/access";
8
- import { createSession } from "../src/auth/api/_shared/session";
7
+ import { assertPermission } from "../src/app/api/_platform/_shared/service/infra/access";
8
+ import { createSession } from "../src/app/api/auth/_shared/session";
9
9
  import { installDb } from "./fixture";
10
10
 
11
11
  let dbo: ReturnType<typeof installDb>;
@@ -5,7 +5,7 @@
5
5
  /* eslint-disable test/prefer-lowercase-title -- AC 编号是验收锚点,保留大写 */
6
6
  import { afterEach, beforeAll, describe, expect, it } from "vitest";
7
7
 
8
- import { loginBlocked, loginFailed, loginSucceeded, resetLoginThrottle } from "../src/_platform/api/_shared/service/infra/rate";
8
+ import { loginBlocked, loginFailed, loginSucceeded, resetLoginThrottle } from "../src/app/api/_platform/_shared/service/infra/rate";
9
9
  import { installDb } from "./fixture";
10
10
 
11
11
  let dbo: ReturnType<typeof installDb>;
@@ -1,6 +1,6 @@
1
1
  // 后端 BDD 夹具:node:sqlite 内存库 + **真实迁移文件**建表,装到 globalThis.db——
2
2
  // 与 oj 运行时注入的全局同名同面,service 代码不改一行即可被测。
3
- // 夹具直接读 src/<模块>/api/migrations/*.sql,与迁移不可能漂移(沿用 oj-wiki 先例)。
3
+ // 夹具直接读 src/app/api/<模块>/migrations/*.sql,与迁移不可能漂移(沿用 oj-wiki 先例)。
4
4
  //
5
5
  // **构造器垫片(db.table / listTables)**:oj 的构造器是二进制内的 Rust 实现,Node 侧无可复用
6
6
  // 模块,故这里按手册契约(oj-api-dev api-manual「查询构造器」)写一层**最小 JS 渲染层**——
@@ -33,6 +33,8 @@ const { join, dirname } = req("node:path");
33
33
  const { fileURLToPath } = req("node:url");
34
34
 
35
35
  const SRC = join(dirname(fileURLToPath(import.meta.url)), "..", "src");
36
+ /** 后端模块根(0.2.55 布局:`src/app/api/<模块>`);迁移按目录发现,不在夹具里抄模块清单。 */
37
+ const API_ROOT = join(SRC, "app", "api");
36
38
 
37
39
  export const ADMIN = 1;
38
40
 
@@ -465,9 +467,9 @@ function listTables(dbo: any, prefix?: string): string[] {
465
467
  /** 建内存库 + 注入全局;返回 DatabaseSync 供用例直插数据/断言。 */
466
468
  export function installDb(): any {
467
469
  const dbo = new DatabaseSync(":memory:");
468
- // 重放 src 下全部模块的迁移(方言专属文件除外)
469
- for (const mod of readdirSync(SRC)) {
470
- const dir = join(SRC, mod, "api", "migrations");
470
+ // 重放 src/app/api 下全部模块的迁移(方言专属文件除外)
471
+ for (const mod of readdirSync(API_ROOT)) {
472
+ const dir = join(API_ROOT, mod, "migrations");
471
473
  if (!existsSync(dir))
472
474
  continue;
473
475
  for (const f of readdirSync(dir).filter((f: string) => f.endsWith(".sql") && !/\.(?:mysql|pg)\.sql$/.test(f)).sort())
@@ -3,7 +3,7 @@
3
3
  // (真实迁移建表 / 运行时全局 db 同面 / 事务往返)——S0 spike 的「一条真实判权 AC + 形态证明」。
4
4
  import { beforeAll, describe, expect, it } from "vitest";
5
5
 
6
- import { decideAccess, moduleOf } from "../src/_platform/api/_shared/service/infra/guard";
6
+ import { decideAccess, moduleOf } from "../src/app/api/_platform/_shared/service/infra/guard";
7
7
  import { ADMIN, installDb } from "./fixture";
8
8
 
9
9
  const as = (roles: string[], bindings: Record<string, string[]> = {}) => ({ roles, bindings });
@@ -4,14 +4,14 @@
4
4
  // 只保证读回自己写出的内容;任何不符格式的字节一律视为损坏 → 空状态,损坏上下文并入
5
5
  // 读结果(corruptNote 出口,禁 console——release isolate console 抛 BadResource,Finding A)。
6
6
  // fs 依赖经 globalThis.fs 注入(平台 isolate 注入 FsApi;测试注入 node:fs/promises 适配)。
7
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
7
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
8
8
  import * as fsp from "node:fs/promises";
9
9
  import { tmpdir } from "node:os";
10
10
  import { join } from "node:path";
11
11
 
12
12
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
13
- import { atomicWriteText } from "../../src/_platform/api/_lifecycle/atomic";
14
- import { createStateStore } from "../../src/_platform/api/_lifecycle/state";
13
+ import { atomicWriteText } from "../../src/app/api/_platform/_lifecycle/atomic";
14
+ import { createStateStore } from "../../src/app/api/_platform/_lifecycle/state";
15
15
 
16
16
  const g = globalThis as any;
17
17
 
@@ -5,14 +5,14 @@
5
5
  // 与 PEM 私钥块实体(header 后跟 ≥100 字符 base64 体;tasks.ts redact 正则与测试假体
6
6
  // 体长 <100 不误伤)双口径扫描。
7
7
  // ③ 集成(market-api.spec):信任库为空 + bootstrapKey 注入 → cdn 安装直达 done 不落 needs_trust。
8
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
8
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
9
9
  import * as fsp from "node:fs/promises";
10
10
  import { tmpdir } from "node:os";
11
11
  import { dirname, join } from "node:path";
12
12
  import { fileURLToPath } from "node:url";
13
13
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
14
- import { createTrustStore } from "../../src/_platform/api/_lifecycle/trust";
15
- import { BOOTSTRAP_KEY, seedBootstrapKey } from "../../src/_platform/api/market/bootstrap";
14
+ import { createTrustStore } from "../../src/app/api/_platform/_lifecycle/trust";
15
+ import { BOOTSTRAP_KEY, seedBootstrapKey } from "../../src/app/api/_platform/market/bootstrap";
16
16
 
17
17
  const g = globalThis as any;
18
18
 
@@ -8,19 +8,19 @@
8
8
  // 快照逐数组 ⊇ 声明,少 = 注册丢失判失败)collect-all 全败齐报。
9
9
  // entry 浏览器加载不在安装器验(Node import 必假绿——设计 §4.1),注释钉口径。
10
10
  // fetch/sha384/query/reload 面经 ctx 注入 stub(同 steps-fs 范式),fs 为 globalThis.fs 面。
11
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
12
- import type { StateStore } from "../../src/_platform/api/_lifecycle/state";
13
- import type { DeployWebContext, ModuleEntry } from "../../src/_platform/api/_lifecycle/steps/deploy-web";
14
- import type { ReloadContext, ReloadFace, ReloadResult } from "../../src/_platform/api/_lifecycle/steps/reload";
15
- import type { SmokeContext } from "../../src/_platform/api/_lifecycle/steps/smoke";
11
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
12
+ import type { StateStore } from "../../src/app/api/_platform/_lifecycle/state";
13
+ import type { DeployWebContext, ModuleEntry } from "../../src/app/api/_platform/_lifecycle/steps/deploy-web";
14
+ import type { ReloadContext, ReloadFace, ReloadResult } from "../../src/app/api/_platform/_lifecycle/steps/reload";
15
+ import type { SmokeContext } from "../../src/app/api/_platform/_lifecycle/steps/smoke";
16
16
  import { createHash } from "node:crypto";
17
17
  import * as fsp from "node:fs/promises";
18
18
  import { tmpdir } from "node:os";
19
19
  import { join } from "node:path";
20
20
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
21
- import { deployWeb, posixPath, upsertModulesJson } from "../../src/_platform/api/_lifecycle/steps/deploy-web";
22
- import { reloadAndAssert } from "../../src/_platform/api/_lifecycle/steps/reload";
23
- import { probeUrlOf, smoke } from "../../src/_platform/api/_lifecycle/steps/smoke";
21
+ import { deployWeb, posixPath, upsertModulesJson } from "../../src/app/api/_platform/_lifecycle/steps/deploy-web";
22
+ import { reloadAndAssert } from "../../src/app/api/_platform/_lifecycle/steps/reload";
23
+ import { probeUrlOf, smoke } from "../../src/app/api/_platform/_lifecycle/steps/smoke";
24
24
 
25
25
  const g = globalThis as any;
26
26
 
@@ -12,22 +12,22 @@
12
12
  // 过期(30min)可接管。setEnabled 四处同写(D12/AC-P1-6):锁摘除/恢复 + sidecar + route + modules。
13
13
  // 步骤面全 stub 注入(DIP):fetch/exec/tar/reload 不真跑;register/smoke/audit 走真实内存库夹具。
14
14
  /* eslint-disable test/prefer-lowercase-title -- 任务/AC 编号是验收锚点,保留大写(register.spec 同约定) */
15
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
16
- import type { InstallRequest } from "../../src/_platform/api/_lifecycle/installer";
17
- import type { StateStore } from "../../src/_platform/api/_lifecycle/state";
18
- import type { ExecFace } from "../../src/_platform/api/_lifecycle/steps/extract";
15
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
16
+ import type { InstallRequest } from "../../src/app/api/_platform/_lifecycle/installer";
17
+ import type { StateStore } from "../../src/app/api/_platform/_lifecycle/state";
18
+ import type { ExecFace } from "../../src/app/api/_platform/_lifecycle/steps/extract";
19
19
  import { createHash } from "node:crypto";
20
20
  import * as fsp from "node:fs/promises";
21
21
  import { tmpdir } from "node:os";
22
22
  import { join } from "node:path";
23
23
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
24
24
  import { defaultTestKey, nodeVerifyEd25519, signedArchiveFor } from "../../../src/lifecycle/__tests__/helpers";
25
- import { atomicWriteText } from "../../src/_platform/api/_lifecycle/atomic";
26
- import { gcVersions, reconcile, runInstall, setEnabled, STAGES } from "../../src/_platform/api/_lifecycle/installer";
27
- import { acquireTaskLock, commitLockEntry, parseLock, readLock, serializeLock, writeLock } from "../../src/_platform/api/_lifecycle/lock";
28
- import { createStateStore } from "../../src/_platform/api/_lifecycle/state";
29
- import { getTask, readTasks, redact, sweepTimedOut, writeTask } from "../../src/_platform/api/_lifecycle/tasks";
30
- import { createTrustStore } from "../../src/_platform/api/_lifecycle/trust";
25
+ import { atomicWriteText } from "../../src/app/api/_platform/_lifecycle/atomic";
26
+ import { gcVersions, reconcile, runInstall, setEnabled, STAGES } from "../../src/app/api/_platform/_lifecycle/installer";
27
+ import { acquireTaskLock, commitLockEntry, parseLock, readLock, serializeLock, writeLock } from "../../src/app/api/_platform/_lifecycle/lock";
28
+ import { createStateStore } from "../../src/app/api/_platform/_lifecycle/state";
29
+ import { getTask, readTasks, redact, sweepTimedOut, writeTask } from "../../src/app/api/_platform/_lifecycle/tasks";
30
+ import { createTrustStore } from "../../src/app/api/_platform/_lifecycle/trust";
31
31
  import { installDb } from "../fixture";
32
32
 
33
33
  const g = globalThis as any;
@@ -7,11 +7,11 @@
7
7
  // 简报 `task/:id` 段参数形态在模板 http 面无先例(contacts/detail?id= 同款 query 惯例),备案于任务报告。
8
8
  // 步骤面全 stub 注入(DIP):fetch/exec/reload 不真跑;register/smoke/audit 走真实内存库夹具。
9
9
  /* eslint-disable test/prefer-lowercase-title -- 端点动词/路径是验收锚点,保留大写(installer.spec 同约定) */
10
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
11
- import type { FetchFace } from "../../src/_platform/api/_lifecycle/steps/download";
12
- import type { ExecFace } from "../../src/_platform/api/_lifecycle/steps/extract";
13
- import type { ReloadFace } from "../../src/_platform/api/_lifecycle/steps/reload";
14
- import type { MarketEnv } from "../../src/_platform/api/market/api";
10
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
11
+ import type { FetchFace } from "../../src/app/api/_platform/_lifecycle/steps/download";
12
+ import type { ExecFace } from "../../src/app/api/_platform/_lifecycle/steps/extract";
13
+ import type { ReloadFace } from "../../src/app/api/_platform/_lifecycle/steps/reload";
14
+ import type { MarketEnv } from "../../src/app/api/_platform/market/api";
15
15
  import { createHash } from "node:crypto";
16
16
  import { readFileSync } from "node:fs";
17
17
  import * as fsp from "node:fs/promises";
@@ -22,14 +22,14 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
22
22
  import { parse } from "yaml";
23
23
  import { defaultTestKey, nodeVerifyEd25519, signedArchiveFor } from "../../../src/lifecycle/__tests__/helpers";
24
24
  import { signIndex } from "../../../src/publish";
25
- import { readLock, writeLock } from "../../src/_platform/api/_lifecycle/lock";
26
- import { compareVersions } from "../../src/_platform/api/_lifecycle/semver";
27
- import { createStateStore } from "../../src/_platform/api/_lifecycle/state";
28
- import { getTask, writeTask } from "../../src/_platform/api/_lifecycle/tasks";
29
- import { createTrustStore } from "../../src/_platform/api/_lifecycle/trust";
30
- import { invalidateAllUsers } from "../../src/_platform/api/_shared/service/infra/access";
31
- import { createMarketApi, marketEnv, parseVersionsJson, versionsJsonPathOf } from "../../src/_platform/api/market/api";
32
- import { localIndexSource } from "../../src/_platform/api/market/index-source";
25
+ import { readLock, writeLock } from "../../src/app/api/_platform/_lifecycle/lock";
26
+ import { compareVersions } from "../../src/app/api/_platform/_lifecycle/semver";
27
+ import { createStateStore } from "../../src/app/api/_platform/_lifecycle/state";
28
+ import { getTask, writeTask } from "../../src/app/api/_platform/_lifecycle/tasks";
29
+ import { createTrustStore } from "../../src/app/api/_platform/_lifecycle/trust";
30
+ import { invalidateAllUsers } from "../../src/app/api/_platform/_shared/service/infra/access";
31
+ import { createMarketApi, marketEnv, parseVersionsJson, versionsJsonPathOf } from "../../src/app/api/_platform/market/api";
32
+ import { localIndexSource } from "../../src/app/api/_platform/market/index-source";
33
33
  import { installDb } from "../fixture";
34
34
 
35
35
  function hexToBytes(hex: string): Uint8Array {
@@ -305,7 +305,7 @@ describe("权限门(R9:市场编排 superadmin 独占,meta 登录即可)
305
305
  it("pair-pin:meta apiVersion === _platform/manifest.yaml version(手工镜像漂移防护,P2-9)", async () => {
306
306
  // API_VERSION(market/api.ts)与 _platform/manifest.yaml version 为手工同步的双真源;
307
307
  // 本测试经 meta 端点间接钉住 API_VERSION,漂移即红(peerPlatform 预检口径失真)。
308
- const manifestPath = join(dirname(fileURLToPath(import.meta.url)), "../../src/_platform/api/manifest.yaml");
308
+ const manifestPath = join(dirname(fileURLToPath(import.meta.url)), "../../src/app/api/_platform/manifest.yaml");
309
309
  const manifestVersion = (parse(readFileSync(manifestPath, "utf8")) as { version: string }).version;
310
310
  const api = createMarketApi(makeEnv());
311
311
  g.http = { user: { id: 1 } };
@@ -7,16 +7,16 @@
7
7
  // rollbackGuard 纯函数:目标产物 max seq < 账本 head = 降版部署,禁止(OJ-9/M003 同源红线);
8
8
  // seq 为数值非版本号,直接数值比较(semver.ts 不适用)。
9
9
  // exec/fs 面注入 stub(同 steps-fs 范式),不真跑 oj/sqlite。
10
- import type { FsFace } from "../../src/_platform/api/_lifecycle/atomic";
11
- import type { StateStore } from "../../src/_platform/api/_lifecycle/state";
12
- import type { ExecFace } from "../../src/_platform/api/_lifecycle/steps/extract";
13
- import type { MigrateContext } from "../../src/_platform/api/_lifecycle/steps/migrate";
10
+ import type { FsFace } from "../../src/app/api/_platform/_lifecycle/atomic";
11
+ import type { StateStore } from "../../src/app/api/_platform/_lifecycle/state";
12
+ import type { ExecFace } from "../../src/app/api/_platform/_lifecycle/steps/extract";
13
+ import type { MigrateContext } from "../../src/app/api/_platform/_lifecycle/steps/migrate";
14
14
  import * as fsp from "node:fs/promises";
15
15
  import { tmpdir } from "node:os";
16
16
  import { join } from "node:path";
17
17
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
18
- import { parseLock } from "../../src/_platform/api/_lifecycle/lock";
19
- import { buildMigrateCmd, maxAppliedSeq, migrate, migrateSvcDirOf, parseMigrateOutput, rollbackGuard } from "../../src/_platform/api/_lifecycle/steps/migrate";
18
+ import { parseLock } from "../../src/app/api/_platform/_lifecycle/lock";
19
+ import { buildMigrateCmd, maxAppliedSeq, migrate, migrateSvcDirOf, parseMigrateOutput, rollbackGuard } from "../../src/app/api/_platform/_lifecycle/steps/migrate";
20
20
 
21
21
  const g = globalThis as any;
22
22
  const DB_BYTES = new TextEncoder().encode("sqlite-header-bytes\n");