@oj-module/cli 0.2.68 → 0.2.71

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 (153) hide show
  1. package/devkit/README.md +10 -8
  2. package/devkit/framework-dev.md +1 -1
  3. package/devkit/manual.md +45 -19
  4. package/devkit/module-dev-and-cdn-playground.md +20 -14
  5. package/devkit/publish-walkthrough.md +132 -43
  6. package/devkit/wecom-customer-reauth-guide.md +110 -0
  7. package/package.json +2 -2
  8. package/shell-dist/assets/{runtime.1xzahckt10.js → runtime.7x99hmm020.js} +2 -2
  9. package/shell-dist/bundles/oj-market-0.2.0.tgz +0 -0
  10. package/shell-dist/bundles/oj-market-0.2.0.tgz.sig +8 -0
  11. package/shell-dist/index.html +4 -4
  12. package/shell-dist/versions.json +3 -3
  13. package/src/__tests__/list.test.ts +7 -7
  14. package/src/__tests__/oj-health-host.test.ts +19 -0
  15. package/src/__tests__/site-tenant-header.test.ts +2 -2
  16. package/src/__tests__/templates-builtin-list.test.ts +47 -0
  17. package/src/__tests__/templates-schema-columns.test.ts +11 -2
  18. package/src/__tests__/templates-tenant-scope.test.ts +2 -3
  19. package/src/__tests__/users-roles-display.test.ts +84 -0
  20. package/src/bundle.ts +1 -1
  21. package/src/contract/__tests__/market-client.integration.test.ts +23 -22
  22. package/src/enable.ts +2 -2
  23. package/src/init.ts +45 -0
  24. package/src/install.ts +5 -5
  25. package/src/lifecycle/__tests__/install.test.ts +29 -29
  26. package/src/lifecycle/__tests__/market-bundle.test.ts +87 -0
  27. package/src/lifecycle/__tests__/publish.test.ts +8 -10
  28. package/src/lifecycle/__tests__/trust.test.ts +14 -14
  29. package/src/lifecycle/__tests__/uninstall.test.ts +4 -4
  30. package/src/list.ts +3 -3
  31. package/src/market/__tests__/bootstrap.test.ts +24 -7
  32. package/src/market/__tests__/failure-playbook.test.ts +0 -1
  33. package/src/market/__tests__/remote-push.test.ts +228 -0
  34. package/src/market/__tests__/verify.test.ts +45 -207
  35. package/src/market/bootstrap.ts +12 -7
  36. package/src/market/command.ts +33 -35
  37. package/src/market/db-seed.ts +129 -0
  38. package/src/market/failure-playbook.ts +3 -9
  39. package/src/market/fixture.ts +2 -6
  40. package/src/market/publish.ts +145 -89
  41. package/src/market/remote-push.ts +236 -0
  42. package/src/market/trial.ts +2 -2
  43. package/src/market/verify.ts +17 -270
  44. package/src/market/walkthrough.ts +38 -63
  45. package/src/oj.ts +13 -2
  46. package/src/preview.ts +5 -3
  47. package/src/publish.ts +3 -2
  48. package/src/scaffold-guard.ts +4 -0
  49. package/src/site.ts +1 -1
  50. package/src/trust.ts +8 -8
  51. package/src/uninstall.ts +2 -2
  52. package/src/usage.ts +23 -21
  53. package/templates/config.yaml +22 -3
  54. package/templates/global.d.ts +6 -4
  55. package/templates/init.include.yaml +16 -11
  56. package/templates/openapi.json +3971 -3663
  57. package/templates/src/.ojm-builtin.json +13 -0
  58. package/templates/src/README.md +5 -2
  59. package/templates/src/app/api/_platform/README.md +3 -2
  60. package/templates/src/app/api/_platform/_lifecycle/atomic.ts +7 -2
  61. package/templates/src/app/api/_platform/_lifecycle/bundle-converge.ts +158 -0
  62. package/templates/src/app/api/_platform/_lifecycle/history.ts +3 -2
  63. package/templates/src/app/api/_platform/_lifecycle/installer.ts +25 -5
  64. package/templates/src/app/api/_platform/_lifecycle/local-archive.ts +61 -0
  65. package/templates/src/app/api/_platform/_lifecycle/manifest-of-upload.ts +62 -0
  66. package/templates/src/app/api/_platform/_lifecycle/steps/preflight.ts +12 -2
  67. package/templates/src/app/api/_platform/_lifecycle/steps/reload.ts +4 -1
  68. package/templates/src/app/api/_platform/_lifecycle/steps/smoke.ts +8 -2
  69. package/templates/src/app/api/_platform/_lifecycle/types.ts +1 -1
  70. package/templates/src/app/api/_platform/_lifecycle/uninstall.ts +14 -5
  71. package/templates/src/app/api/_platform/_shared/service/domain/users.ts +7 -1
  72. package/templates/src/app/api/_platform/_shared/site-facade.ts +31 -0
  73. package/templates/src/app/api/_platform/manifest.yaml +1 -2
  74. package/templates/src/app/api/_platform/migrations/0003__market_catalog.mysql.sql +6 -0
  75. package/templates/src/app/api/_platform/migrations/0003__market_catalog.pg.sql +6 -0
  76. package/templates/src/app/api/_platform/migrations/0003__market_catalog.sql +18 -0
  77. package/templates/src/app/api/_platform/migrations/0004__market_drop_source_seq.mysql.sql +6 -0
  78. package/templates/src/app/api/_platform/migrations/0004__market_drop_source_seq.pg.sql +6 -0
  79. package/templates/src/app/api/_platform/migrations/0004__market_drop_source_seq.sql +6 -0
  80. package/templates/src/app/api/_platform/migrations/0005__market_sig_registry.mysql.sql +8 -0
  81. package/templates/src/app/api/_platform/migrations/0005__market_sig_registry.pg.sql +8 -0
  82. package/templates/src/app/api/_platform/migrations/0005__market_sig_registry.sql +8 -0
  83. package/templates/src/app/api/_platform/schema.yaml +8 -11
  84. package/templates/src/app/api/_platform/seed.mysql.sql +1 -1
  85. package/templates/src/app/api/_platform/seed.pg.sql +1 -1
  86. package/templates/src/app/api/_platform/seed.sql +1 -1
  87. package/templates/src/app/api/auth/_shared/wecom/sync.ts +6 -0
  88. package/templates/src/app/api/system/users/api.ts +3 -0
  89. package/templates/src/app/web/_platform/__client/api.schemas.ts +0 -113
  90. package/templates/src/app/web/_platform/__client/index.ts +1 -333
  91. package/templates/src/app/web/system/__client/api.schemas.ts +2 -0
  92. package/templates/src/app/web/system/locales/en-US.json +15 -1
  93. package/templates/src/app/web/system/locales/zh-CN.json +15 -1
  94. package/templates/src/app/web/system/pages/departments/sync-bar.tsx +9 -3
  95. package/templates/src/app/web/system/pages/roles/index.tsx +4 -8
  96. package/templates/src/app/web/system/pages/roles/role-label.ts +26 -0
  97. package/templates/src/app/web/system/pages/solutions/index.tsx +110 -0
  98. package/templates/src/app/web/system/pages/solutions/market-entry.ts +46 -0
  99. package/templates/src/app/web/system/pages/users/index.tsx +12 -2
  100. package/templates/src/app/web/system/pages/users/match-keyword.ts +19 -0
  101. package/templates/src/app/web/system/pages/users/roles.tsx +43 -14
  102. package/templates/src/app/web/system/routes.ts +18 -1
  103. package/templates/src/{app/api/_platform/market → oj-market/api}/api.ts +475 -592
  104. package/templates/src/oj-market/api/backfill.ts +221 -0
  105. package/templates/src/{app/api/_platform/market → oj-market/api}/bootstrap.ts +2 -2
  106. package/templates/src/oj-market/api/cdn/api.ts +312 -0
  107. package/templates/src/oj-market/api/cdn/shared.ts +104 -0
  108. package/templates/src/oj-market/api/cdn/signed-auth.ts +75 -0
  109. package/templates/src/oj-market/api/manifest.yaml +8 -0
  110. package/templates/src/oj-market/solution.yaml +21 -0
  111. package/templates/src/oj-market/web/__client/api.schemas.ts +167 -0
  112. package/templates/src/oj-market/web/__client/index.ts +509 -0
  113. package/templates/src/oj-market/web/__tests__/marketClient.fake.ts +6 -6
  114. package/templates/src/oj-market/web/api/client.ts +6 -6
  115. package/templates/src/oj-market/web/api/defaultClient.ts +31 -25
  116. package/templates/src/oj-market/web/entry.ts +5 -3
  117. package/templates/src/oj-market/web/hooks/useMarket.ts +6 -6
  118. package/templates/src/oj-market/web/pages/MarketApp.tsx +20 -3
  119. package/templates/src/oj-market/web/publish/locales/en-US.json +1 -1
  120. package/templates/src/oj-market/web/publish/locales/zh-CN.json +1 -1
  121. package/templates/src/oj-market/web/publish/pages/history.tsx +2 -11
  122. package/templates/src/oj-market/web/routes.ts +12 -0
  123. package/templates/src/oj-market/web/types.ts +14 -8
  124. package/templates/tests/lifecycle/anonymous-wiring.spec.ts +65 -0
  125. package/templates/tests/lifecycle/backfill.spec.ts +392 -0
  126. package/templates/tests/lifecycle/bootstrap.spec.ts +1 -1
  127. package/templates/tests/lifecycle/bundle-converge.spec.ts +264 -0
  128. package/templates/tests/lifecycle/cdn-upload.spec.ts +661 -0
  129. package/templates/tests/lifecycle/deploy-smoke.spec.ts +25 -0
  130. package/templates/tests/lifecycle/exec-grant.spec.ts +38 -0
  131. package/templates/tests/lifecycle/installer.spec.ts +36 -0
  132. package/templates/tests/lifecycle/market-api.spec.ts +448 -803
  133. package/templates/tests/lifecycle/preflight.spec.ts +18 -0
  134. package/templates/tests/lifecycle/trust.spec.ts +2 -2
  135. package/templates/tests/lifecycle/uninstall.spec.ts +36 -2
  136. package/templates/tests/lifecycle/verify-checks.spec.ts +1 -1
  137. package/templates/tests/rbac-users.spec.ts +8 -0
  138. package/templates/tests/solutions-entry.spec.ts +55 -0
  139. package/templates/tsconfig.paths.json +1 -0
  140. package/templates/types/stream-probes/stream-handlers.ts +34 -0
  141. package/templates/web.config.ts +6 -6
  142. package/vendor/host-versions.json +3 -3
  143. package/bin/oj +0 -4
  144. package/src/market/__tests__/build-index.test.ts +0 -641
  145. package/src/market/__tests__/sharding.test.ts +0 -90
  146. package/src/market/__tests__/yank-ledger.test.ts +0 -60
  147. package/src/market/build-index.ts +0 -526
  148. package/src/market/index-builder.ts +0 -149
  149. package/src/market/sharding.ts +0 -94
  150. package/src/market/yank-ledger.ts +0 -91
  151. package/templates/src/app/api/_platform/_lifecycle/source-seq.ts +0 -88
  152. package/templates/src/app/api/_platform/market/index-source.ts +0 -112
  153. package/templates/tests/lifecycle/source-seq.spec.ts +0 -55
