@oj-module/cli 0.2.66 → 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.
- package/bin/.oj-version +1 -0
- package/bin/oj +4 -0
- package/devkit/README.md +15 -7
- package/devkit/SKILL.md +30 -1
- package/devkit/framework-dev.md +10 -4
- package/devkit/manual.md +38 -12
- package/devkit/publish-walkthrough.md +110 -37
- package/devkit/wecom-internal-app.md +7 -2
- package/devkit/wecom-multi-corp-links.md +163 -0
- package/package.json +2 -2
- package/shell-dist/assets/{index.i0kqbxp050.js → index.6alqyx6ae0.js} +21 -1
- package/shell-dist/assets/{runtime.1xfsxcajj0.js → runtime.2dgstpjsu0.js} +3449 -3449
- package/shell-dist/index.html +4 -4
- package/shell-dist/versions.json +3 -3
- package/src/__tests__/publish-keygen.test.ts +157 -0
- package/src/__tests__/templates-migration-schema-parity.test.ts +540 -0
- package/src/__tests__/templates-schema-columns.test.ts +15 -2
- package/src/__tests__/templates-tenant-scope.test.ts +6 -2
- package/src/bundle.ts +8 -3
- package/src/cli.ts +17 -5
- package/src/dev.ts +15 -10
- package/src/install.ts +11 -4
- package/src/keygen.ts +54 -0
- package/src/lifecycle/__tests__/bundle.test.ts +21 -3
- package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/manifest.yaml +1 -1
- package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/migrations/0001__init.sql +2 -2
- package/src/lifecycle/__tests__/fixtures/fixture-valid/api/src/demo-solution/schema.yaml +5 -2
- package/src/lifecycle/__tests__/fixtures/fixture-valid/manifest.yaml +1 -1
- package/src/lifecycle/__tests__/manifest-contract.test.ts +75 -3
- package/src/lifecycle/__tests__/smoke.test.ts +2 -2
- package/src/manifest-contract.ts +91 -8
- package/src/market/bootstrap.ts +26 -2
- package/src/market/command.ts +1 -0
- package/src/market/publish.ts +5 -1
- package/src/market/walkthrough.ts +24 -8
- package/src/site.ts +2 -0
- package/src/usage.ts +8 -4
- package/src/vendor.ts +5 -1
- package/templates/config.yaml +4 -2
- package/templates/init.include.yaml +8 -3
- package/templates/openapi.json +460 -1
- package/templates/src/app/api/_platform/_lifecycle/compensate.ts +136 -0
- package/templates/src/app/api/_platform/_lifecycle/installer.ts +71 -22
- package/templates/src/app/api/_platform/_lifecycle/lock.ts +23 -0
- package/templates/src/app/api/_platform/_lifecycle/steps/deploy-web.ts +28 -0
- package/templates/src/app/api/_platform/_lifecycle/steps/preflight.ts +46 -0
- package/templates/src/app/api/_platform/_lifecycle/steps/reload.ts +100 -36
- package/templates/src/app/api/_platform/_lifecycle/steps/smoke.ts +7 -15
- package/templates/src/app/api/_platform/_lifecycle/text-edit.ts +71 -19
- package/templates/src/app/api/_platform/_lifecycle/types.ts +26 -2
- package/templates/src/app/api/_platform/_lifecycle/uninstall.ts +93 -14
- package/templates/src/app/api/_platform/_shared/service/domain/settings.ts +8 -4
- package/templates/src/app/api/_platform/manifest.yaml +1 -0
- package/templates/src/app/api/_platform/market/api.ts +235 -10
- package/templates/src/app/api/_platform/migrations/0001__baseline.mysql.sql +178 -0
- package/templates/src/app/api/_platform/migrations/0001__baseline.pg.sql +173 -0
- package/templates/src/app/api/_platform/migrations/0001__baseline.sql +188 -0
- package/templates/src/app/api/_platform/migrations/0002__market_release.mysql.sql +17 -0
- package/templates/src/app/api/_platform/migrations/0002__market_release.pg.sql +16 -0
- package/templates/src/app/api/_platform/migrations/0002__market_release.sql +33 -0
- package/templates/src/app/api/_platform/schema.yaml +18 -0
- package/templates/src/app/api/_platform/seed.mysql.sql +10 -0
- package/templates/src/app/api/_platform/seed.pg.sql +10 -0
- package/templates/src/app/api/auth/_shared/wecom/client.ts +24 -4
- package/templates/src/app/api/home/migrations/0001__create_home_tables.mysql.sql +14 -0
- package/templates/src/app/api/home/migrations/0001__create_home_tables.pg.sql +14 -0
- package/templates/src/app/api/home/migrations/0002__tenant.pg.sql +4 -0
- package/templates/src/app/api/home/seed.mysql.sql +71 -0
- package/templates/src/app/api/home/seed.pg.sql +71 -0
- package/templates/src/app/api/notification/_shared/notify.ts +6 -6
- package/templates/src/app/api/notification/migrations/0001__create_notifications.mysql.sql +11 -0
- package/templates/src/app/api/notification/migrations/0001__create_notifications.pg.sql +11 -0
- package/templates/src/app/api/notification/migrations/0002__notifications_user.mysql.sql +8 -0
- package/templates/src/app/api/notification/migrations/0003__notifications_broadcast.mysql.sql +21 -0
- package/templates/src/app/api/notification/migrations/0003__notifications_broadcast.pg.sql +22 -0
- package/templates/src/app/api/notification/migrations/0004__notifications_broadcast_hidden.mysql.sql +10 -0
- package/templates/src/app/api/notification/migrations/0004__notifications_broadcast_hidden.pg.sql +11 -0
- package/templates/src/app/api/notification/migrations/0005__tenant.pg.sql +6 -0
- package/templates/src/app/api/notification/seed.mysql.sql +8 -0
- package/templates/src/app/api/notification/seed.pg.sql +11 -0
- package/templates/src/app/api/publish/_shared/identity.ts +69 -0
- package/templates/src/app/api/publish/api.ts +349 -0
- package/templates/src/app/api/publish/manifest.yaml +8 -0
- package/templates/src/app/api/publish/migrations/0001__publish_credential.mysql.sql +23 -0
- package/templates/src/app/api/publish/migrations/0001__publish_credential.pg.sql +23 -0
- package/templates/src/app/api/publish/migrations/0001__publish_credential.sql +39 -0
- package/templates/src/app/api/publish/schema.yaml +33 -0
- package/templates/src/app/api/system/login-methods/api.ts +2 -1
- package/templates/src/app/web/_platform/__client/api.schemas.ts +15 -0
- package/templates/src/app/web/_platform/__client/index.ts +25 -1
- package/templates/src/app/web/entry.ts +9 -5
- package/templates/src/app/web/home/routes.ts +5 -0
- package/templates/src/app/web/login/locales/en-US.json +7 -1
- package/templates/src/app/web/login/locales/zh-CN.json +7 -1
- package/templates/src/app/web/login/pages/login.tsx +79 -86
- package/templates/src/app/web/login/routes.ts +54 -1
- package/templates/src/app/web/personal-center/locales/en-US.json +1 -0
- package/templates/src/app/web/personal-center/locales/zh-CN.json +1 -0
- package/templates/src/app/web/personal-center/pages/my-profile/index.tsx +5 -0
- package/templates/src/app/web/system/__client/api.schemas.ts +2 -1
- package/templates/src/app/web/system/locales/en-US.json +1 -8
- package/templates/src/app/web/system/locales/zh-CN.json +1 -8
- package/templates/src/app/web/system/pages/settings/index.tsx +17 -4
- package/templates/src/app/web/system/routes.ts +0 -6
- package/templates/src/oj-market/web/__tests__/CardGrid.test.tsx +33 -0
- package/templates/src/oj-market/web/__tests__/DetailDrawer.test.tsx +69 -0
- package/templates/src/oj-market/web/__tests__/marketClient.fake.ts +7 -1
- package/templates/src/oj-market/web/api/client.ts +3 -0
- package/templates/src/oj-market/web/api/defaultClient.ts +5 -0
- package/templates/src/oj-market/web/components/CardItem.tsx +44 -1
- package/templates/src/oj-market/web/components/DetailDrawer.tsx +17 -1
- package/templates/src/oj-market/web/entry.ts +31 -2
- package/templates/src/oj-market/web/locales/en-US.json +5 -1
- package/templates/src/oj-market/web/locales/zh-CN.json +5 -1
- package/templates/src/oj-market/web/publish/__client/api.schemas.ts +61 -0
- package/templates/src/oj-market/web/publish/__client/index.ts +164 -0
- package/templates/src/oj-market/web/publish/locales/en-US.json +80 -0
- package/templates/src/oj-market/web/publish/locales/zh-CN.json +80 -0
- package/templates/src/oj-market/web/publish/pages/history.tsx +112 -0
- package/templates/src/oj-market/web/publish/pages/index.tsx +196 -0
- package/templates/src/oj-market/web/publish/pages/model.ts +52 -0
- package/templates/src/oj-market/web/publish/pages/token-modal.tsx +82 -0
- package/templates/src/oj-market/web/publish/routes.ts +41 -0
- package/templates/src/oj-market/web/types.ts +30 -0
- package/templates/tests/lifecycle/compensate.spec.ts +207 -0
- package/templates/tests/lifecycle/deploy-smoke.spec.ts +38 -14
- package/templates/tests/lifecycle/installer.spec.ts +28 -9
- package/templates/tests/lifecycle/market-api.spec.ts +164 -12
- package/templates/tests/lifecycle/preflight.spec.ts +68 -0
- package/templates/tests/lifecycle/role-title-translate.spec.ts +2 -32
- package/templates/tests/lifecycle/text-edit.spec.ts +45 -27
- package/templates/tests/lifecycle/uninstall.spec.ts +36 -0
- package/templates/tests/publish-credential.spec.ts +354 -0
- package/templates/tests/publish-identity.spec.ts +44 -0
- package/templates/tests/wecom-client-timeout.spec.ts +33 -3
- package/templates/tests/wecom-landing.spec.ts +63 -1
- package/templates/tsconfig.paths.json +1 -0
- package/templates/web.config.ts +6 -4
- package/vendor/host-versions.json +3 -3
- package/templates/seed.mysql.sql +0 -6
- package/templates/seed.pg.sql +0 -6
- package/templates/src/app/api/_platform/migrations/0001__create_users.mysql.sql +0 -9
- package/templates/src/app/api/_platform/migrations/0001__create_users.pg.sql +0 -9
- package/templates/src/app/api/_platform/migrations/0001__create_users.sql +0 -7
- package/templates/src/app/api/_platform/migrations/0002__add_users_avatar_base64.mysql.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0002__add_users_avatar_base64.sql +0 -2
- package/templates/src/app/api/_platform/migrations/0003__users_source_display_name.mysql.sql +0 -5
- package/templates/src/app/api/_platform/migrations/0003__users_source_display_name.sql +0 -5
- package/templates/src/app/api/_platform/migrations/0004__sys_rbac.mysql.sql +0 -70
- package/templates/src/app/api/_platform/migrations/0004__sys_rbac.pg.sql +0 -67
- package/templates/src/app/api/_platform/migrations/0004__sys_rbac.sql +0 -67
- package/templates/src/app/api/_platform/migrations/0005__users_must_change.mysql.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0005__users_must_change.sql +0 -2
- package/templates/src/app/api/_platform/migrations/0006__users_is_forbidden.mysql.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0006__users_is_forbidden.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0007__users_email.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0008__sys_refresh_token.mysql.sql +0 -10
- package/templates/src/app/api/_platform/migrations/0008__sys_refresh_token.pg.sql +0 -10
- package/templates/src/app/api/_platform/migrations/0008__sys_refresh_token.sql +0 -11
- package/templates/src/app/api/_platform/migrations/0009__sys_throttle.mysql.sql +0 -12
- package/templates/src/app/api/_platform/migrations/0009__sys_throttle.pg.sql +0 -12
- package/templates/src/app/api/_platform/migrations/0009__sys_throttle.sql +0 -13
- package/templates/src/app/api/_platform/migrations/0010__sys_solution_route.mysql.sql +0 -17
- package/templates/src/app/api/_platform/migrations/0010__sys_solution_route.pg.sql +0 -16
- package/templates/src/app/api/_platform/migrations/0010__sys_solution_route.sql +0 -18
- package/templates/src/app/api/_platform/migrations/0011__sys_setting_group_label.mysql.sql +0 -2
- package/templates/src/app/api/_platform/migrations/0011__sys_setting_group_label.sql +0 -4
- package/templates/src/app/api/_platform/migrations/0012__market_history.mysql.sql +0 -13
- package/templates/src/app/api/_platform/migrations/0012__market_history.pg.sql +0 -12
- package/templates/src/app/api/_platform/migrations/0012__market_history.sql +0 -14
- package/templates/src/app/api/_platform/migrations/0013__users_profile.sql +0 -4
- package/templates/src/app/api/_platform/migrations/0014__reg_source_seq.mysql.sql +0 -9
- package/templates/src/app/api/_platform/migrations/0014__reg_source_seq.pg.sql +0 -7
- package/templates/src/app/api/_platform/migrations/0014__reg_source_seq.sql +0 -8
- package/templates/src/app/api/_platform/migrations/0015__tenant.mysql.sql +0 -12
- package/templates/src/app/api/_platform/migrations/0015__tenant.pg.sql +0 -11
- package/templates/src/app/api/_platform/migrations/0015__tenant.sql +0 -44
- package/templates/src/app/api/_platform/migrations/0016__org.mysql.sql +0 -41
- package/templates/src/app/api/_platform/migrations/0016__org.pg.sql +0 -40
- package/templates/src/app/api/_platform/migrations/0016__org.sql +0 -62
- package/templates/src/app/api/_platform/migrations/0017__users_employee_no.mysql.sql +0 -7
- package/templates/src/app/api/_platform/migrations/0017__users_employee_no.pg.sql +0 -4
- package/templates/src/app/api/_platform/migrations/0017__users_employee_no.sql +0 -8
- package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.mysql.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.pg.sql +0 -3
- package/templates/src/app/api/_platform/migrations/0018__sys_role_title_key.sql +0 -8
- package/templates/src/app/web/system/pages/login-methods/index.tsx +0 -30
package/bin/.oj-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
v0.1.123
|
package/bin/oj
ADDED
package/devkit/README.md
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
# oj-module DevKit——模块开发手册 + agent skill
|
|
2
2
|
|
|
3
|
+
> **你在哪读这份文件?** 两种视图,内容同一份:
|
|
4
|
+
> - **业务工程里**(`.claude/skills/ojm-module-dev/`):文件是随 `@oj-module/cli` 分发的**副本**,
|
|
5
|
+
> 直接读 `SKILL.md` / `manual.md` / 两本企微手册 / `publish-walkthrough.md` 即可闭环;
|
|
6
|
+
> 下文出现的 `docs/devkit/`、`packages/cli/` 是**框架仓库路径**,只用来解释文件怎么来的,
|
|
7
|
+
> 你本地没有也不影响使用。
|
|
8
|
+
> - **框架仓库里**(`docs/devkit/`):这里才是源文件,改文档只改这一处。
|
|
9
|
+
|
|
3
10
|
面向用 `ojm` 开发前后端一体工程的开发者与 AI agent。本目录是**源文件**,
|
|
4
11
|
发布时由 cli 的 `prepack`(`packages/cli/scripts/sync-devkit.mjs`)把其中
|
|
5
|
-
**ojm 侧的
|
|
12
|
+
**ojm 侧的 11 个文件**同步进 `packages/cli/devkit/`,随 `@oj-module/cli`
|
|
6
13
|
发布,再由 `ojm init` 分发到工程内。
|
|
7
14
|
|
|
8
15
|
整棵树覆盖前后端两侧,但仍是**两套独立分发的 skill**(同名不同物,别混):
|
|
9
16
|
|
|
10
17
|
| 侧 | 本仓位置 | 用户工程里的落点 | 分发链 |
|
|
11
18
|
|---|---|---|---|
|
|
12
|
-
| ojm 模块开发(前端模块、契约、前后端对接) | 本目录顶层
|
|
19
|
+
| ojm 模块开发(前端模块、契约、前后端对接) | 本目录顶层 11 个文件 | `.claude/skills/ojm-module-dev/` | `@oj-module/cli` 包(全部 11 个)→ `ojm init` 第 4b 步(只分发其中 6 个,见下) |
|
|
13
20
|
| oj API 开发(后端 handler、全局对象、配置、测试) | `oj-api-dev/` 子目录(快照) | `.claude/skills/oj-api-dev/` | **oj 二进制自带**(`bin/devkit/`)→ `ojm init` 第 4 步 / `ojm vendor` 装·升后同步(0.2.57 起共用 `syncOjDevkit`,全量 `.md` **覆盖**——文档始终与已装 oj 版本一致) |
|
|
14
21
|
|
|
15
22
|
> **`oj-api-dev/` 子目录是快照,不是本仓维护的文档**:它与本机 oj 的
|
|
@@ -25,19 +32,20 @@
|
|
|
25
32
|
|---|---|
|
|
26
33
|
| `SKILL.md` | Claude Code 等 agent 的 skill 入口:工作流、红线、新模块 checklist、陷阱速查,按章节号引用手册 |
|
|
27
34
|
| `manual.md` | 模块开发手册(14 章):工程结构(module-root 单树 + 两处产物根;含 §1.7 handler 四红线——oj 运行时实测钉出,后端必读;§1.8 `#` 别名与 TS 投影)、写模块、契约与代码生成(含 §3.10 旧 contract.ts 工程迁移指南)、provider 接管(覆盖框架内置功能)、布局、i18n、构建发布、排查、CLI 子命令参考(ojm 原生命令 vs oj 透传命令)、方案包打包与分发(含 §11.1.1 引用式方案)、签名与信任、§13.1.1 web.route_prefix 自定义根路径、**§14 组织架构与通讯录**(内置部门/成员底座 + 共享读面 7 端点 + `scope` 两级门禁 + 归属权四条规则 + 源适配器接点,选人与按部门算人都读它) |
|
|
28
|
-
| `publish-walkthrough.md` | 方案包发布全流程(**新手照做版**):构建打包 → keygen 签名 → `market publish` 落 CDN → build-index → verify/trial 验证 → install + TOFU,含多人各自独立发包纪律与失败速查(手册 §11/§12 的过程版) |
|
|
35
|
+
| `publish-walkthrough.md` | 方案包发布全流程(**新手照做版**):构建打包 → **拿发布身份(「发布管理」申请凭证 → `ojm keygen --credential`;无站点才用离线 `--id`)** → 签名 → `market publish` 落 CDN → build-index → verify/trial 验证 → install + TOFU,含多人各自独立发包纪律与失败速查(手册 §11/§12 的过程版) |
|
|
29
36
|
| `framework-dev.md` | 框架贡献者路线(**仅框架仓库**):改 `packages/runtime`、`packages/cli` 时的入口与红线 |
|
|
30
37
|
| `module-dev-and-cdn-playground.md` | 教学文档:开发态 ≠ 分发态、「怎么安装自己的模块 / 会不会重复」、本地 CDN 市场链路五步跑通(setup → publish → build-index → verify → 安装) |
|
|
31
38
|
| `sql-builder-missing-capabilities-pr.md` | 构造器能力缺口 PR 参考(**仅框架仓库**):7 项诉求的闭环记录 |
|
|
32
39
|
| `menu-rules.md` | 方案菜单归组规则专题:单根模块约定、自定义根路径 `web.route_prefix`、`category` 三值语义、admin 重写与视觉归组(手册 §13 的展开版) |
|
|
33
40
|
| `wecom-daikaifa.md` | 企业微信代开发集成:**凭据清单**(8 项 `vars`,按 Tier A/B/C 分层的来源与必填时序 + 可信域名/可信 IP 备案)、首次开通顺序(含 `suite_ticket` 依赖与引导期死锁的破法)、**用户 / 开发 / 部署三角色手册**、错误文案速查、与自建应用共存的语义变化 |
|
|
34
41
|
| `wecom-internal-app.md` | 企业微信**企业内部应用(自建应用)**集成:5 项 `wecom_corp_*` 凭据 + 2 项复用、回调只填「通讯录同步」一处、「验证并接入」四步自检、可见范围与可信 IP/域名、单企业私有化的边界(自建应用收不到成员变更事件)、共存配置、故障速查 |
|
|
42
|
+
| `wecom-multi-corp-links.md` | **平台运维 / 服务商运营**:一套部署服务 N 家企业的**专属登录链接运维清单**(方案一,运维零代码改动)——前置检查、逐企业接入 6 步、链接格式与参数、员工四入口、逐企业验收打勾、故障速查、日常运维;含企微工作台内早跳透传 `corp`/`agent` 的修复说明 |
|
|
35
43
|
|
|
36
|
-
> 上表
|
|
44
|
+
> 上表 10 项 + 本文件 `README.md` = 顶层 11 个文件,全部随 cli 包发货。
|
|
37
45
|
> `ojm init` 第 4b 步**只分发其中 6 个**给业务工程:`SKILL.md` / `manual.md` / `README.md` /
|
|
38
46
|
> `wecom-daikaifa.md` / `wecom-internal-app.md` / `publish-walkthrough.md`。
|
|
39
|
-
> 其余
|
|
40
|
-
> `sql-builder-missing-capabilities-pr.md`)是框架贡献者或专题材料,只在 cli 包内可取。
|
|
47
|
+
> 其余 5 个(`framework-dev.md`、`menu-rules.md`、`module-dev-and-cdn-playground.md`、
|
|
48
|
+
> `sql-builder-missing-capabilities-pr.md`、`wecom-multi-corp-links.md`)是框架贡献者或专题材料,只在 cli 包内可取。
|
|
41
49
|
|
|
42
50
|
后端 handler 本身的写法(`json` 信封、`db` 查询、`manifest.yaml`、`oj test`)、
|
|
43
51
|
站点挂载(`mounts`)、密钥信封(`secrets`)、vars、cors、schema 校验、broker/tasks/oidc/ws 等
|
|
@@ -121,6 +129,6 @@ flowchart LR
|
|
|
121
129
|
框架贡献者要把本仓快照同步到新版:从 `~/.local/share/oj/devkit/` 整目录覆盖
|
|
122
130
|
`docs/devkit/oj-api-dev/`(oj v0.1.116 起 12 个文件,保持逐字节一致以便对账)。
|
|
123
131
|
|
|
124
|
-
> 本目录顶层
|
|
132
|
+
> 本目录顶层 11 个文件是**自包含**的:业务工程拿到即可读懂,不需要工程仓库里的
|
|
125
133
|
> 其它文档。`oj-api-dev/` 子目录是 oj 侧快照,同样自包含(其自身 README 即索引),
|
|
126
134
|
> 但不随 cli 包发货、仅本仓参考。维护者视角的内容来源整理在 `framework-dev.md`(仅本仓)。
|
package/devkit/SKILL.md
CHANGED
|
@@ -19,7 +19,8 @@ description: 用 ojm 开发前后端一体工程时使用——新增/修改前
|
|
|
19
19
|
接管通知/上传/菜单 → §4;换布局或加插槽 → §5;排查 → §8;
|
|
20
20
|
**组织架构 / 通讯录(选人、按部门算收件人、接 LDAP 等新来源)→ §14**;
|
|
21
21
|
**接通企业微信(代开发 / 企业内部应用 / 两套共存)→ `wecom-daikaifa.md` + `wecom-internal-app.md`**;
|
|
22
|
-
**把方案包发出去(打包/签名/CDN/安装一条链,新手照敲)→ `publish-walkthrough.md
|
|
22
|
+
**把方案包发出去(打包/签名/CDN/安装一条链,新手照敲)→ `publish-walkthrough.md`**
|
|
23
|
+
(发布身份走站点凭证见其 §2.1)。
|
|
23
24
|
2. **建目录**:一个目录 = 一个模块——前端一侧 `src/<模块名>/web/`(`entry.ts` 必备,
|
|
24
25
|
`pages/`、`locales/` 按需),后端一侧 `src/<模块名>/api/`(目录镜像路由的 `api.ts`,
|
|
25
26
|
动词挂 `.schema`)。两侧可只存在一侧。
|
|
@@ -140,6 +141,30 @@ description: 用 ojm 开发前后端一体工程时使用——新增/修改前
|
|
|
140
141
|
同步/事件增量与回调解密是**两条独立纪律**:回调必须回 `success`,而事件处理失败**也照样回 `success`**
|
|
141
142
|
(重推补不回已判死的失败,兜底是下一轮全量同步)。
|
|
142
143
|
|
|
144
|
+
## 发布身份(keygen 从哪来)
|
|
145
|
+
|
|
146
|
+
签名前的第一件事是拿到一对 Ed25519 密钥。**两条路,二选一**:
|
|
147
|
+
|
|
148
|
+
| 路径 | 命令 | 何时用 |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| 站点凭证(推荐) | `ojm keygen --credential <token>` | 站点有「方案市场 → 发布管理」页;key_id 由站点按产品名+账号下发(信任仍需首装核对) |
|
|
151
|
+
| 离线自建(旁路) | `ojm keygen --id <publisher>.<key-name>` | 只在站点没有「发布管理」页(纯自建/离线 CDN)或 CI 临时密钥时用 |
|
|
152
|
+
|
|
153
|
+
站点有「方案市场 → 发布管理」页时**不要自建 key_id**:站点侧没有这条身份记录,产品归属与申请人无从追溯,
|
|
154
|
+
能不能装只能靠超管为陌生指纹手工放行。
|
|
155
|
+
|
|
156
|
+
- 凭证是**一次性兑换令牌**:站点「方案市场 → 发布管理 → 申请发布凭证」填产品名 → 明文只显示一次 →
|
|
157
|
+
本机 `ojm keygen --credential <token>`(或 `OJM_PUBLISH_CREDENTIAL=<token> ojm keygen`,
|
|
158
|
+
免得令牌进 shell 历史)。兑换成功即作废,失败(已用/过期/撤销/网络不通)**不落任何密钥文件**。
|
|
159
|
+
- 站点地址优先级:`--site-url` > `OJM_SITE_URL` > `http://127.0.0.1:9778`;兑换端点匿名可达,
|
|
160
|
+
**不需要 `--user/--pass`**。`--credential` 与 `--id` 互斥(同时给 exit 2)。
|
|
161
|
+
- 下发的 key_id 形如 `<产品名>.<编码后用户名>`(例:`demo-solution.wecom-everpan`),CLI 不做推导;
|
|
162
|
+
它同时是 `~/.ojm/keys/<key_id>/` 的目录名——**不要拿它拼路径或改名**,否则签名侧找不到私钥。
|
|
163
|
+
凭证只解决「身份命名与归属」,**不预置信任**:公钥在本地生成,站点兑换时拿不到,首装照样
|
|
164
|
+
`needs_trust`,仍要核对指纹。
|
|
165
|
+
- 两条路的私钥纪律完全一致:只活在 `~/.ojm/keys/<id>/`、不入仓不入包、一次发布固定一把。
|
|
166
|
+
签名/发布/安装全流程见 `publish-walkthrough.md`(§2 凭证、§2.1 离线旁路、§3 签名起)。
|
|
167
|
+
|
|
143
168
|
## 新模块 checklist
|
|
144
169
|
|
|
145
170
|
- [ ] `src/<模块>/web/entry.ts` 默认导出 `defineModule({ name, version, routes, i18n?, lifecycle? })`
|
|
@@ -229,3 +254,7 @@ description: 用 ojm 开发前后端一体工程时使用——新增/修改前
|
|
|
229
254
|
五项 `wecom_corp_*` 从哪取、回调为什么只填「通讯录同步」一处、「验证并接入」的四步自检、
|
|
230
255
|
可见范围 / 可信 IP / 可信域名的处置、单企业私有化的边界(自建应用收不到成员变更事件)、
|
|
231
256
|
以及和代开发共存时的配置与页面差异。**接自建应用时先读它**。
|
|
257
|
+
|
|
258
|
+
另有 `publish-walkthrough.md`(同目录):从「代码写完」到「装进站点」的一条命令链——
|
|
259
|
+
构建/打包 → 拿发布身份(凭证或离线)→ 签名 → 落 CDN → 重建索引 → verify/trial → install/TOFU,
|
|
260
|
+
每步带期望输出与通过判据。**第一次发包照着它敲**。
|
package/devkit/framework-dev.md
CHANGED
|
@@ -15,9 +15,14 @@
|
|
|
15
15
|
| 框架开发完整指南(契约层/运行时/宿主/工具链四章) | `docs/prd/framework-development-guide.md` |
|
|
16
16
|
| 端到端可重复验证流程 | `docs/prd/framework-verification-playbook.md` |
|
|
17
17
|
| 某特性的设计与评审记录 | `docs/prd/<日期>-<特性>-design.md` |
|
|
18
|
-
| 各阶段实战记录(骨架→注入→认证→CRUD
|
|
18
|
+
| 各阶段实战记录(骨架→注入→认证→CRUD→发布) | `docs/prd/framework-verification-playbook.md` 附录 A 基线表(playground 已下线) |
|
|
19
19
|
| 后端 handler 写法 | `docs/devkit/oj-api-dev/api-manual.md`(`oj-api-dev` skill 的本仓快照;用户工程侧在 `.claude/skills/oj-api-dev/`,随 oj 二进制分发) |
|
|
20
20
|
|
|
21
|
+
> **路径口径**:上表除 `CLAUDE.md` 与 `docs/devkit/oj-api-dev/` 外,都是**框架仓库内**的路径,
|
|
22
|
+
> 不随 `@oj-module/cli` 发货。本文件只对「能进框架仓库的人」有用——如果你是在 cli 包或
|
|
23
|
+
> 业务工程里读到这里,说明没有仓库上下文,那些链接按设计不可达,**不是坏链**:业务开发
|
|
24
|
+
> 请回 `SKILL.md` + `manual.md`。
|
|
25
|
+
|
|
21
26
|
> ⚠️ 那两份 guide 有**已知陈旧处**,以源码为准:
|
|
22
27
|
> dev 端口实际是 **3333**(guide 写的 5173 已过时;注意与 `ojm dev` 的前端 devServer
|
|
23
28
|
> **5174** 不是一回事——前者是本仓 vite,后者是业务工程 devServer);包版本以
|
|
@@ -192,9 +197,10 @@ runtime → cli → registry 复核;`--dry-run` 只演练)。下面的手动
|
|
|
192
197
|
|
|
193
198
|
## 附:本 devkit 内容来源(维护者看)
|
|
194
199
|
|
|
195
|
-
`docs/devkit/` 是**自包含**的交付文档(顶层 6
|
|
196
|
-
`sql-builder-missing-capabilities-pr.md` 这份构造器能力缺口 PR
|
|
197
|
-
|
|
200
|
+
`docs/devkit/` 是**自包含**的交付文档(顶层 11 个文件,全部随 cli 包发货,其中 6 个随
|
|
201
|
+
`ojm init` 进业务工程;含 `sql-builder-missing-capabilities-pr.md` 这份构造器能力缺口 PR 参考),
|
|
202
|
+
业务工程拿到即可读懂,不依赖工程仓库里的其它文档。
|
|
203
|
+
另有 `oj-api-dev/` 子目录是 oj 侧 devkit 的快照(oj v0.1.116 起 12 文件,仅本仓参考、不发货),
|
|
198
204
|
结构说明见 `README.md`。
|
|
199
205
|
下面的来源仅用于维护时追溯与同步,不参与交付:
|
|
200
206
|
|
package/devkit/manual.md
CHANGED
|
@@ -35,7 +35,6 @@
|
|
|
35
35
|
├── env.d.ts # ImportMeta.env 类型补丁
|
|
36
36
|
├── types/oj-modules.d.ts # `declare module "#*"` 别名兜底(非模块文件的 ambient 声明)
|
|
37
37
|
├── .ojm-api-exempt.json
|
|
38
|
-
├── seed.mysql.sql / seed.pg.sql
|
|
39
38
|
├── vitest.config.ts # 后端 BDD(include tests/**/*.spec.ts)
|
|
40
39
|
├── tests/ # 后端 BDD 测试(node:sqlite 内存库 + 真实迁移建表)
|
|
41
40
|
├── src/
|
|
@@ -60,7 +59,9 @@
|
|
|
60
59
|
│ │ ├── auth/ # 仅 __client(无页面)
|
|
61
60
|
│ │ ├── _platform/ # 仅 __client(oj-market 引用)
|
|
62
61
|
│ │ └── web/ # 仅 __client
|
|
63
|
-
│ ├── oj-market/web/ #
|
|
62
|
+
│ ├── oj-market/web/ # 解决方案市场(独立模块,删目录即下线;单根 /oj-market 挂三个子页)
|
|
63
|
+
│ │ ├── entry.ts # routes 聚合:/oj-market 根(方案清单 index)+ 发布管理/发布历史子路由
|
|
64
|
+
│ │ └── publish/ # 发布管理 + 发布历史(/oj-market/publish、/oj-market/history:routes + pages + locales + __client)
|
|
64
65
|
├── dist/api/ # 后端产物(release 根:<name>-<ver>/ + manifests.yaml + versions.json)—构建后才有
|
|
65
66
|
├── dist/web/ # 站点产物(shell + modules/ + modules.json)—构建后才有
|
|
66
67
|
├── bin/ # oj 二进制 + plugins/ + devkit/(不入库)
|
|
@@ -198,11 +199,25 @@ src/<模块>/api/<路径...>/api.ts → /api/<模块>/<路径...>
|
|
|
198
199
|
| 文件 | 何时生效 |
|
|
199
200
|
|---|---|
|
|
200
201
|
| `migrations/*.sql` | 启动(dev `migrate_on_start: auto`);release 默认 `verify`,账本落后则**拒绝启动** |
|
|
201
|
-
| `seed.sql` |
|
|
202
|
+
| `seed.sql` / `seed.<方言>.sql` | **每次启动都重放**——方言件(`.sqlite` / `.mysql` / `.pg`)存在即胜出、`seed.sql` 兜底、绝不双跑(oj ≥ 0.1.121);必须幂等 |
|
|
202
203
|
| `schema.yaml` | 声明;加可空列 / 加表 reconcile 能收敛,但**改类型与改名必须走 migrations** |
|
|
203
204
|
|
|
204
205
|
> `seed.sql` 按 `;` 朴素切分,**注释里不许出现分号字面量**,否则语法错。
|
|
205
206
|
|
|
207
|
+
> **方言 seed 放好即生效(oj ≥ 0.1.121,已是 `ojm` 最低版本门禁)**:方言件按模块存放
|
|
208
|
+
> (`src/app/api/<模块>/seed.mysql.sql` / `seed.pg.sql`,与基础件同表同条数),启动时按当前库
|
|
209
|
+
> 方言自动选——方言件胜出、`seed.sql` 兜底、**绝不双跑**;成功日志打 `file=` / `dialect=`,
|
|
210
|
+
> 模块只有别的方言件或文件名拼错(如 `seed.postgres.sql`)会 warn,`oj check` 的 seed 检查会报错。
|
|
211
|
+
> 更早的 oj 只认字面 `seed.sql`(会把 sqlite 写法原样下发 mysql/pg → 1064),已低于门禁,
|
|
212
|
+
> `ojm vendor` / `ojm info` 会直接拒绝。工程根**不再**有方言 seed 副本(历史上那份只含部分
|
|
213
|
+
> 设置、且 mysql 侧裸写 `key`,已删除)。
|
|
214
|
+
|
|
215
|
+
> **框架自带 `_platform` 模块的迁移已压平为单条基线**(`0001__baseline.sql` 及
|
|
216
|
+
> `.mysql.sql` / `.pg.sql` 两个方言版):新工程按基线一次建表,不再有 0002…0018 的增量链。
|
|
217
|
+
> 老开发库的迁移账本里记着旧序号,**基线不会被重放**——若库结构落后(例如缺新列),
|
|
218
|
+
> 直接删 `api/db.sqlite` 重启即可(dev 自动重跑迁移 + seed),本项目**不提供兼容 shim**。
|
|
219
|
+
> 基线必须三方言同步:只写 sqlite 版会让同一模块在 mysql/pg 部署上建表失败。
|
|
220
|
+
|
|
206
221
|
### 1.7 handler 四红线(oj 运行时实测钉出,2026-10-16 RBAC 阶段)
|
|
207
222
|
|
|
208
223
|
以下四条都是真机冒烟(`ojm init` + `ojm dev`/`build`)实测钉出的 oj 运行时行为,
|
|
@@ -1919,9 +1934,9 @@ module-root 下数据源是「扫描 + `web.config.ts` 登记」并集(未登
|
|
|
1919
1934
|
一个脚手架可放多份用户方案(`ojm new` 逐个生成,各自一份 `src/<name>/solution.yaml`),
|
|
1920
1935
|
`ojm build --bundle` 无参时全部打包、逐个校验独立出包;框架自带方案自动排除(名单见
|
|
1921
1936
|
`src/.ojm-builtin.json`,`ojm info` 默认折叠自带模块)。
|
|
1922
|
-
|
|
1923
|
-
[`publish-walkthrough.md`](./publish-walkthrough.md)
|
|
1924
|
-
|
|
1937
|
+
**从开发到发布的完整过程视角(新手照做版,含签名/私钥纪律与多人各自发包纪律)见本目录
|
|
1938
|
+
[`publish-walkthrough.md`](./publish-walkthrough.md)**——那份文件同样随包分发、自包含,
|
|
1939
|
+
发布链路只读它 + 本章即可闭环。
|
|
1925
1940
|
|
|
1926
1941
|
### 11.1 打包:`ojm build --bundle`
|
|
1927
1942
|
|
|
@@ -2075,16 +2090,26 @@ flowchart LR
|
|
|
2075
2090
|
### 12.1 发布流程(作者侧,三步)
|
|
2076
2091
|
|
|
2077
2092
|
```bash
|
|
2078
|
-
# ①
|
|
2079
|
-
|
|
2093
|
+
# ① 一次性:拿发布身份。站点有「方案市场 → 发布管理」页时,用页面签发的一次性凭证兑换(key_id 由站点算好下发,
|
|
2094
|
+
# CLI 不推导;兑换即作废;失败不落任何密钥文件;与 --id 互斥,同时给 exit 2):
|
|
2095
|
+
ojm keygen --credential <token> [--site-url <站点>]
|
|
2096
|
+
OJM_PUBLISH_CREDENTIAL=<token> ojm keygen
|
|
2080
2097
|
|
|
2081
|
-
#
|
|
2082
|
-
|
|
2098
|
+
# ①' 旁路:只在没有「发布管理」页(纯自建/离线 CDN)或 CI 临时密钥时自建 key_id——
|
|
2099
|
+
# 站点侧没有这条身份记录,产品归属与申请人无从追溯,只能靠超管为陌生指纹手工放行:
|
|
2100
|
+
ojm keygen --id <publisher>.<key-name> # 私钥 ~/.ojm/keys/<id>/ 永不出目录;--out 指进工程树须 --yes
|
|
2101
|
+
|
|
2102
|
+
# ② 每次发布:签名(--id 填上一步拿到的 key_id;产物 = 同目录 <tgz>.sig;--check 离线自检)
|
|
2103
|
+
ojm sign dist/<name>-<version>.tgz --id <key_id> --check
|
|
2083
2104
|
|
|
2084
2105
|
# ③ 安装/分发:.sig 自动探测随包上传
|
|
2085
2106
|
ojm install dist/<name>-<version>.tgz --site-url <站点> --user superadmin --pass …
|
|
2086
2107
|
```
|
|
2087
2108
|
|
|
2109
|
+
凭证通道的站点地址:`--site-url` > `OJM_SITE_URL` > `http://127.0.0.1:9778`;兑换端点匿名可达,
|
|
2110
|
+
不需要 `--user/--pass`。key_id 形如 `<产品名>.<编码后用户名>`(例:`demo-solution.wecom-everpan`),
|
|
2111
|
+
由站点按申请时的产品名与账号算好。
|
|
2112
|
+
|
|
2088
2113
|
宿主验签六码(失败文案双语,`--locale` 切换):`sig_missing`(缺签)/ `sig_alg`(算法
|
|
2089
2114
|
不受接受)/ `sig_key`(key_id 命名空间违约)/ `tampered`(验签不过)/ `unknown_key`
|
|
2090
2115
|
(信任库无此 key → **needs_trust 第五终态**)/ `sig_format`(.sig 结构违约)。
|
|
@@ -2093,8 +2118,9 @@ ojm install dist/<name>-<version>.tgz --site-url <站点> --user superadmin --pa
|
|
|
2093
2118
|
|
|
2094
2119
|
**TOFU = Trust On First Use(首次使用即信任)**:陌生发布方的第一个包,安装器无法判断
|
|
2095
2120
|
该不该信,于是把公钥指纹摆出来让人核对——核对通过才入库,之后同一 key_id 直接放行。
|
|
2096
|
-
|
|
2097
|
-
|
|
2121
|
+
信任的建立因此有两条路:**模板内置的 oj-market 方案**用内置公钥免核对(§12.3),其余来源
|
|
2122
|
+
(含你自己 keygen 的身份、站点凭证下发的身份)一律走本节的人工核对——凭证只定 key_id 归属,
|
|
2123
|
+
站点在兑换时拿不到你的公钥,无法代为建立信任。
|
|
2098
2124
|
|
|
2099
2125
|
- 信任库 = `.solutions/trusted-keys.yaml`(key_id → 公钥 + added_at;首次绑定不可改,
|
|
2100
2126
|
轮换 = 新增 key_id)。
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# 方案包发布全流程——新手照做版
|
|
2
2
|
|
|
3
|
-
适用 ojm ≥ 0.2.22(写作时 0.2.
|
|
4
|
-
|
|
5
|
-
`docs/guides/solution-dev-build-publish.md`(不随 cli 包发货)。
|
|
3
|
+
适用 ojm ≥ 0.2.22(写作时 0.2.66)。本文自包含:从「代码写完了」到「装进站点」一条命令链照着敲,每步附期望输出与通过判据。
|
|
4
|
+
字段规范与错误码全口径见同目录 `manual.md` §11/§12——**本文不引用包外文件**,照着读完即可完成发布。
|
|
6
5
|
|
|
7
6
|
---
|
|
8
7
|
|
|
@@ -11,7 +10,7 @@
|
|
|
11
10
|
```mermaid
|
|
12
11
|
flowchart LR
|
|
13
12
|
A["① 构建 ojm build"] --> B["② 打包 ojm build --bundle"]
|
|
14
|
-
B --> C["③ 身份 ojm keygen
|
|
13
|
+
B --> C["③ 身份 方案市场→发布管理 申请凭证 → ojm keygen --credential"]
|
|
15
14
|
C --> D["④ 签名 ojm sign --check"]
|
|
16
15
|
D --> E["⑤ 发布 ojm market publish"]
|
|
17
16
|
E --> F["⑥ 重建索引 ojm market build-index"]
|
|
@@ -20,18 +19,19 @@ flowchart LR
|
|
|
20
19
|
```
|
|
21
20
|
|
|
22
21
|
信任链硬 fail-closed:未签、签错、key 不被信任的包一律拒装,没有「先装了再说」的开关。
|
|
23
|
-
|
|
22
|
+
**开始前确认三件事**:
|
|
24
23
|
|
|
25
24
|
1. 方案契约在位:`src/<模块>/solution.yaml`(`name` / `version` / `summary`;纯前端可省 `api` 段,纯后端可省 `web` 段);
|
|
26
|
-
2.
|
|
27
|
-
|
|
25
|
+
2. 前端入口在位:`src/<模块>/web/entry.ts`。module-root 布局(0.2.20+)以**扫描**为准,
|
|
26
|
+
`web.config.ts` 的 `modules` 登记只影响加载顺序,漏登记**不会**漏构建(登记了不存在的 entry 才会报错);
|
|
27
|
+
3. 双语 locale 在位:`src/<模块>/web/locales/{zh-CN,en-US}.json`(key 必须嵌套,如 `{"menu":{"main":"…"}}`)。
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
31
|
## 1. 构建与打包
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
|
-
ojm build # ① 站点产物 web/
|
|
34
|
+
ojm build # ① 站点产物 dist/web/ + 后端产物 dist/api/
|
|
35
35
|
ojm build --bundle # ② 打包全部用户方案 → dist/<name>-<version>.tgz
|
|
36
36
|
ojm build --bundle crm # 只打 src/crm/ 这一份(多方案工程用)
|
|
37
37
|
```
|
|
@@ -40,7 +40,7 @@ ojm build --bundle crm # 只打 src/crm/ 这一份(多方案工程用)
|
|
|
40
40
|
|
|
41
41
|
```
|
|
42
42
|
[ojm] 方案包已生成 → dist/<name>-<version>.tgz
|
|
43
|
-
[ojm] sha256
|
|
43
|
+
[ojm] sha256:…(N 字节;确定性打包:同输入两次打包哈希一致)
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
三个要点:
|
|
@@ -53,19 +53,58 @@ ojm build --bundle crm # 只打 src/crm/ 这一份(多方案工程用)
|
|
|
53
53
|
|
|
54
54
|
---
|
|
55
55
|
|
|
56
|
-
## 2.
|
|
56
|
+
## 2. 一次性:拿发布身份(站点凭证)
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
发布身份 = 一把 Ed25519 密钥 + 一个**站点认可**的身份名(`<产品名>.<账号>`)。
|
|
59
|
+
身份名不自拟:站点要靠它在「发布管理」页留档,说清「谁、为哪个产品」发的包。
|
|
60
|
+
所以主流程是「先申请凭证,再在本机兑换」:
|
|
61
|
+
|
|
62
|
+
> 站点保证的是 **key_id 唯一**(同一产品名 + 同一账号 → 同一个身份,同一身份换机重申请是允许的),
|
|
63
|
+
> 不是产品名独占:不同账号申请同名产品会各自得到 `<产品名>.<自己的账号>`。
|
|
64
|
+
> 所以「谁有资格发这个方案」仍靠团队约定与市场侧的包名/版本冲突拦截,不能指望站点替你裁决。
|
|
65
|
+
|
|
66
|
+
> 谁能申请:**任何登录用户**,不要求超管。但新建账号首次登录处于「必须先改密」状态,
|
|
67
|
+
> 此时除改密外的写接口一律 `403 password change required`——先在页面或 `PUT /api/auth/password`
|
|
68
|
+
> 改掉临时口令,再申请凭证。
|
|
69
|
+
|
|
70
|
+
1. 浏览器进 **方案市场 → 发布管理 → 申请发布凭证**,填产品名(如 `demo-solution`);
|
|
71
|
+
2. 复制弹窗里的凭证或整条命令(**只显示这一次**,关闭后只能撤销重申请);
|
|
72
|
+
3. 本机执行其中一条:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
ojm keygen --credential <token>
|
|
76
|
+
OJM_PUBLISH_CREDENTIAL=<token> ojm keygen # 或走环境变量,避免令牌进 shell 历史
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- key_id 由站点按 `<产品名>.<编码后用户名>` 算好下发(例:`demo-solution.wecom-everpan`),
|
|
80
|
+
CLI 不做任何推导;落点 `~/.ojm/keys/<key_id>/`:`private.pem`(0600)+ `public.hex`。
|
|
81
|
+
- 期望输出里有一行 **`指纹(sha256(publicKey),公布给宿主核对):…`**——抄下来,首装核对要用(§6)。
|
|
82
|
+
- 凭证**一次性**:兑换成功即作废;兑换失败(已用/已过期/已撤销/网络不通)**不写入任何密钥文件**。
|
|
83
|
+
- 站点地址:`--site-url` > `OJM_SITE_URL` > `http://127.0.0.1:9778`;兑换端点匿名可达,不需要 `--user/--pass`。
|
|
84
|
+
- `--credential` 与 `--id` 互斥(同时给 exit 2);`--out` / `--yes` 仍按离线版规则生效
|
|
85
|
+
(`--out <dir>` 相对路径按**进程 cwd** 解析,指进当前工程树会要求 `--yes`——私钥入仓即泄露)。
|
|
86
|
+
- **它不替你建信任**:公钥是本地生成的,站点在兑换时拿不到,所以首装仍是陌生 key →
|
|
87
|
+
`needs_trust`,仍要按 §6 逐字核对指纹后 `trust confirm`。凭证解决的是「身份命名与归属」,
|
|
88
|
+
不是「信任免检」。
|
|
89
|
+
- 兑换完成后,「方案市场 → 发布管理」该条记录变为**已兑换**并显示 key_id。
|
|
90
|
+
- 发版之后,「方案市场 → 发布历史」(`/oj-market/history`)按发布时间倒序列出所有产品/版本:
|
|
91
|
+
数据由站点从签名索引对账落库,**不需要 CLI 额外上报**;发布时间优先取分发包落 CDN 的
|
|
92
|
+
`Last-Modified`,源不提供时回落索引生成时间并标「索引时间」标签。
|
|
93
|
+
|
|
94
|
+
### 2.1 没有站点时:离线自建身份(旁路)
|
|
95
|
+
|
|
96
|
+
站点不可达、或你维护的是纯自建/离线 CDN(根本没有「发布管理」页)时,才自己起 key_id:
|
|
59
97
|
|
|
60
98
|
```bash
|
|
61
99
|
ojm keygen --id acme.alice.macbook # 例:<团队/产品>.<人名>.<环境>
|
|
62
100
|
```
|
|
63
101
|
|
|
64
|
-
-
|
|
65
|
-
|
|
66
|
-
- key_id 必须**全小写、至少两段**(`<publisher>.<id
|
|
102
|
+
- **站点不知道这个身份**:发布管理页没有它的记录,产品归属与申请人无从追溯;能不能装,
|
|
103
|
+
完全取决于超管愿不愿意为这枚指纹手工放行。有站点的市场请走 §2 凭证,不要自建。
|
|
104
|
+
- 其余规则与凭证通道一致:key_id 必须**全小写、至少两段**(`<publisher>.<id>`),
|
|
105
|
+
禁裸通用名如 `mykey`——违约包能签出来但装不进(`sig_key` 拒装)。
|
|
67
106
|
|
|
68
|
-
|
|
107
|
+
私钥纪律(两条路都适用,逐条照做,违者 = 任何人都能以你的身份发包):
|
|
69
108
|
|
|
70
109
|
| 纪律 | 原因 |
|
|
71
110
|
|---|---|
|
|
@@ -80,12 +119,15 @@ ojm keygen --id acme.alice.macbook # 例:<团队/产品>.<人名>.<环境>
|
|
|
80
119
|
|
|
81
120
|
```bash
|
|
82
121
|
ojm sign dist/<name>-<version>.tgz \
|
|
83
|
-
--id
|
|
84
|
-
--key ~/.ojm/keys/acme.alice.macbook/private.pem \
|
|
122
|
+
--id <key_id> \
|
|
85
123
|
--registry-id oj-registry \
|
|
86
124
|
--check
|
|
87
125
|
```
|
|
88
126
|
|
|
127
|
+
- `<key_id>` 就是上一步拿到的身份:走凭证是站点下发的 `demo-solution.xxx`,走离线旁路是
|
|
128
|
+
§2.1 里 `--id` 填的那个。私钥缺省取 `~/.ojm/keys/<key_id>/private.pem`;只有把 key 放到了
|
|
129
|
+
别处(如 `--out ./keys`)才需要再加 `--key <path>/private.pem`,且此时 `--id` 仍必须给
|
|
130
|
+
(`.sig` 按它记发布方)。
|
|
89
131
|
- `--registry-id` **必填**:发布仓库的自拟标识(同一市场的所有发布方用同一个值,如 `oj-registry`)。它进签名规范串防「把 A 仓库的包重放到 B 仓库」;站点若配置了自身 registry 身份则必须与之一致(默认模板未配置,不比对)。
|
|
90
132
|
- 产物 = 同目录 `<tgz>.sig` 侧车文件。**签了再改包 = 签名作废**;重打包必须重签。
|
|
91
133
|
- `--check` 离线自检(本地验一遍),不必等安装失败才发现签错。期望输出:`[ojm] 自检通过…`。
|
|
@@ -100,13 +142,14 @@ CDN 就是**一个装着固定命名文件的目录**:`<name>-<version>.tgz`
|
|
|
100
142
|
|
|
101
143
|
```bash
|
|
102
144
|
ojm market publish --real . \
|
|
103
|
-
--id
|
|
104
|
-
--key ~/.ojm/keys/acme.alice.macbook/private.pem \
|
|
145
|
+
--id <key_id> \
|
|
105
146
|
--registry-id oj-registry \
|
|
106
147
|
--rebuild-index
|
|
107
148
|
```
|
|
108
149
|
|
|
109
|
-
期望输出:`[ojm] 包已落 CDN → cdn/<name>-<version>.tgz(N 字节)` +
|
|
150
|
+
期望输出:`[ojm] 包已落 CDN → cdn/<name>-<version>.tgz(N 字节)` + 发布方指纹。
|
|
151
|
+
`cdn/` 是**三级索引**布局:`catalog.yaml`(+`.sig`)、`shards/<n>.yaml`(+`.sig`)、
|
|
152
|
+
`<name>-<version>.tgz`(+`.tgz.sig`);分片数随方案数增长,不要按固定文件数核对。
|
|
110
153
|
|
|
111
154
|
> 引用式方案(契约 `api` 段省略 `tables`/`deps`)会打印「引用式方案…非漏配」提示——那是合法形态,不是错误。
|
|
112
155
|
|
|
@@ -116,9 +159,8 @@ ojm market publish --real . \
|
|
|
116
159
|
|
|
117
160
|
```bash
|
|
118
161
|
ojm build --bundle crm # ① 打包指定方案
|
|
119
|
-
ojm sign dist/crm-1.0.0.tgz --id
|
|
120
|
-
--
|
|
121
|
-
--registry-id oj-registry --check # ② 签名
|
|
162
|
+
ojm sign dist/crm-1.0.0.tgz --id <key_id> \
|
|
163
|
+
--registry-id oj-registry --check # ② 签名(私钥缺省取 ~/.ojm/keys/<key_id>/)
|
|
122
164
|
cp dist/crm-1.0.0.tgz dist/crm-1.0.0.tgz.sig cdn/ # ③ tgz + .sig 放进 cdn/
|
|
123
165
|
ojm market build-index --id <维护者key> --check # ④ 重建索引(见 §4.3)
|
|
124
166
|
```
|
|
@@ -137,11 +179,16 @@ ojm market build-index --id <维护者key> --check # ④ 重
|
|
|
137
179
|
## 5. 验证发布(不起站点)
|
|
138
180
|
|
|
139
181
|
```bash
|
|
140
|
-
ojm market verify --rebuild-index #
|
|
182
|
+
ojm market verify --rebuild-index # 逐项验:完整性 / 包级签 / manifest 结构 / 索引签(catalog+分片)/ 索引同代 / 索引时效 / 迁移一致性
|
|
141
183
|
ojm market trial --fixture <name>@<version> # 隔离站点跑完整安装,跑完即毁,不污染工程
|
|
142
|
-
ojm market walkthrough --fixture <name
|
|
184
|
+
ojm market walkthrough --fixture <name>@0.1.0 # 14 项 AC:主链 9 + 负向 5;报告落 logs/walkthrough-<ts>.md
|
|
143
185
|
```
|
|
144
186
|
|
|
187
|
+
- `verify` 的检查项数随包数与分片数变化,判据是**末行「全部校验通过(N 项)」且无未通过项**,
|
|
188
|
+
不要按固定条数核对。
|
|
189
|
+
- `walkthrough` 的 fixture **必须是 `@0.1.0`**:主链的 upgrade/rollback 硬编码 0.1.0→0.2.0,
|
|
190
|
+
给别的版本这两项会 FAIL(是 fixture 选错,不是框架缺陷)。
|
|
191
|
+
|
|
145
192
|
walkthrough 期望 `14 PASS / 0 FAIL`。负向 5 项是**预期失败口径**:防线没拦住(恶意包装成了)才算 FAIL。
|
|
146
193
|
|
|
147
194
|
---
|
|
@@ -149,24 +196,41 @@ walkthrough 期望 `14 PASS / 0 FAIL`。负向 5 项是**预期失败口径**:
|
|
|
149
196
|
## 6. 装到站点(运维侧)
|
|
150
197
|
|
|
151
198
|
```bash
|
|
199
|
+
# ⓪ 先构建:release 形态服务的是产物,不先 build 会缺 dist/api(或装到旧产物)
|
|
200
|
+
ojm build
|
|
201
|
+
|
|
152
202
|
# ① 站点以 release 形态起(dev/preview 完不成安装的热更段)
|
|
203
|
+
# 先把 config.yaml 的 platform.root 从 src 改成 dist/api——必须与 --api-path 同源,
|
|
204
|
+
# 否则 oj serve 直接拒启:platform.root (...) must be mounted as an api mount
|
|
205
|
+
# 再配 vars.canonical_origin(本地演练填 http://127.0.0.1:9778):市场基址 /cdn 是相对路径,
|
|
206
|
+
# 服务端要靠它补全成绝对 URL;缺了安装直接 503(REG-CS6 不读 Host 头,不会替你猜)
|
|
153
207
|
oj serve -c config.yaml -b /api --api-path dist/api
|
|
154
208
|
|
|
155
|
-
# ②
|
|
156
|
-
|
|
209
|
+
# ② 市场地址:本地自供 /cdn 是模板默认,**不需要 setup**;
|
|
210
|
+
# 只有指向外部真 CDN 才要执行(首跑可能强制改密,故下一步的 --pass 用新口令):
|
|
211
|
+
# ojm market setup --site-url http://127.0.0.1:9778 --user superadmin --pass <当前口令> --new-pass <新口令>
|
|
157
212
|
|
|
158
213
|
# ③ 安装(从 CDN 拉:name[@version];直传文件则给 tgz 路径,.sig 同目录自动随包)
|
|
159
|
-
|
|
214
|
+
# 只对 release 站点装;别对着 `ojm dev` 装——dev 的文件监听会在安装器 reload 之前
|
|
215
|
+
# 先吃掉 config 改动,安装器自己再 reload 时得到 unchanged,判 partial(reloading)
|
|
216
|
+
ojm install crm@1.0.0 --site-url http://127.0.0.1:9778 --user superadmin --pass <口令>
|
|
160
217
|
|
|
161
218
|
# ④ 首装陌生 key 必停在 needs_trust:响应 trust.fingerprint 与 §2 记下的指纹【逐字】核对
|
|
162
219
|
# 核对通道用当面/电话/既有加密聊天——不要用 CDN 页面上的指纹(那正是攻击者能改的地方)
|
|
163
|
-
ojm trust confirm --task <task-id> --site-url http://127.0.0.1:9778 --user superadmin --pass
|
|
164
|
-
ojm install crm@1.0.0 --site-url http://127.0.0.1:9778 --user superadmin --pass
|
|
220
|
+
ojm trust confirm --task <task-id> --site-url http://127.0.0.1:9778 --user superadmin --pass <口令>
|
|
221
|
+
ojm install crm@1.0.0 --site-url http://127.0.0.1:9778 --user superadmin --pass <口令> # 重跑续跑
|
|
165
222
|
```
|
|
166
223
|
|
|
167
|
-
|
|
224
|
+
`<口令>` 就是登录站点用的那个:没跑过 `market setup` 时是脚手架种子口令(`superadmin / aA123456`),
|
|
225
|
+
跑过则用 `--new-pass` 设的新口令。
|
|
226
|
+
|
|
227
|
+
通过判据:`dist/api/manifests.yaml` 出现 `<name>: <version>`;**重启一次 `oj serve`**(新方案路由要重启才注册——reload 由 manifests 锁 diff 驱动装配,但新模块路由/模块注册属 restart_pending,热改不即时生效)后
|
|
228
|
+
`curl -H 'X-TENANT-ID: default' /api/<name>/ping` 返回 `code:0`——**不带租户头会 400 `missing tenant header`**,
|
|
229
|
+
那是缺参数,不是装失败。
|
|
168
230
|
|
|
169
|
-
|
|
231
|
+
TOFU 没有免检通道:**只有模板内置的 oj-market(第一方包)用内置 bootstrap 公钥首装免核对**;
|
|
232
|
+
你自己 keygen 出来的任何身份——包括站点凭证下发的——首装都要人工核指纹。key 轮换 = 新 key_id
|
|
233
|
+
重发 + 站点 `trust add` 新公钥 + `trust revoke` 旧 key(吊销只阻断新装,不动已装方案)。
|
|
170
234
|
|
|
171
235
|
---
|
|
172
236
|
|
|
@@ -176,15 +240,22 @@ ojm install crm@1.0.0 --site-url http://127.0.0.1:9778 --user superadmin --pass
|
|
|
176
240
|
|---|---|---|
|
|
177
241
|
| `sig_missing` | 没签,或 `.sig` 没随包在同目录 | `ojm sign --check` 后确认 `.sig` 与 tgz 同目录再装 |
|
|
178
242
|
| `tampered` | 签完又动过包,或传输损坏 | 重打包重签;核对 sha256 |
|
|
179
|
-
| `sig_key` | key_id 不是 `<publisher>.<id>` 全小写两段式 |
|
|
243
|
+
| `sig_key` | key_id 不是 `<publisher>.<id>` 全小写两段式 | 凭证通道回「发布管理」重申请(产品名须小写合规);离线旁路换合规 id 重 keygen 再重签 |
|
|
244
|
+
| `keygen --credential` 报「发布凭证无效或已失效」 | 一次性令牌已兑换/已过期/已撤销,或站点地址不对 | 回「发布管理」重新申请;同一 token 不能给第二台机器 |
|
|
245
|
+
| 申请凭证 403 `password change required` | 新建账号首次登录还在「必须先改密」悬挂期 | 先改密(页面或 `PUT /api/auth/password`),再申请 |
|
|
246
|
+
| 安装 503 `安装需要站点 canonical origin` | `config.yaml` 的 `vars.canonical_origin` 没配,而市场基址是相对路径 `/cdn` | 配成站点对外地址(本地演练 `http://127.0.0.1:9778`)后重启 |
|
|
247
|
+
| 安装 partial(stage `reloading`,`reloadConfig 返回 unchanged`) | 对着 `ojm dev` 装:dev 的文件监听先吃掉了 config 改动 | 换成 `oj serve` release 形态再装;别在 dev 上验证安装 |
|
|
248
|
+
| 装完 `curl /api/<name>/ping` 返回 400 `missing tenant header` | 少了 `X-TENANT-ID` 头 | 加 `-H 'X-TENANT-ID: default'` 再探 |
|
|
180
249
|
| `sig_alg` / `sig_format` | 签名算法/.sig 结构不符 | 用当前 ojm 重签(旧版本工具产物) |
|
|
181
250
|
| `needs_trust`(第五终态,非成功) | 站点第一次见这个 key | 与发布方逐字核对指纹 → `trust confirm --task <id>` → 重跑 install |
|
|
182
251
|
| publish 报「包冲突:…字节不一致」 | 同 name@version 已有字节不同的包 | **升 version 重发**;确需覆盖先删 CDN 旧 tgz |
|
|
183
252
|
| publish 报「一次只发布一份方案」 | 多方案工程走了自动链路 | §4.2 显式四步 |
|
|
184
|
-
| 打包报「缺少本方案的站点产物」 | `web.config.ts`
|
|
253
|
+
| 打包报「缺少本方案的站点产物」 | 没先 `ojm build`,或前端入口不在 `src/<模块>/web/entry.ts`(module-root 扫描不到;`web.config.ts` 漏登记**不是**原因) | 补 entry / 先构建再打包 |
|
|
185
254
|
| 打包报「缺少 bundle 级契约」 | 没有 `solution.yaml` | 补契约(§0 前提 1) |
|
|
186
|
-
| 装不到刚发的包 | 索引没重建 | `ojm market build-index`(§4.3 纪律 2),确认
|
|
255
|
+
| 装不到刚发的包 | 索引没重建 | `ojm market build-index`(§4.3 纪律 2),确认 `cdn/` 的 catalog + 分片 + tgz(+sig) 已同步 |
|
|
187
256
|
| 装成后 404 | 方案路由没注册 | 重启 `oj serve`(§6 通过判据) |
|
|
257
|
+
| `oj serve` 报 `platform.root (...) must be mounted as an api mount` | release 起站了,但 config 的 `platform.root` 还是 `src` | 改成 `dist/api`(与 `--api-path dist/api` 同源)后重启 |
|
|
258
|
+
| `oj serve` 报 `lock points to missing version dir: dist/api/<name>-<ver>` | 锁文件记着某个已装版本,但那个版本目录不在了(清过 dist / 换过机器) | 先 `ojm build`;锁与目录对不上时按站点安装记录重装该方案,别手改锁 |
|
|
188
259
|
| 私钥误入工程 | 打包自检拦下 / 被外部拷走 | 立即 `trust revoke` 该 key → 换新 key_id 重发 |
|
|
189
260
|
|
|
190
261
|
---
|
|
@@ -194,6 +265,8 @@ ojm install crm@1.0.0 --site-url http://127.0.0.1:9778 --user superadmin --pass
|
|
|
194
265
|
- [ ] `solution.yaml` 的 `version` 已升,与文件名一致;
|
|
195
266
|
- [ ] `ojm build` + `ojm build --bundle` 均通过,sha256 记进发布单;
|
|
196
267
|
- [ ] `ojm sign … --check` 通过,`.sig` 与 tgz 同目录;
|
|
197
|
-
- [ ]
|
|
198
|
-
- [ ]
|
|
268
|
+
- [ ] 发布身份来自站点凭证(有发布管理页时),「方案市场 → 发布管理」页该条已变**已兑换**;
|
|
269
|
+
- [ ] 发版后「方案市场 → 发布历史」能看到这次的 产品 / 版本 / 发布时间(时间来源为索引兜底时会标「索引时间」);
|
|
270
|
+
- [ ] 新 key 首次发布:指纹已提前交给运维(免得卡在 needs_trust);
|
|
271
|
+
- [ ] `ojm market verify` 末行「全部校验通过(N 项)」,无未通过项;
|
|
199
272
|
- [ ] 多人团队:索引重建触发点已约定(维护者或 CI)。
|
|
@@ -124,14 +124,19 @@
|
|
|
124
124
|
> 通用回调配置(96238 §3.2)与「通讯录变更事件」(90967)都没写;服务端两种包体都收(XML / JSON
|
|
125
125
|
> 都能归一),所以地址照文档写,不加参数。
|
|
126
126
|
|
|
127
|
-
### 2.3
|
|
127
|
+
### 2.3 三项备案(都在企微后台,不写在代码里)
|
|
128
128
|
|
|
129
129
|
| 备案项 | 填在哪 | 填什么 | 不做的症状 |
|
|
130
130
|
|---|---|---|---|
|
|
131
131
|
| **可信域名**(网页授权 / JS-SDK) | 应用详情 → **网页授权及JS-SDK** | 站点域名(如 `example.com`),按企微要求上传校验文件到该域名根路径 | 企微授权页直接拒绝:`redirect_uri 需使用应用可信域名`;员工点了登录弹不出来 |
|
|
132
|
+
| **授权完成回调域名**(扫码登录 / 企业微信授权登录) | 应用详情 → **企业微信授权登录** → 授权回调域 | **纯域名**(如 `example.com`):不带 `https://`、不带路径、不带端口、不带尾斜杠 | 普通浏览器点「一键登录」后企微报 `redirect_uri 与配置的授权完成回调域名不一致`;**企微工作台内的静默登录不受影响** |
|
|
132
133
|
| **可信 IP**(API 调用白名单) | 应用详情 → **企业可信IP** | 服务器的**出口**公网 IP(多机部署要全列) | 上游接口报 `errcode=60020 not allow to access from your ip` → 本站回 502 并把 errcode 透出 |
|
|
133
134
|
|
|
134
|
-
>
|
|
135
|
+
> 菜单名随企微版本会变;认准**可信域名**、**授权完成回调域名**、**企业可信IP**这三处。
|
|
136
|
+
> **可信域名 ≠ 授权完成回调域名**:前者给企微客户端内的 `snsapi_base` 静默授权用,后者给外部浏览器的
|
|
137
|
+
> `qrConnect` 扫码登录用,是两处独立配置。工作台能登录只说明前者已配好,扫码是第一次真正走到后者。
|
|
138
|
+
> 本站发出的 `redirect_uri` 由 `wecom_base_url` 派生,形如 `https://<站点>/api/auth/wecom/callback`,
|
|
139
|
+
> 校验时只比对其中的域名部分,不要把这个完整地址填进回调域。
|
|
135
140
|
> 回调与网页授权都要求 **https**:本地开发必须用内网穿透(见 §6.5),企微不会回调 http。
|
|
136
141
|
|
|
137
142
|
还有一项**不是备案、但决定同步能拿到多少人**:
|