@oj-module/cli 0.2.46 → 0.2.48

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 (97) hide show
  1. package/devkit/SKILL.md +1 -1
  2. package/devkit/framework-dev.md +2 -2
  3. package/devkit/manual.md +24 -14
  4. package/devkit/module-dev-and-cdn-playground.md +2 -2
  5. package/devkit/wecom-daikaifa.md +282 -27
  6. package/package.json +2 -2
  7. package/shell-dist/assets/runtime.js +2 -2
  8. package/shell-dist/index.html +2 -2
  9. package/shell-dist/versions.json +3 -3
  10. package/src/__tests__/templates-tenant-scope.test.ts +7 -2
  11. package/src/init.ts +1 -1
  12. package/src/market/__tests__/bootstrap.test.ts +1 -1
  13. package/src/market/__tests__/setup.test.ts +7 -7
  14. package/src/market/bootstrap.ts +1 -1
  15. package/templates/README.md +1 -1
  16. package/templates/config.yaml +26 -7
  17. package/templates/init.include.yaml +12 -0
  18. package/templates/openapi.json +2863 -549
  19. package/templates/src/_platform/api/_shared/service/domain/contacts.ts +7 -0
  20. package/templates/src/_platform/api/_shared/service/domain/org-read.ts +451 -0
  21. package/templates/src/_platform/api/_shared/service/domain/org.ts +566 -0
  22. package/templates/src/_platform/api/_shared/service/domain/users.ts +140 -2
  23. package/templates/src/_platform/api/_shared/service/infra/query.ts +2 -1
  24. package/templates/src/_platform/api/fixtures/l1-test-user.sql +1 -1
  25. package/templates/src/_platform/api/manifest.yaml +3 -0
  26. package/templates/src/_platform/api/migrations/0016__org.mysql.sql +41 -0
  27. package/templates/src/_platform/api/migrations/0016__org.pg.sql +40 -0
  28. package/templates/src/_platform/api/migrations/0016__org.sql +62 -0
  29. package/templates/src/_platform/api/org/departments/api.ts +51 -0
  30. package/templates/src/_platform/api/org/is-member/api.ts +33 -0
  31. package/templates/src/_platform/api/org/members/api.ts +55 -0
  32. package/templates/src/_platform/api/org/picker/api.ts +59 -0
  33. package/templates/src/_platform/api/org/resolve/api.ts +38 -0
  34. package/templates/src/_platform/api/org/tree/api.ts +37 -0
  35. package/templates/src/_platform/api/org/user/api.ts +46 -0
  36. package/templates/src/_platform/api/schema.yaml +48 -0
  37. package/templates/src/_platform/api/seed.mysql.sql +10 -5
  38. package/templates/src/_platform/api/seed.pg.sql +10 -5
  39. package/templates/src/_platform/api/seed.sql +13 -5
  40. package/templates/src/_platform/web/__client/api.schemas.ts +126 -0
  41. package/templates/src/_platform/web/__client/api.ts +170 -1
  42. package/templates/src/auth/api/_shared/ldap-login.ts +2 -0
  43. package/templates/src/auth/api/_shared/session.ts +5 -1
  44. package/templates/src/auth/api/_shared/wecom/admin.ts +17 -10
  45. package/templates/src/auth/api/_shared/wecom/client.ts +311 -40
  46. package/templates/src/auth/api/_shared/wecom/config.ts +40 -17
  47. package/templates/src/auth/api/_shared/wecom/contacts.ts +263 -0
  48. package/templates/src/auth/api/_shared/wecom/crypt.ts +17 -7
  49. package/templates/src/auth/api/_shared/wecom/events.ts +111 -26
  50. package/templates/src/auth/api/_shared/wecom/login.ts +152 -1
  51. package/templates/src/auth/api/_shared/wecom/payload.ts +116 -0
  52. package/templates/src/auth/api/_shared/wecom/store.ts +65 -0
  53. package/templates/src/auth/api/_shared/wecom/sync.ts +487 -0
  54. package/templates/src/auth/api/manifest.yaml +3 -0
  55. package/templates/src/auth/api/migrations/0002__org_sync.mysql.sql +28 -0
  56. package/templates/src/auth/api/migrations/0002__org_sync.pg.sql +27 -0
  57. package/templates/src/auth/api/migrations/0002__org_sync.sql +41 -0
  58. package/templates/src/auth/api/schema.yaml +31 -0
  59. package/templates/src/auth/api/wecom/admin/api.ts +7 -5
  60. package/templates/src/auth/api/wecom/api.ts +100 -18
  61. package/templates/src/auth/api/wecom/sync/api.ts +199 -0
  62. package/templates/src/auth/web/__client/api.schemas.ts +97 -0
  63. package/templates/src/auth/web/__client/api.ts +73 -1
  64. package/templates/src/login/web/auth-provider.ts +9 -1
  65. package/templates/src/notification/api/seed.sql +1 -1
  66. package/templates/src/system/api/departments/api.ts +142 -0
  67. package/templates/src/system/api/departments/detach/api.ts +34 -0
  68. package/templates/src/system/api/departments/members/api.ts +142 -0
  69. package/templates/src/system/api/departments/move/api.ts +39 -0
  70. package/templates/src/system/api/users/api.ts +45 -5
  71. package/templates/src/system/api/users/profile/api.ts +44 -0
  72. package/templates/src/system/web/__client/api.schemas.ts +143 -0
  73. package/templates/src/system/web/__client/api.ts +293 -1
  74. package/templates/src/system/web/entry.ts +6 -0
  75. package/templates/src/system/web/locales/en-US.json +113 -7
  76. package/templates/src/system/web/locales/zh-CN.json +113 -7
  77. package/templates/src/system/web/pages/departments/index.tsx +346 -0
  78. package/templates/src/system/web/pages/departments/sync-bar.tsx +257 -0
  79. package/templates/src/system/web/pages/departments/tree.ts +66 -0
  80. package/templates/src/system/web/pages/errors.ts +1 -1
  81. package/templates/src/system/web/pages/users/index.tsx +49 -7
  82. package/templates/src/system/web/pages/wecom/index.tsx +16 -1
  83. package/templates/tests/l1-builder.test.ts +1 -1
  84. package/templates/tests/org-departments.spec.ts +311 -0
  85. package/templates/tests/org-read.spec.ts +469 -0
  86. package/templates/tests/rbac-contacts.spec.ts +13 -3
  87. package/templates/tests/rbac-seed.spec.ts +11 -0
  88. package/templates/tests/seed-role-accounts.spec.ts +6 -2
  89. package/templates/tests/wecom-admin.spec.ts +64 -42
  90. package/templates/tests/wecom-client-timeout.spec.ts +99 -0
  91. package/templates/tests/wecom-connector.spec.ts +115 -11
  92. package/templates/tests/wecom-crypt.spec.ts +12 -3
  93. package/templates/tests/wecom-endpoints.spec.ts +182 -14
  94. package/templates/tests/wecom-org-sync.spec.ts +598 -0
  95. package/templates/tests/wecom-sync.spec.ts +286 -0
  96. package/vendor/host-versions.json +3 -3
  97. package/templates/src/auth/api/_shared/wecom/xml.ts +0 -36
