@aalis/plugin-webui-server 0.14.1 → 0.16.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 (118) hide show
  1. package/README.md +5 -1
  2. package/dist/admin-channel.d.ts +10 -0
  3. package/dist/admin-channel.d.ts.map +1 -0
  4. package/dist/admin-channel.js +57 -0
  5. package/dist/admin-channel.js.map +1 -0
  6. package/dist/attention.d.ts +36 -0
  7. package/dist/attention.d.ts.map +1 -0
  8. package/dist/attention.js +74 -0
  9. package/dist/attention.js.map +1 -0
  10. package/dist/auth.d.ts +32 -4
  11. package/dist/auth.d.ts.map +1 -1
  12. package/dist/auth.js +253 -79
  13. package/dist/auth.js.map +1 -1
  14. package/dist/client-discovery.d.ts +7 -3
  15. package/dist/client-discovery.d.ts.map +1 -1
  16. package/dist/client-discovery.js +9 -3
  17. package/dist/client-discovery.js.map +1 -1
  18. package/dist/client-switch-page.d.ts +26 -2
  19. package/dist/client-switch-page.d.ts.map +1 -1
  20. package/dist/client-switch-page.js +76 -45
  21. package/dist/client-switch-page.js.map +1 -1
  22. package/dist/gate.d.ts +3 -3
  23. package/dist/gate.d.ts.map +1 -1
  24. package/dist/gate.js +5 -4
  25. package/dist/gate.js.map +1 -1
  26. package/dist/html.d.ts +15 -0
  27. package/dist/html.d.ts.map +1 -0
  28. package/dist/html.js +38 -0
  29. package/dist/html.js.map +1 -0
  30. package/dist/index.d.ts +24 -26
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +627 -1102
  33. package/dist/index.js.map +1 -1
  34. package/dist/layout.d.ts +19 -0
  35. package/dist/layout.d.ts.map +1 -0
  36. package/dist/layout.js +200 -0
  37. package/dist/layout.js.map +1 -0
  38. package/dist/locales/en.d.ts +191 -0
  39. package/dist/locales/en.d.ts.map +1 -0
  40. package/dist/locales/en.js +191 -0
  41. package/dist/locales/en.js.map +1 -0
  42. package/dist/localize.d.ts +24 -0
  43. package/dist/localize.d.ts.map +1 -0
  44. package/dist/localize.js +68 -0
  45. package/dist/localize.js.map +1 -0
  46. package/dist/logs.d.ts +10 -0
  47. package/dist/logs.d.ts.map +1 -0
  48. package/dist/logs.js +50 -0
  49. package/dist/logs.js.map +1 -0
  50. package/dist/page-check.d.ts +56 -0
  51. package/dist/page-check.d.ts.map +1 -0
  52. package/dist/page-check.js +376 -0
  53. package/dist/page-check.js.map +1 -0
  54. package/dist/page-files.d.ts +13 -0
  55. package/dist/page-files.d.ts.map +1 -0
  56. package/dist/page-files.js +39 -0
  57. package/dist/page-files.js.map +1 -0
  58. package/dist/pages.d.ts +62 -0
  59. package/dist/pages.d.ts.map +1 -0
  60. package/dist/pages.js +486 -0
  61. package/dist/pages.js.map +1 -0
  62. package/dist/route.d.ts +14 -0
  63. package/dist/route.d.ts.map +1 -0
  64. package/dist/route.js +16 -0
  65. package/dist/route.js.map +1 -0
  66. package/dist/routes/files.d.ts +8 -9
  67. package/dist/routes/files.d.ts.map +1 -1
  68. package/dist/routes/files.js +33 -119
  69. package/dist/routes/files.js.map +1 -1
  70. package/dist/routes/frames.d.ts +4 -0
  71. package/dist/routes/frames.d.ts.map +1 -0
  72. package/dist/routes/frames.js +103 -0
  73. package/dist/routes/frames.js.map +1 -0
  74. package/dist/routes/i18n.d.ts +20 -0
  75. package/dist/routes/i18n.d.ts.map +1 -0
  76. package/dist/routes/i18n.js +78 -0
  77. package/dist/routes/i18n.js.map +1 -0
  78. package/dist/routes/marketplace.d.ts +46 -129
  79. package/dist/routes/marketplace.d.ts.map +1 -1
  80. package/dist/routes/marketplace.js +156 -106
  81. package/dist/routes/marketplace.js.map +1 -1
  82. package/dist/routes/media.d.ts +10 -0
  83. package/dist/routes/media.d.ts.map +1 -0
  84. package/dist/routes/media.js +66 -0
  85. package/dist/routes/media.js.map +1 -0
  86. package/dist/routes/pages.d.ts +22 -0
  87. package/dist/routes/pages.d.ts.map +1 -0
  88. package/dist/routes/pages.js +104 -0
  89. package/dist/routes/pages.js.map +1 -0
  90. package/dist/routes/pipe-to-response.d.ts +9 -0
  91. package/dist/routes/pipe-to-response.d.ts.map +1 -0
  92. package/dist/routes/pipe-to-response.js +16 -0
  93. package/dist/routes/pipe-to-response.js.map +1 -0
  94. package/dist/routes/plugins.d.ts +22 -28
  95. package/dist/routes/plugins.d.ts.map +1 -1
  96. package/dist/routes/plugins.js +339 -344
  97. package/dist/routes/plugins.js.map +1 -1
  98. package/dist/routes/proxy.d.ts +3 -2
  99. package/dist/routes/proxy.d.ts.map +1 -1
  100. package/dist/routes/proxy.js +64 -42
  101. package/dist/routes/proxy.js.map +1 -1
  102. package/dist/session-transport.d.ts +6 -0
  103. package/dist/session-transport.d.ts.map +1 -0
  104. package/dist/session-transport.js +32 -0
  105. package/dist/session-transport.js.map +1 -0
  106. package/dist/texts.d.ts +381 -0
  107. package/dist/texts.d.ts.map +1 -0
  108. package/dist/texts.js +194 -0
  109. package/dist/texts.js.map +1 -0
  110. package/package.json +38 -32
  111. package/dist/protocol.d.ts +0 -113
  112. package/dist/protocol.d.ts.map +0 -1
  113. package/dist/protocol.js +0 -59
  114. package/dist/protocol.js.map +0 -1
  115. package/dist/routes/uploaded-files.d.ts +0 -20
  116. package/dist/routes/uploaded-files.d.ts.map +0 -1
  117. package/dist/routes/uploaded-files.js +0 -161
  118. package/dist/routes/uploaded-files.js.map +0 -1
