iflow-engine-base 3.9.20 → 3.9.21

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/README.md CHANGED
@@ -22,10 +22,128 @@ import { createEngine } from 'iflow-engine-base';
22
22
  ```javascript
23
23
  const engine = createEngine({
24
24
  container: document.getElementById('app'),
25
+ sdkAuth: {
26
+ endpoint: 'https://api.example.com/openapi',
27
+ token: 'sdk_token_xxx'
28
+ },
25
29
  // ... other options
26
30
  });
27
31
  ```
28
32
 
33
+ `sdkAuth` 现在是必填项。不传时,SDK 会直接拒绝初始化。
34
+
35
+ 推荐生产环境由你自己的后端先使用 `AppId/AppKey/AppSecret` 调用 `POST /openapi/oauth/sdk-token`,再把短期 `sdkToken` 下发给浏览器。开发联调时也可以直接给 `sdkAuth.appId/appKey/appSecret`,但不建议在公网前端长期暴露 `AppSecret`。
36
+
37
+ ### `sdkAuth.appId/appKey/appSecret` 认证方式说明
38
+
39
+ 当你这样传参时:
40
+
41
+ ```ts
42
+ const engine = await createEngine({
43
+ containerId: 'viewer',
44
+ sdkAuth: {
45
+ endpoint: 'https://api.example.com/openapi',
46
+ appId: 'app_xxx',
47
+ appKey: 'key_xxx',
48
+ appSecret: 'sk_xxx'
49
+ }
50
+ });
51
+ ```
52
+
53
+ SDK 内部会自动执行两步:
54
+
55
+ 1. 调用 `POST /openapi/oauth/sdk-token/secure`
56
+ SDK 先使用 `appSecret` 计算密钥材料,再结合当前 UTC 年月日、时间戳、nonce 派生当日密钥,对 `appKey/appSecret` 做 AES-GCM 加密后发给服务端
57
+ 2. 调用 `POST /openapi/oauth/sdk-verify`
58
+ 使用上一步返回的 `sdkToken` 做二次校验,校验通过后才继续初始化引擎
59
+
60
+ 服务端收到后,会:
61
+
62
+ 1. 按明文 `appId` 找到应用
63
+ 2. 读取平台保存的安全密钥材料
64
+ 3. 按同一天的日期重新派生密钥并解密
65
+ 4. 解密出 `appKey/appSecret` 后,再走原有的应用凭证校验
66
+ 5. 校验通过才签发短期 `sdkToken`
67
+
68
+ 也就是说,`sdkAuth.appId/appKey/appSecret` 本质上仍然是“SDK 代替你的服务端去换票”,但传输时不再直接明文发送 `appKey/appSecret`。
69
+
70
+ 补充说明:
71
+
72
+ - `appId` 仍然会明文发送,因为服务端必须先知道是哪个应用,才能取出对应的安全材料解密
73
+ - 当前年月日已经参与密钥派生
74
+ - 同时还加入了时间戳和 `nonce`,避免“只有日期、整天可重放”的问题
75
+ - 老应用如果是在这套安全方案上线前创建的,需要先在控制台重置一次 `AppSecret`,让平台补齐安全密钥材料后才能使用该模式
76
+
77
+ 适用场景:
78
+
79
+ - 本地开发
80
+ - 内网环境联调
81
+ - 受控测试环境
82
+
83
+ 不建议用于公网生产前端,原因是:
84
+
85
+ - `AppSecret` 会下发到浏览器
86
+ - 用户可以在 DevTools、抓包工具或源码中拿到密钥
87
+ - 一旦泄露,第三方可以伪造你的应用去申请新的 `sdkToken`
88
+
89
+ 生产环境推荐方式:
90
+
91
+ 1. 你的业务后端安全保存 `AppId/AppKey/AppSecret`
92
+ 2. 业务后端调用 `POST /openapi/oauth/sdk-token`
93
+ 3. 业务后端把短期 `sdkToken` 返回给前端
94
+ 4. 前端 SDK 只传 `sdkAuth.token`
95
+
96
+ 这样浏览器侧永远不会接触 `AppSecret`,安全边界才是正确的。
97
+
98
+ ### 权限与校验结果
99
+
100
+ - 运营后台现在只给团队成员配置引擎权限,如 `3d`、`2d`、`720`、`gis`、`gaussian`
101
+ - OpenAPI 在签发 `sdkToken`,以及后续执行 `sdk-verify` 时,都会重新计算当前有效权限:
102
+ - `effectiveScopes = 应用 scopes`
103
+ - `effectiveEnginePermissions = 成员引擎权限`
104
+ - 这意味着:
105
+ - 去掉成员的某个引擎权限后,即使 `appKey/appSecret` 正确,也不能再初始化对应引擎
106
+ - `POST /openapi/oauth/sdk-verify` 的返回会带上 `enginePermissions`,可作为当前 SDK 会话最终生效引擎权限的结果。
107
+
108
+ ### 使用 viewToken 加载
109
+
110
+ 除了原有的 URL 加载方式,现在还支持通过 `viewToken` 让 SDK 自行去 OpenAPI 解析真实加载地址:
111
+
112
+ ```ts
113
+ const engine = await createEngine({
114
+ containerId: 'viewer',
115
+ sdkAuth: {
116
+ endpoint: 'https://api.example.com/openapi',
117
+ token: 'sdk_token_xxx'
118
+ }
119
+ });
120
+
121
+ await engine.loadModelByViewToken('vt_xxx');
122
+ ```
123
+
124
+ 目前支持的方法:
125
+
126
+ - `createEngine(...).loadModelByViewToken(viewToken, options?)`
127
+ - `createEngine2d(...).loadModelByViewToken(viewToken, options?)`
128
+ - `createEngineGIS(...).loadModelByViewToken(viewToken, options?)`
129
+ - `createEngineGaussian(...).loadModelByViewToken(viewToken, options?)`
130
+ - `createEngine720(...).loadPanoramaByViewToken(viewToken)`
131
+
132
+ SDK 会自动请求 `POST /openapi/models/view-token/resolve`,并带上:
133
+
134
+ - `sdkToken`
135
+ - `viewToken`
136
+ - `engineType`
137
+
138
+ 服务端会同时校验:
139
+
140
+ - `sdkToken` 是否有效
141
+ - 当前应用是否仍具备 `sdk:use`
142
+ - 当前成员是否仍具备对应引擎权限
143
+ - `viewToken` 所属模型是否与当前 `sdkToken` 对应的是同一个应用
144
+
145
+ 最后这一条是强制限制:A 应用的 `sdkToken` 不能去解析 B 应用下的 `viewToken`。
146
+
29
147
  ## Important Notes