@@ -62,7 +62,7 @@ describe("given 站点可直连且凭据未悬挂 When setup", () => {
62
62
  const root = newProject();
63
63
  const site = fakeSite();
64
64
 
65
- const result = await setupMarketUrl({ projectRoot: root, user: "admin", pass: "123456", siteUrl: "http://127.0.0.1:9778", fetchFn: site.fetchFn });
65
+ const result = await setupMarketUrl({ projectRoot: root, user: "admin", pass: "aA123456", siteUrl: "http://127.0.0.1:9778", fetchFn: site.fetchFn });
66
66
 
67
67
  expect(result.cdnBaseUrl).toBe("http://127.0.0.1:9778/cdn");
68
68
  const put = site.calls.find(c => c.url.endsWith("/api/system/settings"))!;
@@ -77,12 +77,12 @@ describe("given seed admin 处于 must_change 悬挂期 When setup", () => {
77
77
  const root = newProject();
78
78
  const site = fakeSite({ restricted: true });
79
79
 
80
- const result = await setupMarketUrl({ projectRoot: root, user: "admin", pass: "123456", newPass: "newpass-123", siteUrl: "http://127.0.0.1:9778", fetchFn: site.fetchFn });
80
+ const result = await setupMarketUrl({ projectRoot: root, user: "admin", pass: "aA123456", newPass: "newpass-123", siteUrl: "http://127.0.0.1:9778", fetchFn: site.fetchFn });
81
81
 
82
82
  expect(result.changedPassword).toBe(true);
83
83
  const pwd = site.calls.find(c => c.url.endsWith("/api/auth/password"))!;
84
84
  expect(pwd.method).toBe("PUT");
85
- expect(pwd.body).toEqual({ old_password: "123456", new_password: "newpass-123" });
85
+ expect(pwd.body).toEqual({ old_password: "aA123456", new_password: "newpass-123" });
86
86
  const put = site.calls.find(c => c.url.endsWith("/api/system/settings"))!;
87
87
  expect(put.auth).toBe("Bearer token-full");
88
88
  });
@@ -96,7 +96,7 @@ describe("given 站点不可达 When setup", () => {
96
96
  };
97
97
  const fetchFn = failing as unknown as typeof fetch;
98
98
 
99
- await expect(setupMarketUrl({ projectRoot: root, user: "admin", pass: "123456", siteUrl: "http://127.0.0.1:9778", fetchFn }))
99
+ await expect(setupMarketUrl({ projectRoot: root, user: "admin", pass: "aA123456", siteUrl: "http://127.0.0.1:9778", fetchFn }))
100
100
  .rejects
101
101
  .toThrow(/ojm dev/);
102
102
  });
@@ -107,7 +107,7 @@ describe("given 改密后写设置仍被拒 When setup", () => {
107
107
  const root = newProject();
108
108
  const site = fakeSite({ restricted: true, rejectSettings: true });
109
109
 
110
- await expect(setupMarketUrl({ projectRoot: root, user: "admin", pass: "123456", newPass: "newpass-123", siteUrl: "http://127.0.0.1:9778", fetchFn: site.fetchFn }))
110
+ await expect(setupMarketUrl({ projectRoot: root, user: "admin", pass: "aA123456", newPass: "newpass-123", siteUrl: "http://127.0.0.1:9778", fetchFn: site.fetchFn }))
111
111
  .rejects
112
112
  .toThrow(/must_change|权限|设置/);
113
113
  });
@@ -115,7 +115,7 @@ describe("given 改密后写设置仍被拒 When setup", () => {
115
115
 
116
116
  describe("given setup 返回 changedPassword When 选 setup 后的登录口令", () => {
117
117
  it("then 改密已发生用 newPass,未改密用原种子口令(must_change=0 是当前 seed 常态)", () => {
118
- expect(loginPasswordAfterSetup({ siteUrl: "", cdnBaseUrl: "", changedPassword: true }, "new-pass", "123456")).toBe("new-pass");
119
- expect(loginPasswordAfterSetup({ siteUrl: "", cdnBaseUrl: "", changedPassword: false }, "new-pass", "123456")).toBe("123456");
118
+ expect(loginPasswordAfterSetup({ siteUrl: "", cdnBaseUrl: "", changedPassword: true }, "new-pass", "aA123456")).toBe("new-pass");
119
+ expect(loginPasswordAfterSetup({ siteUrl: "", cdnBaseUrl: "", changedPassword: false }, "new-pass", "aA123456")).toBe("aA123456");
120
120
  });
121
121
  });
@@ -39,7 +39,7 @@ import { publishPackage } from "./publish";
39
39
  // 种子管理员 = superadmin:模板 seed 账号名=角色名(3d93063f 起 admin 被幂等解绑 superadmin
40
40
  // 角色),写 market 安全设置要求 actor 持 superadmin 角色——用 admin 会 403(2026-10-23 演练实踩)。
41
41
  const SEED_ADMIN_USER = "superadmin";
42
- const SEED_ADMIN_PASS = "123456";
42
+ const SEED_ADMIN_PASS = "aA123456";
43
43
 
44
44
  /** Ed25519 PKCS#8 DER 前缀(RFC 8410 固定头;与 gen-signed-fixtures.mjs 同一手法) */
45
45
  const ED25519_PKCS8_PREFIX = "302e020100300506032b657004220420";
@@ -11,7 +11,7 @@ pnpm build # 构建:oj build + 前端全站合并到 dist/web
11
11
  pnpm preview # 预览:oj migrate + server(release 产物)
12
12
  ```
13
13
 
14
- 登录账号:`admin` / `123456`(见 `api/src/_platform/seed.sql`)。
14
+ 登录账号:`admin` / `aA123456`(见 `api/src/_platform/seed.sql`)。
15
15
 
16
16
  ## 系统管理模块
17
17
 
@@ -9,7 +9,9 @@
9
9
  # 站点挂载:一行 = 一个 URL 前缀 + 一个目录(api/web 二选一)。
10
10
  # 这里不放 api 挂载是有意的:本工程是 src/<name>/{api,web} 布局,源码由 ojm dev
11
11
  # 以 --module-root src 供给、产物由 ojm preview 以 --api-path dist/api 供给,都不走 mounts。
12
- # 手写部署(不用 ojm)时才需要加回一条 `- prefix: /api` + `api: dist`。
12
+ # 手写部署(不用 ojm)时才需要加回一条 `- prefix: /api` + `api: dist/api`。
13
+ # 注意:手写部署同时要把下面 platform.root 从 `src` 改成 `dist/api`(二者必须同源),
14
+ # 否则 oj serve 报 `platform.root must be mounted as an api mount`。
13
15
  mounts:
14
16
  # 本地市场 CDN:直出工程根 cdn/。seed 里 framework.market_cdn_url 缺省就是 /cdn,本站点自供免 setup。
15
17
  # 生产删掉这条,并把该设置指向真 CDN(绝对地址)。不能设 spa:缺文件必须 404,
@@ -55,20 +57,33 @@ vars:
55
57
  # canonical_origin: "https://your-site.example.com"
56
58
 
57
59
  # ---- 企业微信代开发(服务商侧,部署级常量)----
58
- # 一个部署 = 一个服务商账号下的一套代开发模板,所以这 6 个值是「部署」维度,不进 DB。
60
+ # 一个部署 = 一个服务商账号下的一套代开发模板,所以这 8 个值是「部署」维度,不进 DB。
59
61
  # 「哪个企业装了哪个应用」不写在这里:企微 create_auth 回调推 AuthCode → 换永久授权码 →
60
62
  # 落 wecom_app_profile(主键含 agent_id,同一企业测试/正式两个应用天然并存),
61
63
  # 新增企业只需企业管理员扫码授权,**不改配置、不重启**。
62
- # 六项任一为空 → 企微登录整体 400 拒绝服务(宁可不登录,也不明文存 permanent_code)。
63
- # wecom_suite_id 代开发应用模版 id(企微后台「应用管理 → 代开发」里的 SuiteID)
64
- # wecom_suite_secret 套件密钥(SuiteSecret)
64
+ # 分三层(来源与必填时序不同,破解「先有 suite 还是先有回调」死锁):
65
+ # · Tier A 本地生成、创建模版前即可配齐:callback_token / encoding_aes_key / enc_key / base_url;
66
+ # 任一为空 → 启动即 400 拒绝服务(宁可不登录,也不明文存 permanent_code)。
67
+ # · Tier B 企微创建模版后下发:suite_id / suite_secret;为空时仍可启动并响应 echostr 验证,
68
+ # 仅 login/callback/create_auth 等 suite 流程会 400——先配 Tier A 跑通回调、建模版、拿 suite 再补全。
69
+ # · Tier C 服务商后台「通用开发参数」页拿(不是企业、也不是 suite):provider_id / provider_secret;
70
+ # 只供「生成授权链接」用(get_customized_auth_url 要 provider_access_token)。缺它不影响登录,
71
+ # 但 /system/wecom 点「生成开通链接」会 400 点名缺这两项。
72
+ # wecom_suite_id 代开发应用「模板详情 → 基本信息」页里的「模板ID」(即 SuiteID);创建模板成功后才出现
73
+ # wecom_suite_secret 同页「模板Secret」(即 SuiteSecret,可重置);不是企业 CorpID/CorpSecret
74
+ # wecom_provider_id 服务商的 corpid(服务商自己的,不是企业 corpid);**位置:服务商后台「服务商信息」页显示的 CorpID**
75
+ # wecom_provider_secret 服务商 provider_secret;**位置:服务商后台 → 应用管理 → 通用开发参数(点「编辑」)→ ProviderSecret 字段**,
76
+ # **不在「服务商信息」页**(那里只显示 CorpID),也**不是** suite_secret(suite 那个是另一套)。
65
77
  # wecom_callback_token 「Token」(回调验签用)
66
78
  # wecom_encoding_aes_key 「EncodingAESKey」(43 字符,回调解密用)
67
79
  # wecom_enc_key permanent_code 落库加密密钥(hex/base64,16 或 32 字节;建议 oj secret keygen)
68
- # wecom_base_url 连接器基址(绝对地址,无尾斜杠),如 https://site.example.com/api/auth/wecom
69
- # → 企微后台「指令回调URL」填 <base>/event;网页授权 redirect_uri 用 <base>/callback
80
+ # wecom_base_url 连接器基址(绝对地址,无尾斜杠,**这是基址、不是回调 URL**),如 https://site.example.com/api/auth/wecom
81
+ # → 企微后台「指令回调URL」填 <base>/event(必须带 /event,裸 <base> 无 handler 会 404);
82
+ # 网页授权 redirect_uri 用 <base>/callback
70
83
  wecom_suite_id: ''
71
84
  wecom_suite_secret: ''
85
+ wecom_provider_id: ''
86
+ wecom_provider_secret: ''
72
87
  wecom_callback_token: ''
73
88
  wecom_encoding_aes_key: ''
74
89
  wecom_enc_key: ''
@@ -77,6 +92,10 @@ vars:
77
92
  platform:
78
93
  modules: [_platform] # 安装器热更 config 需要 _platform 特权;删了会在装到最后一步报 NotCapable
79
94
  root: src # 后端模块源码根(src/<name>/api 的父目录);preview 派生 config 时改写成 dist
95
+ # ⚠️ 手写部署(不用 ojm,靠 mounts 起站)时:root 必须等于 api 挂载目录,
96
+ # 否则 oj serve 直接拒启 `platform.root (…) must be mounted as an api mount`。
97
+ # 编译产物下把这里改成 `dist/api`(与下面 `- prefix: /api api: dist/api` 同源)。
98
+ # ojm dev/preview 不受影响:dev 用 --module-root、preview 派生 config 会重写 root。
80
99
  # 信任库默认落在安装子树之外(.solutions/trusted-keys.yaml),第三方方案模块碰不到。
81
100
  # U6 加固:第三方模块的 fs 读写范围 = fs.root(= modules/ 安装子树),信任库必须在其外,
82
101
  # 否则方案包可写自己的信任锚(伪造签名来源)。部署侧还应补 platform.trusted_paths 授权
@@ -59,6 +59,8 @@ include:
59
59
  - tests/auth-throttle.spec.ts
60
60
  - tests/guard.spec.ts
61
61
  - tests/l1-builder.test.ts
62
+ - tests/org-departments.spec.ts
63
+ - tests/org-read.spec.ts
62
64
  - tests/query-utils.spec.ts
63
65
  - tests/rbac-audit.spec.ts
64
66
  - tests/rbac-contacts.spec.ts
@@ -70,6 +72,16 @@ include:
70
72
  - tests/rbac-tables.spec.ts
71
73
  - tests/rbac-users.spec.ts
72
74
  - tests/seed-role-accounts.spec.ts
75
+ # 企微连接器与组织同步:源码本就分发(src/auth/api),对应回归也必须跟着走,
76
+ # 否则用户工程升级后改配置/换套件时没有任何本地自检手段。
77
+ - tests/wecom-admin.spec.ts
78
+ - tests/wecom-client-timeout.spec.ts
79
+ - tests/wecom-connector.spec.ts
80
+ - tests/wecom-crypt.spec.ts
81
+ - tests/wecom-endpoints.spec.ts
82
+ - tests/wecom-landing.spec.ts
83
+ - tests/wecom-org-sync.spec.ts
84
+ - tests/wecom-sync.spec.ts
73
85
  - tests/lifecycle/atomic-state.spec.ts
74
86
  - tests/lifecycle/deploy-smoke.spec.ts
75
87
  - tests/lifecycle/migrate.spec.ts