@aalis/plugin-webui-server 0.1.0

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 (50) hide show
  1. package/README.md +21 -0
  2. package/dist/auth.d.ts +43 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +279 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/gate.d.ts +39 -0
  7. package/dist/gate.d.ts.map +1 -0
  8. package/dist/gate.js +43 -0
  9. package/dist/gate.js.map +1 -0
  10. package/dist/i18n.d.ts +3 -0
  11. package/dist/i18n.d.ts.map +1 -0
  12. package/dist/i18n.js +37 -0
  13. package/dist/i18n.js.map +1 -0
  14. package/dist/index.d.ts +22 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +1341 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/protocol.d.ts +113 -0
  19. package/dist/protocol.d.ts.map +1 -0
  20. package/dist/protocol.js +59 -0
  21. package/dist/protocol.js.map +1 -0
  22. package/dist/routes/files.d.ts +13 -0
  23. package/dist/routes/files.d.ts.map +1 -0
  24. package/dist/routes/files.js +127 -0
  25. package/dist/routes/files.js.map +1 -0
  26. package/dist/routes/marketplace.d.ts +56 -0
  27. package/dist/routes/marketplace.d.ts.map +1 -0
  28. package/dist/routes/marketplace.js +101 -0
  29. package/dist/routes/marketplace.js.map +1 -0
  30. package/dist/routes/plugins.d.ts +11 -0
  31. package/dist/routes/plugins.d.ts.map +1 -0
  32. package/dist/routes/plugins.js +388 -0
  33. package/dist/routes/plugins.js.map +1 -0
  34. package/dist/routes/proxy.d.ts +12 -0
  35. package/dist/routes/proxy.d.ts.map +1 -0
  36. package/dist/routes/proxy.js +114 -0
  37. package/dist/routes/proxy.js.map +1 -0
  38. package/dist/routes/services.d.ts +14 -0
  39. package/dist/routes/services.d.ts.map +1 -0
  40. package/dist/routes/services.js +237 -0
  41. package/dist/routes/services.js.map +1 -0
  42. package/dist/routes/system.d.ts +7 -0
  43. package/dist/routes/system.d.ts.map +1 -0
  44. package/dist/routes/system.js +95 -0
  45. package/dist/routes/system.js.map +1 -0
  46. package/dist/routes/uploaded-files.d.ts +10 -0
  47. package/dist/routes/uploaded-files.d.ts.map +1 -0
  48. package/dist/routes/uploaded-files.js +162 -0
  49. package/dist/routes/uploaded-files.js.map +1 -0
  50. package/package.json +63 -0