30
148
 
31
149
  - CSS样式已自动包含,无需手动导入
@@ -1,5 +1,5 @@
1
- import { s as t } from "./chunks/sdk-assets-F7qFhuao.js";
2
- import { g as f, r as u } from "./chunks/sdk-assets-F7qFhuao.js";
1
+ import { s as t } from "./chunks/sdk-assets-1SXx8Tbf.js";
2
+ import { g as f, r as u } from "./chunks/sdk-assets-1SXx8Tbf.js";
3
3
  const i = {
4
4
  Initialized: "gis:initialized",
5
5
  CameraChanged: "gis:camera-changed",
@@ -16,27 +16,27 @@ function r(e) {
16
16
  }
17
17
  async function d(e) {
18
18
  r(e);
19
- const { EngineKernelV2: n } = await import("./chunks/engine-3d-CeUaONqH.js").then((a) => a.E);
19
+ const { EngineKernelV2: n } = await import("./chunks/engine-3d-CdsLfd0i.js").then((a) => a.E);
20
20
  return new n(e);
21
21
  }
22
22
  async function o(e) {
23
23
  r(e);
24
- const { EngineKernel2d: n } = await import("./chunks/engine-2d-mQsokiAx.js").then((a) => a.a);
24
+ const { EngineKernel2d: n } = await import("./chunks/engine-2d-DAiE1uAy.js").then((a) => a.a);
25
25
  return new n(e);
26
26
  }
27
27
  async function c(e) {
28
28
  r(e);
29
- const { EngineKernel720: n } = await import("./chunks/engine-720-BrYzIaAW.js");
29
+ const { EngineKernel720: n } = await import("./chunks/engine-720-K9uF9wMA.js");
30
30
  return new n(e);
31
31
  }
32
32
  async function g(e) {
33
33
  r(e);
34
- const { EngineKernelGaussian: n } = await import("./chunks/engine-gaussian-BDphGvln.js");
34
+ const { EngineKernelGaussian: n } = await import("./chunks/engine-gaussian-x3jsCp15.js");
35
35
  return new n(e);
36
36
  }
37
37
  async function l(e) {
38
38
  r(e);
39
- const { EngineKernelGIS: n } = await import("./chunks/engine-gis-nj5ISJb2.js");
39
+ const { EngineKernelGIS: n } = await import("./chunks/engine-gis-BStgPU0Q.js");
40
40
  return new n(e);
41
41
  }
42
42
  export {