@@ -4,62 +4,37 @@
4
4
  // 前端(如第三方裸前端),就没有切回去的入口(只能改 aalis.config.yaml + 重启)。本页由
5
5
  // webui-server 直接托管在 `/__clients`,无论当前活跃前端多裸都可达,是「永不卡死」的恢复入口。
6
6
  //
7
- // 零新增后端逻辑:纯静态 HTML + 同源 fetch 复用既有接口(同源 cookie 自动鉴权)——
7
+ // 零新增后端逻辑:静态 HTML 加同源的脚本(`/__clients.js`),fetch 复用既有接口(同源 cookie 自动鉴权)——
8
8
  // · 列表:GET /api/services (取 webui-client 服务的 providers + preferred)
9
9
  // · 切换:POST /api/services/webui-client/prefer (owner 闸,body {contextId})
10
10
  // 全局 auth 中间件已要求登录方可到达本路由(未认证 GET 直接回登录页),故本页无需自加 gate;
11
- // 切换动作仍受既有 owner 闸约束。抽成纯函数仅为便于单测——route 只 res.type('html').send(它)。
12
- /** 渲染前端切换恢复页的完整 HTML 文档(无运行时入参,数据由页面自身 fetch)。 */
13
- export function renderClientSwitchPage() {
14
- return `<!doctype html>
15
- <html lang="zh-CN">
16
- <head>
17
- <meta charset="utf-8" />
18
- <meta name="viewport" content="width=device-width, initial-scale=1" />
19
- <title>切换前端 · Aalis</title>
20
- <style>
21
- :root { color-scheme: light dark; }
22
- body { font: 15px/1.6 system-ui, -apple-system, sans-serif; max-width: 32rem; margin: 8vh auto; padding: 0 1.2rem; }
23
- h1 { font-size: 1.25rem; margin: 0 0 .25rem; }
24
- p.hint { color: #888; margin: .25rem 0 1.25rem; }
25
- label { display: block; font-weight: 600; margin-bottom: .4rem; }
26
- select, button { font: inherit; padding: .55rem .7rem; border-radius: .5rem; }
27
- select { width: 100%; box-sizing: border-box; }
28
- button { margin-top: 1rem; cursor: pointer; border: 1px solid currentColor; background: transparent; }
29
- button:disabled { opacity: .5; cursor: default; }
30
- #msg { margin-top: 1rem; min-height: 1.4em; }
31
- #msg.err { color: #c0392b; }
32
- #msg.ok { color: #2e7d32; }
33
- code { font-family: ui-monospace, monospace; }
34
- </style>
35
- </head>
36
- <body>
37
- <h1>切换前端</h1>
38
- <p class="hint">这是 Aalis 的前端恢复入口,由 webui-server 直接提供、独立于当前前端——即使当前前端没有切换界面,你也能在这里切回去。</p>
39
- <label for="client">可用前端(webui-client)</label>
40
- <select id="client"><option>加载中…</option></select>
41
- <button id="go" type="button">切换并刷新</button>
42
- <div id="msg"></div>
43
- <p class="hint">切回原来的前端:在上面选中它,再点「切换并刷新」。</p>
44
- <script>
45
- (function () {
11
+ // 切换动作仍受既有 owner 闸约束。抽成纯函数仅为便于单测——route 按全局层的语言渲染文字后交给它。
12
+ // 文字按全局层的语言:页面上的在服务端写进 HTML,脚本要的放进 texts 块,带参数的留着 {名} 占位,由脚本填(见 html.ts)。
13
+ import { escapeHtml, textsBlock } from './html.js';
14
+ import { t } from './texts.js';
15
+ /** 前端切换恢复页的脚本:文档级 CSP 只许同源脚本,由 `/__clients.js` 给出 */
16
+ export const CLIENT_SWITCH_SCRIPT = `(function () {
46
17
  var SVC = 'webui-client';
47
18
  var PREFER = '/api/services/' + SVC + '/prefer';
19
+ var T = JSON.parse(document.getElementById('texts').textContent);
48
20
  var sel = document.getElementById('client');
49
21
  var btn = document.getElementById('go');
50
22
  var msg = document.getElementById('msg');
23
+ function fill(text, values) {
24
+ return text.replace(/\\{(\\w+)\\}/g, function (m, k) { return k in values ? String(values[k]) : m; });
25
+ }
51
26
  function setMsg(t, err) { msg.textContent = t; msg.className = err ? 'err' : 'ok'; }
52
27
  function load() {
53
28
  fetch('/api/services', { headers: { Accept: 'application/json' } })
54
29
  .then(function (r) {
55
- if (!r.ok) throw new Error('读取服务列表失败(HTTP ' + r.status + ')');
30
+ if (!r.ok) throw new Error(fill(T.listFailed, { status: r.status }));
56
31
  return r.json();
57
32
  })
58
33
  .then(function (data) {
59
34
  var svc = data && data.services && data.services[SVC];
60
35
  var providers = (svc && svc.providers) || [];
61
36
  if (!providers.length) {
62
- setMsg('未发现任何前端(webui-client provider)。请先安装一个 aalis.client 包。', true);
37
+ setMsg(T.noClients, true);
63
38
  btn.disabled = true;
64
39
  sel.innerHTML = '';
65
40
  return;
@@ -68,8 +43,9 @@ export function renderClientSwitchPage() {
68
43
  sel.innerHTML = '';
69
44
  providers.forEach(function (p) {
70
45
  var o = document.createElement('option');
46
+ var name = p.displayName || p.label || p.contextId;
71
47
  o.value = p.contextId;
72
- o.textContent = (p.displayName || p.label || p.contextId) + (p.contextId === active ? '(当前)' : '');
48
+ o.textContent = p.contextId === active ? fill(T.current, { name: name }) : name;
73
49
  if (p.contextId === active) o.selected = true;
74
50
  sel.appendChild(o);
75
51
  });
@@ -80,26 +56,81 @@ export function renderClientSwitchPage() {
80
56
  var contextId = sel.value;
81
57
  if (!contextId) return;
82
58
  btn.disabled = true;
83
- setMsg('切换中…', false);
59
+ setMsg(T.switching, false);
84
60
  fetch(PREFER, {
85
61
  method: 'POST',
86
62
  headers: { 'Content-Type': 'application/json' },
87
63
  body: JSON.stringify({ contextId: contextId })
88
64
  })
89
65
  .then(function (r) {
90
- if (r.ok) { setMsg('已切换,正在跳转…', false); location.href = '/'; return; }
66
+ if (r.ok) { setMsg(T.switched, false); location.href = '/'; return; }
91
67
  return r.json().catch(function () { return {}; }).then(function (j) {
92
68
  var detail = (j && j.error) || ('HTTP ' + r.status);
93
- var hint = (r.status === 401 || r.status === 403) ? '(需要 owner 权限:请用一键登录链接打开 WebUI 后再试)' : '';
94
- setMsg('切换失败:' + detail + hint, true);
69
+ var hint = (r.status === 401 || r.status === 403) ? T.ownerHint : '';
70
+ setMsg(fill(T.failed, { detail: detail, hint: hint }), true);
95
71
  btn.disabled = false;
96
72
  });
97
73
  })
98
- .catch(function (e) { setMsg('切换请求出错:' + String((e && e.message) || e), true); btn.disabled = false; });
74
+ .catch(function (e) { setMsg(fill(T.error, { error: String((e && e.message) || e) }), true); btn.disabled = false; });
99
75
  });
100
76
  load();
101
77
  })();
102
- </script>
78
+ `;
79
+ /** 恢复页的文字;带参数的以 {名} 占位交给脚本填。pageLanguage 是渲染时实际用的语言,写进 <html lang> */
80
+ export const CLIENT_SWITCH_TEXTS = {
81
+ lang: t('pageLanguage'),
82
+ title: t('switchTitle'),
83
+ heading: t('switchHeading'),
84
+ intro: t('switchIntro'),
85
+ label: t('switchLabel'),
86
+ loading: t('switchLoading'),
87
+ button: t('switchButton'),
88
+ back: t('switchBack'),
89
+ script: {
90
+ listFailed: t('switchListFailed', { status: '{status}' }),
91
+ noClients: t('switchNoClients'),
92
+ current: t('switchCurrent', { name: '{name}' }),
93
+ switching: t('switching'),
94
+ switched: t('switched'),
95
+ ownerHint: t('switchOwnerHint'),
96
+ failed: t('switchFailed', { detail: '{detail}', hint: '{hint}' }),
97
+ error: t('switchError', { error: '{error}' }),
98
+ },
99
+ };
100
+ /** 渲染前端切换恢复页的完整 HTML 文档:text 是渲染过的文字,数据由页面自身 fetch */
101
+ export function renderClientSwitchPage(text) {
102
+ return `<!doctype html>
103
+ <html lang="${escapeHtml(text.lang)}">
104
+ <head>
105
+ <meta charset="utf-8" />
106
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
107
+ <title>${escapeHtml(text.title)}</title>
108
+ <style>
109
+ :root { color-scheme: light dark; }
110
+ body { font: 15px/1.6 system-ui, -apple-system, sans-serif; max-width: 32rem; margin: 8vh auto; padding: 0 1.2rem; }
111
+ h1 { font-size: 1.25rem; margin: 0 0 .25rem; }
112
+ p.hint { color: #888; margin: .25rem 0 1.25rem; }
113
+ label { display: block; font-weight: 600; margin-bottom: .4rem; }
114
+ select, button { font: inherit; padding: .55rem .7rem; border-radius: .5rem; }
115
+ select { width: 100%; box-sizing: border-box; }
116
+ button { margin-top: 1rem; cursor: pointer; border: 1px solid currentColor; background: transparent; }
117
+ button:disabled { opacity: .5; cursor: default; }
118
+ #msg { margin-top: 1rem; min-height: 1.4em; }
119
+ #msg.err { color: #c0392b; }
120
+ #msg.ok { color: #2e7d32; }
121
+ code { font-family: ui-monospace, monospace; }
122
+ </style>
123
+ </head>
124
+ <body>
125
+ <h1>${escapeHtml(text.heading)}</h1>
126
+ <p class="hint">${escapeHtml(text.intro)}</p>
127
+ <label for="client">${escapeHtml(text.label)}</label>
128
+ <select id="client"><option>${escapeHtml(text.loading)}</option></select>
129
+ <button id="go" type="button">${escapeHtml(text.button)}</button>
130
+ <div id="msg"></div>
131
+ <p class="hint">${escapeHtml(text.back)}</p>
132
+ ${textsBlock(text.script)}
133
+ <script src="/__clients.js"></script>
103
134
  </body>
104
135
  </html>`;
105
136
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client-switch-page.js","sourceRoot":"","sources":["../src/client-switch-page.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,gDAAgD;AAChD,wDAAwD;AACxD,8DAA8D;AAC9D,EAAE;AACF,uDAAuD;AACvD,6FAA6F;AAC7F,8EAA8E;AAC9E,wDAAwD;AACxD,oEAAoE;AAEpE,kDAAkD;AAClD,MAAM,UAAU,sBAAsB;IACpC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA0FD,CAAC;AACT,CAAC"}
1
+ {"version":3,"file":"client-switch-page.js","sourceRoot":"","sources":["../src/client-switch-page.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,gDAAgD;AAChD,wDAAwD;AACxD,8DAA8D;AAC9D,EAAE;AACF,yEAAyE;AACzE,6FAA6F;AAC7F,8EAA8E;AAC9E,wDAAwD;AACxD,yDAAyD;AACzD,0EAA0E;AAG1E,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAE/B,qDAAqD;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8DnC,CAAC;AAEF,sEAAsE;AACtE,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,CAAC,CAAC,cAAc,CAAC;IACvB,KAAK,EAAE,CAAC,CAAC,aAAa,CAAC;IACvB,OAAO,EAAE,CAAC,CAAC,eAAe,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,aAAa,CAAC;IACvB,KAAK,EAAE,CAAC,CAAC,aAAa,CAAC;IACvB,OAAO,EAAE,CAAC,CAAC,eAAe,CAAC;IAC3B,MAAM,EAAE,CAAC,CAAC,cAAc,CAAC;IACzB,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC;IACrB,MAAM,EAAE;QACN,UAAU,EAAE,CAAC,CAAC,kBAAkB,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;QACzD,SAAS,EAAE,CAAC,CAAC,iBAAiB,CAAC;QAC/B,OAAO,EAAE,CAAC,CAAC,eAAe,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC/C,SAAS,EAAE,CAAC,CAAC,WAAW,CAAC;QACzB,QAAQ,EAAE,CAAC,CAAC,UAAU,CAAC;QACvB,SAAS,EAAE,CAAC,CAAC,iBAAiB,CAAC;QAC/B,MAAM,EAAE,CAAC,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QACjE,KAAK,EAAE,CAAC,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;KAC9C;CACF,CAAC;AAEF,sDAAsD;AACtD,MAAM,UAAU,sBAAsB,CAAC,IAA2C;IAChF,OAAO;cACK,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;;;;SAI1B,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;QAkBvB,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;oBACZ,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;wBAClB,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;gCACd,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;kCACtB,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;;oBAErC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;EACvC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;;;QAGjB,CAAC;AACT,CAAC"}
package/dist/gate.d.ts CHANGED
@@ -19,12 +19,12 @@ type GateMiddleware = (req: GateRequest, res: GateResponse, next: () => void) =>
19
19
  /**
20
20
  * REST 路由 owner 闸工厂(单 owner 终态)。
21
21
  *
22
- * 单 token 模式下「持 token ⟺ webui:console ⟺ owner」,故连接级认证(auth.middleware
22
+ * 单 token 模式下「持 token ⟺ webui:console ⟺ owner」,故连接级认证(auth.ts 挂上的认证
23
23
  * 校验 token,未认证一律 401)即等价于 owner 鉴权。本闸是 REST 层的显式 owner 复核
24
- * (防御纵深,与 auth.middleware 同义):身份解析得到即放行,否则 403。
24
+ * (防御纵深,与连接级认证同义):身份解析得到即放行,否则 403。
25
25
  *
26
26
  * 多账户 / 能力委托已剥离,故不再有 per-route capability 与档位裁决——所有需登录的
27
- * REST 路由统一是 owner-only。页面/状态等只读路由同样在 auth.middleware 之后,对
27
+ * REST 路由统一是 owner-only。页面/状态等只读路由同样在连接级认证之后,对
28
28
  * 单 owner 无区别。
29
29
  */
30
30
  export declare function createRouteGate(identify: IdentifyFn): () => GateMiddleware;
@@ -1 +1 @@
1
- {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,gDAAgD;AAChD,KAAK,UAAU,GAAG,CAAC,GAAG,EAAE;IAAE,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,KAAK,YAAY,GAAG,SAAS,CAAC;AAKtF,UAAU,WAAW;IACnB,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9B;AACD,UAAU,YAAY;IACpB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;KAAE,CAAC;CACxD;AACD,KAAK,cAAc,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;AAEtF;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,UAAU,SACvC,cAAc,CAO1B;AAED,kDAAkD;AAClD,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC"}
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGzD,gDAAgD;AAChD,KAAK,UAAU,GAAG,CAAC,GAAG,EAAE;IAAE,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,KAAK,YAAY,GAAG,SAAS,CAAC;AAKtF,UAAU,WAAW;IACnB,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9B;AACD,UAAU,YAAY;IACpB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;KAAE,CAAC;CACxD;AACD,KAAK,cAAc,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;AAEtF;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,UAAU,SACvC,cAAc,CAO1B;AAED,kDAAkD;AAClD,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC"}
package/dist/gate.js CHANGED
@@ -1,18 +1,19 @@
1
+ import { t } from './texts.js';
1
2
  /**
2
3
  * REST 路由 owner 闸工厂(单 owner 终态)。
3
4
  *
4
- * 单 token 模式下「持 token ⟺ webui:console ⟺ owner」,故连接级认证(auth.middleware
5
+ * 单 token 模式下「持 token ⟺ webui:console ⟺ owner」,故连接级认证(auth.ts 挂上的认证
5
6
  * 校验 token,未认证一律 401)即等价于 owner 鉴权。本闸是 REST 层的显式 owner 复核
6
- * (防御纵深,与 auth.middleware 同义):身份解析得到即放行,否则 403。
7
+ * (防御纵深,与连接级认证同义):身份解析得到即放行,否则 403。
7
8
  *
8
9
  * 多账户 / 能力委托已剥离,故不再有 per-route capability 与档位裁决——所有需登录的
9
- * REST 路由统一是 owner-only。页面/状态等只读路由同样在 auth.middleware 之后,对
10
+ * REST 路由统一是 owner-only。页面/状态等只读路由同样在连接级认证之后,对
10
11
  * 单 owner 无区别。
11
12
  */
12
13
  export function createRouteGate(identify) {
13
14
  return () => (req, res, next) => {
14
15
  if (!identify(req)) {
15
- res.status(403).json({ error: '需要 owner 权限' });
16
+ res.status(403).json({ error: t('ownerRequired') });
16
17
  return;
17
18
  }
18
19
  next();
package/dist/gate.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,QAAoB;IAClD,OAAO,GAAmB,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC9C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACnB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;YAC/C,OAAO;QACT,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAgB/B;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,QAAoB;IAClD,OAAO,GAAmB,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC9C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACnB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
package/dist/html.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { type PageLanguage } from '@aalis/api-webui';
2
+ export declare function escapeHtml(s: string): string;
3
+ /** 外链脚本要的文字 */
4
+ export declare const textsBlock: (texts: Readonly<Record<string, string>>) => string;
5
+ /**
6
+ * 前端入口写上界面的语言:`<html>` 写页面的语言 lang(入口文件里写的 lang 换掉,没写的加上),`</head>` 前放语言块。
7
+ * 没有 `<html>` 或 `</head>` 时那一样不写(没有语言块的前端取 GET /api/i18n/languages)
8
+ */
9
+ export declare function withPageLanguage(html: string, lang: string, language: PageLanguage): string;
10
+ /**
11
+ * 页面实际用的语言,写进 `<html lang>`:取字典键 pageLanguage 渲染出的译文。第三方语言包可以把它译成任意字符串,规范化
12
+ * 不了时用全局层语言链的第一项;链为空(i18n 缺席)时渲染的是原文 zh-CN,本来就规范化得了
13
+ */
14
+ export declare function pageLanguageOf(rendered: string, chain: readonly string[]): string;
15
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAQA,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGvE,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5C;AAOD,eAAe;AACf,eAAO,MAAM,UAAU,GAAI,OAAO,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,KAAG,MAAmC,CAAC;AAEzG;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,MAAM,CAO3F;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAEjF"}
package/dist/html.js ADDED
@@ -0,0 +1,38 @@
1
+ // ============================================================
2
+ // html.ts — 服务端直出的页面(登录页、换前端的恢复页)与前端入口共用的几样
3
+ //
4
+ // 两页都按全局层的语言输出:文字在服务端渲染好写进 HTML,外链脚本要的文字放进 texts 块(type 为 application/json,
5
+ // 不执行,文档级 CSP 不用动)。页面上不设语言切换:登录之前改不了全局设置。前端的入口同样在服务端写好 <html lang>,首帧的
6
+ // 字体就按全局层的语言选;界面的语言(语言链与服务进程的时区)照 texts 块的写法放进语言块,前端首帧按它画、不等往返。
7
+ // ============================================================
8
+ import { PAGE_LANGUAGE_ID } from '@aalis/api-webui';
9
+ import { normalizeLanguages } from '@aalis/schema-i18n';
10
+ export function escapeHtml(s) {
11
+ return s.replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c]);
12
+ }
13
+ /** 给页面里的脚本读的数据块:`<` 写成 `\u003c`,值里的 `</script>` 结束不了这个块 */
14
+ function jsonBlock(id, value) {
15
+ return `<script type="application/json" id="${id}">${JSON.stringify(value).replace(/</g, '\\u003c')}</script>`;
16
+ }
17
+ /** 外链脚本要的文字 */
18
+ export const textsBlock = (texts) => jsonBlock('texts', texts);
19
+ /**
20
+ * 前端入口写上界面的语言:`<html>` 写页面的语言 lang(入口文件里写的 lang 换掉,没写的加上),`</head>` 前放语言块。
21
+ * 没有 `<html>` 或 `</head>` 时那一样不写(没有语言块的前端取 GET /api/i18n/languages)
22
+ */
23
+ export function withPageLanguage(html, lang, language) {
24
+ return html
25
+ .replace(/<html\b([^>]*)>/i, (_tag, attributes) => {
26
+ const rest = attributes.replace(/\s+lang\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'>]+)/i, '');
27
+ return `<html lang="${escapeHtml(lang)}"${rest}>`;
28
+ })
29
+ .replace(/<\/head>/i, tag => jsonBlock(PAGE_LANGUAGE_ID, language) + tag);
30
+ }
31
+ /**
32
+ * 页面实际用的语言,写进 `<html lang>`:取字典键 pageLanguage 渲染出的译文。第三方语言包可以把它译成任意字符串,规范化
33
+ * 不了时用全局层语言链的第一项;链为空(i18n 缺席)时渲染的是原文 zh-CN,本来就规范化得了
34
+ */
35
+ export function pageLanguageOf(rendered, chain) {
36
+ return normalizeLanguages([rendered])[0] ?? chain[0];
37
+ }
38
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,4CAA4C;AAC5C,EAAE;AACF,4EAA4E;AAC5E,uEAAuE;AACvE,gEAAgE;AAChE,+DAA+D;AAE/D,OAAO,EAAE,gBAAgB,EAAqB,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,OAAO,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;AACnH,CAAC;AAED,2DAA2D;AAC3D,SAAS,SAAS,CAAC,EAAU,EAAE,KAAc;IAC3C,OAAO,uCAAuC,EAAE,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,WAAW,CAAC;AACjH,CAAC;AAED,eAAe;AACf,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAuC,EAAU,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAEzG;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,IAAY,EAAE,QAAsB;IACjF,OAAO,IAAI;SACR,OAAO,CAAC,kBAAkB,EAAE,CAAC,IAAI,EAAE,UAAkB,EAAE,EAAE;QACxD,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,8CAA8C,EAAE,EAAE,CAAC,CAAC;QACpF,OAAO,eAAe,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC;IACpD,CAAC,CAAC;SACD,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,gBAAgB,EAAE,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC;AAC9E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,KAAwB;IACvE,OAAO,kBAAkB,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,16 +1,3 @@
1
- import { type PlatformAdapter } from '@aalis/api-platform';
2
- import type { StorageService } from '@aalis/api-storage';
3
- /**
4
- * 会话归属的平台名:取 sessionId 首个 `:` 前的前缀,命中**已注册平台名**才采信,否则 `'webui'`。
5
- *
6
- * WebUI 是管理面,订阅的会话可以属于任何平台(`onebot:123`)。它发 `token:request` 时若
7
- * 一律报 `'webui'`,消费方(plugin-agent)就按 webui 档解析模型与会话配置,把别人的预算
8
- * 快照算进错的上下文窗口。前缀不认识时才落回 `'webui'`——CLI 的 `cli-default` 这类不带
9
- * 前缀的会话,以及 webui 自己的会话,都归在这一档。
10
- *
11
- * 纯函数:平台名集合由调用方从 `getPlatformNames(platform)` 取(adapter 可热插拔,每次现取)。
12
- */
13
- export declare function resolveSessionPlatform(sessionId: string, known: ReadonlySet<string>): string;
14
1
  declare const _default: import("@aalis/core").PluginDefinition<{
15
2
  events: import("@aalis/core").ServiceDescriptor<(identity: symbol) => import("@aalis/core").Events, import("@aalis/core").Events>;
16
3
  logger: import("@aalis/core").ServiceDescriptor<(identity: symbol) => import("@aalis/core").Logger, import("@aalis/core").Logger>;
@@ -18,21 +5,32 @@ declare const _default: import("@aalis/core").PluginDefinition<{
18
5
  config: import("@aalis/core").ServiceDescriptor<(identity: symbol) => Readonly<Record<string, unknown>>, Readonly<Record<string, unknown>>>;
19
6
  provide: import("@aalis/core").ServiceDescriptor<(identity: symbol) => import("@aalis/core").Provide, import("@aalis/core").Provide>;
20
7
  services: import("@aalis/core").ServiceDescriptor<(identity: symbol) => import("@aalis/core").Services, import("@aalis/core").Services>;
21
- hostConfig: import("@aalis/core").OptionalUse<import("@aalis/api-host-config").HostConfig, import("@aalis/core").ServiceRef<import("@aalis/api-host-config").HostConfig>>;
22
- app: import("@aalis/core").OptionalUse<import("@aalis/core").AppService, import("@aalis/core").ServiceRef<import("@aalis/core").AppService>>;
23
- plugins: import("@aalis/core").OptionalUse<import("@aalis/core").PluginManagerService, import("@aalis/core").ServiceRef<import("@aalis/core").PluginManagerService>>;
24
- source: import("@aalis/core").OptionalUse<import("@aalis/api-plugin-source").PluginSourceService, import("@aalis/core").ServiceRef<import("@aalis/api-plugin-source").PluginSourceService>>;
25
- packageManager: import("@aalis/core").OptionalUse<import("@aalis/api-package-manager").PackageManagerService, import("@aalis/core").ServiceRef<import("@aalis/api-package-manager").PackageManagerService>>;
26
- storage: import("@aalis/core").OptionalUse<StorageService, import("@aalis/core").ServiceRef<StorageService>>;
27
- authority: import("@aalis/core").OptionalUse<import("@aalis/api-authority").AuthorityService, import("@aalis/core").ServiceRef<import("@aalis/api-authority").AuthorityService>>;
8
+ hostConfig: import("@aalis/core").OptionalUse<import("@aalis/api-host-config").HostConfig, import("@aalis/api-host-config").HostConfig>;
9
+ /** 日志页的历史(/api/logs/*)与 /admin 上的实时推送 */
10
+ logs: import("@aalis/core").OptionalUse<import("@aalis/api-host-logs").HostLogs, import("@aalis/api-host-logs").HostLogs>;
11
+ paths: import("@aalis/core").OptionalUse<import("@aalis/api-host-paths").HostPaths, import("@aalis/api-host-paths").HostPaths>;
12
+ app: import("@aalis/core").OptionalUse<import("@aalis/core").AppService, import("@aalis/core").AppService>;
13
+ plugins: import("@aalis/core").OptionalUse<import("@aalis/core").PluginManagerService, import("@aalis/core").PluginManagerService>;
14
+ source: import("@aalis/core").OptionalUse<import("@aalis/api-plugin-source").PluginSourceService, import("@aalis/api-plugin-source").PluginSourceService>;
15
+ packageManager: import("@aalis/core").OptionalUse<import("@aalis/api-package-manager").PackageManagerService, import("@aalis/api-package-manager").PackageManagerService>;
16
+ storage: import("@aalis/core").OptionalUse<import("@aalis/api-storage").StorageService, import("@aalis/api-storage").BoundStorage>;
28
17
  commands: import("@aalis/core").OptionalUse<import("@aalis/api-commands").CommandService, import("@aalis/api-commands").BoundCommands>;
29
- platform: import("@aalis/core").OptionalUse<PlatformAdapter, import("@aalis/core").ServiceRef<PlatformAdapter>>;
30
- process: import("@aalis/core").OptionalUse<import("@aalis/api-process").ProcessService, import("@aalis/core").ServiceRef<import("@aalis/api-process").ProcessService>>;
31
- sessionConfirm: import("@aalis/core").OptionalUse<import("@aalis/api-session-confirm").SessionConfirmService, import("@aalis/core").ServiceRef<import("@aalis/api-session-confirm").SessionConfirmService>>;
18
+ platform: import("@aalis/core").OptionalUse<import("@aalis/api-platform").PlatformAdapter, import("@aalis/api-platform").PlatformAdapter>;
19
+ process: import("@aalis/core").OptionalUse<import("@aalis/api-process").ProcessService, import("@aalis/api-process").ProcessService>;
20
+ /** /session 的连接接到它;每条连接到达时现取,不在时关闭连接并写明原因。会话文件的下载请它核验句柄 */
21
+ sessionProtocol: import("@aalis/core").OptionalUse<import("@aalis/api-session-protocol").SessionProtocolService, import("@aalis/api-session-protocol").SessionProtocolService>;
22
+ /** 登记 webui 审批面:/session 连接上的在场上报与应答记在这个登记身份名下。推出未结的审批进「需要处理」 */
23
+ approvals: import("@aalis/core").OptionalUse<import("@aalis/api-approval").ApprovalService, import("@aalis/api-approval").BoundApprovals>;
24
+ /** 会话面板:调用前核会话存在、按会话种类判断适用的页面 */
25
+ sessions: import("@aalis/core").OptionalUse<import("@aalis/api-session-manager").SessionManagerService, import("@aalis/api-session-manager").SessionManagerService>;
32
26
  tools: import("@aalis/core").OptionalUse<import("@aalis/api-tools").ToolService, import("@aalis/api-tools").BoundTools>;
33
- llm: import("@aalis/core").OptionalUse<import("@aalis/api-llm").LLMModel, import("@aalis/core").ServiceRef<import("@aalis/api-llm").LLMModel>>;
34
- persona: import("@aalis/core").OptionalUse<import("@aalis/api-persona").PersonaService, import("@aalis/core").ServiceRef<import("@aalis/api-persona").PersonaService>>;
35
- agent: import("@aalis/core").OptionalUse<import("@aalis/api-agent").AgentService, import("@aalis/api-agent").BoundAgent>;
27
+ llm: import("@aalis/core").OptionalUse<import("@aalis/api-llm").LLMModel, import("@aalis/api-llm").LLMModel>;
28
+ /** 插件经贡献点 webui:theme 提供的主题(GET /api/themes) */
29
+ contributions: import("@aalis/core").OptionalUse<import("@aalis/api-contributions").ContributionRegistry, import("@aalis/api-contributions").Contributions>;
30
+ /** 发往 WebUI 自己会话的出站消息经它的 recordOutbound 记进会话历史,记成了才算送达 */
31
+ archive: import("@aalis/core").OptionalUse<import("@aalis/api-message-archive").MessageArchiveService, import("@aalis/api-message-archive").MessageArchiveService>;
32
+ /** 回应里的文字对象按全局层的语言渲染(localize.ts);缺席时按原文 */
33
+ i18n: import("@aalis/core").OptionalUse<import("@aalis/api-i18n").I18nService, import("@aalis/api-i18n").I18nService>;
36
34
  }>;
37
35
  export default _default;
38
36
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAmBA,OAAO,EAIL,KAAK,eAAe,EAGrB,MAAM,qBAAqB,CAAC;AAK7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAmRzD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAK5F;;;;;;;;;;;;;;;;;;;;;;;;AA+BD,wBAQG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;IAmOE,yCAAyC;;;;;;;;;;;IAYzC,2DAA2D;;IAE3D,iEAAiE;;IAEjE,iCAAiC;;;;IAIjC,gDAAgD;;IAEhD,0DAA0D;;IAE1D,4CAA4C;;;AAK9C,wBAQG"}