@@ -0,0 +1,11 @@
1
+ import type { AppService, Context, PluginManagerService } from '@aalis/core';
2
+ import type { UserIdentity } from '@aalis/plugin-authority-api';
3
+ import type express from 'express';
4
+ import type { RouteGate } from '../gate.js';
5
+ /** 注册插件管理 + 全局配置相关 REST 路由 */
6
+ export declare function registerPluginRoutes(expressApp: express.Express, ctx: Context, getApp: () => AppService | undefined, getPluginMgr: () => PluginManagerService | undefined, identify: (req: {
7
+ headers: {
8
+ cookie?: string;
9
+ };
10
+ }) => UserIdentity | undefined, gate: RouteGate): void;
11
+ //# sourceMappingURL=plugins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../../src/routes/plugins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAE7E,OAAO,KAAK,EAAoB,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAKlF,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,8BAA8B;AAC9B,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,OAAO,CAAC,OAAO,EAC3B,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,UAAU,GAAG,SAAS,EACpC,YAAY,EAAE,MAAM,oBAAoB,GAAG,SAAS,EACpD,QAAQ,EAAE,CAAC,GAAG,EAAE;IAAE,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,KAAK,YAAY,GAAG,SAAS,EAC7E,IAAI,EAAE,SAAS,GACd,IAAI,CA4YN"}
@@ -0,0 +1,388 @@
1
+ import { CORE_CONFIG_SCHEMA } from '@aalis/core';
2
+ /** 注册插件管理 + 全局配置相关 REST 路由 */
3
+ export function registerPluginRoutes(expressApp, ctx, getApp, getPluginMgr, identify, gate) {
4
+ // 获取插件列表及状态
5
+ expressApp.get('/api/plugins', gate('webui:plugins:read', 4), (_req, res) => {
6
+ const app = getApp();
7
+ const pm = getPluginMgr();
8
+ if (!app || !pm) {
9
+ res.json({ plugins: [] });
10
+ return;
11
+ }
12
+ // 反向索引:pluginName -> tools / commands(名字列表 + 聚合敏感能力)。
13
+ // 方便 UI 搜索框命中工具名/指令名时定位到注册插件;能力披露用聚合 capability。
14
+ const toolsByPlugin = new Map();
15
+ const capsByPlugin = new Map();
16
+ const addCaps = (plugin, perms, safety) => {
17
+ if (!perms?.length && safety !== 'dangerous')
18
+ return;
19
+ const set = capsByPlugin.get(plugin) ?? new Set();
20
+ for (const p of perms ?? [])
21
+ set.add(p);
22
+ if (safety === 'dangerous')
23
+ set.add('safety:dangerous');
24
+ capsByPlugin.set(plugin, set);
25
+ };
26
+ const tools = ctx.getService('tools')?.getAll() ?? [];
27
+ for (const t of tools) {
28
+ const list = toolsByPlugin.get(t.pluginName) ?? [];
29
+ list.push(t.name);
30
+ toolsByPlugin.set(t.pluginName, list);
31
+ addCaps(t.pluginName, t.permissions, t.safety);
32
+ }
33
+ const commandsByPlugin = new Map();
34
+ const cmds = ctx.getService('commands')?.getAll() ?? [];
35
+ for (const c of cmds) {
36
+ const owner = c.pluginName ?? 'unknown';
37
+ const list = commandsByPlugin.get(owner) ?? [];
38
+ list.push(c.name);
39
+ commandsByPlugin.set(owner, list);
40
+ addCaps(owner, c.permissions, c.safety);
41
+ }
42
+ const plugins = pm.getStatus().map(p => ({
43
+ name: p.name,
44
+ instanceId: p.instanceId,
45
+ displayName: p.displayName,
46
+ state: p.state,
47
+ provides: p.provides ?? [],
48
+ // 能力披露:该插件「要调用哪些子系统」(inject 依赖)+「触达哪些敏感能力」
49
+ // (工具/指令声明的 permissions + dangerous 标记),供安装后知情查看。
50
+ requiredServices: p.requiredServices ?? [],
51
+ optionalServices: p.optionalServices ?? [],
52
+ capabilities: [...(capsByPlugin.get(p.name) ?? [])],
53
+ tools: toolsByPlugin.get(p.name) ?? [],
54
+ commands: commandsByPlugin.get(p.name) ?? [],
55
+ core: p.core ?? false,
56
+ reusable: p.reusable ?? false,
57
+ config: p.config,
58
+ configSchema: p.configSchema,
59
+ defaultConfig: p.defaultConfig,
60
+ error: p.error,
61
+ }));
62
+ res.json({ plugins });
63
+ });
64
+ // 获取可用的 WebUI 页面(由活跃插件通过 useWebuiService.registerPage 注册)
65
+ expressApp.get('/api/pages', gate('webui:pages:read', 1), (_req, res) => {
66
+ const app = getApp();
67
+ const pm = getPluginMgr();
68
+ if (!app || !pm) {
69
+ res.json([]);
70
+ return;
71
+ }
72
+ const webuiSvc = ctx.getService('webui-server');
73
+ if (!webuiSvc) {
74
+ res.json([]);
75
+ return;
76
+ }
77
+ const displayNameByPlugin = new Map();
78
+ for (const p of pm.getStatus())
79
+ displayNameByPlugin.set(p.name, p.displayName);
80
+ const pages = [];
81
+ for (const page of webuiSvc.getPages()) {
82
+ pages.push({ ...page, plugin: page.pluginName, pluginDisplayName: displayNameByPlugin.get(page.pluginName) });
83
+ }
84
+ pages.sort((a, b) => (a.order ?? 99) - (b.order ?? 99));
85
+ res.json(pages);
86
+ });
87
+ // 通用声明式页面操作:调用插件的 actions
88
+ expressApp.post('/api/page-action/:plugin/:method', async (req, res) => {
89
+ const { plugin: pluginName, method } = req.params;
90
+ const args = req.body ?? {};
91
+ const app = getApp();
92
+ const pm = getPluginMgr();
93
+ if (!app || !pm) {
94
+ res.status(500).json({ error: 'App 不可用' });
95
+ return;
96
+ }
97
+ const entry = pm.getPlugin(pluginName);
98
+ if (!entry || entry.state !== 'active') {
99
+ res.status(404).json({ error: `插件 ${pluginName} 不存在或未激活` });
100
+ return;
101
+ }
102
+ const handler = entry.module.actions?.[method];
103
+ if (typeof handler !== 'function') {
104
+ res.status(404).json({ error: `处理器 ${method} 不存在` });
105
+ return;
106
+ }
107
+ if (!entry.context) {
108
+ res.status(500).json({ error: `插件 ${pluginName} 上下文不可用` });
109
+ return;
110
+ }
111
+ // ===== 权限闸门 + 调用者身份 =====
112
+ // 调用者从会话解析:账户登录 → webui:<username>,单 token 模式 → webui:console
113
+ // (owner 级)。未声明 actionsMeta 的 action 默认要求 owner 等级(默认拒绝),
114
+ // 插件需显式声明才能降低门槛。capability `action:<plugin>:<method>` 走
115
+ // authorize 统一闸(支持 per-user grant/deny)。authority 服务缺席时放行
116
+ // (与 tools/commands 守卫缺席时一致)。
117
+ const caller = identify(req) ?? { platform: 'webui', userId: 'console' };
118
+ const authority = ctx.getService('authority');
119
+ const required = entry.module.actionsMeta?.[method]?.authority ?? ctx.config.get('ownerAuthority') ?? 5;
120
+ if (authority) {
121
+ const denied = authority.authorize(caller, {
122
+ capabilities: [`action:${pluginName}:${method}`],
123
+ declaredAuthority: required,
124
+ });
125
+ if (denied) {
126
+ res.status(403).json({ error: `操作 ${pluginName}/${method} 被拒: ${denied}` });
127
+ return;
128
+ }
129
+ }
130
+ else if (required > 1) {
131
+ // fail-closed:authority 缺席(未配置/bounce 窗口期)无法裁决等级。
132
+ // 未声明 actionsMeta 的 action 默认要求 owner,缺席时一律拒绝;只放行
133
+ // 显式声明了公共级(<=1)的 action。避免敏感 action 在权限服务缺位时裸奔。
134
+ res.status(503).json({ error: `权限服务不可用,操作 ${pluginName}/${method} 暂时被拒绝` });
135
+ return;
136
+ }
137
+ try {
138
+ const result = await handler(entry.context, args, caller);
139
+ res.json({ ok: true, data: result });
140
+ }
141
+ catch (err) {
142
+ const msg = err instanceof Error ? err.message : String(err);
143
+ res.status(500).json({ error: msg });
144
+ }
145
+ });
146
+ // 获取当前全局配置
147
+ expressApp.get('/api/config', gate('webui:config:read', 4), (_req, res) => {
148
+ const allConfig = ctx.config.getAll();
149
+ res.json({ ...allConfig, _schema: CORE_CONFIG_SCHEMA });
150
+ });
151
+ // 更新全局配置字段
152
+ expressApp.put('/api/config', gate('webui:config:write', 'owner'), (req, res) => {
153
+ const updates = req.body;
154
+ if (!updates || typeof updates !== 'object') {
155
+ res.status(400).json({ error: '请求体必须是对象' });
156
+ return;
157
+ }
158
+ const app = getApp();
159
+ const pm = getPluginMgr();
160
+ if (!app || !pm) {
161
+ res.status(500).json({ error: 'App 不可用' });
162
+ return;
163
+ }
164
+ // 只允许更新安全的顶级字段
165
+ const allowed = ['name', 'logLevel'];
166
+ for (const key of allowed) {
167
+ if (key in updates) {
168
+ ctx.config.set(key, updates[key]);
169
+ }
170
+ }
171
+ // 检查是否有需要重启才能生效的字段
172
+ const restartNeeded = ['name', 'persona', 'logLevel'].some(k => k in updates);
173
+ try {
174
+ app.saveConfig();
175
+ if (restartNeeded) {
176
+ res.json({ ok: true, message: '全局配置已更新,正在重启应用以生效…', restart: true });
177
+ app.restart();
178
+ }
179
+ else {
180
+ res.json({ ok: true, message: '全局配置已更新并保存' });
181
+ }
182
+ }
183
+ catch (err) {
184
+ const msg = err instanceof Error ? err.message : String(err);
185
+ res.status(500).json({ error: msg });
186
+ }
187
+ });
188
+ // 获取单个插件的原始配置(未脱敏,给编辑器用)
189
+ expressApp.get('/api/plugins/:name/config', gate('webui:config:read', 4), (req, res) => {
190
+ const pluginName = req.params.name;
191
+ const pluginConfig = ctx.config.getPluginConfig(pluginName);
192
+ res.json({ name: pluginName, config: pluginConfig });
193
+ });
194
+ // 更新插件配置
195
+ expressApp.put('/api/plugins/:name/config', gate('webui:config:write', 'owner'), async (req, res) => {
196
+ const pluginName = req.params.name;
197
+ const newConfig = req.body?.config;
198
+ if (!newConfig || typeof newConfig !== 'object') {
199
+ res.status(400).json({ error: 'config 字段必须是对象' });
200
+ return;
201
+ }
202
+ const app = getApp();
203
+ const pm = getPluginMgr();
204
+ if (!app || !pm) {
205
+ res.status(500).json({ error: 'App 不可用' });
206
+ return;
207
+ }
208
+ const success = await pm.updatePluginConfig(pluginName, newConfig);
209
+ if (success) {
210
+ app.saveConfig();
211
+ res.json({ ok: true, message: `插件 ${pluginName} 配置已更新` });
212
+ }
213
+ else {
214
+ res.status(404).json({ error: `插件 ${pluginName} 不存在` });
215
+ }
216
+ });
217
+ // 启用插件
218
+ expressApp.post('/api/plugins/:name/enable', gate('webui:plugins:manage', 'owner'), async (req, res) => {
219
+ const pluginName = req.params.name;
220
+ const app = getApp();
221
+ const pm = getPluginMgr();
222
+ if (!app || !pm) {
223
+ res.status(500).json({ error: 'App 不可用' });
224
+ return;
225
+ }
226
+ const success = await pm.enablePlugin(pluginName);
227
+ if (success) {
228
+ app.saveConfig();
229
+ res.json({ ok: true, message: `插件 ${pluginName} 已启用` });
230
+ }
231
+ else {
232
+ res.status(404).json({ error: `插件 ${pluginName} 不存在` });
233
+ }
234
+ });
235
+ // 禁用插件
236
+ expressApp.post('/api/plugins/:name/disable', gate('webui:plugins:manage', 'owner'), async (req, res) => {
237
+ const pluginName = req.params.name;
238
+ const app = getApp();
239
+ const pm = getPluginMgr();
240
+ if (!app || !pm) {
241
+ res.status(500).json({ error: 'App 不可用' });
242
+ return;
243
+ }
244
+ const success = await pm.disablePlugin(pluginName);
245
+ if (success) {
246
+ app.saveConfig();
247
+ res.json({ ok: true, message: `插件 ${pluginName} 已禁用` });
248
+ }
249
+ else {
250
+ res.status(400).json({ error: `核心插件不能被禁用` });
251
+ }
252
+ });
253
+ // 重新扫描 packages/ 并加载新插件
254
+ expressApp.post('/api/plugins/scan', gate('webui:plugins:manage', 'owner'), async (_req, res) => {
255
+ const app = getApp();
256
+ const pm = getPluginMgr();
257
+ if (!app || !pm) {
258
+ res.status(500).json({ error: 'App 不可用' });
259
+ return;
260
+ }
261
+ try {
262
+ const loaded = await app.rescanPlugins();
263
+ res.json({ ok: true, loaded, message: loaded.length > 0 ? `新加载 ${loaded.length} 个插件` : '无新插件' });
264
+ }
265
+ catch (err) {
266
+ const msg = err instanceof Error ? err.message : String(err);
267
+ res.status(500).json({ error: msg });
268
+ }
269
+ });
270
+ // 安装插件(从 npm 下载到 packages/ 并加载)
271
+ expressApp.post('/api/plugins/install', gate('webui:plugins:manage', 'owner'), async (req, res) => {
272
+ const app = getApp();
273
+ const pm = getPluginMgr();
274
+ if (!app || !pm) {
275
+ res.status(500).json({ error: 'App 不可用' });
276
+ return;
277
+ }
278
+ const npmPkg = req.body?.name;
279
+ if (!npmPkg || typeof npmPkg !== 'string') {
280
+ res.status(400).json({ error: 'name 字段必须是 npm 包名字符串' });
281
+ return;
282
+ }
283
+ if (!/^(@[a-z0-9\-_.]+\/)?[a-z0-9\-_.]+$/i.test(npmPkg)) {
284
+ res.status(400).json({ error: '非法包名' });
285
+ return;
286
+ }
287
+ try {
288
+ const pkgMgr = ctx.getService('package-manager');
289
+ if (!pkgMgr) {
290
+ res.status(503).json({ error: 'package-manager 服务未启用,无法安装插件' });
291
+ return;
292
+ }
293
+ const result = await pkgMgr.install(npmPkg);
294
+ res.json(result);
295
+ }
296
+ catch (err) {
297
+ const msg = err instanceof Error ? err.message : String(err);
298
+ res.status(500).json({ error: msg });
299
+ }
300
+ });
301
+ // 卸载插件
302
+ expressApp.post('/api/plugins/:name/uninstall', gate('webui:plugins:manage', 'owner'), async (req, res) => {
303
+ const app = getApp();
304
+ const pm = getPluginMgr();
305
+ if (!app || !pm) {
306
+ res.status(500).json({ error: 'App 不可用' });
307
+ return;
308
+ }
309
+ const pluginName = req.params.name;
310
+ try {
311
+ const pkgMgr = ctx.getService('package-manager');
312
+ if (!pkgMgr) {
313
+ res.status(503).json({ error: 'package-manager 服务未启用,无法卸载插件' });
314
+ return;
315
+ }
316
+ const result = await pkgMgr.uninstall(pluginName);
317
+ res.json(result);
318
+ }
319
+ catch (err) {
320
+ const msg = err instanceof Error ? err.message : String(err);
321
+ res.status(500).json({ error: msg });
322
+ }
323
+ });
324
+ // 创建插件多实例
325
+ expressApp.post('/api/plugins/:name/instances', gate('webui:plugins:manage', 'owner'), async (req, res) => {
326
+ const moduleName = req.params.name;
327
+ const suffix = req.body?.suffix;
328
+ const config = req.body?.config ?? {};
329
+ if (!suffix || typeof suffix !== 'string') {
330
+ res.status(400).json({ error: 'suffix 必须是非空字符串' });
331
+ return;
332
+ }
333
+ if (!/^[\w-]+$/.test(suffix)) {
334
+ res.status(400).json({ error: 'suffix 只能包含字母、数字、下划线和连字符' });
335
+ return;
336
+ }
337
+ const app = getApp();
338
+ const pm = getPluginMgr();
339
+ if (!app || !pm) {
340
+ res.status(500).json({ error: 'App 不可用' });
341
+ return;
342
+ }
343
+ const instanceId = await pm.createInstance(moduleName, suffix, config);
344
+ if (instanceId) {
345
+ app.saveConfig();
346
+ res.json({ ok: true, instanceId, message: `已创建实例 ${instanceId}` });
347
+ }
348
+ else {
349
+ res.status(400).json({ error: `无法创建实例(模块不存在、未声明 reusable 或实例已存在)` });
350
+ }
351
+ });
352
+ // 删除插件多实例
353
+ expressApp.delete('/api/plugins/:instanceId/instance', gate('webui:plugins:manage', 'owner'), async (req, res) => {
354
+ const instanceId = req.params.instanceId;
355
+ const app = getApp();
356
+ const pm = getPluginMgr();
357
+ if (!app || !pm) {
358
+ res.status(500).json({ error: 'App 不可用' });
359
+ return;
360
+ }
361
+ const ok = await pm.removeInstance(instanceId);
362
+ if (ok) {
363
+ app.saveConfig();
364
+ res.json({ ok: true, message: `已删除实例 ${instanceId}` });
365
+ }
366
+ else {
367
+ res.status(400).json({ error: `无法删除(实例不存在或不允许删除主实例)` });
368
+ }
369
+ });
370
+ // 保存配置到磁盘
371
+ expressApp.post('/api/config/save', gate('webui:config:write', 'owner'), (_req, res) => {
372
+ const app = getApp();
373
+ const pm = getPluginMgr();
374
+ if (!app || !pm) {
375
+ res.status(500).json({ error: 'App 不可用' });
376
+ return;
377
+ }
378
+ try {
379
+ app.saveConfig();
380
+ res.json({ ok: true, message: '配置已保存到磁盘' });
381
+ }
382
+ catch (err) {
383
+ const msg = err instanceof Error ? err.message : String(err);
384
+ res.status(500).json({ error: msg });
385
+ }
386
+ });
387
+ }
388
+ //# sourceMappingURL=plugins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.js","sourceRoot":"","sources":["../../src/routes/plugins.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Context } from '@aalis/core';
2
+ import type express from 'express';
3
+ import type { RouteGate } from '../gate.js';
4
+ /**
5
+ * 图片代理:浏览器直连第三方图片常因 hotlink 防护 / referer / CORS / mixed content 失败;
6
+ * 由服务端代为 fetch 后回吐字节流,前端始终从 `/api/proxy/image?url=...` 读取。
7
+ *
8
+ * 仅做透明代理:不缓存到磁盘,不重写 EXIF,纯流式转发。auth 中间件已在外层挂载,
9
+ * 未登录请求拿不到 cookie 自然过不来;这里只关心 SSRF 与体积/超时。
10
+ */
11
+ export declare function registerProxyRoutes(expressApp: express.Express, ctx: Context, gate: RouteGate): void;
12
+ //# sourceMappingURL=proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/routes/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAO5C;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CAmGpG"}
@@ -0,0 +1,114 @@
1
+ import { assertSafeHost } from '@aalis/util-network-guard';
2
+ /** 单张图最大体积(避免代理被滥用成大文件中转)。 */
3
+ const MAX_IMAGE_BYTES = 20 * 1024 * 1024;
4
+ /** 上游连接 + 完整下载总超时。 */
5
+ const FETCH_TIMEOUT_MS = 15_000;
6
+ /**
7
+ * 图片代理:浏览器直连第三方图片常因 hotlink 防护 / referer / CORS / mixed content 失败;
8
+ * 由服务端代为 fetch 后回吐字节流,前端始终从 `/api/proxy/image?url=...` 读取。
9
+ *
10
+ * 仅做透明代理:不缓存到磁盘,不重写 EXIF,纯流式转发。auth 中间件已在外层挂载,
11
+ * 未登录请求拿不到 cookie 自然过不来;这里只关心 SSRF 与体积/超时。
12
+ */
13
+ export function registerProxyRoutes(expressApp, ctx, gate) {
14
+ expressApp.get('/api/proxy/image', gate('webui:proxy:read', 1), async (req, res) => {
15
+ const raw = req.query.url;
16
+ if (typeof raw !== 'string' || !raw) {
17
+ res.status(400).json({ error: '缺少 url 参数' });
18
+ return;
19
+ }
20
+ let parsed;
21
+ try {
22
+ parsed = new URL(raw);
23
+ }
24
+ catch {
25
+ res.status(400).json({ error: '非法 URL' });
26
+ return;
27
+ }
28
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
29
+ res.status(400).json({ error: '仅支持 http/https' });
30
+ return;
31
+ }
32
+ try {
33
+ await assertSafeHost(parsed.hostname);
34
+ }
35
+ catch (err) {
36
+ res.status(403).json({ error: err instanceof Error ? err.message : 'SSRF 防护拒绝' });
37
+ return;
38
+ }
39
+ const ac = new AbortController();
40
+ const timer = setTimeout(() => ac.abort(), FETCH_TIMEOUT_MS);
41
+ try {
42
+ const upstream = await fetch(parsed.toString(), {
43
+ signal: ac.signal,
44
+ // 不带 cookie / 不发用户 referer,避免泄露认证态
45
+ redirect: 'follow',
46
+ headers: {
47
+ // 部分站点会按 UA 鉴别,给个常见 desktop UA
48
+ 'user-agent': 'Mozilla/5.0 (Aalis WebUI Image Proxy)',
49
+ accept: 'image/*,*/*;q=0.8',
50
+ },
51
+ });
52
+ if (!upstream.ok) {
53
+ res.status(upstream.status).json({ error: `上游返回 ${upstream.status}` });
54
+ return;
55
+ }
56
+ const ctype = upstream.headers.get('content-type') ?? 'application/octet-stream';
57
+ if (!ctype.startsWith('image/')) {
58
+ res.status(415).json({ error: `非图片 Content-Type: ${ctype}` });
59
+ return;
60
+ }
61
+ const lenHeader = upstream.headers.get('content-length');
62
+ if (lenHeader) {
63
+ const len = Number(lenHeader);
64
+ if (Number.isFinite(len) && len > MAX_IMAGE_BYTES) {
65
+ res.status(413).json({ error: `图片过大 (${len} > ${MAX_IMAGE_BYTES})` });
66
+ return;
67
+ }
68
+ }
69
+ res.setHeader('content-type', ctype);
70
+ res.setHeader('cache-control', 'private, max-age=86400');
71
+ // 防止浏览器把响应当 HTML 渲染
72
+ res.setHeader('x-content-type-options', 'nosniff');
73
+ if (!upstream.body) {
74
+ res.status(502).json({ error: '上游无响应体' });
75
+ return;
76
+ }
77
+ // 手动累计字节,超限即中断
78
+ const reader = upstream.body.getReader();
79
+ let received = 0;
80
+ while (true) {
81
+ const { value, done } = await reader.read();
82
+ if (done)
83
+ break;
84
+ if (value) {
85
+ received += value.byteLength;
86
+ if (received > MAX_IMAGE_BYTES) {
87
+ ac.abort();
88
+ if (!res.headersSent)
89
+ res.status(413);
90
+ res.end();
91
+ return;
92
+ }
93
+ if (!res.write(Buffer.from(value))) {
94
+ // 背压:等 drain
95
+ await new Promise(resolve => res.once('drain', () => resolve()));
96
+ }
97
+ }
98
+ }
99
+ res.end();
100
+ }
101
+ catch (err) {
102
+ const msg = err instanceof Error ? err.message : String(err);
103
+ ctx.logger.warn(`图片代理失败 url=${raw} err=${msg}`);
104
+ if (!res.headersSent)
105
+ res.status(502).json({ error: `代理失败: ${msg}` });
106
+ else
107
+ res.end();
108
+ }
109
+ finally {
110
+ clearTimeout(timer);
111
+ }
112
+ });
113
+ }
114
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/routes/proxy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAI3D,8BAA8B;AAC9B,MAAM,eAAe,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AACzC,sBAAsB;AACtB,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEhC;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAA2B,EAAE,GAAY,EAAE,IAAe;IAC5F,UAAU,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACjF,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC;QAC1B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC;YACpC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;YAC7C,OAAO;QACT,CAAC;QAED,IAAI,MAAW,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC1C,OAAO;QACT,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAChE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YAClF,OAAO;QACT,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,eAAe,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,gBAAgB,CAAC,CAAC;QAE7D,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE;gBAC9C,MAAM,EAAE,EAAE,CAAC,MAAM;gBACjB,mCAAmC;gBACnC,QAAQ,EAAE,QAAQ;gBAClB,OAAO,EAAE;oBACP,+BAA+B;oBAC/B,YAAY,EAAE,uCAAuC;oBACrD,MAAM,EAAE,mBAAmB;iBAC5B;aACF,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBACvE,OAAO;YACT,CAAC;YACD,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,0BAA0B,CAAC;YACjF,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAChC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,KAAK,EAAE,EAAE,CAAC,CAAC;gBAC9D,OAAO;YACT,CAAC;YACD,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;YACzD,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC9B,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,eAAe,EAAE,CAAC;oBAClD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,eAAe,GAAG,EAAE,CAAC,CAAC;oBACtE,OAAO;gBACT,CAAC;YACH,CAAC;YAED,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;YACrC,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,wBAAwB,CAAC,CAAC;YACzD,oBAAoB;YACpB,GAAG,CAAC,SAAS,CAAC,wBAAwB,EAAE,SAAS,CAAC,CAAC;YAEnD,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACnB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAC1C,OAAO;YACT,CAAC;YAED,eAAe;YACf,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACzC,IAAI,QAAQ,GAAG,CAAC,CAAC;YACjB,OAAO,IAAI,EAAE,CAAC;gBACZ,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC5C,IAAI,IAAI;oBAAE,MAAM;gBAChB,IAAI,KAAK,EAAE,CAAC;oBACV,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC;oBAC7B,IAAI,QAAQ,GAAG,eAAe,EAAE,CAAC;wBAC/B,EAAE,CAAC,KAAK,EAAE,CAAC;wBACX,IAAI,CAAC,GAAG,CAAC,WAAW;4BAAE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;wBACtC,GAAG,CAAC,GAAG,EAAE,CAAC;wBACV,OAAO;oBACT,CAAC;oBACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;wBACnC,aAAa;wBACb,MAAM,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;oBACzE,CAAC;gBACH,CAAC;YACH,CAAC;YACD,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,GAAG,QAAQ,GAAG,EAAE,CAAC,CAAC;YAChD,IAAI,CAAC,GAAG,CAAC,WAAW;gBAAE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,GAAG,EAAE,EAAE,CAAC,CAAC;;gBACjE,GAAG,CAAC,GAAG,EAAE,CAAC;QACjB,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 服务 / 平台 / 工具分组 / LLM 模型路由
3
+ */
4
+ import type { Express } from 'express';
5
+ import type { Context, App } from '@aalis/core';
6
+ interface ServiceRouteDeps {
7
+ getApp: () => App | undefined;
8
+ onClientDirChange?: (dir: string) => void;
9
+ /** 广播给所有客户端 */
10
+ broadcast?: (payload: Record<string, unknown>) => void;
11
+ }
12
+ export declare function registerServiceRoutes(app: Express, ctx: Context, deps: ServiceRouteDeps): void;
13
+ export {};
14
+ //# sourceMappingURL=services.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/routes/services.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAoD,MAAM,aAAa,CAAC;AAGlG,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,GAAG,GAAG,SAAS,CAAC;IAC9B,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,eAAe;IACf,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACxD;AAED,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,gBAAgB,GACrB,IAAI,CAoPN"}