@oj-module/cli 0.2.64 → 0.2.67

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 (369) hide show
  1. package/bin/.oj-version +1 -0
  2. package/bin/oj +4 -0
  3. package/devkit/README.md +18 -4
  4. package/devkit/SKILL.md +35 -3
  5. package/devkit/framework-dev.md +22 -11
  6. package/devkit/manual.md +161 -68
  7. package/devkit/menu-rules.md +4 -1
  8. package/devkit/publish-walkthrough.md +110 -37
  9. package/devkit/wecom-internal-app.md +7 -2
  10. package/devkit/wecom-multi-corp-links.md +163 -0
  11. package/package.json +3 -2
  12. package/shell-dist/assets/ahooks.2l850ymmh0.js +18 -0
  13. package/shell-dist/assets/antd-es-locale-zh_CN.1gjjsvxbf0.js +1 -0
  14. package/shell-dist/assets/antd-img-crop.196rkty480.js +99 -0
  15. package/shell-dist/assets/antd-locale-en_US.561tozx860.js +1 -0
  16. package/shell-dist/assets/antd-locale-zh_CN.1cmpmgzh70.js +1 -0
  17. package/shell-dist/assets/antd.uw1coq15l0.js +348 -0
  18. package/shell-dist/assets/clsx.jotilrt2e0.js +1 -0
  19. package/shell-dist/assets/contract-errors.2o17vz9u50.js +1 -0
  20. package/shell-dist/assets/contract.3h327o3mj0.js +57 -0
  21. package/shell-dist/assets/cssinjs.atgmhan7m0.js +3 -0
  22. package/shell-dist/assets/dayjs-plugin-advancedFormat.3vsyd2eis0.js +1 -0
  23. package/shell-dist/assets/dayjs-plugin-customParseFormat.13xhghv1d0.js +1 -0
  24. package/shell-dist/assets/dayjs-plugin-isoWeek.1fn1tw82p0.js +1 -0
  25. package/shell-dist/assets/dayjs-plugin-localeData.2rwbjp9nk0.js +1 -0
  26. package/shell-dist/assets/dayjs-plugin-quarterOfYear.2oqo1lcx80.js +1 -0
  27. package/shell-dist/assets/dayjs-plugin-relativeTime.2pqs3et4y0.js +1 -0
  28. package/shell-dist/assets/dayjs-plugin-weekOfYear.gvn9iph080.js +1 -0
  29. package/shell-dist/assets/dayjs-plugin-weekYear.2u78l3ex10.js +1 -0
  30. package/shell-dist/assets/dayjs-plugin-weekday.2d56a4tml0.js +1 -0
  31. package/shell-dist/assets/dayjs.pa60l6wjo0.js +1 -0
  32. package/shell-dist/assets/dnd-kit-core.1xyreig6r0.js +5 -0
  33. package/shell-dist/assets/dnd-kit-sortable.s94qciuft0.js +1 -0
  34. package/shell-dist/assets/dnd-kit-utilities.1umw4xphb0.js +1 -0
  35. package/shell-dist/assets/i18next.33zqofapj0.js +1 -0
  36. package/shell-dist/assets/icons-es-components-Context.365qmflwg0.js +1 -0
  37. package/shell-dist/assets/icons.159wjwals0.js +57 -0
  38. package/shell-dist/assets/{index-xZhmujjM.js → index.6alqyx6ae0.js} +21 -1
  39. package/shell-dist/assets/jsx-dev-runtime.3uibuc82f0.js +14 -0
  40. package/shell-dist/assets/jsx-runtime.h40xu4o5b0.js +14 -0
  41. package/shell-dist/assets/keepalive-for-react.xenu5vam70.js +1 -0
  42. package/shell-dist/assets/kit.37kr8ui390.js +1 -0
  43. package/shell-dist/assets/ky.1cgch7gu60.js +6 -0
  44. package/shell-dist/assets/motion-react.pjsxytvjs0.js +12 -0
  45. package/shell-dist/assets/motion.21tciqutg0.js +4 -0
  46. package/shell-dist/assets/nprogress.2nz76n9ny0.js +7 -0
  47. package/shell-dist/assets/ojm-dayjs-locale-zh-cn.3vr3beiyp0.js +9 -0
  48. package/shell-dist/assets/pinyin-pro.3v0mnxczn0.js +1 -0
  49. package/shell-dist/assets/pro-components.2kyot1vcl0.js +83 -0
  50. package/shell-dist/assets/react-countup.2x8idnr8y0.js +10 -0
  51. package/shell-dist/assets/react-dom-client.ick0v05ob0.js +43 -0
  52. package/shell-dist/assets/react-dom.2so9uxg3f0.js +22 -0
  53. package/shell-dist/assets/react-error-boundary.2dncdfj3b0.js +1 -0
  54. package/shell-dist/assets/react-i18next.auhvjhqv00.js +1 -0
  55. package/shell-dist/assets/react-jss.1ei6dr9e00.js +29 -0
  56. package/shell-dist/assets/react-query.3rln45ow00.js +1 -0
  57. package/shell-dist/assets/react-router-dom.3c6zld8h40.js +44 -0
  58. package/shell-dist/assets/react-router.2h9rebcv50.js +56 -0
  59. package/shell-dist/assets/react.1ew63mwts0.js +14 -0
  60. package/shell-dist/assets/runtime.2dgstpjsu0.js +21702 -0
  61. package/shell-dist/assets/simplebar-react.2g739uvai0.js +1 -0
  62. package/shell-dist/assets/spin-delay.yjeyj6iwp0.js +9 -0
  63. package/shell-dist/assets/tailwind-merge.125ehiqjw0.js +1 -0
  64. package/shell-dist/assets/use-sync-external-store-shim.1nz1pi9bb0.js +22 -0
  65. package/shell-dist/assets/zustand-middleware.1xaxjueme0.js +6 -0
  66. package/shell-dist/assets/zustand-react-shallow.pybayv1hx0.js +1 -0
  67. package/shell-dist/assets/zustand-react.9fsl9brxy0.js +1 -0
  68. package/shell-dist/assets/zustand-shallow.6cz0yyv9x0.js +1 -0
  69. package/shell-dist/assets/zustand-vanilla-shallow.3424monv50.js +1 -0
  70. package/shell-dist/assets/zustand-vanilla.1mo3ka3g30.js +1 -0
  71. package/shell-dist/assets/zustand.3gjgzlnqo0.js +1 -0
  72. package/shell-dist/index.html +67 -118
  73. package/shell-dist/versions.json +3 -3
  74. package/src/__tests__/publish-keygen.test.ts +157 -0
  75. package/src/__tests__/templates-migration-schema-parity.test.ts +540 -0
  76. package/src/__tests__/templates-schema-columns.test.ts +15 -2
  77. package/src/__tests__/templates-tenant-scope.test.ts +6 -2
  78. package/src/bundle.ts +8 -3
  79. package/src/cli.ts +17 -5
  80. package/src/dev.ts +15 -10
  81. package/src/esm-exports.ts +152 -70
  82. package/src/install.ts +11 -4
  83. package/src/keygen.ts +54 -0
  84. package/src/lifecycle/__tests__/bundle.test.ts +21 -3
  85. package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/manifest.yaml +1 -1
  86. package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/migrations/0001__init.sql +2 -2
  87. package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/schema.yaml +5 -2
  88. package/src/lifecycle/__tests__/fixtures/fixture-valid/manifest.yaml +1 -1
  89. package/src/lifecycle/__tests__/manifest-contract.test.ts +75 -3
  90. package/src/lifecycle/__tests__/smoke.test.ts +2 -2
  91. package/src/manifest-contract.ts +91 -8
  92. package/src/market/bootstrap.ts +26 -2
  93. package/src/market/command.ts +1 -0
  94. package/src/market/publish.ts +5 -1
  95. package/src/market/walkthrough.ts +24 -8
  96. package/src/shared-deps.ts +69 -19
  97. package/src/site-base.ts +4 -0
  98. package/src/site.ts +2 -0
  99. package/src/usage.ts +8 -4
  100. package/src/vendor.ts +5 -1
  101. package/templates/config.yaml +4 -2
  102. package/templates/init.include.yaml +8 -3
  103. package/templates/openapi.json +460 -1
  104. package/templates/src/app/api/_platform/_lifecycle/compensate.ts +136 -0
  105. package/templates/src/app/api/_platform/_lifecycle/installer.ts +71 -22
  106. package/templates/src/app/api/_platform/_lifecycle/lock.ts +23 -0
  107. package/templates/src/app/api/_platform/_lifecycle/steps/deploy-web.ts +28 -0
  108. package/templates/src/app/api/_platform/_lifecycle/steps/preflight.ts +46 -0
  109. package/templates/src/app/api/_platform/_lifecycle/steps/reload.ts +100 -36
  110. package/templates/src/app/api/_platform/_lifecycle/steps/smoke.ts +7 -15
  111. package/templates/src/app/api/_platform/_lifecycle/text-edit.ts +71 -19
  112. package/templates/src/app/api/_platform/_lifecycle/types.ts +26 -2
  113. package/templates/src/app/api/_platform/_lifecycle/uninstall.ts +93 -14
  114. package/templates/src/app/api/_platform/_shared/service/domain/settings.ts +8 -4
  115. package/templates/src/app/api/_platform/manifest.yaml +1 -0
  116. package/templates/src/app/api/_platform/market/api.ts +235 -10
  117. package/templates/src/app/api/_platform/migrations/0001__baseline.mysql.sql +178 -0
  118. package/templates/src/app/api/_platform/migrations/0001__baseline.pg.sql +173 -0
  119. package/templates/src/app/api/_platform/migrations/0001__baseline.sql +188 -0
  120. package/templates/src/app/api/_platform/migrations/0002__market_release.mysql.sql +17 -0
  121. package/templates/src/app/api/_platform/migrations/0002__market_release.pg.sql +16 -0
  122. package/templates/src/app/api/_platform/migrations/0002__market_release.sql +33 -0
  123. package/templates/src/app/api/_platform/schema.yaml +18 -0
  124. package/templates/src/app/api/_platform/seed.mysql.sql +10 -0
  125. package/templates/src/app/api/_platform/seed.pg.sql +10 -0
  126. package/templates/src/app/api/auth/_shared/wecom/client.ts +24 -4
  127. package/templates/src/app/api/home/migrations/0001__create_home_tables.mysql.sql +14 -0
  128. package/templates/src/app/api/home/migrations/0001__create_home_tables.pg.sql +14 -0
  129. package/templates/src/app/api/home/migrations/0002__tenant.pg.sql +4 -0
  130. package/templates/src/app/api/home/seed.mysql.sql +71 -0
  131. package/templates/src/app/api/home/seed.pg.sql +71 -0
  132. package/templates/src/app/api/notification/_shared/notify.ts +6 -6
  133. package/templates/src/app/api/notification/migrations/0001__create_notifications.mysql.sql +11 -0
  134. package/templates/src/app/api/notification/migrations/0001__create_notifications.pg.sql +11 -0
  135. package/templates/src/app/api/notification/migrations/0002__notifications_user.mysql.sql +8 -0
  136. package/templates/src/app/api/notification/migrations/0003__notifications_broadcast.mysql.sql +21 -0
  137. package/templates/src/app/api/notification/migrations/0003__notifications_broadcast.pg.sql +22 -0
  138. package/templates/src/app/api/notification/migrations/0004__notifications_broadcast_hidden.mysql.sql +10 -0
  139. package/templates/src/app/api/notification/migrations/0004__notifications_broadcast_hidden.pg.sql +11 -0
  140. package/templates/src/app/api/notification/migrations/0005__tenant.pg.sql +6 -0
  141. package/templates/src/app/api/notification/seed.mysql.sql +8 -0
  142. package/templates/src/app/api/notification/seed.pg.sql +11 -0
  143. package/templates/src/app/api/publish/_shared/identity.ts +69 -0
  144. package/templates/src/app/api/publish/api.ts +349 -0
  145. package/templates/src/app/api/publish/manifest.yaml +8 -0
  146. package/templates/src/app/api/publish/migrations/0001__publish_credential.mysql.sql +23 -0
  147. package/templates/src/app/api/publish/migrations/0001__publish_credential.pg.sql +23 -0
  148. package/templates/src/app/api/publish/migrations/0001__publish_credential.sql +39 -0
  149. package/templates/src/app/api/publish/schema.yaml +33 -0
  150. package/templates/src/app/api/system/login-methods/api.ts +2 -1
  151. package/templates/src/app/web/_platform/__client/api.schemas.ts +15 -0
  152. package/templates/src/app/web/_platform/__client/index.ts +25 -1
  153. package/templates/src/app/web/entry.ts +9 -5
  154. package/templates/src/app/web/home/pages/components/bar-chart.tsx +5 -3
  155. package/templates/src/app/web/home/pages/components/echarts-setup.ts +30 -0
  156. package/templates/src/app/web/home/pages/components/line-chart.tsx +5 -3
  157. package/templates/src/app/web/home/pages/components/pie-chart.tsx +6 -3
  158. package/templates/src/app/web/home/pages/index.tsx +3 -2
  159. package/templates/src/app/web/home/routes.ts +8 -2
  160. package/templates/src/app/web/login/locales/en-US.json +7 -1
  161. package/templates/src/app/web/login/locales/zh-CN.json +7 -1
  162. package/templates/src/app/web/login/pages/login.tsx +79 -86
  163. package/templates/src/app/web/login/routes.ts +54 -1
  164. package/templates/src/app/web/personal-center/locales/en-US.json +1 -0
  165. package/templates/src/app/web/personal-center/locales/zh-CN.json +1 -0
  166. package/templates/src/app/web/personal-center/pages/my-profile/index.tsx +5 -0
  167. package/templates/src/app/web/system/__client/api.schemas.ts +2 -1
  168. package/templates/src/app/web/system/locales/en-US.json +1 -8
  169. package/templates/src/app/web/system/locales/zh-CN.json +1 -8
  170. package/templates/src/app/web/system/pages/settings/index.tsx +17 -4
  171. package/templates/src/app/web/system/routes.ts +0 -6
  172. package/templates/src/oj-market/web/__tests__/CardGrid.test.tsx +33 -0
  173. package/templates/src/oj-market/web/__tests__/DetailDrawer.test.tsx +69 -0
  174. package/templates/src/oj-market/web/__tests__/marketClient.fake.ts +7 -1
  175. package/templates/src/oj-market/web/api/client.ts +3 -0
  176. package/templates/src/oj-market/web/api/defaultClient.ts +5 -0
  177. package/templates/src/oj-market/web/components/CardItem.tsx +44 -1
  178. package/templates/src/oj-market/web/components/DetailDrawer.tsx +17 -1
  179. package/templates/src/oj-market/web/entry.ts +31 -2
  180. package/templates/src/oj-market/web/locales/en-US.json +5 -1
  181. package/templates/src/oj-market/web/locales/zh-CN.json +5 -1
  182. package/templates/src/oj-market/web/publish/__client/api.schemas.ts +61 -0
  183. package/templates/src/oj-market/web/publish/__client/index.ts +164 -0
  184. package/templates/src/oj-market/web/publish/locales/en-US.json +80 -0
  185. package/templates/src/oj-market/web/publish/locales/zh-CN.json +80 -0
  186. package/templates/src/oj-market/web/publish/pages/history.tsx +112 -0
  187. package/templates/src/oj-market/web/publish/pages/index.tsx +196 -0
  188. package/templates/src/oj-market/web/publish/pages/model.ts +52 -0
  189. package/templates/src/oj-market/web/publish/pages/token-modal.tsx +82 -0
  190. package/templates/src/oj-market/web/publish/routes.ts +41 -0
  191. package/templates/src/oj-market/web/types.ts +30 -0
  192. package/templates/tests/lifecycle/compensate.spec.ts +207 -0
  193. package/templates/tests/lifecycle/deploy-smoke.spec.ts +38 -14
  194. package/templates/tests/lifecycle/installer.spec.ts +28 -9
  195. package/templates/tests/lifecycle/market-api.spec.ts +164 -12
  196. package/templates/tests/lifecycle/preflight.spec.ts +68 -0
  197. package/templates/tests/lifecycle/role-title-translate.spec.ts +2 -32
  198. package/templates/tests/lifecycle/text-edit.spec.ts +45 -27
  199. package/templates/tests/lifecycle/uninstall.spec.ts +36 -0
  200. package/templates/tests/publish-credential.spec.ts +354 -0
  201. package/templates/tests/publish-identity.spec.ts +44 -0
  202. package/templates/tests/wecom-client-timeout.spec.ts +33 -3
  203. package/templates/tests/wecom-landing.spec.ts +63 -1
  204. package/templates/tsconfig.paths.json +1 -0
  205. package/templates/web.config.ts +6 -4
  206. package/vendor/host-versions.json +3 -3
  207. package/shell-dist/assets/ahooks.js +0 -6909
  208. package/shell-dist/assets/antd-es-locale-zh_CN.js +0 -257
  209. package/shell-dist/assets/antd-img-crop.js +0 -1495
  210. package/shell-dist/assets/antd-locale-en_US.js +0 -374
  211. package/shell-dist/assets/antd-locale-zh_CN.js +0 -379
  212. package/shell-dist/assets/antd.js +0 -106874
  213. package/shell-dist/assets/clsx.js +0 -33
  214. package/shell-dist/assets/contract-errors.js +0 -30
  215. package/shell-dist/assets/contract.js +0 -18900
  216. package/shell-dist/assets/cssinjs.js +0 -2595
  217. package/shell-dist/assets/dayjs-plugin-advancedFormat.js +0 -98
  218. package/shell-dist/assets/dayjs-plugin-customParseFormat.js +0 -173
  219. package/shell-dist/assets/dayjs-plugin-isoWeek.js +0 -77
  220. package/shell-dist/assets/dayjs-plugin-localeData.js +0 -126
  221. package/shell-dist/assets/dayjs-plugin-quarterOfYear.js +0 -77
  222. package/shell-dist/assets/dayjs-plugin-relativeTime.js +0 -92
  223. package/shell-dist/assets/dayjs-plugin-weekOfYear.js +0 -73
  224. package/shell-dist/assets/dayjs-plugin-weekYear.js +0 -63
  225. package/shell-dist/assets/dayjs-plugin-weekday.js +0 -63
  226. package/shell-dist/assets/dayjs.js +0 -326
  227. package/shell-dist/assets/dnd-kit-core.js +0 -3601
  228. package/shell-dist/assets/dnd-kit-sortable.js +0 -717
  229. package/shell-dist/assets/dnd-kit-utilities.js +0 -340
  230. package/shell-dist/assets/i18next.js +0 -2321
  231. package/shell-dist/assets/icons-es-components-Context.js +0 -20
  232. package/shell-dist/assets/icons.js +0 -34961
  233. package/shell-dist/assets/jsx-dev-runtime.js +0 -75
  234. package/shell-dist/assets/jsx-runtime.js +0 -97
  235. package/shell-dist/assets/keepalive-for-react.js +0 -283
  236. package/shell-dist/assets/kit.js +0 -151
  237. package/shell-dist/assets/ky.js +0 -1427
  238. package/shell-dist/assets/motion-react.js +0 -13589
  239. package/shell-dist/assets/motion.js +0 -9959
  240. package/shell-dist/assets/nprogress.js +0 -314
  241. package/shell-dist/assets/ojm--ant-design-icons-es-icons-BarsOutlined.js +0 -7
  242. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CalendarOutlined.js +0 -7
  243. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CaretDownFilled.js +0 -7
  244. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CaretDownOutlined.js +0 -7
  245. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CaretUpOutlined.js +0 -7
  246. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CheckCircleFilled.js +0 -7
  247. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CheckOutlined.js +0 -7
  248. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ClockCircleOutlined.js +0 -7
  249. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CloseCircleFilled.js +0 -7
  250. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CloseOutlined.js +0 -7
  251. package/shell-dist/assets/ojm--ant-design-icons-es-icons-CopyOutlined.js +0 -7
  252. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DeleteOutlined.js +0 -7
  253. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DoubleLeftOutlined.js +0 -7
  254. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DoubleRightOutlined.js +0 -7
  255. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DownOutlined.js +0 -7
  256. package/shell-dist/assets/ojm--ant-design-icons-es-icons-DownloadOutlined.js +0 -7
  257. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EditOutlined.js +0 -7
  258. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EllipsisOutlined.js +0 -7
  259. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EnterOutlined.js +0 -7
  260. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ExclamationCircleFilled.js +0 -7
  261. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EyeInvisibleOutlined.js +0 -7
  262. package/shell-dist/assets/ojm--ant-design-icons-es-icons-EyeOutlined.js +0 -7
  263. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FileOutlined.js +0 -7
  264. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FileTextOutlined.js +0 -7
  265. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FilterFilled.js +0 -7
  266. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FolderOpenOutlined.js +0 -7
  267. package/shell-dist/assets/ojm--ant-design-icons-es-icons-FolderOutlined.js +0 -7
  268. package/shell-dist/assets/ojm--ant-design-icons-es-icons-HolderOutlined.js +0 -7
  269. package/shell-dist/assets/ojm--ant-design-icons-es-icons-InfoCircleFilled.js +0 -7
  270. package/shell-dist/assets/ojm--ant-design-icons-es-icons-LeftOutlined.js +0 -7
  271. package/shell-dist/assets/ojm--ant-design-icons-es-icons-LoadingOutlined.js +0 -7
  272. package/shell-dist/assets/ojm--ant-design-icons-es-icons-MinusOutlined.js +0 -7
  273. package/shell-dist/assets/ojm--ant-design-icons-es-icons-MinusSquareOutlined.js +0 -7
  274. package/shell-dist/assets/ojm--ant-design-icons-es-icons-PaperClipOutlined.js +0 -7
  275. package/shell-dist/assets/ojm--ant-design-icons-es-icons-PictureOutlined.js +0 -7
  276. package/shell-dist/assets/ojm--ant-design-icons-es-icons-PlusOutlined.js +0 -7
  277. package/shell-dist/assets/ojm--ant-design-icons-es-icons-PlusSquareOutlined.js +0 -7
  278. package/shell-dist/assets/ojm--ant-design-icons-es-icons-QuestionCircleOutlined.js +0 -7
  279. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ReloadOutlined.js +0 -7
  280. package/shell-dist/assets/ojm--ant-design-icons-es-icons-RightOutlined.js +0 -7
  281. package/shell-dist/assets/ojm--ant-design-icons-es-icons-RotateLeftOutlined.js +0 -7
  282. package/shell-dist/assets/ojm--ant-design-icons-es-icons-RotateRightOutlined.js +0 -7
  283. package/shell-dist/assets/ojm--ant-design-icons-es-icons-SearchOutlined.js +0 -7
  284. package/shell-dist/assets/ojm--ant-design-icons-es-icons-StarFilled.js +0 -7
  285. package/shell-dist/assets/ojm--ant-design-icons-es-icons-SwapOutlined.js +0 -7
  286. package/shell-dist/assets/ojm--ant-design-icons-es-icons-SwapRightOutlined.js +0 -7
  287. package/shell-dist/assets/ojm--ant-design-icons-es-icons-UpOutlined.js +0 -7
  288. package/shell-dist/assets/ojm--ant-design-icons-es-icons-VerticalAlignTopOutlined.js +0 -7
  289. package/shell-dist/assets/ojm--ant-design-icons-es-icons-WarningFilled.js +0 -7
  290. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ZoomInOutlined.js +0 -7
  291. package/shell-dist/assets/ojm--ant-design-icons-es-icons-ZoomOutOutlined.js +0 -7
  292. package/shell-dist/assets/ojm-dayjs-locale-zh-cn.js +0 -81
  293. package/shell-dist/assets/pinyin-pro.js +0 -25175
  294. package/shell-dist/assets/pro-components.js +0 -44627
  295. package/shell-dist/assets/react-countup.js +0 -525
  296. package/shell-dist/assets/react-dom-client.js +0 -12134
  297. package/shell-dist/assets/react-dom.js +0 -266
  298. package/shell-dist/assets/react-error-boundary.js +0 -141
  299. package/shell-dist/assets/react-i18next.js +0 -1773
  300. package/shell-dist/assets/react-jss.js +0 -4089
  301. package/shell-dist/assets/react-query.js +0 -3072
  302. package/shell-dist/assets/react-router-dom.js +0 -9927
  303. package/shell-dist/assets/react-router.js +0 -14584
  304. package/shell-dist/assets/react.js +0 -590
  305. package/shell-dist/assets/runtime.js +0 -28311
  306. package/shell-dist/assets/simplebar-react.js +0 -1119
  307. package/shell-dist/assets/spin-delay.js +0 -111
  308. package/shell-dist/assets/tailwind-merge.js +0 -3364
  309. package/shell-dist/assets/use-sync-external-store-shim.js +0 -130
  310. package/shell-dist/assets/zustand-middleware.js +0 -513
  311. package/shell-dist/assets/zustand-react-shallow.js +0 -27
  312. package/shell-dist/assets/zustand-react.js +0 -39
  313. package/shell-dist/assets/zustand-shallow.js +0 -22
  314. package/shell-dist/assets/zustand-vanilla-shallow.js +0 -71
  315. package/shell-dist/assets/zustand-vanilla.js +0 -40
  316. package/shell-dist/assets/zustand.js +0 -32
  317. package/templates/seed.mysql.sql +0 -6
  318. package/templates/seed.pg.sql +0 -6
  319. package/templates/src/app/api/_platform/migrations/0001__create_users.mysql.sql +0 -9
  320. package/templates/src/app/api/_platform/migrations/0001__create_users.pg.sql +0 -9
  321. package/templates/src/app/api/_platform/migrations/0001__create_users.sql +0 -7
  322. package/templates/src/app/api/_platform/migrations/0002__add_users_avatar_base64.mysql.sql +0 -3
  323. package/templates/src/app/api/_platform/migrations/0002__add_users_avatar_base64.sql +0 -2
  324. package/templates/src/app/api/_platform/migrations/0003__users_source_display_name.mysql.sql +0 -5
  325. package/templates/src/app/api/_platform/migrations/0003__users_source_display_name.sql +0 -5
  326. package/templates/src/app/api/_platform/migrations/0004__sys_rbac.mysql.sql +0 -70
  327. package/templates/src/app/api/_platform/migrations/0004__sys_rbac.pg.sql +0 -67
  328. package/templates/src/app/api/_platform/migrations/0004__sys_rbac.sql +0 -67
  329. package/templates/src/app/api/_platform/migrations/0005__users_must_change.mysql.sql +0 -3
  330. package/templates/src/app/api/_platform/migrations/0005__users_must_change.sql +0 -2
  331. package/templates/src/app/api/_platform/migrations/0006__users_is_forbidden.mysql.sql +0 -3
  332. package/templates/src/app/api/_platform/migrations/0006__users_is_forbidden.sql +0 -3
  333. package/templates/src/app/api/_platform/migrations/0007__users_email.sql +0 -3
  334. package/templates/src/app/api/_platform/migrations/0008__sys_refresh_token.mysql.sql +0 -10
  335. package/templates/src/app/api/_platform/migrations/0008__sys_refresh_token.pg.sql +0 -10
  336. package/templates/src/app/api/_platform/migrations/0008__sys_refresh_token.sql +0 -11
  337. package/templates/src/app/api/_platform/migrations/0009__sys_throttle.mysql.sql +0 -12
  338. package/templates/src/app/api/_platform/migrations/0009__sys_throttle.pg.sql +0 -12
  339. package/templates/src/app/api/_platform/migrations/0009__sys_throttle.sql +0 -13
  340. package/templates/src/app/api/_platform/migrations/0010__sys_solution_route.mysql.sql +0 -17
  341. package/templates/src/app/api/_platform/migrations/0010__sys_solution_route.pg.sql +0 -16
  342. package/templates/src/app/api/_platform/migrations/0010__sys_solution_route.sql +0 -18
  343. package/templates/src/app/api/_platform/migrations/0011__sys_setting_group_label.mysql.sql +0 -2
  344. package/templates/src/app/api/_platform/migrations/0011__sys_setting_group_label.sql +0 -4
  345. package/templates/src/app/api/_platform/migrations/0012__market_history.mysql.sql +0 -13
  346. package/templates/src/app/api/_platform/migrations/0012__market_history.pg.sql +0 -12
  347. package/templates/src/app/api/_platform/migrations/0012__market_history.sql +0 -14
  348. package/templates/src/app/api/_platform/migrations/0013__users_profile.sql +0 -4
  349. package/templates/src/app/api/_platform/migrations/0014__reg_source_seq.mysql.sql +0 -9
  350. package/templates/src/app/api/_platform/migrations/0014__reg_source_seq.pg.sql +0 -7
  351. package/templates/src/app/api/_platform/migrations/0014__reg_source_seq.sql +0 -8
  352. package/templates/src/app/api/_platform/migrations/0015__tenant.mysql.sql +0 -12
  353. package/templates/src/app/api/_platform/migrations/0015__tenant.pg.sql +0 -11
  354. package/templates/src/app/api/_platform/migrations/0015__tenant.sql +0 -44
  355. package/templates/src/app/api/_platform/migrations/0016__org.mysql.sql +0 -41
  356. package/templates/src/app/api/_platform/migrations/0016__org.pg.sql +0 -40
  357. package/templates/src/app/api/_platform/migrations/0016__org.sql +0 -62
  358. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.mysql.sql +0 -7
  359. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.pg.sql +0 -4
  360. package/templates/src/app/api/_platform/migrations/0017__users_employee_no.sql +0 -8
  361. package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.mysql.sql +0 -3
  362. package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.pg.sql +0 -3
  363. package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.sql +0 -8
  364. package/templates/src/app/web/system/pages/login-methods/index.tsx +0 -30
  365. /package/shell-dist/assets/{nprogress-css.js → nprogress-css.1cq2pzkre0.js} +0 -0
  366. /package/shell-dist/assets/{ojm-antd-es-button.js → ojm-antd-es-button.2uriphio50.js} +0 -0
  367. /package/shell-dist/assets/{ojm-antd-es-modal.js → ojm-antd-es-modal.bf3tgdh8v0.js} +0 -0
  368. /package/shell-dist/assets/{ojm-antd-es-slider.js → ojm-antd-es-slider.xyvptuhid0.js} +0 -0
  369. /package/shell-dist/assets/{ojm-antd-es-upload.js → ojm-antd-es-upload.13gx45e0g0.js} +0 -0
