dsh-daily-brief 2.0.2 → 2.0.3

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 (2) hide show
  1. package/lib/host/routes.js +44 -12
  2. package/package.json +1 -1
@@ -1,14 +1,52 @@
1
1
  /**
2
- * Loopback HTTP surface used by the client panel (relative `api/daily-brief/*`).
3
- * Every mutating call must carry the private header; all calls are loopback-only.
2
+ * Host HTTP surface used by the client panel (relative `api/daily-brief/*`).
3
+ *
4
+ * The plugin owns this route, so it also owns the request policy. On hosts that
5
+ * expose DSH's Connection service every call passes the same Host/Origin fence
6
+ * and browser-session check as the built-in `/api` bridge
7
+ * (`connection.requestRejection`). That covers both the Web GUI and the Desktop
8
+ * renderer: the `dsh-app` scheme forwards an authenticated, same-origin request
9
+ * for the panel, so no plugin-specific header is needed.
10
+ *
11
+ * Hosts without that service keep the historical local-only gate, now strict:
12
+ * the request must come from loopback *and* carry `x-daily-brief-client: 1`.
4
13
  */
5
14
  const PREFIX = '/api/daily-brief';
15
+ const CLIENT_HEADER = 'x-daily-brief-client';
6
16
 
7
17
  function isLoopback(req) {
8
18
  const address = req.socket?.remoteAddress || '';
9
19
  return address === '127.0.0.1' || address === '::1' || address === '::ffff:127.0.0.1';
10
20
  }
11
21
 
22
+ /**
23
+ * Decide whether one request may reach the panel API.
24
+ * @returns `undefined` when admitted, otherwise the status and error to answer with.
25
+ */
26
+ function admissionFailure(ctx, req) {
27
+ let connection;
28
+ try {
29
+ connection = typeof ctx.get === 'function' ? ctx.get('connection') : ctx.connection;
30
+ } catch {
31
+ connection = undefined;
32
+ }
33
+ if (connection && typeof connection.requestRejection === 'function') {
34
+ try {
35
+ const status = connection.requestRejection(req);
36
+ if (!status) return undefined;
37
+ return {
38
+ status,
39
+ error: status === 401 ? '需要 DSH 网页会话认证' : '请求来源不受信任',
40
+ };
41
+ } catch {
42
+ return { status: 503, error: '暂时无法校验请求来源' };
43
+ }
44
+ }
45
+ if (!isLoopback(req)) return { status: 403, error: '仅限本机访问' };
46
+ if (req.headers[CLIENT_HEADER] !== '1') return { status: 403, error: '客户端标识无效' };
47
+ return undefined;
48
+ }
49
+
12
50
  function sendJson(res, status, body) {
13
51
  const payload = JSON.stringify(body ?? null);
14
52
  res.statusCode = status;
@@ -55,12 +93,13 @@ export function registerRoutes(ctx, service) {
55
93
  const sub = url.pathname.slice(PREFIX.length) || '/';
56
94
  const method = req.method || 'GET';
57
95
 
58
- if (!isLoopback(req)) {
59
- sendJson(res, 403, { error: '仅限本机访问' });
96
+ const failure = admissionFailure(ctx, req);
97
+ if (failure) {
98
+ sendJson(res, failure.status, { error: failure.error });
60
99
  return;
61
100
  }
62
101
 
63
- // The exported page is opened by the user directly; it needs no header.
102
+ // `/artifact` has no panel client of its own; admission above is the guard.
64
103
  if (sub === '/artifact' && method === 'GET') {
65
104
  const id = url.searchParams.get('id') || 'latest';
66
105
  const briefing = await service.getBriefing(id);
@@ -88,13 +127,6 @@ export function registerRoutes(ctx, service) {
88
127
  return;
89
128
  }
90
129
 
91
- // The Desktop renderer reaches these routes through the dsh-app scheme handler,
92
- // which cannot attach custom headers on preflighted reads; loopback is the guard.
93
- if (req.headers['x-daily-brief-client'] !== undefined && req.headers['x-daily-brief-client'] !== '1') {
94
- sendJson(res, 403, { error: '客户端标识无效' });
95
- return;
96
- }
97
-
98
130
  try {
99
131
  if (sub === '/state' && method === 'GET') {
100
132
  sendJson(res, 200, await service.getState());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-daily-brief",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },