@oj-module/cli 0.2.18 → 0.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. package/devkit/README.md +4 -4
  2. package/devkit/SKILL.md +41 -37
  3. package/devkit/framework-dev.md +6 -5
  4. package/devkit/manual.md +352 -168
  5. package/devkit/menu-rules.md +3 -3
  6. package/devkit/module-dev-and-cdn-playground.md +31 -22
  7. package/devkit/sql-builder-missing-capabilities-pr.md +11 -6
  8. package/package.json +2 -2
  9. package/shell-dist/assets/runtime.js +59 -34
  10. package/shell-dist/index.html +2 -2
  11. package/shell-dist/versions.json +3 -3
  12. package/src/__tests__/build-backend.test.ts +54 -0
  13. package/src/__tests__/init-packagejson.test.ts +29 -0
  14. package/src/__tests__/layout.test.ts +176 -0
  15. package/src/__tests__/module-list.test.ts +66 -0
  16. package/src/__tests__/oj-caps.test.ts +114 -0
  17. package/src/alias-paths.ts +102 -0
  18. package/src/args.ts +54 -4
  19. package/src/build.ts +41 -9
  20. package/src/builtin.ts +93 -0
  21. package/src/bundle.ts +254 -38
  22. package/src/config.ts +82 -9
  23. package/src/contract/__tests__/market-client.integration.test.ts +14 -14
  24. package/src/contract/check.ts +56 -28
  25. package/src/contract/emit-stub.ts +21 -9
  26. package/src/contract/handler-scan.ts +19 -10
  27. package/src/contract/run.ts +14 -4
  28. package/src/dev.ts +80 -9
  29. package/src/index.ts +27 -7
  30. package/src/info.ts +69 -10
  31. package/src/init.ts +114 -28
  32. package/src/layout.ts +282 -24
  33. package/src/lifecycle/__tests__/args.test.ts +20 -0
  34. package/src/lifecycle/__tests__/builtin.test.ts +97 -0
  35. package/src/lifecycle/__tests__/bundle.test.ts +184 -6
  36. package/src/lifecycle/__tests__/manifest-contract.test.ts +2 -2
  37. package/src/lifecycle/__tests__/new.test.ts +129 -0
  38. package/src/lifecycle/__tests__/smoke.test.ts +2 -1
  39. package/src/manifest-contract.ts +16 -3
  40. package/src/market/__tests__/bootstrap.test.ts +17 -9
  41. package/src/market/bootstrap.ts +30 -17
  42. package/src/market/publish.ts +10 -2
  43. package/src/new.ts +208 -0
  44. package/src/oj-caps.ts +133 -0
  45. package/src/oj.ts +27 -4
  46. package/src/preview-config.ts +1 -1
  47. package/src/preview.ts +2 -1
  48. package/src/site.ts +2 -2
  49. package/src/usage.ts +24 -12
  50. package/src/vendor.ts +5 -1
  51. package/templates/{api/config.yaml → config.yaml} +13 -8
  52. package/templates/global.d.ts +7 -6
  53. package/templates/init.include.yaml +41 -36
  54. package/templates/package.json +3 -1
  55. package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/installer.ts +4 -0
  56. package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/smoke.ts +21 -2
  57. package/templates/{api/src/_platform → src/_platform/api}/fixtures/l1-test-user.sql +4 -5
  58. package/templates/{api/src/_platform → src/_platform/api}/market/api.ts +25 -15
  59. package/templates/src/_platform/api/seed.mysql.sql +16 -0
  60. package/templates/src/_platform/api/seed.pg.sql +16 -0
  61. package/templates/{api/src/_platform → src/_platform/api}/seed.sql +16 -5
  62. package/templates/{api/src/_platform → src/_platform/api}/solutions/api.ts +7 -3
  63. package/templates/{api/src/auth → src/auth/api}/_shared/login-throttle.ts +2 -2
  64. package/templates/{api/src/auth → src/auth/api}/_shared/session.ts +2 -2
  65. package/templates/{api/src/auth → src/auth/api}/api.ts +1 -1
  66. package/templates/{api/src/auth → src/auth/api}/ldap-login/api.ts +1 -1
  67. package/templates/src/auth/api/manifest.yaml +6 -0
  68. package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/InstallDialog.test.tsx +53 -1
  69. package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/marketClient.fake.ts +6 -0
  70. package/templates/{web/src/oj-market → src/oj-market/web}/api/client.ts +5 -0
  71. package/templates/{web/src/oj-market → src/oj-market/web}/api/context.ts +2 -2
  72. package/templates/{web/src/oj-market → src/oj-market/web}/api/defaultClient.ts +7 -1
  73. package/templates/src/oj-market/web/components/InstallDialog.tsx +194 -0
  74. package/templates/{web/src/oj-market → src/oj-market/web}/components/RollbackDialog.tsx +7 -3
  75. package/templates/{web/src/oj-market → src/oj-market/web}/components/UninstallConfirm.tsx +18 -7
  76. package/templates/{web/src/oj-market → src/oj-market/web}/hooks/useMarket.ts +29 -12
  77. package/templates/{web/src/oj-market → src/oj-market/web}/locales/en-US.json +10 -1
  78. package/templates/{web/src/oj-market → src/oj-market/web}/locales/zh-CN.json +10 -1
  79. package/templates/{web/src/oj-market → src/oj-market/web}/types.ts +15 -2
  80. package/templates/{api/src/system → src/system/api}/login-methods/api.ts +2 -2
  81. package/templates/src/system/api/manifest.yaml +6 -0
  82. package/templates/{api/src/system → src/system/api}/oplog/api.ts +4 -4
  83. package/templates/{api/src/system → src/system/api}/permissions/api.ts +3 -3
  84. package/templates/{api/src/system → src/system/api}/roles/api.ts +3 -3
  85. package/templates/{api/src/system → src/system/api}/roles/bindings/api.ts +3 -3
  86. package/templates/{api/src/system → src/system/api}/settings/api.ts +3 -3
  87. package/templates/{api/src/system → src/system/api}/users/api.ts +3 -3
  88. package/templates/{api/src/system → src/system/api}/users/forbidden/api.ts +3 -3
  89. package/templates/{api/src/system → src/system/api}/users/password/api.ts +3 -3
  90. package/templates/{api/src/system → src/system/api}/users/roles/api.ts +3 -3
  91. package/templates/{web/src/system → src/system/web}/locales/en-US.json +1 -1
  92. package/templates/{web/src/system → src/system/web}/locales/zh-CN.json +1 -1
  93. package/templates/src/tsconfig.json +16 -0
  94. package/templates/{api/tests → tests}/auth-ldap.spec.ts +2 -2
  95. package/templates/{api/tests → tests}/auth-session.spec.ts +3 -3
  96. package/templates/{api/tests → tests}/auth-throttle.spec.ts +1 -1
  97. package/templates/tests/fixture.ts +434 -0
  98. package/templates/{api/tests → tests}/guard.spec.ts +1 -1
  99. package/templates/{api/tests → tests}/l1-builder.test.ts +2 -2
  100. package/templates/{api/tests → tests}/lifecycle/atomic-state.spec.ts +3 -3
  101. package/templates/{api/tests → tests}/lifecycle/bootstrap.spec.ts +8 -4
  102. package/templates/{api/tests → tests}/lifecycle/deploy-smoke.spec.ts +31 -8
  103. package/templates/{api/tests → tests}/lifecycle/installer.spec.ts +15 -15
  104. package/templates/{api/tests → tests}/lifecycle/market-api.spec.ts +30 -29
  105. package/templates/{api/tests → tests}/lifecycle/migrate.spec.ts +6 -6
  106. package/templates/{api/tests → tests}/lifecycle/preflight.spec.ts +4 -4
  107. package/templates/{api/tests → tests}/lifecycle/register.spec.ts +1 -1
  108. package/templates/{api/tests → tests}/lifecycle/semver.spec.ts +1 -1
  109. package/templates/{api/tests → tests}/lifecycle/sha384.spec.ts +1 -1
  110. package/templates/{api/tests → tests}/lifecycle/sig-fixtures.spec.ts +1 -1
  111. package/templates/{api/tests → tests}/lifecycle/solutions-api.spec.ts +1 -1
  112. package/templates/{api/tests → tests}/lifecycle/steps-fs.spec.ts +12 -12
  113. package/templates/{api/tests → tests}/lifecycle/text-edit.spec.ts +1 -1
  114. package/templates/{api/tests → tests}/lifecycle/tgz-guard.spec.ts +1 -1
  115. package/templates/{api/tests → tests}/lifecycle/trust.spec.ts +5 -5
  116. package/templates/{api/tests → tests}/lifecycle/uninstall.spec.ts +7 -7
  117. package/templates/{api/tests → tests}/lifecycle/verify-checks.spec.ts +9 -6
  118. package/templates/{api/tests → tests}/lifecycle/verify-report.spec.ts +7 -5
  119. package/templates/{api/tests → tests}/query-utils.spec.ts +1 -1
  120. package/templates/{api/tests → tests}/rbac-audit.spec.ts +1 -1
  121. package/templates/{api/tests → tests}/rbac-contacts.spec.ts +2 -2
  122. package/templates/{api/tests → tests}/rbac-guard.spec.ts +1 -1
  123. package/templates/{api/tests → tests}/rbac-roles.spec.ts +2 -2
  124. package/templates/{api/tests → tests}/rbac-seed.spec.ts +13 -11
  125. package/templates/{api/tests → tests}/rbac-settings.spec.ts +2 -2
  126. package/templates/{api/tests → tests}/rbac-solution-perms.spec.ts +1 -1
  127. package/templates/{api/tests → tests}/rbac-users.spec.ts +3 -3
  128. package/templates/tests/seed-role-accounts.spec.ts +87 -0
  129. package/templates/tsconfig.json +5 -1
  130. package/templates/tsconfig.paths.json +19 -0
  131. package/templates/types/oj-modules.d.ts +17 -0
  132. package/templates/vitest.config.ts +22 -0
  133. package/templates/web.config.ts +23 -13
  134. package/vendor/host-versions.json +3 -3
  135. package/templates/api/src/_platform/seed.mysql.sql +0 -8
  136. package/templates/api/src/_platform/seed.pg.sql +0 -8
  137. package/templates/api/src/auth/manifest.yaml +0 -5
  138. package/templates/api/src/system/manifest.yaml +0 -5
  139. package/templates/api/tests/fixture.ts +0 -58
  140. package/templates/api/tsconfig.json +0 -8
  141. package/templates/api/vitest.config.ts +0 -12
  142. package/templates/logs/task-events.jsonl +0 -0
  143. package/templates/web/src/oj-market/components/InstallDialog.tsx +0 -112
  144. /package/templates/{api/.ojm-api-exempt.json → .ojm-api-exempt.json} +0 -0
  145. /package/templates/{api/seed.mysql.sql → seed.mysql.sql} +0 -0
  146. /package/templates/{api/seed.pg.sql → seed.pg.sql} +0 -0
  147. /package/templates/{api/src/_platform → src/_platform/api}/README.md +0 -0
  148. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/atomic.ts +0 -0
  149. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/history.ts +0 -0
  150. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/lock.ts +0 -0
  151. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/semver.ts +0 -0
  152. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/sha384.ts +0 -0
  153. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/state.ts +0 -0
  154. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/deploy-web.ts +0 -0
  155. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/download.ts +0 -0
  156. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/extract.ts +0 -0
  157. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/migrate.ts +0 -0
  158. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/preflight.ts +0 -0
  159. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/register.ts +0 -0
  160. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/reload.ts +0 -0
  161. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/steps/verify.ts +0 -0
  162. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/tasks.ts +0 -0
  163. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/text-edit.ts +0 -0
  164. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/tgz-guard.ts +0 -0
  165. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/trust.ts +0 -0
  166. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/types.ts +0 -0
  167. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/uninstall.ts +0 -0
  168. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/verify-checks.ts +0 -0
  169. /package/templates/{api/src/_platform → src/_platform/api}/_lifecycle/verify-report.ts +0 -0
  170. /package/templates/{api/src/_platform → src/_platform/api}/_shared/respond.ts +0 -0
  171. /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/contacts.ts +0 -0
  172. /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/roles.ts +0 -0
  173. /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/settings.ts +0 -0
  174. /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/solution-perms.ts +0 -0
  175. /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/domain/users.ts +0 -0
  176. /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/access.ts +0 -0
  177. /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/audit.ts +0 -0
  178. /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/guard.ts +0 -0
  179. /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/oplog.ts +0 -0
  180. /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/query.ts +0 -0
  181. /package/templates/{api/src/_platform → src/_platform/api}/_shared/service/infra/rate.ts +0 -0
  182. /package/templates/{api/src/_platform → src/_platform/api}/_shared/site-facade.ts +0 -0
  183. /package/templates/{api/src/_platform → src/_platform/api}/contacts/api.ts +0 -0
  184. /package/templates/{api/src/_platform → src/_platform/api}/contacts/detail/api.ts +0 -0
  185. /package/templates/{api/src/_platform → src/_platform/api}/manifest.yaml +0 -0
  186. /package/templates/{api/src/_platform → src/_platform/api}/market/bootstrap.ts +0 -0
  187. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0001__create_users.mysql.sql +0 -0
  188. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0001__create_users.pg.sql +0 -0
  189. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0001__create_users.sql +0 -0
  190. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0002__add_users_avatar_base64.mysql.sql +0 -0
  191. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0002__add_users_avatar_base64.sql +0 -0
  192. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0003__users_source_display_name.mysql.sql +0 -0
  193. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0003__users_source_display_name.sql +0 -0
  194. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0004__sys_rbac.mysql.sql +0 -0
  195. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0004__sys_rbac.pg.sql +0 -0
  196. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0004__sys_rbac.sql +0 -0
  197. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0005__users_must_change.mysql.sql +0 -0
  198. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0005__users_must_change.sql +0 -0
  199. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0006__users_is_forbidden.mysql.sql +0 -0
  200. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0006__users_is_forbidden.sql +0 -0
  201. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0007__users_email.sql +0 -0
  202. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0008__sys_refresh_token.mysql.sql +0 -0
  203. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0008__sys_refresh_token.pg.sql +0 -0
  204. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0008__sys_refresh_token.sql +0 -0
  205. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0009__sys_throttle.mysql.sql +0 -0
  206. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0009__sys_throttle.pg.sql +0 -0
  207. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0009__sys_throttle.sql +0 -0
  208. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0010__sys_solution_route.mysql.sql +0 -0
  209. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0010__sys_solution_route.pg.sql +0 -0
  210. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0010__sys_solution_route.sql +0 -0
  211. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0011__sys_setting_group_label.mysql.sql +0 -0
  212. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0011__sys_setting_group_label.sql +0 -0
  213. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0012__market_history.mysql.sql +0 -0
  214. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0012__market_history.pg.sql +0 -0
  215. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0012__market_history.sql +0 -0
  216. /package/templates/{api/src/_platform → src/_platform/api}/migrations/0013__users_profile.sql +0 -0
  217. /package/templates/{api/src/_platform → src/_platform/api}/schema.yaml +0 -0
  218. /package/templates/{web/src/_platform → src/_platform/web}/__client/api.schemas.ts +0 -0
  219. /package/templates/{web/src/_platform → src/_platform/web}/__client/api.ts +0 -0
  220. /package/templates/{web/src/about → src/about/web}/entry.ts +0 -0
  221. /package/templates/{web/src/about → src/about/web}/locales/en-US.json +0 -0
  222. /package/templates/{web/src/about → src/about/web}/locales/zh-CN.json +0 -0
  223. /package/templates/{web/src/about → src/about/web}/pages/constants.ts +0 -0
  224. /package/templates/{web/src/about → src/about/web}/pages/index.tsx +0 -0
  225. /package/templates/{web/src/access → src/access/web}/entry.ts +0 -0
  226. /package/templates/{web/src/access → src/access/web}/locales/en-US.json +0 -0
  227. /package/templates/{web/src/access → src/access/web}/locales/zh-CN.json +0 -0
  228. /package/templates/{web/src/access → src/access/web}/pages/access-mode/index.tsx +0 -0
  229. /package/templates/{web/src/access → src/access/web}/pages/admin-visible/index.tsx +0 -0
  230. /package/templates/{web/src/access → src/access/web}/pages/button-control/index.tsx +0 -0
  231. /package/templates/{web/src/access → src/access/web}/pages/common-visible/index.tsx +0 -0
  232. /package/templates/{web/src/access → src/access/web}/pages/page-control/index.tsx +0 -0
  233. /package/templates/{api/src/auth → src/auth/api}/_shared/ldap-login.ts +0 -0
  234. /package/templates/{api/src/auth → src/auth/api}/_shared/ldap.ts +0 -0
  235. /package/templates/{web/src/auth → src/auth/web}/__client/api.schemas.ts +0 -0
  236. /package/templates/{web/src/auth → src/auth/web}/__client/api.ts +0 -0
  237. /package/templates/{api/src/demo → src/demo/api}/manifest.yaml +0 -0
  238. /package/templates/{api/src/demo → src/demo/api}/migrations/0001__create_todos.sql +0 -0
  239. /package/templates/{api/src/demo → src/demo/api}/schema.yaml +0 -0
  240. /package/templates/{api/src/demo → src/demo/api}/seed.sql +0 -0
  241. /package/templates/{api/src/demo → src/demo/api}/todos/api.ts +0 -0
  242. /package/templates/{web/src/demo → src/demo/web}/__client/api.schemas.ts +0 -0
  243. /package/templates/{web/src/demo → src/demo/web}/__client/api.ts +0 -0
  244. /package/templates/{web/src/demo → src/demo/web}/entry.ts +0 -0
  245. /package/templates/{web/src/demo → src/demo/web}/locales/en-US.json +0 -0
  246. /package/templates/{web/src/demo → src/demo/web}/locales/zh-CN.json +0 -0
  247. /package/templates/{web/src/demo → src/demo/web}/pages/index.tsx +0 -0
  248. /package/templates/{web/src/exception → src/exception/web}/entry.ts +0 -0
  249. /package/templates/{web/src/exception → src/exception/web}/locales/en-US.json +0 -0
  250. /package/templates/{web/src/exception → src/exception/web}/locales/zh-CN.json +0 -0
  251. /package/templates/{web/src/exception → src/exception/web}/pages/403/index.tsx +0 -0
  252. /package/templates/{web/src/exception → src/exception/web}/pages/404/index.tsx +0 -0
  253. /package/templates/{web/src/exception → src/exception/web}/pages/500/index.tsx +0 -0
  254. /package/templates/{web/src/exception → src/exception/web}/pages/unknown-component/index.tsx +0 -0
  255. /package/templates/{api/src/home → src/home/api}/line/api.ts +0 -0
  256. /package/templates/{api/src/home → src/home/api}/manifest.yaml +0 -0
  257. /package/templates/{api/src/home → src/home/api}/migrations/0001__create_home_tables.sql +0 -0
  258. /package/templates/{api/src/home → src/home/api}/pie/api.ts +0 -0
  259. /package/templates/{api/src/home → src/home/api}/schema.yaml +0 -0
  260. /package/templates/{api/src/home → src/home/api}/seed.sql +0 -0
  261. /package/templates/{web/src/home → src/home/web}/__client/api.schemas.ts +0 -0
  262. /package/templates/{web/src/home → src/home/web}/__client/api.ts +0 -0
  263. /package/templates/{web/src/home → src/home/web}/entry.ts +0 -0
  264. /package/templates/{web/src/home → src/home/web}/locales/en-US.json +0 -0
  265. /package/templates/{web/src/home → src/home/web}/locales/zh-CN.json +0 -0
  266. /package/templates/{web/src/home → src/home/web}/pages/components/bar-chart.tsx +0 -0
  267. /package/templates/{web/src/home → src/home/web}/pages/components/card-list.tsx +0 -0
  268. /package/templates/{web/src/home → src/home/web}/pages/components/line-chart.tsx +0 -0
  269. /package/templates/{web/src/home → src/home/web}/pages/components/pie-chart.tsx +0 -0
  270. /package/templates/{web/src/home → src/home/web}/pages/index.tsx +0 -0
  271. /package/templates/{web/src/login → src/login/web}/entry.ts +0 -0
  272. /package/templates/{web/src/login → src/login/web}/locales/en-US.json +0 -0
  273. /package/templates/{web/src/login → src/login/web}/locales/zh-CN.json +0 -0
  274. /package/templates/{web/src/login → src/login/web}/pages/change-password.tsx +0 -0
  275. /package/templates/{web/src/login → src/login/web}/pages/login.tsx +0 -0
  276. /package/templates/{api/src/notification → src/notification/api}/_shared/notify.ts +0 -0
  277. /package/templates/{api/src/notification → src/notification/api}/api.ts +0 -0
  278. /package/templates/{api/src/notification → src/notification/api}/del/api.ts +0 -0
  279. /package/templates/{api/src/notification → src/notification/api}/manifest.yaml +0 -0
  280. /package/templates/{api/src/notification → src/notification/api}/migrations/0001__create_notifications.sql +0 -0
  281. /package/templates/{api/src/notification → src/notification/api}/migrations/0002__notifications_user.sql +0 -0
  282. /package/templates/{api/src/notification → src/notification/api}/migrations/0003__notifications_broadcast.sql +0 -0
  283. /package/templates/{api/src/notification → src/notification/api}/migrations/0004__notifications_broadcast_hidden.sql +0 -0
  284. /package/templates/{api/src/notification → src/notification/api}/schema.yaml +0 -0
  285. /package/templates/{api/src/notification → src/notification/api}/seed.sql +0 -0
  286. /package/templates/{web/src/notification → src/notification/web}/__client/api.schemas.ts +0 -0
  287. /package/templates/{web/src/notification → src/notification/web}/__client/api.ts +0 -0
  288. /package/templates/{web/src/notification → src/notification/web}/entry.ts +0 -0
  289. /package/templates/{web/src/notification → src/notification/web}/locales/en-US.json +0 -0
  290. /package/templates/{web/src/notification → src/notification/web}/locales/zh-CN.json +0 -0
  291. /package/templates/{web/src/notification → src/notification/web}/pages/center/index.tsx +0 -0
  292. /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/CardGrid.test.tsx +0 -0
  293. /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/HistoryDrawer.test.tsx +0 -0
  294. /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/RollbackDialog.test.tsx +0 -0
  295. /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/UninstallConfirm.test.tsx +0 -0
  296. /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/poll.test.ts +0 -0
  297. /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/render.tsx +0 -0
  298. /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/runtime-stub.ts +0 -0
  299. /package/templates/{web/src/oj-market → src/oj-market/web}/__tests__/semver.test.ts +0 -0
  300. /package/templates/{web/src/oj-market → src/oj-market/web}/api/MarketClientProvider.tsx +0 -0
  301. /package/templates/{web/src/oj-market → src/oj-market/web}/components/CardGrid.tsx +0 -0
  302. /package/templates/{web/src/oj-market → src/oj-market/web}/components/CardItem.tsx +0 -0
  303. /package/templates/{web/src/oj-market → src/oj-market/web}/components/DetailDrawer.tsx +0 -0
  304. /package/templates/{web/src/oj-market → src/oj-market/web}/components/HistoryDrawer.tsx +0 -0
  305. /package/templates/{web/src/oj-market → src/oj-market/web}/entry.ts +0 -0
  306. /package/templates/{web/src/oj-market → src/oj-market/web}/pages/MarketApp.tsx +0 -0
  307. /package/templates/{web/src/oj-market → src/oj-market/web}/utils/poll.ts +0 -0
  308. /package/templates/{web/src/oj-market → src/oj-market/web}/utils/semver.ts +0 -0
  309. /package/templates/{web/src/outside → src/outside/web}/entry.ts +0 -0
  310. /package/templates/{web/src/outside → src/outside/web}/locales/en-US.json +0 -0
  311. /package/templates/{web/src/outside → src/outside/web}/locales/zh-CN.json +0 -0
  312. /package/templates/{api/src/personal-center → src/personal-center/api}/manifest.yaml +0 -0
  313. /package/templates/{api/src/personal-center → src/personal-center/api}/profile/api.ts +0 -0
  314. /package/templates/{api/src/personal-center → src/personal-center/api}/upload/api.ts +0 -0
  315. /package/templates/{web/src/personal-center → src/personal-center/web}/__client/api.schemas.ts +0 -0
  316. /package/templates/{web/src/personal-center → src/personal-center/web}/__client/api.ts +0 -0
  317. /package/templates/{web/src/personal-center → src/personal-center/web}/entry.ts +0 -0
  318. /package/templates/{web/src/personal-center → src/personal-center/web}/locales/en-US.json +0 -0
  319. /package/templates/{web/src/personal-center → src/personal-center/web}/locales/zh-CN.json +0 -0
  320. /package/templates/{web/src/personal-center → src/personal-center/web}/pages/my-profile/index.tsx +0 -0
  321. /package/templates/{web/src/route-nest → src/route-nest/web}/entry.ts +0 -0
  322. /package/templates/{web/src/route-nest → src/route-nest/web}/locales/en-US.json +0 -0
  323. /package/templates/{web/src/route-nest → src/route-nest/web}/locales/zh-CN.json +0 -0
  324. /package/templates/{web/src/route-nest → src/route-nest/web}/pages/menu1/menu1-1/index.tsx +0 -0
  325. /package/templates/{web/src/route-nest → src/route-nest/web}/pages/menu1/menu1-2/index.tsx +0 -0
  326. /package/templates/{web/src/route-nest → src/route-nest/web}/pages/menu2/index.tsx +0 -0
  327. /package/templates/{web/src → src}/styles.css +0 -0
  328. /package/templates/{web/src/system → src/system/web}/__client/api.schemas.ts +0 -0
  329. /package/templates/{web/src/system → src/system/web}/__client/api.ts +0 -0
  330. /package/templates/{web/src/system → src/system/web}/entry.ts +0 -0
  331. /package/templates/{web/src/system → src/system/web}/pages/errors.ts +0 -0
  332. /package/templates/{web/src/system → src/system/web}/pages/forbidden.tsx +0 -0
  333. /package/templates/{web/src/system → src/system/web}/pages/login-methods/index.tsx +0 -0
  334. /package/templates/{web/src/system → src/system/web}/pages/oplog/index.tsx +0 -0
  335. /package/templates/{web/src/system → src/system/web}/pages/roles/index.tsx +0 -0
  336. /package/templates/{web/src/system → src/system/web}/pages/settings/index.tsx +0 -0
  337. /package/templates/{web/src/system → src/system/web}/pages/users/index.tsx +0 -0
  338. /package/templates/{web/src/system → src/system/web}/pages/users/roles.tsx +0 -0
  339. /package/templates/{api/src/web → src/web/api}/get-async-routes/api.ts +0 -0
  340. /package/templates/{api/src/web → src/web/api}/hello/api.ts +0 -0
  341. /package/templates/{api/src/web → src/web/api}/manifest.yaml +0 -0
  342. /package/templates/{api/src/web → src/web/api}/user-info/api.ts +0 -0
  343. /package/templates/{web/src → src/web}/web/__client/api.schemas.ts +0 -0
  344. /package/templates/{web/src → src/web}/web/__client/api.ts +0 -0
  345. /package/templates/{api/tests → tests}/rbac-tables.spec.ts +0 -0
@@ -8,7 +8,7 @@
8
8
  # 2. 模板自运行专属文件——templates 本身也是一个可 `pnpm dev` 的工程,
9
9
  # 它的 package.json / node_modules / bin / lock 绝不能泄漏进用户工程;
10
10
  # package.json 由 init 现场生成(钉死真实版本,不带 workspace:/catalog:)。
11
- # 3. 构建与运行期产物——api/dist、web/dist、api/logs、签发的私钥与 sqlite;
11
+ # 3. 构建与运行期产物——api/dist、web/dist、logs、签发的私钥与 sqlite;
12
12
  # 模板跑过 build/dev 后会留在本地,既不进用户工程,更不该让所有工程
13
13
  # 共享同一把私钥。
14
14
  # 4. 模板自用元数据——本清单自身。
@@ -32,43 +32,48 @@ include:
32
32
  - tsconfig.json
33
33
  - global.d.ts
34
34
  - env.d.ts
35
+ # 别名类型兜底(`declare module "#*"`,非模块文件)。`tsconfig.paths.json` 不在此列:
36
+ # 它由 init 按目标工程的模块清单**现场生成**(模板兼作 demo 工程,多出的模块不该带过去)
37
+ - types
35
38
  - web.config.ts
36
39
  - openapi.json
37
- - web/src/styles.css
38
- # —— api 后端:配置 / 种子 / 测试 / 默认模块 ——
39
- - api/config.yaml
40
- - api/.ojm-api-exempt.json
41
- - api/seed.mysql.sql
42
- - api/seed.pg.sql
43
- - api/vitest.config.ts
44
- - api/tests
45
- - api/src/_platform
46
- - api/src/auth
47
- - api/src/notification
48
- - api/src/personal-center
49
- - api/src/system
50
- - api/src/web
40
+ - src/styles.css
41
+ # 后端 typecheck 工程(package.json 的 typecheck:api 指向它);只收后端、排除 web
42
+ - src/tsconfig.json
43
+ # —— 后端:配置 / 种子 / 测试 / 默认模块(模块源码在 src/<name>/api)——
44
+ - config.yaml
45
+ - .ojm-api-exempt.json
46
+ - seed.mysql.sql
47
+ - seed.pg.sql
48
+ - vitest.config.ts
49
+ - tests
50
+ - src/_platform/api
51
+ - src/auth/api
52
+ - src/notification/api
53
+ - src/personal-center/api
54
+ - src/system/api
55
+ - src/web/api
51
56
  # —— web 前端模块(demo/home 只列页面源码,__client 见顶部说明)——
