@oj-module/cli 0.2.42 → 0.2.43

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oj-module/cli",
3
3
  "type": "module",
4
- "version": "0.2.42",
4
+ "version": "0.2.43",
5
5
  "description": "React Antd 模块工程命令行工具(含预构建宿主 shell-dist)",
6
6
  "license": "MIT",
7
7
  "exports": {
@@ -49,7 +49,7 @@
49
49
  ],
50
50
  "dependencies": {
51
51
  "@faker-js/faker": "^10.4.0",
52
- "@oj-module/runtime": "0.2.42",
52
+ "@oj-module/runtime": "0.2.43",
53
53
  "@redocly/cli": "^2.51.0",
54
54
  "@tailwindcss/vite": "^4.3.0",
55
55
  "cac": "^6.7.14",
@@ -304,10 +304,10 @@ function getAppInfo() {
304
304
  return {
305
305
  "pkg": {
306
306
  "name": "@oj-module/runtime",
307
- "version": "0.2.42",
307
+ "version": "0.2.43",
308
308
  "license": "MIT"
309
309
  },
310
- "lastBuildTime": "2026-10-26 10:15:05"
310
+ "lastBuildTime": "2026-10-26 20:27:40"
311
311
  };
312
312
  }
313
313
  var init_get_app_info = __esmMin((() => {}));
@@ -5,9 +5,9 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" href="/favicon.ico" />
7
7
  <title>React Antd Admin · Shell</title>
8
- <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' https://modules.cdn.example.com 'wasm-unsafe-eval' 'nonce-2Xr8lEfbU8h45hDFonkWmg=='; script-src-attr 'none'; connect-src 'self' data: https://modules.cdn.example.com; style-src 'self' https://modules.cdn.example.com 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-src https://ant.design https://react.dev https://condorheroblog.github.io; form-action 'none'; base-uri 'none'; object-src 'none'; upgrade-insecure-requests" />
8
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' https://modules.cdn.example.com 'wasm-unsafe-eval' 'nonce-1qa+1NwgUpqlsLbbr7v3iQ=='; script-src-attr 'none'; connect-src 'self' data: https://modules.cdn.example.com; style-src 'self' https://modules.cdn.example.com 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; frame-src https://ant.design https://react.dev https://condorheroblog.github.io; form-action 'none'; base-uri 'none'; object-src 'none'; upgrade-insecure-requests" />
9
9
 
10
- <script type="importmap" nonce="2Xr8lEfbU8h45hDFonkWmg==">{
10
+ <script type="importmap" nonce="1qa+1NwgUpqlsLbbr7v3iQ==">{
11
11
  "imports": {
12
12
  "react": "/assets/react.js",
13
13
  "react/jsx-runtime": "/assets/jsx-runtime.js",
@@ -7,9 +7,9 @@
7
7
  "react-router": "7.18.2",
8
8
  "react-router/dom": "7.18.2",
9
9
  "@tanstack/react-query": "5.102.8",
10
- "@oj-module/runtime": "0.2.42",
11
- "@oj-module/runtime/contract": "0.2.42",
12
- "@oj-module/runtime/contract/errors": "0.2.42",
10
+ "@oj-module/runtime": "0.2.43",
11
+ "@oj-module/runtime/contract": "0.2.43",
12
+ "@oj-module/runtime/contract/errors": "0.2.43",
13
13
  "@plane/kit": "0.1.20",
14
14
  "@plane/editor": "0.1.20",
15
15
  "antd": "6.6.1",
@@ -11,13 +11,16 @@ import { Buffer } from "node:buffer";
11
11
  import { generateKeyPairSync } from "node:crypto";
12
12
  import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
13
13
  import { tmpdir } from "node:os";
14
- import { join } from "node:path";
14
+ import { dirname, join } from "node:path";
15
15
 
16
16
  import { describe, expect, it } from "vitest";
17
17
  import { parse, stringify } from "yaml";
18
+ import { createDeterministicTgz } from "../../deterministic-tar";
18
19
  import { recomputeIndex, signIndex } from "../../publish";
20
+ import { signCommand } from "../../sign";
19
21
  import { buildIndex } from "../build-index";
20
22
  import { publishPackage } from "../publish";
23
+ import { readTgz } from "../scan";
21
24
  import { verifyMarket } from "../verify";
22
25
 
23
26
  const MOUNTS = " - prefix: /api\n api: src\n - prefix: /cdn\n web: ../cdn\n";
@@ -72,6 +75,34 @@ describe("given 一个正常落地的包 When 离线验包", () => {
72
75
  });
73
76
  });
74
77
 
78
+ describe("given 真实工程包(manifest 按 C2-29 整体省略 archive 占位)When 离线验包", () => {
79
+ it("then manifest 结构不因此打叉(REG-CS8 回归:ojm build --bundle 不落 archive ≠ 缺必填)", async () => {
80
+ const root = await seed();
81
+ const tgz = join(root, "cdn", "demo-solution-0.1.0.tgz");
82
+ // 解包 → 摘掉根 manifest 的 archive(fixture 有、真实方案无的字段差)→ 确定性重打包 → 重签 → 重建索引
83
+ const staging = mkdtempSync(join(tmpdir(), "ojm-verify-noarchive-"));
84
+ for (const e of readTgz(new Uint8Array(readFileSync(tgz)))) {
85
+ if (e.name.endsWith("/"))
86
+ continue;
87
+ const abs = join(staging, e.name);
88
+ mkdirSync(dirname(abs), { recursive: true });
89
+ writeFileSync(abs, e.content);
90
+ }
91
+ const rootManifestPath = join(staging, "manifest.yaml");
92
+ const manifest = parse(readFileSync(rootManifestPath, "utf-8")) as Record<string, unknown>;
93
+ delete manifest.archive;
94
+ writeFileSync(rootManifestPath, stringify(manifest));
95
+ await createDeterministicTgz(staging, tgz);
96
+ signCommand(tgz, { id: KEY_ID, privateKeyPem: testPem(), registryId: REGISTRY_ID });
97
+ await buildIndex({ projectRoot: root, id: KEY_ID, privateKeyPem: testPem() });
98
+
99
+ const result = await verifyMarket({ projectRoot: root });
100
+
101
+ expect(itemOf(result, "manifest 结构")?.ok).toBe(true);
102
+ rmSync(staging, { recursive: true, force: true });
103
+ });
104
+ });
105
+
75
106
  describe("given 预发布版本(文件名含连字符)包体被篡改 When 离线验包", () => {
76
107
  it("then 完整性仍能检出(不再因文件名解析错配而静默放过,REG-CS1 边界)", async () => {
77
108
  const root = newProject();
@@ -295,7 +295,11 @@ export function expiryCheck(text: string | null): VerifyCheck {
295
295
  };
296
296
  }
297
297
 
298
- /** manifest 结构:必填字段 + 内嵌原生清单 desc 非空(等值要求已移除,见 §9.3) */
298
+ /**
299
+ * manifest 结构:必填字段 + 内嵌原生清单 desc 非空(等值要求已移除,见 §9.3)。
300
+ * 必填不含 archive:契约 C2-29 允许 Form A 整体省略,`ojm build --bundle` 对真实方案的
301
+ * 落盘形态即省略(fixture 会写 file 占位)——把它当必填会让一切真实工程包验包必红。
302
+ */
299
303
  function structureCheck(bytes: Uint8Array, file: string): VerifyCheck[] {
300
304
  let entries: ReturnType<typeof readTgz>;
301
305
  try {
@@ -308,12 +312,12 @@ function structureCheck(bytes: Uint8Array, file: string): VerifyCheck[] {
308
312
  if (root === undefined)
309
313
  return [{ item: "manifest 结构", ok: false, detail: `${file}:包内缺 manifest.yaml` }];
310
314
  const manifest = (yamlParse(root.content.toString("utf-8")) ?? {}) as Record<string, unknown>;
311
- const missing = ["name", "version", "summary", "peerRuntime", "archive"].filter(k => manifest[k] === undefined);
315
+ const missing = ["name", "version", "summary", "peerRuntime"].filter(k => manifest[k] === undefined);
312
316
  const out: VerifyCheck[] = [{
313
317
  item: "manifest 结构",
314
318
  ok: missing.length === 0,
315
319
  detail: missing.length === 0
316
- ? `${file}:必填字段齐备(name/version/summary/peerRuntime/archive)`
320
+ ? `${file}:必填字段齐备(name/version/summary/peerRuntime)`
317
321
  : `${file}:缺必填字段 ${missing.join(", ")}`,
318
322
  }];
319
323
 
@@ -1,117 +1,61 @@
1
- # oj 服务配置(字段语义见 oj 后端开发手册 oj-api-dev / 或 oj --help)。
2
- # 本文件由 ojm init 生成;config 内相对路径相对本文件所在目录。
3
- # 更多能力见手册 §10:vars / broker / tasks / oidc / ws / html_meta / secrets。
4
- #
5
- # ---- 证书与密钥的唯一落点:`./config/`(= 工程根/config/,本文件同级的子目录)----
6
- # 三件套由 `ojm init` 现场签发(dev 缺证时 `ojm dev` 也会补签),逐把的作用与后果:
7
- #
8
- # ① private.pem —— 证书签发私钥(RSA 2048)。只用来给 ③ 签名,**oj 不读它**,
9
- # 也绝不出部署机、不入仓。丢了不需要恢复:重新签发一套即可(旧的 ③ 作废)。
10
- # ② public.pem —— 与 ① 成对的公钥,oj 启动时拿它验 ③ 的 RS256 签名。
11
- # 配错路径或与 ③ 不是同一对 → oj 验签失败、启动即拒(读作「证书无效」)。
12
- # 由 server.public_key_path 引用。
13
- # ③ cert.jws —— JWS 证书(RS256 紧凑三段,payload 仅 nbf/exp),**oj 的启动闸门**:
14
- # 缺失、过期、签名对不上 → fail-fast 拒启,devkit 手册明确**无任何 config/CLI 绕过开关**。
15
- # 默认有效期 3650 天(短有效期等于定时炸弹);过期只能重新签发一套 ①②③。
16
- # 由 server.certificate_path 引用。
17
- #
18
- # 另有一对与上面无关的密钥(jwt_secret 密封用,init 密封成功后追加到本文件末尾):
19
- # secrets.private_key_path ./config/secrets-private.pem 解 jwt_secret 密文的私钥,只放部署机
20
- # secrets.public_key_path ./config/secrets-public.pem 加密用的公钥,可随代码分发
21
- # (密文形如 ENC 后跟方括号包裹的字串;未密封时 config 里是 32 字节十六进制明文)
22
- #
23
- # ojm 侧的解析入口是 `resolveCertDir()`,由本文件位置派生;改落点只需动那一处 + 本段注释。
1
+ # oj 服务配置(字段手册见 devkit oj-api-dev / oj --help)。由 ojm init 生成,相对路径相对本文件所在目录。
24
2
 
25
- # 站点挂载(oj v0.1.58 起顶层扁平表:一行=一个 URL 前缀+一个目录,api/web 恰好其一)。
26
- # 被删除的 server.api_prefix / app_spa_fallback 已由 mounts 取代;
27
- # ojm dev 的 SPA 深链回落由 ojm 自建静态层负责;ojm preview 为单服务形态,
28
- # 现场生成派生 config(.ojm-preview.yaml,注入下面的 web 挂载 + spa: true),无需手改。
29
- #
30
- # **这里没有 api 挂载是有意的**:本工程是 `src/<name>/{api,web}` 布局,模块分散在
31
- # `src/<name>/api` 下,而 mounts 的 api 项按 `<目录>/<模块>/manifest.yaml` 单层解析、
32
- # 不会递归(实测报 `module 'x' missing manifest.yaml`)。源码形态由 `ojm dev` 用
33
- # `--module-root src` 供给,产物形态由 `ojm preview` 用 `--api-path dist/api` 供给;
34
- # 两者与 mounts 的 api 项同时出现还会报「module declared in multiple places」。
35
- # 手写部署(不经 ojm)时才需要自己加回一条 `- prefix: /api` + `api: dist`。
3
+ # ---- 证书三件套在 ./config/(init 已现场签发)----
4
+ # private.pem 签发私钥:只用于给 cert.jws 签名,oj 不读它;泄露或丢失就重签一套,旧的作废
5
+ # public.pem oj 启动时验 cert.jws 签名用(server.public_key_path)
6
+ # cert.jws 启动闸门:缺失/过期/签名不符直接拒启,无 config/CLI 绕过(默认 10 年有效期)
7
+ # 另有一对 secrets-*.pem:init 密封 jwt_secret 后追加到本文件末尾,同样只放部署机。
8
+
9
+ # 站点挂载:一行 = 一个 URL 前缀 + 一个目录(api/web 二选一)。
10
+ # 这里不放 api 挂载是有意的:本工程是 src/<name>/{api,web} 布局,源码由 ojm dev
11
+ # 以 --module-root src 供给、产物由 ojm preview 以 --api-path dist/api 供给,都不走 mounts。
12
+ # 手写部署(不用 ojm)时才需要加回一条 `- prefix: /api` + `api: dist`。
36
13
  mounts:
37
- # 本地模拟 CDN:直出工程根 cdn/(内容见 cdn/README.md)。
38
- # seed 的 framework.market_cdn_url 缺省即 /cdn(本站点自供),新工程免 market setup。
39
- # 红线:生产请删掉这一条挂载,并把 system 设置 framework.market_cdn_url 指向真 CDN(绝对地址)。
40
- # 不得给此挂载设 spa——缺文件必须返回 404,回落 index.html 会让市场解析崩。
14
+ # 本地市场 CDN:直出工程根 cdn/。seed 里 framework.market_cdn_url 缺省就是 /cdn,本站点自供免 setup。
15
+ # 生产删掉这条,并把该设置指向真 CDN(绝对地址)。不能设 spa:缺文件必须 404,
16
+ # 回落 index.html 会让市场解析崩。
41
17
  - prefix: /cdn
42
18
  web: ./cdn
43
- # REG-CS5 缓存头(读面):索引(catalog/shards)与内容寻址包体统一 60s 新鲜窗口 +
44
- # 60s stale-while-revalidate——yank 最坏 120s 生效(Q8)。ETag/If-None-Match→304 由 oj
45
- # 静态层原生支持;本头只加在读面(静态挂载),写入面(ojm market build-index)不发 HTTP 头。
19
+ # 读面缓存 60s:yank 最坏 120s 生效。304 由 oj 静态层自带,这里只管新鲜窗口。
46
20
  headers:
47
21
  Cache-Control: 'max-age=60, stale-while-revalidate=60'
48
- # 生产若由 oj 直出前端(前后端同进程,即 ojm preview 的生产形态),
49
- # 取消下一行注释并先 build 出 dist/web(preview 会自动注入等价挂载,无需手改):
22
+ # 生产要 oj 直出前端(前后端同端口的 SPA,即 ojm preview 形态)就解开下面三行,先 ojm build。
23
+ # preview 自己会注入等价挂载,平时不用动。
50
24
  # - prefix: "/"
51
- # web: "./dist/web" # 相对 config.yaml → 工程根/dist/web
25
+ # web: "./dist/web" # 相对本文件 → 工程根/dist/web
52
26
  # spa: true
53
- # 嵌套 handler 组(oj v0.1.64)无需配置开关,组织方式见 api.ts 顶部注释。
54
27
 
55
28
  server:
56
- host: 127.0.0.1 # 显式钉死:oj 默认绑 [::1],localhost 解析序在反代下有坑
29
+ host: 127.0.0.1 # 钉死 127.0.0.1:oj 默认绑 [::1],localhost 在反代下解析有坑
57
30
  port: 9778
58
31
  public_key_path: ./config/public.pem
59
32
  certificate_path: ./config/cert.jws
60
- # ---- 迁移门禁 ----
61
- # auto = dev 缺省(启动即应用);release 缺省是 verify(账本落后拒启,先 oj migrate)。
62
- # 显式写出来:dev 的隐式 DB 副作用不再靠「模式默认值」猜,release 也不会因缺省拒启。
63
- migrate_on_start: auto
64
- # ---- 入参契约(v0.1.44)----
65
- # 契约生成的 .schema 在此生效:违反即 400 且 handler 不执行。
66
- # 关掉等于退回「后端零校验」,仅在 .schema 与旧 handler 冲突时临时逃生。
67
- schema_validation: true
68
- # ---- 单 isolate 堆限额(v0.1.40,默认 256MiB,下限 32MiB)----
69
- js_heap_limit_bytes: 268435456
70
- # ---- 路由级超时(v0.1.30):pattern 按声明序首个命中,且是含 base 的全路径 ----
71
- route_timeouts: []
72
- # ---- 统一响应头(v0.1.30)----
73
- response_headers: {}
74
- # ---- 热更端点(ojm install 的 reloading 段消费;显式关闭 = 安装器 reloading 段可读失败)----
75
- # ojm:reload
76
- reload_api: true
77
- # ---- 直传路由 PUT {base}/blob/{key} 的体积上限(v0.1.30,默认 1GiB)----
78
- # 与 max_upload_bytes(multipart,默认 10MB)是两套闸,别混。
79
- blob_upload_max_bytes: 1073741824
80
- # ---- CORS(v0.1.35):缺这一段 = 不挂 CORS 层;credentials 必须配显式 origins ----
33
+ migrate_on_start: auto # auto=启动即迁移;release 建议 verify(账本落后拒启,先 oj migrate)
34
+ schema_validation: true # 契约 .schema 违反即 400;仅当 .schema 与旧 handler 冲突时临时关
35
+ js_heap_limit_bytes: 268435456 # 单 isolate 堆限额(默认 256MiB,下限 32MiB)
36
+ route_timeouts: [] # 路由级超时;pattern 按声明序首个命中,写含 base 的全路径
37
+ response_headers: {} # 统一响应头
38
+ reload_api: true # 热更端点 /ojm:reload:安装器的 reloading 段要用,关掉安装会失败
39
+ blob_upload_max_bytes: 1073741824 # PUT blob 单文件上限(默认 1GiB;与 multipart 的 10MB 是两道闸)
81
40
  cors:
82
- origins: [] # 留空 = 放行任意源;要带凭据则必须列出具体源
41
+ origins: [] # 空 = 放行任意源;要带凭据必须列具体源
83
42
  credentials: false
84
43
  db:
85
44
  default: 'sqlite://db.sqlite' # 相对 config 目录;缺文件自动建空库
86
45
 
87
- # ---- 安装序号(ojm install 每次安装自增一次,作为 reload 热更触发变更——unchanged 判定基于
88
- # config 文档 sha,无落盘变更则热更恒失败。勿删行/勿手工回写旧值。)----
89
46
  vars:
47
+ # ojm install 每次安装自增一次,用来触发 reload 热更。别删行、别手工回写旧值。
90
48
  ojm_install_seq: 0
91
- # canonical_origin(REG-CS6 Host 头 SSRF 修复):相对 URL(/cdn 本地自供、深检路由可达性探针)
92
- # 补全为同源绝对 URL 用的站点权威 origin;**绝不读请求头 Host/x-forwarded-proto**(伪造 Host 会牵引
93
- # 出站到攻击者指定源)。部署侧填真实对外地址(如 https://your-site.example.com);缺省留空 = fail-closed,
94
- # 相对 URL 原样透传由下层 fetch 按站点自身地址解析,深检探针则跳过(info 项),绝不猜一个源。
49
+ # 站点对外地址。装方案时服务端拿它把 /cdn 这类相对地址补全成绝对 URL(深检探针也用它)。
50
+ # 故意不读请求头 Host(防伪造 Host 牵引出站):不配 = fail-closed,不会替你猜。
95
51
  # canonical_origin: "https://your-site.example.com"
96
52
 
97
- # ---- 平台 exec 白名单(ojm install 的解压/迁移两处消费;模板逐字对齐 lifecycle steps 命令构造,
98
- # 不得增删——多一个 token 都会 NotCapable)----
99
53
  platform:
100
- # 平台特权模块名单:方案安装器的 reloading 段调 server.reloadConfig 触发热更。
101
- # 缺这条 → 装在最后一步报 NotCapable: module '_platform' is not platform-privileged。
102
- modules: [_platform]
103
- # 受控根 = 后端模块源码根(`src/<name>/api` 的父目录,即 src)。ojm preview 派生 config
104
- # 时会改写为 dist,保持与 `--api-path` 同源;手写部署请按实际模块根填写。
105
- root: src
106
- # ---- U6 信任库加固(P3-1;P2-2 红线 spike 结论)----
107
- # 威胁:devkit 0.1.75 语义下 fs: 段对全部模块生效(jail = fs.root),且 trusted_paths 只增不减——
108
- # 信任库落在第三方方案模块可写的 fs.root 内即可被其伪造信任锚。加固须两端配合:
109
- # ① 本模板已将信任库默认路径移出 modules/ 安装子树(见 _lifecycle 默认 `.solutions/trusted-keys.yaml`,
110
- # 落在 fs.root 之外的部署根,第三方方案模块 jail 之外);
111
- # ② **部署侧**须按 oj 语义补 `platform.trusted_paths`(指向运行时信任库目录,该目录须先存在——
112
- # oj 对不存在的 trusted_paths 装配 fail-fast,故不可在模板里硬编码尚未创建的目录)显式授权给
113
- # _platform 特权模块写信任库,并收窄第三方方案模块的 fs.root 至其安装子树,方可彻底隔离。
114
- # 本仓负责①(代码路径);②属部署/上游配置,留作部署清单项(P3-1 备案,不硬编码以免 oj serve 装配失败)。
54
+ modules: [_platform] # 安装器热更 config 需要 _platform 特权;删了会在装到最后一步报 NotCapable
55
+ root: src # 后端模块源码根(src/<name>/api 的父目录);preview 派生 config 时改写成 dist
56
+ # 信任库默认落在安装子树之外(.solutions/trusted-keys.yaml),第三方方案模块碰不到。
57
+ # 部署侧还应补 platform.trusted_paths 授权 _platform 写库、并把第三方模块 fs.root 收窄到各自安装子树。
58
+ # 下面三条命令与安装器逐字对齐(含每个空格),一个 token 都不能动:
115
59
  exec:
116
60
  _platform:
117
61
  allow:
@@ -119,18 +63,14 @@ platform:
119
63
  - 'tar -xzf ${path:archive} -C ${path:dest}'
120
64
  - 'oj migrate --format json -d ${path:dir} ${raw:module}'
121
65
 
122
- # ---- 方案安装器的落盘面(fs.* 全局)----
123
- # 安装器要解包到 modules/.staging、写 dist/modules、备份 db.sqlite——全在 config 目录内。
124
- # 缺本段 = fs.* 抛 NotCapable,且失败发生在任务文件落盘之前:表现为「POST 安装返回 200、
125
- # 但 task 查询恒 404」,排查成本极高。生产收紧口径见上面 platform 段的 U6 说明
126
- # (fs.root 收窄到安装子树 + third-party 模块 jail 隔离属部署清单项)。
66
+ # 安装器落盘面:解包到 modules/.staging、写 dist/modules、备份 db.sqlite,全在 config 目录内。
67
+ # 缺这段会 NotCapable,且症状很阴:安装提交返回 200,但 task 查询恒 404。
127
68
  fs:
128
69
  root: .
129
70
  readonly: false
130
71
 
131
- # ---- LDAP 连接段(D1/PRD §4.5 两段式之连接段;oj 插件严格解析本段,未知键启动 fail-fast——只写下列键)----
132
- # 取消注释并填值即接通目录服务;应用映射段在 DB/设置页(ldap.enabled、ldap_base_dn、
133
- # ldap_uid_attr、ldap_email_attr,group=ldap,即时生效)。本段改动属 restart_pending,需重启。
72
+ # LDAP 连接段:解开注释填值即接通目录服务;字段映射在「系统设置 → LDAP」里配,改本段要重启。
73
+ # oj 严格解析这段,只写下面这些键,多一个键启动 fail-fast。
134
74
  # ldap:
135
75
  # url: "ldap://127.0.0.1:389"
136
76
  # bind_dn: ""
@@ -138,14 +78,14 @@ fs:
138
78
  # timeout_ms: 3000
139
79
  # start_tls: false
140
80
  # tls_skip_verify: false
81
+
141
82
  auth:
142
83
  jwt_secret: __JWT_SECRET__ # init 随机生成;生产必须更换
143
84
  access_token_duration: 2h
144
85
  refresh_token_duration: 720h
145
- anonymous_paths: # auth 端点是业务路由,必须显式匿名(漏列则握手 401,见 ojm 开发手册 §8)
86
+ anonymous_paths: # 不带 token 也能访问的路径;漏列则未登录访问一律 401
146
87
  - /auth/login
147
- - /auth/ldap-login # D1/AC1-12:LDAP 目录登录(匿名)
88
+ - /auth/ldap-login # LDAP 目录登录
148
89
  - /auth/refresh
149
90
  - /auth/logout
150
- # 登录页登录方式 Tabs 数据源(C5/AC2-2):未登录即可读
151
- - /system/login-methods
91
+ - /system/login-methods # 登录页「登录方式」Tabs 的数据源,未登录就要能读
@@ -8,9 +8,9 @@
8
8
  "react-router": "7.18.2",
9
9
  "react-router/dom": "7.18.2",
10
10
  "@tanstack/react-query": "5.102.8",
11
- "@oj-module/runtime": "0.2.42",
12
- "@oj-module/runtime/contract": "0.2.42",
13
- "@oj-module/runtime/contract/errors": "0.2.42",
11
+ "@oj-module/runtime": "0.2.43",
12
+ "@oj-module/runtime/contract": "0.2.43",
13
+ "@oj-module/runtime/contract/errors": "0.2.43",
14
14
  "@plane/kit": "0.1.20",
15
15
  "@plane/editor": "0.1.20",
16
16
  "antd": "6.6.1",