@@ -33,6 +33,7 @@ import { createTrustStore } from "../_lifecycle/trust";
33
33
  import { computeUninstallImpact, runUninstall } from "../_lifecycle/uninstall";
34
34
  import { respond } from "../_shared/respond";
35
35
  import { assertSuperAdmin } from "../_shared/service/infra/access";
36
+ import { pageArgs } from "../_shared/service/infra/query";
36
37
  import { journalHeadOf, readCanonicalOrigin, siteOrigin, withSiteOrigin } from "../_shared/site-facade";
37
38
  import { BOOTSTRAP_KEY, seedBootstrapKey } from "./bootstrap";
38
39
  import { httpIndexSource, indexPathOf } from "./index-source";
@@ -64,7 +65,7 @@ export interface MarketEnv {
64
65
  modulesJsonPath: string
65
66
  /** API 前缀(smoke 探针 URL 前缀) */
66
67
  apiPrefix: string
67
- /** 站点 config.yaml 路径(reloading 段 vars.ojm_install_seq 自增落盘处;缺省 config.yaml) */
68
+ /** 站点 config.yaml 路径(reloading 段 anonymous_paths 接线落盘处;缺省 config.yaml) */
68
69
  configPath?: string
69
70
  /** canonical origin(REG-CS6:相对 URL 补全用,取自 config.yaml vars.canonical_origin,绝不读请求头 Host) */
70
71
  canonicalOrigin?: string
@@ -236,7 +237,14 @@ function execOf(env: MarketEnv): ExecFace {
236
237
 
237
238
  function reloadOf(env: MarketEnv): ReloadFace {
238
239
  const g = globalThis as { server?: { reloadConfig: ReloadFace["reloadConfig"] } };
239
- return env.reload ?? { reloadConfig: () => g.server!.reloadConfig() };
240
+ // env 显式注入优先;其次平台 server.reloadConfig(生产 oj 提供,oj 0.1.123+ 支持 force 透传);
241
+ // 均缺失(dev/test 无 server)回落 no-op——等价于「不 reload」,保持卸载旧语义(reload 实际掉
242
+ // 挂载依赖 US-1 上游 manifests 摘要驱动,未到位前回落 no-op 不阻断卸载主流程);force 透传给宿主。
243
+ if (env.reload !== undefined)
244
+ return env.reload;
245
+ if (g.server?.reloadConfig !== undefined)
246
+ return { reloadConfig: (force?: boolean) => g.server!.reloadConfig(force) };
247
+ return { reloadConfig: async () => ({ unchanged: true, applied: [], restart_pending: [] }) };
240
248
  }
241
249
 
242
250
  function sha384Of(env: MarketEnv): (data: Uint8Array) => string {
@@ -249,6 +257,23 @@ function sha256Of(env: MarketEnv): (data: Uint8Array) => string {
249
257
  return env.sha256Hex ?? ((data: Uint8Array) => g.crypto!.sha256Hex!(data));
250
258
  }
251
259
 
260
+ /**
261
+ * 宿主能力集(P0 §3.9 / §2.3):从 ojInfo().build.capabilities 读取(v0.1.122 增量能力位闭集:
262
+ * reload.assemble_modules / reload.anon_per_reload / reload.cron_survives)。注入安装上下文后,
263
+ * preflight 据 manifest.requiresCapabilities 做写锁前门禁——宿主缺能力即 fail-closed(零痕迹)。
264
+ * ojInfo 不可用时回落空数组(等同未上报 → 若模板声明了 requiresCapabilities 则拒装)。
265
+ */
266
+ function capabilitiesOf(): string[] {
267
+ try {
268
+ const info = (globalThis as { ojInfo?: () => any }).ojInfo?.();
269
+ const caps = info?.build?.capabilities;
270
+ return Array.isArray(caps) ? caps : [];
271
+ }
272
+ catch {
273
+ return [];
274
+ }
275
+ }
276
+
252
277
  /** CDN 基址:注入面优先,缺省读设置表(值/缺省值俱空 = 未配置;表异常视同未配置,消费端给可读错误) */
253
278
  async function cdnBaseOf(env: MarketEnv): Promise<string | null> {
254
279
  if (env.cdnBase) {
@@ -694,6 +719,8 @@ async function installBody(env: MarketEnv): Promise<{ task_id: string }> {
694
719
  if (name === "")
695
720
  fail(400, "name is required");
696
721
  const reqVersion = body.version == null || body.version === "" ? null : String(body.version);
722
+ // force 透传(oj 0.1.123+ reloadConfig(force)):body.force 或 query.force=1 任一为真即强制重装配
723
+ const force = body.force === true || String((http as { query?: Record<string, unknown> }).query?.force ?? "") === "1";
697
724
 
698
725
  let manifest: unknown;
699
726
  let version: string;
@@ -786,7 +813,10 @@ async function installBody(env: MarketEnv): Promise<{ task_id: string }> {
786
813
  hostVersions: await hostVersionsOf(env),
787
814
  // 宿主平台编排面 API 版本(C2-11 peerPlatform 比对真源;与 meta 端点 apiVersion、_platform/manifest.yaml 版本同源,手工同步)
788
815
  hostApiVersion: API_VERSION,
816
+ // 宿主能力集(P0 §3.9):ojInfo().build.capabilities,preflight 的 requiresCapabilities 门禁据此生效
817
+ capabilities: capabilitiesOf(),
789
818
  trustPath: env.trustPath,
819
+ force,
790
820
  registryId: env.registryId,
791
821
  cancel: () => {
792
822
  if (!cancelled) {
@@ -818,10 +848,16 @@ interface IndexRow {
818
848
  available: string[]
819
849
  /** 最新版 changelog(C2-08,R27 卡片版本对比;分片条目缺 changelog → null) */
820
850
  changelog: unknown
821
- /** 最新版发布方(manifest author,C2-07;分片条目缺 author → null) */
851
+ /** 最新版发布方(manifest author,C2-07;分片条目缺 author → null,自报字段) */
822
852
  publisher: string | null
853
+ /** 最新版包级签名 key_id(Form B archive.signature.key_id,经 Ed25519 验签的发布身份;缺签名 → null) */
854
+ publisher_key: string | null
823
855
  /** 最新版许可(manifest license,C2-07 SPDX 简化式;分片条目缺/非字符串 → null) */
824
856
  license: string | null
857
+ /** 方案简介(manifest summary,C2-04 单行 1..200;分片条目缺/非字符串 → null) */
858
+ summary: string | null
859
+ /** 方案详细描述(manifest description,C2-05 ≤2000;分片条目缺/非字符串 → null) */
860
+ description: string | null
825
861
  /** 本地安装的发布方 key 状态(P2-6 sidecar;未装 → null) */
826
862
  trust_state: "trusted" | "untrusted" | "absent" | null
827
863
  /** 本地安装记录的签名 key id(sidecar archive.key_id;未装/P1 时代条目 → null) */
@@ -853,19 +889,32 @@ async function lastInstallOperatorOf(name: string): Promise<string | null> {
853
889
  *
854
890
  * 三级化后市场页的拉取形态:1 catalog + catalog 指向的全部分片(去重)——`available`
855
891
  * 回滚候选要版本全集,故页面比安装多拉几片;安装侧仍 ≤ 1 catalog + 1 shard(resolveCdn)。
856
- * 版本条目即 Form B manifest:latest 的 changelog/publisher 直接从分片条目取,不再有
857
- * 独立的聚合清单拉取。分片拉取/验签失败即 502(REG-CS1:可用性不再压过完整性)。
892
+ * 版本条目即 Form B manifest:latest 的 changelog/publisher/summary/description 直接从
893
+ * 分片条目取,不再有独立的聚合清单拉取。分片拉取/验签失败即 502(REG-CS1:可用性不再
894
+ * 压过完整性)。
858
895
  */
859
- async function indexBody(env: MarketEnv): Promise<IndexRow[]> {
860
- // 对账即服务(P1-10 接线③):市场页刷新即对账时点——锁 ↔ 账本 head 自愈 + enabled 不一致
861
- // 自动修复先行(失败只记日志,不阻断索引),失败站点的市场页每次打开都在尝试自愈
862
- await reconcileOf(env);
896
+ interface IndexSnapshot {
897
+ base: string
898
+ /** catalog 的签名时效(发布时间兜底;索引缺时效 → null) */
899
+ issuedAt: number | null
900
+ solutions: Array<{ name: string, shards: number[] }>
901
+ shardDocs: Map<number, ShardSolution[]>
902
+ }
903
+
904
+ /**
905
+ * 读整份签名索引(catalog + 它指向的全部分片)。
906
+ *
907
+ * 市场卡片(indexBody)与发布历史(syncReleases)都要「全量版本视图」,读取与验签只有这一份:
908
+ * 两处各写一遍 `fetchIndex` 循环,迟早漂移成「卡片看得见、历史看不见」。
909
+ */
910
+ async function readIndexSnapshot(env: MarketEnv): Promise<IndexSnapshot> {
863
911
  const base = await cdnBaseOf(env);
864
912
  if (base === null)
865
913
  fail(503, "未配置市场地址(framework.market_cdn_url)——卡片区显示「未配置市场地址」(PRD §5.3)");
866
914
  await assertOriginForRelativeBase(env, base);
867
915
  const source = await indexSourceOf(env, base);
868
916
  const catalog = await fetchIndex(env, source, base, "catalog");
917
+ const times = await indexTimesOfText(catalog.text);
869
918
  const { pins, solutions } = await parseCatalog(catalog.text, base);
870
919
  // 拉取 catalog 指向的全部分片(去重、升序确定性);每片都过签名 + 钉值同代校验
871
920
  const shardDocs = new Map<number, ShardSolution[]>();
@@ -873,6 +922,14 @@ async function indexBody(env: MarketEnv): Promise<IndexRow[]> {
873
922
  const shard = await fetchIndex(env, source, base, "shard", no, pins.get(no)!);
874
923
  shardDocs.set(no, await parseShard(shard.text, `shards/${no}.yaml`));
875
924
  }
925
+ return { base, issuedAt: times?.issued_at ?? null, solutions, shardDocs };
926
+ }
927
+
928
+ async function indexBody(env: MarketEnv): Promise<IndexRow[]> {
929
+ // 对账即服务(P1-10 接线③):市场页刷新即对账时点——锁 ↔ 账本 head 自愈 + enabled 不一致
930
+ // 自动修复先行(失败只记日志,不阻断索引),失败站点的市场页每次打开都在尝试自愈
931
+ await reconcileOf(env);
932
+ const { solutions, shardDocs } = await readIndexSnapshot(env);
876
933
  const local = (await createStateStore(env.statePath)).read().solutions;
877
934
  const rows: IndexRow[] = [];
878
935
  const seen = new Set<string>();
@@ -898,6 +955,11 @@ async function indexBody(env: MarketEnv): Promise<IndexRow[]> {
898
955
  const manifestOf = (version: string | null): Record<string, unknown> | undefined =>
899
956
  version === null ? undefined : merged.find(v => String(v.version) === version);
900
957
  const m = manifestOf(latest) ?? manifestOf(side?.version ?? null);
958
+ // 发布者:取最新版 Form B archive.signature.key_id(Ed25519 验签过的签名身份,离线可验);
959
+ // manifest.author 是自报字段,二者不同。缺签名/畸形 → null(不拿自报值冒充)。
960
+ const rawArchive: unknown = m?.archive;
961
+ const rawSig: unknown = isObj(rawArchive) ? rawArchive.signature : undefined;
962
+ const publisherKey = isObj(rawSig) && typeof rawSig.key_id === "string" ? rawSig.key_id : null;
901
963
  rows.push({
902
964
  name,
903
965
  latest,
@@ -907,7 +969,10 @@ async function indexBody(env: MarketEnv): Promise<IndexRow[]> {
907
969
  available,
908
970
  changelog: m?.changelog ?? null,
909
971
  publisher: typeof m?.author === "string" ? m.author : null,
972
+ publisher_key: publisherKey,
910
973
  license: typeof m?.license === "string" ? m.license : null,
974
+ summary: typeof m?.summary === "string" ? m.summary : null,
975
+ description: typeof m?.description === "string" ? m.description : null,
911
976
  trust_state: side?.trust_state ?? null,
912
977
  key_id: side?.archive?.key_id ?? null,
913
978
  operator: await lastInstallOperatorOf(name),
@@ -924,7 +989,10 @@ async function indexBody(env: MarketEnv): Promise<IndexRow[]> {
924
989
  available: [],
925
990
  changelog: null,
926
991
  publisher: null,
992
+ publisher_key: null,
927
993
  license: null,
994
+ summary: null,
995
+ description: null,
928
996
  trust_state: side.trust_state ?? null,
929
997
  key_id: side.archive?.key_id ?? null,
930
998
  operator: await lastInstallOperatorOf(name),
@@ -934,6 +1002,106 @@ async function indexBody(env: MarketEnv): Promise<IndexRow[]> {
934
1002
  return rows;
935
1003
  }
936
1004
 
1005
+ /** 发布历史行(市场索引里出现过的每个 product@version) */
1006
+ interface ReleaseRow {
1007
+ id: number
1008
+ product: string
1009
+ version: string
1010
+ key_id: string | null
1011
+ sha256: string | null
1012
+ size: number | null
1013
+ published_at: number
1014
+ /** 时间来源:file = tgz 的 Last-Modified;index = 索引生成时间兜底 */
1015
+ published_at_source: "file" | "index"
1016
+ yanked: number
1017
+ }
1018
+
1019
+ /**
1020
+ * 取单版本的「发布时间」。
1021
+ *
1022
+ * 优先 HEAD 分发包读 `Last-Modified`——那是包落进 CDN 的时刻,也就是真正的发版时间。
1023
+ * 读不到(源不吐该头 / 网络失败 / file 名缺失)才回落到索引生成时间:`ojm market publish`
1024
+ * 与 `build-index` 在推荐流程里紧邻执行,两者差值只有重建索引的耗时。
1025
+ *
1026
+ * 只对**首次入库**的版本调一次(已入库的 published_at 不再改写)——历史一旦记录就是事实,
1027
+ * 不该被后续重新发布/重新建索引刷新。
1028
+ */
1029
+ async function archivePublishedAt(env: MarketEnv, base: string, file: unknown, fallback: number): Promise<{ at: number, source: "file" | "index" }> {
1030
+ if (typeof file !== "string" || file === "")
1031
+ return { at: fallback, source: "index" };
1032
+ try {
1033
+ // HEAD 不在 FetchInitFace 的最小形状里(安装面只需要 GET),但运行时 fetch 原生支持:
1034
+ // 这里显式窄化一次,避免为「读一个头」再造第二种下载实现。
1035
+ const head = await fetchOf(env) as unknown as (url: string, init?: { method?: string }) => Promise<{ headers?: { get: (n: string) => string | null } }>;
1036
+ const res = await head(`${base}/${encodeURIComponent(file)}`, { method: "HEAD" });
1037
+ const raw = res.headers?.get?.("last-modified") ?? res.headers?.get?.("Last-Modified") ?? null;
1038
+ if (raw !== null && raw !== "") {
1039
+ const at = Math.floor(Date.parse(raw) / 1000);
1040
+ if (Number.isFinite(at) && at > 0)
1041
+ return { at, source: "file" };
1042
+ }
1043
+ }
1044
+ catch {}
1045
+ return { at: fallback, source: "index" };
1046
+ }
1047
+
1048
+ /**
1049
+ * 把索引里出现过的版本同步进 `market_release`(幂等:只补新行)。
1050
+ *
1051
+ * 为什么不做成「发版时由 CLI 上报」:`ojm market publish` 是纯本地操作(不登录、可离线),
1052
+ * 加一条上报链路就得再造一套写端授权的信任面;而**签名索引本身就是发版事实**——
1053
+ * 站点已经在读它,历史直接从这里落库,既不用新协议,也不会出现「上报成功但包里没有」的分叉。
1054
+ *
1055
+ * 索引拉取/验签失败由调用方兜住(历史是观测面,不阻断页面:返回已入库行 + syncError)。
1056
+ */
1057
+ async function syncReleases(env: MarketEnv): Promise<void> {
1058
+ const snapshot = await readIndexSnapshot(env);
1059
+ const fallbackAt = snapshot.issuedAt ?? (env.now?.() ?? Math.floor(Date.now() / 1000));
1060
+ const now = env.now?.() ?? Math.floor(Date.now() / 1000);
1061
+
1062
+ const existingRows = await db.table("market_release").select(["id", "product", "version", "yanked"]).all() as Array<Record<string, unknown>>;
1063
+ const existing = new Map(existingRows.map(r => [`${String(r.product)}@${String(r.version)}`, { id: Number(r.id), yanked: Number(r.yanked) }]));
1064
+
1065
+ for (const sol of snapshot.shardDocs.values()) {
1066
+ for (const entry of sol) {
1067
+ for (const v of entry.versions) {
1068
+ const version = typeof v.version === "string" ? v.version : "";
1069
+ if (version === "")
1070
+ continue;
1071
+ const dedupeKey = `${entry.name}@${version}`;
1072
+ const yanked = v.yanked === true ? 1 : 0;
1073
+ const prior = existing.get(dedupeKey);
1074
+ if (prior !== undefined) {
1075
+ // 已入库的 published_at 是事实、不再改写;但 yank 状态是「当前是否在架」,跟随索引
1076
+ if (prior.yanked !== yanked)
1077
+ await db.table("market_release").update({ yanked }).where({ field: "id", op: "eq", value: prior.id }).run();
1078
+ continue;
1079
+ }
1080
+ const archive = isObj(v.archive) ? v.archive : {};
1081
+ const sig = isObj(archive.signature) ? archive.signature : {};
1082
+ const { at, source } = await archivePublishedAt(env, snapshot.base, archive.file, fallbackAt);
1083
+ try {
1084
+ await db.table("market_release").insert({
1085
+ product: entry.name,
1086
+ version,
1087
+ key_id: typeof sig.key_id === "string" ? sig.key_id : null,
1088
+ sha256: typeof archive.sha256 === "string" ? archive.sha256 : null,
1089
+ size: typeof archive.size === "number" ? archive.size : null,
1090
+ published_at: at,
1091
+ published_at_source: source,
1092
+ yanked,
1093
+ first_seen_at: now,
1094
+ }).run();
1095
+ existing.set(dedupeKey, { id: -1, yanked });
1096
+ }
1097
+ catch {
1098
+ // 并发同步(两个页面同时打开)撞唯一键:另一路已写入,跳过即可
1099
+ }
1100
+ }
1101
+ }
1102
+ }
1103
+ }
1104
+
937
1105
  /** 四端点组(oj v0.1.64 嵌套组:组名 = 路径段);全业务体经 respond 收口(status 语义不落 500) */
938
1106
  export function createMarketApi(env: MarketEnv) {
939
1107
  return {
@@ -1131,6 +1299,7 @@ export function createMarketApi(env: MarketEnv) {
1131
1299
  state: await createStateStore(env.statePath),
1132
1300
  modulesJsonPath: env.modulesJsonPath,
1133
1301
  operator: (await operatorOf()) ?? undefined,
1302
+ reload: reloadOf(env),
1134
1303
  });
1135
1304
  // P3-11 宿主打磨:方案已不存在 = 资源缺失语义 → 404(非 200 {ok:false});
1136
1305
  // 其余领域拒绝(空锁拒启/级联失败等)维持 200 {ok:false} 人话在 message。
@@ -1156,6 +1325,42 @@ export function createMarketApi(env: MarketEnv) {
1156
1325
  });
1157
1326
  },
1158
1327
  },
1328
+
1329
+ // GET /api/_platform/market/releases —— 发布历史(登录即可读;与市场卡片同口径的「浏览开放」)。
1330
+ //
1331
+ // 数据源是签名索引本身:每次读取先把索引里出现过的 product@version 补进 market_release
1332
+ // (只补新行,published_at 一次写定),再按发布时间倒序分页返回。索引拉不到/验签失败
1333
+ // 不阻断查询:返回已入库行 + syncError,页面照常显示历史并提示「暂时读不到索引」。
1334
+ "releases": {
1335
+ async get() {
1336
+ await respond(async () => {
1337
+ const uid = (http as { user?: { id?: unknown } }).user?.id;
1338
+ if (uid == null)
1339
+ fail(401, "unauthorized");
1340
+ let syncError: string | null = null;
1341
+ try {
1342
+ await syncReleases(env);
1343
+ }
1344
+ catch (e) {
1345
+ syncError = e instanceof Error ? e.message : String(e);
1346
+ }
1347
+ const q = (http.query ?? {}) as Record<string, unknown>;
1348
+ const { page, size, offset } = pageArgs({ page: q.page, size: q.limit });
1349
+ const total = Number(
1350
+ (await db.table("market_release").select([{ fn: "count", as: "c" }]).all() as Array<Record<string, unknown>>)[0]?.c ?? 0,
1351
+ );
1352
+ const list = total === 0
1353
+ ? []
1354
+ : await db.table("market_release")
1355
+ .select(["id", "product", "version", "key_id", "sha256", "size", "published_at", "published_at_source", "yanked"])
1356
+ .orderBy([{ field: "published_at", dir: "desc" }, { field: "id", dir: "desc" }])
1357
+ .limit(size)
1358
+ .offset(offset)
1359
+ .all() as unknown as ReleaseRow[];
1360
+ return { list, total, page, size, syncError };
1361
+ });
1362
+ },
1363
+ },
1159
1364
  };
1160
1365
  }
1161
1366
 
@@ -1169,7 +1374,7 @@ marketApi.install.post.schema = {
1169
1374
  body: {
1170
1375
  type: "object",
1171
1376
  required: ["source", "name"],
1172
- properties: { source: { type: "string", enum: ["cdn", "file"] }, name: { type: "string" }, version: { type: ["string", "null"] } },
1377
+ properties: { source: { type: "string", enum: ["cdn", "file"] }, name: { type: "string" }, version: { type: ["string", "null"] }, force: { type: "boolean" } },
1173
1378
  },
1174
1379
  };
1175
1380
  marketApi.install.post.response = { type: "object", properties: { task_id: { type: "string" } }, additionalProperties: true };
@@ -1193,5 +1398,25 @@ marketApi["uninstall-impact"].get.response = { type: "object", additionalPropert
1193
1398
  marketApi.uninstall.post.schema = { body: { type: "object", required: ["name"], properties: { name: { type: "string" } } } };
1194
1399
  marketApi.uninstall.post.response = { type: "object", additionalProperties: true };
1195
1400
  marketApi.history.get.response = { type: "array", items: { type: "object", additionalProperties: true } };
1401
+ marketApi.releases.get.schema = {
1402
+ query: {
1403
+ type: "object",
1404
+ properties: {
1405
+ page: { type: "number", minimum: 1 },
1406
+ limit: { type: "number", minimum: 1, maximum: 200 },
1407
+ },
1408
+ },
1409
+ };
1410
+ marketApi.releases.get.response = {
1411
+ type: "object",
1412
+ required: ["list", "total", "page", "size"],
1413
+ properties: {
1414
+ list: { type: "array", items: { type: "object", additionalProperties: true } },
1415
+ total: { type: "number" },
1416
+ page: { type: "number" },
1417
+ size: { type: "number" },
1418
+ syncError: { type: ["string", "null"] },
1419
+ },
1420
+ };
1196
1421
 
1197
1422
  export default marketApi;
@@ -0,0 +1,178 @@
1
+ -- 0001__baseline(mysql 方言):_platform 全部表结构基线。压平口径见 0001__baseline.sql 头注释。
2
+ --
3
+ -- 方言差异(沿用历史迁移的既有惯例,逐条都有先例):
4
+ -- · TEXT 不能带字面默认值、不能直接做唯一键/主键 → 需要的那几列收窄为 VARCHAR;
5
+ -- · 字符串默认值写成表达式 DEFAULT ('x')(mysql ≥8.0.13 对 TEXT 系只允许表达式默认);
6
+ -- · AUTOINCREMENT → BIGINT AUTO_INCREMENT;秒级 epoch 的整型取 BIGINT;
7
+ -- · 保留字(key / group / order)用反引号(mysql 默认 sql_mode 下双引号是字符串字面量);
8
+ -- · 无 CREATE INDEX IF NOT EXISTS → 索引写在 CREATE TABLE 内,由迁移账本保证只执行一次。
9
+
10
+ CREATE TABLE IF NOT EXISTS users (
11
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
12
+ tenant_id VARCHAR(255) NOT NULL DEFAULT 'default',
13
+ corp_id VARCHAR(255) NULL,
14
+ username VARCHAR(255) NOT NULL,
15
+ password_hash TEXT NOT NULL,
16
+ avatar_base64 TEXT NOT NULL DEFAULT (''),
17
+ source TEXT NOT NULL DEFAULT ('local'),
18
+ display_name TEXT,
19
+ must_change BIGINT NOT NULL DEFAULT 0,
20
+ is_forbidden BIGINT NOT NULL DEFAULT 0,
21
+ email TEXT,
22
+ phone_number TEXT,
23
+ description TEXT,
24
+ activated INTEGER NOT NULL DEFAULT 0,
25
+ position VARCHAR(255) NULL,
26
+ external_status INTEGER NULL,
27
+ employee_no VARCHAR(64) NULL,
28
+ UNIQUE KEY uk_users_tenant_username (tenant_id, username),
29
+ -- 无部分索引:NULL 在 mysql 唯一键里可重复出现,效果等同「有值才唯一」
30
+ UNIQUE KEY uk_users_employee_no (tenant_id, employee_no)
31
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
32
+
33
+ CREATE TABLE IF NOT EXISTS sys_setting (
34
+ `key` VARCHAR(255) PRIMARY KEY,
35
+ module TEXT NOT NULL,
36
+ `group` TEXT NOT NULL,
37
+ type TEXT NOT NULL DEFAULT ('string'),
38
+ value TEXT,
39
+ default_value TEXT,
40
+ label_key TEXT,
41
+ options TEXT,
42
+ secret BIGINT NOT NULL DEFAULT 0,
43
+ update_time BIGINT NOT NULL DEFAULT 0,
44
+ deprecated BIGINT NOT NULL DEFAULT 0,
45
+ group_label_key TEXT
46
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
47
+
48
+ CREATE TABLE IF NOT EXISTS sys_oplog (
49
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
50
+ create_time BIGINT NOT NULL,
51
+ user_id BIGINT NOT NULL DEFAULT 0,
52
+ username TEXT NOT NULL DEFAULT ('system'),
53
+ action TEXT NOT NULL,
54
+ module TEXT NOT NULL DEFAULT ('framework'),
55
+ version TEXT,
56
+ target TEXT,
57
+ detail TEXT,
58
+ level BIGINT NOT NULL DEFAULT 6,
59
+ tenant_id VARCHAR(255) NOT NULL DEFAULT 'default',
60
+ KEY idx_sys_oplog_time (create_time, id)
61
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
62
+
63
+ CREATE TABLE IF NOT EXISTS sys_solution_route (
64
+ path VARCHAR(255) PRIMARY KEY,
65
+ -- module 参与索引 → 必须收窄为 VARCHAR:mysql 的 TEXT 列建索引必须给前缀长度
66
+ -- (历史 0010 的 mysql 版在这里直接 ERROR 1170,MySQL 全新安装根本起不来)
67
+ module VARCHAR(255) NOT NULL,
68
+ category TEXT NOT NULL,
69
+ `order` BIGINT NOT NULL DEFAULT 0,
70
+ title_key TEXT,
71
+ required_permission TEXT,
72
+ deprecated BIGINT NOT NULL DEFAULT 0,
73
+ enabled BIGINT NOT NULL DEFAULT 1,
74
+ KEY idx_sys_solution_route_module (module)
75
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
76
+
77
+ CREATE TABLE IF NOT EXISTS sys_role (
78
+ name VARCHAR(255) PRIMARY KEY,
79
+ module TEXT NOT NULL,
80
+ title TEXT,
81
+ description TEXT,
82
+ builtin BIGINT NOT NULL DEFAULT 0,
83
+ deprecated BIGINT NOT NULL DEFAULT 0,
84
+ title_key TEXT
85
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
86
+
87
+ CREATE TABLE IF NOT EXISTS sys_permission (
88
+ code VARCHAR(255) PRIMARY KEY,
89
+ module TEXT NOT NULL,
90
+ title_key TEXT,
91
+ deprecated BIGINT NOT NULL DEFAULT 0
92
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
93
+
94
+ CREATE TABLE IF NOT EXISTS sys_role_permission (
95
+ role_name VARCHAR(255) NOT NULL,
96
+ permission_code VARCHAR(255) NOT NULL,
97
+ PRIMARY KEY (role_name, permission_code)
98
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
99
+
100
+ CREATE TABLE IF NOT EXISTS sys_user_role (
101
+ user_id BIGINT NOT NULL,
102
+ role_name VARCHAR(255) NOT NULL,
103
+ tenant_id VARCHAR(255) NOT NULL DEFAULT 'default',
104
+ PRIMARY KEY (user_id, role_name)
105
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
106
+
107
+ CREATE TABLE IF NOT EXISTS sys_refresh_token (
108
+ token_hash VARCHAR(255) PRIMARY KEY,
109
+ user_id BIGINT NOT NULL,
110
+ expires_at BIGINT NOT NULL,
111
+ revoked BIGINT NOT NULL DEFAULT 0,
112
+ created_at BIGINT NOT NULL,
113
+ tenant_id VARCHAR(255) NOT NULL DEFAULT 'default',
114
+ KEY idx_sys_refresh_token_user (user_id)
115
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
116
+
117
+ CREATE TABLE IF NOT EXISTS sys_rate_window (
118
+ rate_key VARCHAR(255) PRIMARY KEY,
119
+ window_start BIGINT NOT NULL,
120
+ count BIGINT NOT NULL
121
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
122
+
123
+ CREATE TABLE IF NOT EXISTS sys_login_throttle (
124
+ pair_key VARCHAR(255) PRIMARY KEY,
125
+ fail_count BIGINT NOT NULL,
126
+ blocked_until BIGINT NOT NULL DEFAULT 0
127
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
128
+
129
+ CREATE TABLE IF NOT EXISTS market_history (
130
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
131
+ solution VARCHAR(255) NOT NULL,
132
+ version VARCHAR(255) NOT NULL,
133
+ action VARCHAR(64) NOT NULL,
134
+ status VARCHAR(64) NOT NULL,
135
+ operator VARCHAR(255),
136
+ message TEXT,
137
+ created_at BIGINT NOT NULL
138
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
139
+
140
+ CREATE TABLE IF NOT EXISTS reg_source_seq (
141
+ -- 主键参与索引 → 受 InnoDB 3072 字节上限约束:utf8mb4 每字符 4 字节,
142
+ -- 768 字符正好顶满,取 700 留余量(历史 0014 用 1024 → ERROR 1071,同样起不来)
143
+ source_url VARCHAR(700) NOT NULL,
144
+ max_seq BIGINT NOT NULL,
145
+ updated_at BIGINT NOT NULL,
146
+ PRIMARY KEY (source_url)
147
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
148
+
149
+ CREATE TABLE IF NOT EXISTS sys_department (
150
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
151
+ tenant_id VARCHAR(255) NOT NULL,
152
+ origin VARCHAR(32) NOT NULL DEFAULT 'local',
153
+ origin_id VARCHAR(128) NOT NULL,
154
+ parent_id BIGINT NULL,
155
+ name VARCHAR(255),
156
+ name_en VARCHAR(255),
157
+ order_no INTEGER NOT NULL DEFAULT 0,
158
+ sync_visible INTEGER NOT NULL DEFAULT 1,
159
+ deleted INTEGER NOT NULL DEFAULT 0,
160
+ seen_job_id BIGINT,
161
+ update_time BIGINT NOT NULL,
162
+ UNIQUE KEY uk_sys_department_origin (tenant_id, origin, origin_id),
163
+ KEY idx_sys_department_parent (tenant_id, parent_id, deleted, order_no)
164
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
165
+
166
+ CREATE TABLE IF NOT EXISTS sys_dept_member (
167
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
168
+ tenant_id VARCHAR(255) NOT NULL,
169
+ user_id BIGINT NOT NULL,
170
+ dept_id BIGINT NOT NULL,
171
+ order_no INTEGER NOT NULL DEFAULT 0,
172
+ is_leader INTEGER NOT NULL DEFAULT 0,
173
+ is_main INTEGER NOT NULL DEFAULT 0,
174
+ update_time BIGINT NOT NULL,
175
+ UNIQUE KEY uk_sys_dept_member (tenant_id, user_id, dept_id),
176
+ KEY idx_sys_dept_member_dept (tenant_id, dept_id, order_no),
177
+ KEY idx_sys_dept_member_user (tenant_id, user_id, is_main)
178
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;