52
- - web/src/_platform
53
- - web/src/auth
54
- - web/src/demo/entry.ts
55
- - web/src/demo/locales
56
- - web/src/demo/pages
57
- - web/src/home/entry.ts
58
- - web/src/home/locales
59
- - web/src/home/pages
60
- - web/src/login
61
- - web/src/notification
57
+ - src/_platform/web
58
+ - src/auth/web
59
+ - src/demo/web/entry.ts
60
+ - src/demo/web/locales
61
+ - src/demo/web/pages
62
+ - src/home/web/entry.ts
63
+ - src/home/web/locales
64
+ - src/home/web/pages
65
+ - src/login/web
66
+ - src/notification/web
62
67
  # 解决方案市场:默认第一方能力。逐子路径收录——__tests__ 不进脚手架
63
68
  # (用户工程无 web 侧 vitest 基建,拷了是死文件)
64
- - web/src/oj-market/entry.ts
65
- - web/src/oj-market/types.ts
66
- - web/src/oj-market/api
67
- - web/src/oj-market/components
68
- - web/src/oj-market/hooks
69
- - web/src/oj-market/locales
70
- - web/src/oj-market/pages
71
- - web/src/oj-market/utils
72
- - web/src/personal-center
73
- - web/src/system
74
- - web/src/web
69
+ - src/oj-market/web/entry.ts
70
+ - src/oj-market/web/types.ts
71
+ - src/oj-market/web/api
72
+ - src/oj-market/web/components
73
+ - src/oj-market/web/hooks
74
+ - src/oj-market/web/locales
75
+ - src/oj-market/web/pages
76
+ - src/oj-market/web/utils
77
+ - src/personal-center/web
78
+ - src/system/web
79
+ - src/web/web
@@ -9,7 +9,9 @@
9
9
  "preview": "ojm preview",