@@ -0,0 +1,75 @@
1
+ // 签名请求共享「验面」(上传端点与 market yank 端点同一鉴权序,勿各写一遍)。
2
+ // 独立成非入口文件:api.ts / cdn/api.ts 都是路由入口(oj S 规则:路由入口不可被模块内 import)。
3
+ //
4
+ // 顺序即计划钉死的 fail-fast 序:头 → 信任库(403,不消费 body)→ 限速(按 key_id)→
5
+ // registry_id(503)。验签(verifyCanonicalSig)由调用方在拿到 canonical 后执行——
6
+ // 上传端点的 canonical 依赖实收字节 sha256(body 后),yank 的 canonical 不依赖 body。
7
+
8
+ import { createTrustStore } from "#/_platform/_lifecycle/trust";
9
+ import { rateLimit } from "#/_platform/_shared/service/infra/rate";
10
+ import { readSiteVar } from "#/_platform/_shared/site-facade";
11
+
12
+ /** 上传/推送限速:每 key_id 每 60s 30 次(发布是低频操作;防凭证泄漏后被刷爆磁盘/库) */
13
+ export const UPLOAD_RATE_LIMIT = { limit: 30, windowMs: 60_000 };
14
+
15
+ export interface SignedAuthEnv {
16
+ trustPath: string
17
+ /** 本站点 registry 身份;缺省读 config vars.registry_id */
18
+ registryId?: string
19
+ configPath?: string
20
+ verifyEd25519?: (publicKey: string, msg: string, sigBase64: string) => boolean
21
+ }
22
+
23
+ /** 带 status 的可读失败(respond 收口成 json.fail,D14/B5 同通道) */
24
+ function fail(status: number, msg: string): never {
25
+ throw Object.assign(new Error(msg), { status });
26
+ }
27
+
28
+ /** 请求头读取(大小写不敏感——http.headers 键大小写随客户端,wecom/api.ts 同款) */
29
+ function headerOf(headers: Record<string, unknown>, name: string): string {
30
+ const lower = name.toLowerCase();
31
+ for (const [k, v] of Object.entries(headers)) {
32
+ if (k.toLowerCase() === lower)
33
+ return String(v).trim();
34
+ }
35
+ return "";
36
+ }
37
+
38
+ /**
39
+ * 签名请求鉴权(pre-canonical):返回签名身份三要素。错误口径:
40
+ * 缺头 401(签名制不经账号口令);key 不受信 403(附 trust add 指引);限速 429;
41
+ * registry_id 未配置 503(指向 vars.registry_id 设置项)。canonical 验签不在此——
42
+ * 调用方持 canonical 后调 verifyCanonicalSig。
43
+ */
44
+ export async function authenticateSignedKey(env: SignedAuthEnv, rateBucket: string): Promise<{ keyId: string, publicKey: string, registryId: string, signature: string }> {
45
+ const headers = ((http as { headers?: Record<string, unknown> }).headers ?? {}) as Record<string, unknown>;
46
+ const keyId = headerOf(headers, "x-ojm-key-id");
47
+ const signature = headerOf(headers, "x-ojm-signature");
48
+ if (keyId === "" || signature === "")
49
+ fail(401, "缺 X-OJM-Key-Id / X-OJM-Signature 请求头——签名推送须经 ojm keygen --credential 兑换的发布身份(不经账号口令;403 指引的前提是手上已有 key_id)");
50
+ const publicKey = await createTrustStore(env.trustPath).get(keyId);
51
+ if (publicKey === null)
52
+ fail(403, `发布 key「${keyId}」不在本站点信任库——请站点 superadmin 经市场信任确认(或 ojm trust add)入录后重试`);
53
+ await rateLimit(`${rateBucket}:${keyId}`, UPLOAD_RATE_LIMIT.limit, UPLOAD_RATE_LIMIT.windowMs);
54
+ const registryId = await resolveRegistryId(env);
55
+ if (registryId === null)
56
+ fail(503, "站点未配置 registry_id(签名 canonical 首段 = 站点身份,防跨站重放):请在 config.yaml vars 增加 registry_id: <站点身份> 后重试");
57
+ return { keyId, publicKey, registryId, signature };
58
+ }
59
+
60
+ /** canonical 验签(Ed25519;verifyEd25519 面注入优先,缺省 isolate 全局) */
61
+ export function verifyCanonicalSig(env: { verifyEd25519?: SignedAuthEnv["verifyEd25519"] }, publicKey: string, canonical: string, signature: string): boolean {
62
+ return verifyEd25519Of({ verifyEd25519: env.verifyEd25519 })(publicKey, canonical, signature);
63
+ }
64
+
65
+ function verifyEd25519Of(env: { verifyEd25519?: SignedAuthEnv["verifyEd25519"] }): (publicKey: string, msg: string, sigBase64: string) => boolean {
66
+ const g = globalThis as { crypto?: { verifyEd25519?: (pk: string, msg: string, sig: string) => boolean } };
67
+ return env.verifyEd25519 ?? ((pk, msg, sig) => g.crypto!.verifyEd25519!(pk, msg, sig));
68
+ }
69
+
70
+ /** registry_id 解析:注入优先,缺省 config.yaml vars.registry_id;俱缺 → null(调用方 503) */
71
+ async function resolveRegistryId(env: SignedAuthEnv): Promise<string | null> {
72
+ if (env.registryId !== undefined)
73
+ return env.registryId;
74
+ return await readSiteVar(env.configPath, "registry_id");
75
+ }
@@ -0,0 +1,8 @@
1
+ name: oj-market
2
+ desc: 解决方案市场模块 api 面(市场目录 catalog/detail/yank、发布推送 cdn/upload、方案生命周期编排 install/enable/uninstall、信任库管理 trust)。表(market_release/market_history)与安装引擎归 _platform 内核,本模块经 deps 声明跨模块依赖。
3
+ version: 0.1.0
4
+ deps: # 跨模块别名 `#/_platform/…` 的依赖门禁(oj build S008)+ 表归属守卫:
5
+ # 读 market_release/market_history(_platform 拥有的 tenant:false 共享目录表)+
6
+ # import _lifecycle 安装引擎/_shared 服务层原语
7
+ _platform: ^0.1.0
8
+ tables: [] # 无自有表:目录表归 _platform(平台共享设施,CLI 回填与站点双写同一真相)
@@ -0,0 +1,21 @@
1
+ # oj-market 第一方方案包契约(Phase B T1,20261106-2122 拍板版本基线 0.2.0)。
2
+ # 市场 UI 以**纯 web 方案包**形态随站分发:包内只有 manifest + web/ 产物——
3
+ # api 编排面(install/catalog/yank/trust 推送,src/oj-market/api)留在平台内置不随包,
4
+ # 市场未装/已卸载时安装编排端点仍在(方案 §2「安装入口(市场外)」);
5
+ # 无 DB 迁移(catalog/release 等表属平台,不随包)。
6
+ # R29 红线随包:第一方专用,第三方方案不得仿照 /api/oj-market/* 调用面(包 README 同标注)。
7
+ name: oj-market
8
+ version: 0.2.0
9
+ title: 方案市场
10
+ summary: 官方 方案市场 UI——方案清单/发布管理/发布历史(第一方专用,R29)
11
+ description: 第一方专用方案包(R29 红线):第三方方案不得仿照 /api/oj-market/* 调用面。api 编排面(install/catalog/yank/trust)随站点脚手架内置,本包仅承载市场 UI(纯 web,无 DB 迁移)。出厂未装:superadmin 经 system 域「方案安装」首装;ojm init --yes 落位的随站 bundle 由首个市场编排请求自动收敛(上拉不下拉)。
12
+ changelog:
13
+ - 0.2.0 Phase B 首包:市场 UI 模块化随站分发(出厂未装,superadmin 经 system 域首装)
14
+ peerRuntime: '>=0.0.0'
15
+ web:
16
+ entry: modules/oj-market/0.2.0/entry.js
17
+ routes:
18
+ - path: /oj-market
19
+ category: main
20
+ order: 200
21
+ title_key: oj-market:menu.main
@@ -0,0 +1,167 @@
1
+ /* eslint-disable */
2
+ // 生成物:ojm api 从契约生成,勿手改(改动请改契约文件后重跑 ojm api)
3
+ // AC-D15:仅供 DEV 校验动态 import,生产构建不进产物
4
+ import { z } from "@oj-module/runtime";
5
+
6
+ export const schemas = {
7
+ "ojMarketInstallPost": {
8
+ body: z.object({
9
+ "source": z.enum(["cdn", "file"]),
10
+ "name": z.string(),
11
+ "version": z.string().nullable().optional(),
12
+ "force": z.boolean().optional()
13
+ }),
14
+ data: z.object({
15
+ "task_id": z.string().optional()
16
+ }).catchall(z.unknown()),
17
+ },
18
+ "ojMarketInstallCancelPost": {
19
+ body: z.object({
20
+ "name": z.string()
21
+ }),
22
+ data: z.object({
23
+ "ok": z.boolean().optional(),
24
+ "name": z.string().optional(),
25
+ "requested": z.boolean().optional()
26
+ }).catchall(z.unknown()),
27
+ },
28
+ "ojMarketTaskGet": {
29
+ query: z.object({
30
+ "id": z.string()
31
+ }),
32
+ data: z.object({
33
+
34
+ }).catchall(z.unknown()),
35
+ },
36
+ "ojMarketEnablePost": {
37
+ body: z.object({
38
+ "name": z.string(),
39
+ "enabled": z.boolean()
40
+ }),
41
+ data: z.object({
42
+
43
+ }).catchall(z.unknown()),
44
+ },
45
+ "ojMarketMetaGet": {
46
+ data: z.object({
47
+ "apiVersion": z.string().optional()
48
+ }).catchall(z.unknown()),
49
+ },
50
+ "ojMarketTrustGet": {
51
+ data: z.object({
52
+
53
+ }).catchall(z.unknown()),
54
+ },
55
+ "ojMarketTrustPost": {
56
+ body: z.object({
57
+ "task_id": z.string()
58
+ }),
59
+ data: z.object({
60
+
61
+ }).catchall(z.unknown()),
62
+ },
63
+ "ojMarketTrustKeyPost": {
64
+ body: z.object({
65
+ "key_id": z.string(),
66
+ "public_key": z.string()
67
+ }),
68
+ data: z.object({
69
+
70
+ }).catchall(z.unknown()),
71
+ },
72
+ "ojMarketTrustKeyDelete": {
73
+ query: z.object({
74
+ "key_id": z.string()
75
+ }),
76
+ data: z.object({
77
+
78
+ }).catchall(z.unknown()),
79
+ },
80
+ "ojMarketIndexGet": {
81
+ query: z.object({
82
+ "page": z.number().min(1).optional(),
83
+ "size": z.number().min(1).max(200).optional()
84
+ }),
85
+ data: z.object({
86
+ "list": z.array(z.object({
87
+
88
+ }).catchall(z.unknown())),
89
+ "total": z.number(),
90
+ "page": z.number(),
91
+ "size": z.number()
92
+ }),
93
+ },
94
+ "ojMarketCatalogGet": {
95
+ query: z.object({
96
+ "page": z.number().min(1).optional(),
97
+ "size": z.number().min(1).max(200).optional()
98
+ }),
99
+ data: z.object({
100
+
101
+ }).catchall(z.unknown()),
102
+ },
103
+ "ojMarketCatalogDetailGet": {
104
+ query: z.object({
105
+ "name": z.string(),
106
+ "version": z.string().optional()
107
+ }),
108
+ data: z.object({
109
+
110
+ }).catchall(z.unknown()),
111
+ },
112
+ "ojMarketYankPost": {
113
+ body: z.object({
114
+ "product": z.string(),
115
+ "version": z.string(),
116
+ "reason": z.string().optional(),
117
+ "by": z.string().optional(),
118
+ "restore": z.boolean().optional()
119
+ }),
120
+ data: z.object({
121
+
122
+ }).catchall(z.unknown()),
123
+ },
124
+ "ojMarketUninstallImpactGet": {
125
+ query: z.object({
126
+ "name": z.string()
127
+ }),
128
+ data: z.object({
129
+
130
+ }).catchall(z.unknown()),
131
+ },
132
+ "ojMarketUninstallPost": {
133
+ body: z.object({
134
+ "name": z.string()
135
+ }),
136
+ data: z.object({
137
+
138
+ }).catchall(z.unknown()),
139
+ },
140
+ "ojMarketHistoryGet": {
141
+ data: z.array(z.object({
142
+
143
+ }).catchall(z.unknown())),
144
+ },
145
+ "ojMarketReleasesGet": {
146
+ query: z.object({
147
+ "page": z.number().min(1).optional(),
148
+ "limit": z.number().min(1).max(200).optional()
149
+ }),
150
+ data: z.object({
151
+ "list": z.array(z.object({
152
+
153
+ }).catchall(z.unknown())),
154
+ "total": z.number(),
155
+ "page": z.number(),
156
+ "size": z.number()
157
+ }),
158
+ },
159
+ "ojMarketCdnUploadPost": {
160
+ query: z.object({
161
+ "name": z.string()
162
+ }),
163
+ data: z.object({
164
+
165
+ }).catchall(z.unknown()),
166
+ },
167
+ };