10
10
  "info": "ojm info",
11
11
  "typecheck": "tsc --noEmit -p tsconfig.json",
12
- "typecheck:api": "tsc --noEmit -p api/tsconfig.json"
12
+ "typecheck:api": "tsc --noEmit -p src/tsconfig.json",
13
+ "bundle": "ojm build --bundle",
14
+ "new": "ojm new"
13
15
  },
14
16
  "devDependencies": {
15
17
  "@ant-design/cssinjs": "catalog:",
@@ -89,6 +89,8 @@ export interface InstallRequest extends InstallContext {
89
89
  expected?: string[]
90
90
  /** 安装器内部通道(外部调用方忽略):终态审计读 status/failedStage/taskId + migrate 捕获 appliedSeq */
91
91
  outcome?: { taskId?: string, status?: TaskStatus, failedStage?: InstallStage, appliedSeq?: number | null, cancelled?: boolean }
92
+ /** 安装器内部通道(reloading → smoke):本次新追加进 auth.anonymous_paths 的路径(restart_pending,探活对其 401 判待重启) */
93
+ deferredAnonymous?: string[]
92
94
  /**
93
95
  * 取消安装令牌(U5,P3-8):运行期外部置位(端点 `?cancel=<taskId>` 或任务状态 `cancelling` 驱动的闭包),
94
96
  * 返回 true 即中止——PIPELINE 循环每段前探测 + migrating 段 spawn 流逐块探测;幂等、可重入
@@ -347,6 +349,8 @@ async function reloadStage(req: InstallRequest): Promise<StageResult> {
347
349
  const anonymous = Array.isArray(api.anonymous) ? api.anonymous.filter((x): x is string => typeof x === "string") : [];
348
350
  const patched = anonymous.length > 0 ? appendAnonymousPaths(bumped.content, anonymous) : { content: bumped.content, added: [] };
349
351
  await atomicWriteText(path, patched.content);
352
+ // 交接给 smoke:本次真正新增的匿名路径在重启前恒 401(restart_pending)
353
+ req.deferredAnonymous = patched.added;
350
354
  }
351
355
  catch (e) {
352
356
  return { stage: "reloading", ok: false, retryable: true, message: `热更失败:config.yaml 安装序号自增/匿名端点接线失败(${String(e)})——reload unchanged 判定基于 config 文档 sha,无变更则热更不可能生效(B-11,PRD E5 口径)` };
@@ -30,6 +30,12 @@ export interface SmokeContext extends InstallContext {
30
30
  modulesJsonPath: string
31
31
  /** 注册快照查询;缺省回落 queryRegistered(isolate db 全局;测试注入 stub) */
32
32
  query?: (name: string) => Promise<SolutionSnapshot | null>
33
+ /**
34
+ * 本次安装**新追加**进 config.yaml `auth.anonymous_paths` 的路径(reloading 段注入,带前导 /)。
35
+ * 匿名接线属 restart_pending(reload 段已明示),本进程内尚未生效——探活对这些路径的
36
+ * 401 判「待重启」而非失败;已在配置里却仍 401 才是真失败(说明接线或守卫有问题)。
37
+ */
38
+ deferredAnonymous?: string[]
33
39
  }
34
40
 
35
41
  export interface StageResult {
@@ -116,6 +122,7 @@ export async function smoke(ctx: SmokeContext): Promise<StageResult> {
116
122
  const web = isObj(m.web) ? m.web : undefined;
117
123
 
118
124
  // ① 探活:health 优先(2xx),缺省首个非匿名路由(连通即可)
125
+ const notes: string[] = [];
119
126
  try {
120
127
  const health = typeof m.health === "string" && m.health !== "" ? m.health : undefined;
121
128
  const target = health ?? firstNonAnonymousRoute(m);
@@ -123,7 +130,18 @@ export async function smoke(ctx: SmokeContext): Promise<StageResult> {
123
130
  const url = probeUrlOf(ctx.apiPrefix, target);
124
131
  const res = await ctx.fetch(url);
125
132
  if (health !== undefined && !res.ok) {
126
- failures.push({ item: "探活", retryable: true, reason: `health 端点 ${url} 返回 HTTP ${res.status}(C2-33 冒烟探针要求 2xx)` });
133
+ // 本次安装新增的匿名接线尚未生效(restart_pending):此处的 401 反映的是
134
+ // 「宿主还没重启」,不是包或站点的问题——判待重启并落人话提示,不判失败。
135
+ const rel = (s: string) => s.replace(/^\/+/, "");
136
+ const healthRel = rel(health);
137
+ const pending = (ctx.deferredAnonymous ?? []).some(a => healthRel === rel(a) || healthRel.startsWith(`${rel(a)}/`));
138
+ const deferred = res.status === 401 && pending;
139
+ if (deferred) {
140
+ notes.push(`探活 ${url} 返回 401:该端点是本次安装新增的匿名路径(auth.anonymous_paths),匿名接线属 restart_pending——重启宿主后生效,不计入失败`);
141
+ }
142
+ else {
143
+ failures.push({ item: "探活", retryable: true, reason: `health 端点 ${url} 返回 HTTP ${res.status}(C2-33 冒烟探针要求 2xx)` });
144
+ }
127
145
  }
128
146
  }
129
147
  }
@@ -205,7 +223,8 @@ export async function smoke(ctx: SmokeContext): Promise<StageResult> {
205
223
  // ④ entry 浏览器加载不在安装器验(设计 §4.1)——无判定,口径钉于成功 message。
206
224
 
207
225
  if (failures.length === 0) {
208
- return { stage: "smoke", ok: true, message: "冒烟通过:探活 / integrity / 注册一致性 三项全过(entry 浏览器加载不在安装器验——设计 §4.1,宿主 error boundary + P2 深检兜底)" };
226
+ const passed = "冒烟通过:探活 / integrity / 注册一致性 三项全过(entry 浏览器加载不在安装器验——设计 §4.1,宿主 error boundary + P2 深检兜底)";
227
+ return { stage: "smoke", ok: true, message: notes.length === 0 ? passed : `${passed}\n${notes.map(n => `- ${n}`).join("\n")}` };
209
228
  }
210
229
  return {
211
230
  stage: "smoke",
@@ -1,10 +1,9 @@
1
1
  -- L1 测试账号(仅 `oj test` 经 fixtures 灌入,不进 release 产物)。
2
2
  --
3
- -- 为什么不能用 seed 的 admin 跑 L1 登录:
4
- -- ① seed 的 admin 是 must_change=1 首登改密态(D12 生产语义),且初始口令 123456
5
- -- 只是开发便利——用户在 dev 库改过口令后(产品主动引导),admin 口令即漂移,
6
- -- L1 若绑 admin/123456 会随机 401(本地实测踩过:dev 库口令被改成 12345678)。
7
- -- ② 判权类端点(currentAccess/assertPermission)对 must_change=1 恒 403。
3
+ -- 为什么不能用 seed 的开发账号(admin/superadmin 等)跑 L1 登录:
4
+ -- ① seed 账号初始口令 123456 只是开发便利——用户在 dev 库改过口令后,口令即漂移,
5
+ -- L1 若绑 seed 账号会随机 401(本地实测踩过:dev 库口令被改成 12345678)。
6
+ -- ② 不用 must_change 解释:seed 账号现均为 0(2026-10-22 口径);真正的原因是 ①。
8
7
  -- 故 additive 建一个专用账号:INSERT OR IGNORE 幂等,反复重放终态一致,
9
8
  -- 不碰 admin 既有行(用户的口令/状态不受 `oj test` 影响)。
10
9
  -- 按分号朴素切分(S006):语句内不得含分号字面量。
@@ -52,9 +52,9 @@ export interface MarketEnv {
52
52
  trustPath: string
53
53
  /** sqlite 库文件路径(R14 备份源;模板 config.yaml `sqlite://db.sqlite` → 站点根 db.sqlite) */
54
54
  dbPath: string
55
- /** 站点 web 模块目录(<dist>/modules) */
55
+ /** 站点 web 模块目录(站点产物根下的 modules/) */
56
56
  siteModulesDir: string
57
- /** 站点 modules.json 路径(与 siteModulesDir 同属 <dist>) */
57
+ /** 站点 modules.json 路径(与 siteModulesDir 同属站点产物根) */
58
58
  modulesJsonPath: string
59
59
  /** API 前缀(smoke 探针 URL 前缀) */
60
60
  apiPrefix: string
@@ -78,17 +78,20 @@ export interface MarketEnv {
78
78
  /** 迁移账本 head 真源(P1-16 接线①:缺省读 oj journal 表 `_oj_migrations` max(version),查询失败 → null 不猜) */
79
79
  ledgerHeadOf?: (name: string) => Promise<number | bigint | null>
80
80
  /**
81
- * 宿主 versions.json(C2-09 peerRuntime 比对真源,键 = 包名)。缺省读 <dist>/versions.json
82
- * (ojm 宿主构建产物,与 modules.json 同目录,B-11 接线);不可得 → undefined,manifest 声明
83
- * peerRuntime 即预检 fail-closed 拒装(C2-09 语义保持)。
81
+ * 宿主 versions.json(C2-09 peerRuntime 比对真源,键 = 包名)。缺省读 `<sharedRoot>/versions.json`
82
+ * (ojm 宿主构建产物:与 manifests.yaml 同层,即 release 根,B-11 接线);不可得 → undefined,
83
+ * manifest 声明 peerRuntime 即预检 fail-closed 拒装(C2-09 语义保持)。
84
84
  */
85
85
  hostVersions?: Record<string, string>
86
86
  }
87
87
 
88
- /** 宿主 versions.json 布局推导(B-11):与 modules.json 同目录(<dist>/versions.json) */
89
- export function versionsJsonPathOf(modulesJsonPath: string): string {
90
- const slash = modulesJsonPath.lastIndexOf("/");
91
- return slash === -1 ? "versions.json" : `${modulesJsonPath.slice(0, slash)}/versions.json`;
88
+ /**
89
+ * 宿主 versions.json 布局推导(B-11):与 manifests.yaml 同层(release 根 = `sharedRoot`),
90
+ * 不是站点产物根——两处根在 `src/<name>/{api,web}` 布局下已分流(`api/dist` vs `web/dist`)。
91
+ */
92
+ export function versionsJsonPathOf(releaseRoot: string): string {
93
+ const trimmed = releaseRoot.replace(/\/+$/, "");
94
+ return trimmed === "" ? "versions.json" : `${trimmed}/versions.json`;
92
95
  }
93
96
 
94
97
  /** 宿主 versions.json 解析(纯函数):{packageName: version} 全字符串表;任何违约 → undefined(fail-closed 不猜) */
@@ -113,30 +116,37 @@ export function parseVersionsJson(text: string): Record<string, string> | undefi
113
116
  return out;
114
117
  }
115
118
 
116
- /** hostVersions 注入缺省:显式注入优先,否则读 <dist>/versions.json;不可得 → undefined(C2-09 fail-closed) */
119
+ /** hostVersions 注入缺省:显式注入优先,否则读 `<sharedRoot>/versions.json`;不可得 → undefined(C2-09 fail-closed) */
117
120
  async function hostVersionsOf(env: MarketEnv): Promise<Record<string, string> | undefined> {
118
121
  if (env.hostVersions !== undefined) {
119
122
  return env.hostVersions;
120
123
  }
121
124
  try {
122
- return parseVersionsJson(new TextDecoder().decode(await fsFace().readFile(versionsJsonPathOf(env.modulesJsonPath))));
125
+ return parseVersionsJson(new TextDecoder().decode(await fsFace().readFile(versionsJsonPathOf(env.sharedRoot))));
123
126
  }
124
127
  catch {
125
128
  return undefined;
126
129
  }
127
130
  }
128
131
 
129
- /** oj serve release 布局生产缺省(P1-16 B3/B4;全字段可覆盖) */
132
+ /**
133
+ * oj serve release 布局生产缺省(P1-16 B3/B4;全字段可覆盖)。
134
+ *
135
+ * 两处根在 `src/<name>/{api,web}` 布局下分流,且都相对 fs jail 根(= config.yaml 所在目录):
136
+ * release 根 `api/dist` —— 版本目录 / manifests.yaml / versions.json / migrations(后端产物)
137
+ * 站点根 `web/dist` —— modules/ + modules.json(前端产物,安装器 upsert 的落点)
138
+ * 暂存 / 任务 / 状态 / 信任库 / db 仍在 jail 根下:它们是安装器私有面,不属任何产物根。
139
+ */
130
140
  export function marketEnv(): MarketEnv {
131
141
  return {
132
- sharedRoot: "dist",
142
+ sharedRoot: "api/dist",
133
143
  staging: "modules/.staging",
134
144
  taskDir: "modules/.install-tasks",
135
145
  statePath: "modules/.solutions/state.yaml",
136
146
  trustPath: ".solutions/trusted-keys.yaml",
137
147
  dbPath: "db.sqlite",
138
- siteModulesDir: "dist/modules",
139
- modulesJsonPath: "dist/modules.json",
148
+ siteModulesDir: "web/dist/modules",
149
+ modulesJsonPath: "web/dist/modules.json",
140
150
  apiPrefix: "/api",
141
151
  ledgerHeadOf: journalHeadOf,
142
152
  };
@@ -0,0 +1,16 @@
1
+ -- _platform 种子数据 mysql 方言覆盖(方言覆盖文件必胜;INSERT OR IGNORE → INSERT IGNORE)。
2
+ -- 语句分号切分与注释纪律同基线 seed.sql;账号口径同基线(账号名=角色名,must_change=0)。
3
+ INSERT IGNORE INTO users (username, password_hash, must_change) VALUES ('superadmin', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 0);
4
+ INSERT IGNORE INTO users (username, password_hash, must_change) VALUES ('admin', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 0);
5
+ INSERT IGNORE INTO users (username, password_hash, must_change) VALUES ('editor', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 0);
6
+ INSERT IGNORE INTO users (username, password_hash, must_change) VALUES ('viewer', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 0);
7
+ DELETE FROM sys_user_role WHERE role_name = 'superadmin' AND user_id = (SELECT id FROM users WHERE username = 'admin');
8
+ UPDATE users SET must_change = 0 WHERE username IN ('superadmin', 'admin', 'editor', 'viewer');
9
+ INSERT IGNORE INTO sys_role (name, module, title, builtin) VALUES ('superadmin', 'framework', '超级管理员', 1);
10
+ INSERT IGNORE INTO sys_role (name, module, title, builtin) VALUES ('admin', 'framework', '系统管理员', 1);
11
+ INSERT IGNORE INTO sys_role (name, module, title, builtin) VALUES ('editor', 'framework', '编辑', 1);
12
+ INSERT IGNORE INTO sys_role (name, module, title, builtin) VALUES ('viewer', 'framework', '访客', 1);
13
+ INSERT IGNORE INTO sys_user_role (user_id, role_name) SELECT id, 'superadmin' FROM users WHERE username = 'superadmin';
14
+ INSERT IGNORE INTO sys_user_role (user_id, role_name) SELECT id, 'admin' FROM users WHERE username = 'admin';
15
+ INSERT IGNORE INTO sys_user_role (user_id, role_name) SELECT id, 'editor' FROM users WHERE username = 'editor';
16
+ INSERT IGNORE INTO sys_user_role (user_id, role_name) SELECT id, 'viewer' FROM users WHERE username = 'viewer';
@@ -0,0 +1,16 @@
1
+ -- _platform 种子数据 pg 方言覆盖(方言覆盖文件必胜;INSERT OR IGNORE → ON CONFLICT DO NOTHING)。
2
+ -- 语句分号切分与注释纪律同基线 seed.sql;账号口径同基线(账号名=角色名,must_change=0)。
3
+ INSERT INTO users (username, password_hash, must_change) VALUES ('superadmin', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 0) ON CONFLICT DO NOTHING;
4
+ INSERT INTO users (username, password_hash, must_change) VALUES ('admin', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 0) ON CONFLICT DO NOTHING;
5
+ INSERT INTO users (username, password_hash, must_change) VALUES ('editor', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 0) ON CONFLICT DO NOTHING;
6
+ INSERT INTO users (username, password_hash, must_change) VALUES ('viewer', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 0) ON CONFLICT DO NOTHING;
7
+ DELETE FROM sys_user_role WHERE role_name = 'superadmin' AND user_id = (SELECT id FROM users WHERE username = 'admin');
8
+ UPDATE users SET must_change = 0 WHERE username IN ('superadmin', 'admin', 'editor', 'viewer');
9
+ INSERT INTO sys_role (name, module, title, builtin) VALUES ('superadmin', 'framework', '超级管理员', 1) ON CONFLICT DO NOTHING;
10
+ INSERT INTO sys_role (name, module, title, builtin) VALUES ('admin', 'framework', '系统管理员', 1) ON CONFLICT DO NOTHING;
11
+ INSERT INTO sys_role (name, module, title, builtin) VALUES ('editor', 'framework', '编辑', 1) ON CONFLICT DO NOTHING;
12
+ INSERT INTO sys_role (name, module, title, builtin) VALUES ('viewer', 'framework', '访客', 1) ON CONFLICT DO NOTHING;
13
+ INSERT INTO sys_user_role (user_id, role_name) SELECT id, 'superadmin' FROM users WHERE username = 'superadmin' ON CONFLICT DO NOTHING;
14
+ INSERT INTO sys_user_role (user_id, role_name) SELECT id, 'admin' FROM users WHERE username = 'admin' ON CONFLICT DO NOTHING;
15
+ INSERT INTO sys_user_role (user_id, role_name) SELECT id, 'editor' FROM users WHERE username = 'editor' ON CONFLICT DO NOTHING;
16
+ INSERT INTO sys_user_role (user_id, role_name) SELECT id, 'viewer' FROM users WHERE username = 'viewer' ON CONFLICT DO NOTHING;
@@ -1,16 +1,27 @@
1
1
  -- _platform 种子数据(只放数据,DDL 归 migrations/,S006 纪律)。
2
2
  -- 方言覆盖:seed.mysql.sql / seed.pg.sql(INSERT OR IGNORE 为 sqlite 专属语法,方言文件必胜,同迁移约定)。
3
3
  -- 注意:seed 按英文分号朴素切分语句,注释里也不要出现英文分号。
4
- -- 账号生命周期 D12:admin 为 seed 超管(superadmin),must_change=1 首登强制改密——
5
- -- 初始口令 123456 仅为开发便利,must_change 闭环兜底默认凭据窗口(口令不随机生成的取舍见计划 A2 阶段小结)
6
- INSERT OR IGNORE INTO users (username, password_hash, must_change) VALUES ('admin', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 1);
4
+ -- 账号生命周期 D12(2026-10-22 调整):四个内置角色各一个开发账号,**账号名=角色名**
5
+ -- (superadmin/admin/editor/viewer,避免心智负担),口令均 123456,must_change=0 免首登改密——
6
+ -- 纯开发便利(默认凭据不随机生成的取舍见计划 A2 阶段小结),生产部署须改密或删除
7
+ INSERT OR IGNORE INTO users (username, password_hash, must_change) VALUES ('superadmin', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 0);
8
+ INSERT OR IGNORE INTO users (username, password_hash, must_change) VALUES ('admin', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 0);
9
+ INSERT OR IGNORE INTO users (username, password_hash, must_change) VALUES ('editor', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 0);
10
+ INSERT OR IGNORE INTO users (username, password_hash, must_change) VALUES ('viewer', '$2y$10$ylE2NdjnYiuEcq54W/KxH.bhvGaQQg3rbmYI3mcEHX6LdQEAqixeC', 0);
11
+ -- 历史 seed 把 admin 绑在 superadmin 上;收敛为账号名=角色名(幂等 DELETE,每次重放生效)。
12
+ -- 已存在库中 admin 行可能残留 must_change=1,UPDATE 统一收敛为 0(四个均为 seed 自有开发账号)
13
+ DELETE FROM sys_user_role WHERE role_name = 'superadmin' AND user_id = (SELECT id FROM users WHERE username = 'admin');
14
+ UPDATE users SET must_change = 0 WHERE username IN ('superadmin', 'admin', 'editor', 'viewer');
7
15
  -- 框架内置角色(ROLES 单一真相:superadmin 安全敏感项 / admin 日常管理 / editor、viewer 页面门)
8
16
  INSERT OR IGNORE INTO sys_role (name, module, title, builtin) VALUES ('superadmin', 'framework', '超级管理员', 1);
9
17
  INSERT OR IGNORE INTO sys_role (name, module, title, builtin) VALUES ('admin', 'framework', '系统管理员', 1);
10
18
  INSERT OR IGNORE INTO sys_role (name, module, title, builtin) VALUES ('editor', 'framework', '编辑', 1);
11
19
  INSERT OR IGNORE INTO sys_role (name, module, title, builtin) VALUES ('viewer', 'framework', '访客', 1);
12
- -- seed 超管绑定(sys_user_role 唯一权威源,D10)
13
- INSERT OR IGNORE INTO sys_user_role (user_id, role_name) SELECT id, 'superadmin' FROM users WHERE username = 'admin';
20
+ -- 角色绑定(sys_user_role 唯一权威源,D10):账号名=角色名一一对应
21
+ INSERT OR IGNORE INTO sys_user_role (user_id, role_name) SELECT id, 'superadmin' FROM users WHERE username = 'superadmin';
22
+ INSERT OR IGNORE INTO sys_user_role (user_id, role_name) SELECT id, 'admin' FROM users WHERE username = 'admin';
23
+ INSERT OR IGNORE INTO sys_user_role (user_id, role_name) SELECT id, 'editor' FROM users WHERE username = 'editor';
24
+ INSERT OR IGNORE INTO sys_user_role (user_id, role_name) SELECT id, 'viewer' FROM users WHERE username = 'viewer';
14
25
  -- 系统设置默认值(PRD §4.3;C4 设置页 / C5 登录方式发现的数据源;framework.market_cdn_url 安全组仅 superadmin 可改)
15
26
  INSERT OR IGNORE INTO sys_setting (key, module, "group", type, value, default_value, label_key, secret, update_time) VALUES ('framework.site_title', 'framework', 'general', 'string', 'React Antd Admin', 'React Antd Admin', 'system:setting.site_title', 0, 0);
16
27
  INSERT OR IGNORE INTO sys_setting (key, module, "group", type, value, default_value, label_key, secret, update_time) VALUES ('framework.market_cdn_url', 'framework', 'security', 'string', '', '', 'system:setting.market_cdn_url', 0, 0);
@@ -30,12 +30,16 @@ import { journalHeadOf, siteOrigin } from "../_shared/site-facade";
30
30
  /** 线格式行(与 packages/runtime/src/api/rbac/types.ts 的 SolutionRouteRow 对齐) */
31
31
  export type { SolutionRouteRow } from "../_lifecycle/verify-checks";
32
32
 
33
- /** oj serve release 布局生产缺省 */
33
+ /**
34
+ * oj serve release 布局生产缺省(与 marketEnv 同口径,见其注释):
35
+ * sharedRoot = 后端 release 根(版本目录 / migrations)
36
+ * modulesJsonPath = 站点产物根下的 modules.json(web 入口 integrity 真源)
37
+ */
34
38
  export function solutionsEnv(): SolutionsEnv {
35
39
  return {
36
- sharedRoot: "dist",
40
+ sharedRoot: "api/dist",
37
41
  statePath: "modules/.solutions/state.yaml",
38
- modulesJsonPath: "dist/modules.json",
42
+ modulesJsonPath: "web/dist/modules.json",
39
43
  apiPrefix: "/api",
40
44
  };
41
45
  }
@@ -1,7 +1,7 @@
1
1
  // 登录限速编排(B5/AC1-26):auth/login 与 auth/ldap-login 共用的 (IP, username) 双维度守卫 + 失败记账。
2
2
  // 状态全在 DB(sys_login_throttle/sys_rate_window,迁移 0009)——oj 每请求重建模块,进程内方案恒失效(§9.3-3)。
3
- import { auditOp } from "../../_platform/_shared/service/infra/audit";
4
- import { loginBlocked, loginFailed, loginSucceeded, rateLimit } from "../../_platform/_shared/service/infra/rate";
3
+ import { auditOp } from "#/_platform/_shared/service/infra/audit";
4
+ import { loginBlocked, loginFailed, loginSucceeded, rateLimit } from "#/_platform/_shared/service/infra/rate";
5
5
 
6
6
  /** 客户端 IP:优先 X-Forwarded-For(反代后),回落 X-Real-IP。 */
7
7
  export async function clientIp(): Promise<string> {
@@ -4,8 +4,8 @@
4
4
  // B4:access JWT 仅载 sub(roles 走登录响应/资料接口,不进 claims)。
5
5
  // B3:refresh = 不透明随机串 + DB 存 SHA-256 哈希(sys_refresh_token,迁移 0008);
6
6
  // 轮换作旧 + 滑动窗口(绝对 TTL 封顶 min(旧 exp, now+30d));reuse 检测全族吊销 + 审计。
7
- import { invalidateUser } from "../../_platform/_shared/service/infra/access";
8
- import { recordOp } from "../../_platform/_shared/service/infra/audit";
7
+ import { invalidateUser } from "#/_platform/_shared/service/infra/access";
8
+ import { recordOp } from "#/_platform/_shared/service/infra/audit";
9
9
 
10
10
  export interface SessionDeps {
11
11
  randomHex: (bytes: number) => string
@@ -1,4 +1,4 @@
1
- import { respond } from "../_platform/_shared/respond";
1
+ import { respond } from "#/_platform/_shared/respond";
2
2
  // oj 嵌套 handler 组:组名 = 路径段(login → POST /api/auth/login,路由不变)。
3
3
  // 阶段 B 登录链(D14/AC1-12/13/27):领域逻辑全在 _shared/session.ts(createSession 注入 deps),
4
4
  // handler 只编排:取参 → 调 service → json 信封;B5 限速收敛在 _shared/login-throttle.ts(与 ldap-login 共用)。
@@ -1,6 +1,6 @@
1
1
  // POST /api/auth/ldap-login —— LDAP 目录登录(D1/AC1-12;匿名端点,E6:anonymous_paths 写 /auth/ldap-login)。
2
2
  // 编排:B5 限速守卫(与 auth/login 同维度)→ ldapLogin 九步 → respond 统一异常出口(§9.3-1)。
3
- import { respond } from "../../_platform/_shared/respond";
3
+ import { respond } from "#/_platform/_shared/respond";
4
4
  import { createLdapLogin, runtimeLdapGateway } from "../_shared/ldap-login";
5
5
  import { clientIp, guardLoginThrottle, noteLoginFailed, noteLoginSucceeded } from "../_shared/login-throttle";
6
6
  import { createSession } from "../_shared/session";
@@ -0,0 +1,6 @@
1
+ name: auth
2
+ desc: JWT 鉴权端点(业务路由):login/refresh/logout;Bearer 守卫由 oj-auth 提供
3
+ version: 0.1.0
4
+ deps: # 两处都用:login/refresh 读 _platform.users(ownership_guard: deny,§3/S003);
5
+ # 跨模块别名 `#/_platform/…` 的依赖门禁(oj build S008)
6
+ _platform: ^0.1.0
@@ -1,5 +1,6 @@
1
1
  import type { IndexRow, TaskRecord } from "../types";
2
- import { screen, waitFor } from "@testing-library/react";
2
+ import { fireEvent, screen, waitFor } from "@testing-library/react";
3
+ import { useState } from "react";
3
4
  import { describe, expect, it } from "vitest";
4
5
  import { InstallDialog } from "../components/InstallDialog";
5
6
  import { InMemoryMarketClient } from "./marketClient.fake";
@@ -69,4 +70,55 @@ describe("installDialog 安装进度(BDD)", () => {
69
70
  expect(client.calls).toContain("install:alpha@1.1.0");
70
71
  expect(screen.getByText("回滚中")).toBeTruthy();
71
72
  });
73
+
74
+ it("given needs_trust 终态 When 打开 Then 渲染指纹与确认按钮,点击后入库并重试安装", async () => {
75
+ const pending: TaskRecord = {
76
+ id: "t1",
77
+ name: "alpha",
78
+ version: "1.2.0",
79
+ status: "needs_trust",
80
+ stage: "verifying",
81
+ progress: 18,
82
+ message: "待信任确认",
83
+ stages: [],
84
+ trust: { key_id: "alpha.author", public_key: "ab".repeat(32), fingerprint: "6afc".repeat(16) },
85
+ startedAt: 0,
86
+ };
87
+ const client = new InMemoryMarketClient({ taskRecord: pending, installResult: { task_id: "t1" } });
88
+ renderWithProviders(<InstallDialog row={row} open onClose={() => {}} />, client);
89
+ const button = await screen.findByText("指纹一致,信任并重试");
90
+ expect(screen.getByTestId("install-trust-fingerprint").textContent).toContain("6afc".repeat(16));
91
+ fireEvent.click(button);
92
+ await waitFor(() => expect(client.calls).toContain("trust:t1"));
93
+ // 确认入库后自动重试同一安装(staging 未清场,服务端续跑)
94
+ await waitFor(() => expect(client.calls.filter(c => c.startsWith("install:")).length).toBe(2));
95
+ });
96
+
97
+ it("given 终态 When 父级每次渲染都换 onDone 身份 Then 只通告一次(防 onDone→重渲染→onDone 死循环)", async () => {
98
+ const done: TaskRecord = {
99
+ id: "t1",
100
+ name: "alpha",
101
+ version: "1.2.0",
102
+ status: "done",
103
+ stage: "done",
104
+ progress: 100,
105
+ message: "ok",
106
+ stages: [],
107
+ startedAt: 0,
108
+ };
109
+ const client = new InMemoryMarketClient({ taskRecord: done, installResult: { task_id: "t1" } });
110
+ function Harness() {
111
+ const [n, setN] = useState(0);
112
+ return (
113
+ <>
114
+ <span data-testid="done-count">{n}</span>
115
+ <InstallDialog row={row} open onClose={() => {}} onDone={() => setN(v => v + 1)} />
116
+ </>
117
+ );
118
+ }
119
+ renderWithProviders(<Harness />, client);
120
+ await waitFor(() => expect(screen.getByTestId("install-message").textContent).toBe("ok"), { timeout: 5000 });
121
+ await new Promise(resolve => setTimeout(resolve, 50));
122
+ expect(screen.getByTestId("done-count").textContent).toBe("1");
123
+ });
72
124
  });
@@ -11,6 +11,7 @@ export interface FakeState {
11
11
  enableResult?: { ok: boolean, message: string }
12
12
  uninstallResult?: { ok: boolean, message: string }
13
13
  installResult?: { task_id: string }
14
+ trustResult?: { result: string, key_id: string, fingerprint: string }
14
15
  }
15
16
 
16
17
  export class InMemoryMarketClient implements MarketClient {
@@ -49,6 +50,11 @@ export class InMemoryMarketClient implements MarketClient {
49
50
  } as TaskRecord);
50
51
  }
51
52
 
53
+ async trust(taskId: string) {
54
+ this.calls.push(`trust:${taskId}`);
55
+ return this.state.trustResult ?? { result: "added", key_id: "k1", fingerprint: "f".repeat(64) };
56
+ }
57
+
52
58
  async enable(input: EnableInput) {
53
59
  this.calls.push(`enable:${input.name}:${input.enabled}`);
54
60
  return this.state.enableResult ?? { ok: true, message: "ok" };
@@ -24,6 +24,11 @@ export interface MarketClient {
24
24
  install: (input: InstallInput) => Promise<{ task_id: string }>
25
25
  /** 任务轮询消费面(query 形态 ?id=) */
26
26
  task: (id: string) => Promise<TaskRecord>
27
+ /**
28
+ * TOFU 确认:把 needs_trust 任务记录里的发布方 key 写入宿主信任库(服务端自任务记录重取
29
+ * 公钥复算指纹比对,防混淆代理)。确认成功后重试同一安装即可续跑(staging 未清场)。
30
+ */
31
+ trust: (taskId: string) => Promise<{ result: string, key_id: string, fingerprint: string }>
27
32
  /** 生命周期开关(同步 setEnabled,无任务模型;{ok,message} 200 渲染 ok 位) */
28
33
  enable: (input: EnableInput) => Promise<{ ok: boolean, message: string }>
29
34
  /** 市场操作历史(_platform 只读端点 /_platform/market/history) */
@@ -1,5 +1,5 @@
1
1
  import type { MarketClient } from "./client";
2
- import { createContext, useContext } from "react";
2
+ import { createContext, use } from "react";
3
3
  import { httpMarketClient } from "./defaultClient";
4
4
 
5
5
  /** 市场客户端上下文(依赖注入位;默认 = 运行时 HTTP 实现,测试可替换为内存 fake) */
@@ -7,5 +7,5 @@ export const MarketClientContext = createContext<MarketClient>(httpMarketClient)
7
7
 
8
8
  /** 组件/hooks 取客户端(默认注入运行时实现) */
9
9
  export function useMarketClient(): MarketClient {
10
- return useContext(MarketClientContext);
10
+ return use(MarketClientContext);
11
11
  }
@@ -25,9 +25,10 @@ import {
25
25
  platformMarketInstallPost,
26
26
  platformMarketMetaGet,
27
27
  platformMarketTaskGet,
28
+ platformMarketTrustPost,
28
29
  platformMarketUninstallImpactGet,
29
30
  platformMarketUninstallPost,
30
- } from "../../_platform/__client/api";
31
+ } from "../../../_platform/web/__client/api";
31
32
 
32
33
  // 绑定运行时 request 到生成 client 的模块级单槽(生成 client 是静态单例,必须由某处
33
34
  // bindRequest;此处兜底确保市场子应用无论挂载顺序都能拿到请求实例)。与 _platform 模块
@@ -96,6 +97,11 @@ export const httpMarketClient: MarketClient = {
96
97
  // 运行时形状由服务端真源 + DEV zod 校验保证,TS 侧以领域类型为准。
97
98
  return (await platformMarketTaskGet({ id })) as unknown as TaskRecord;
98
99
  },
100
+ async trust(taskId: string) {
101
+ // 服务端自任务记录重取公钥复算指纹比对(防混淆代理),前端只传 task_id,不传公钥
102
+ const data = await platformMarketTrustPost({ task_id: taskId });
103
+ return data as { result: string, key_id: string, fingerprint: string };
104
+ },
99
105
  async enable(input: EnableInput) {
100
106
  const data = await platformMarketEnablePost({ name: input.name, enabled: input.enabled });
101
107
  return data as { ok: boolean, message: string };