@fonlan/dsh-web-auth 0.1.7 → 0.1.8
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/cordis.patch.yml +5 -5
- package/lib/client/index.d.ts +2 -2
- package/lib/client.js +19 -62
- package/lib/handlers.d.ts +2 -2
- package/lib/handlers.js +1 -1
- package/lib/index.js +5 -5
- package/lib/settings.d.ts +4 -4
- package/lib/settings.js +4 -4
- package/package.json +1 -1
package/cordis.patch.yml
CHANGED
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
# the auth gate wraps the HTTP listener after it is bound.
|
|
12
12
|
#
|
|
13
13
|
# The package also declares `dsh.client` (see package.json), so the web app
|
|
14
|
-
# loads the browser half (the plugin's own
|
|
15
|
-
#
|
|
16
|
-
# settings
|
|
17
|
-
#
|
|
14
|
+
# loads the browser half (the plugin's own settings page) automatically. The
|
|
15
|
+
# page registers on the `settings.section` slot as the 「访问认证」 entry in the
|
|
16
|
+
# settings sidebar and binds the `web-auth` settings namespace
|
|
17
|
+
# (see src/settings.ts) through the client settings scope.
|
|
18
18
|
#
|
|
19
19
|
# There is intentionally NO password here: passwords are maintained through
|
|
20
|
-
# the GUI (login page first setup / settings
|
|
20
|
+
# the GUI (login page first setup / settings page change), stored hashed
|
|
21
21
|
# under $DSH_HOME/web-auth/. Until a password is set, auth stays disabled
|
|
22
22
|
# (see the README for the warning this prints at boot).
|
|
23
23
|
- insert:
|
package/lib/client/index.d.ts
CHANGED
|
@@ -4,11 +4,11 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
4
4
|
* `connection` and `remote` are required by `settingsScope.bind` (it resolves
|
|
5
5
|
* the settings transport and the forwarded-invalidation subscription through
|
|
6
6
|
* `ctx.get`), so the fiber must wait for them — exactly like
|
|
7
|
-
*
|
|
7
|
+
* the settings shell does for its own sections.
|
|
8
8
|
*/
|
|
9
9
|
export declare const inject: string[];
|
|
10
10
|
/**
|
|
11
|
-
* Client plugin body: register the settings
|
|
11
|
+
* Client plugin body: register the settings page. The shell's declaration
|
|
12
12
|
* may not be on the ledger yet, so the registration waits via slots.inject.
|
|
13
13
|
* @param ctx - the client cordis context.
|
|
14
14
|
*/
|
package/lib/client.js
CHANGED
|
@@ -40,33 +40,11 @@ var dshWebAuthModule = (() => {
|
|
|
40
40
|
var inject = ["slots", "settingsScope", "connection", "remote"];
|
|
41
41
|
var WEB_AUTH_NS = "web-auth";
|
|
42
42
|
var CSS = `
|
|
43
|
-
.dwa-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
transition: border-color .16s, background .16s;
|
|
49
|
-
}
|
|
50
|
-
.dwa-card:hover { border-color: var(--dsw-alias-label-dimmed, #4a5468); }
|
|
51
|
-
.dwa-card[data-open] {
|
|
52
|
-
background: var(--dsw-alias-bg-layer-2, #141a26);
|
|
53
|
-
border-color: var(--dsw-alias-label-dimmed, #4a5468);
|
|
54
|
-
}
|
|
55
|
-
.dwa-head {
|
|
56
|
-
appearance: none; width: 100%; font: inherit; color: inherit; text-align: left; cursor: pointer;
|
|
57
|
-
background: transparent; border: 0; border-radius: 12px;
|
|
58
|
-
align-items: center; gap: 12px; padding: 14px 16px; display: flex;
|
|
59
|
-
}
|
|
60
|
-
.dwa-head:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #4f8cff); outline-offset: -2px; }
|
|
61
|
-
.dwa-headText { flex-direction: column; flex: 1; gap: 4px; min-width: 0; display: flex; }
|
|
62
|
-
.dwa-title { color: var(--dsw-alias-label-primary, #e6e6e6); font-size: 15px; font-weight: 600; line-height: 1.4; }
|
|
63
|
-
.dwa-sub { color: var(--dsw-alias-label-tertiary, #8b93a7); font-size: 13px; line-height: 1.5; }
|
|
64
|
-
.dwa-chevron {
|
|
65
|
-
color: var(--dsw-alias-label-tertiary, #8b93a7); flex: none;
|
|
66
|
-
transition: transform .16s; display: flex; align-items: center; justify-content: center;
|
|
67
|
-
}
|
|
68
|
-
.dwa-chevron[data-open] { transform: rotate(180deg); }
|
|
69
|
-
.dwa-body { border-top: 1px solid var(--dsw-alias-border-l2, #2a3346); margin: 0 16px; padding: 12px 0 8px; display: flex; flex-direction: column; gap: 12px; }
|
|
43
|
+
.dwa-page { display: flex; flex-direction: column; gap: 16px; max-width: 720px; }
|
|
44
|
+
.dwa-page-head { display: flex; flex-direction: column; gap: 6px; }
|
|
45
|
+
.dwa-page-title { color: var(--dsw-alias-label-primary, #e6e6e6); margin: 0; font-size: 15px; font-weight: 600; line-height: 1.4; }
|
|
46
|
+
.dwa-page-sub { color: var(--dsw-alias-label-tertiary, #8b93a7); margin: 0; font-size: 13px; line-height: 1.5; }
|
|
47
|
+
.dwa-body { display: flex; flex-direction: column; gap: 12px; }
|
|
70
48
|
.dwa-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
|
71
49
|
.dwa-status { font-size: 13px; line-height: 1.6; }
|
|
72
50
|
.dwa-status[data-on] { color: var(--dsw-alias-label-primary, #e6e6e6); }
|
|
@@ -99,7 +77,7 @@ var dshWebAuthModule = (() => {
|
|
|
99
77
|
.dwa-divider { border: none; border-top: 1px solid var(--dsw-alias-border-l1, #232a38); margin: 2px 0; }
|
|
100
78
|
`;
|
|
101
79
|
function injectStyle() {
|
|
102
|
-
const id = "dsh-web-auth/
|
|
80
|
+
const id = "dsh-web-auth/settings-section.css";
|
|
103
81
|
if (document.getElementById(id) !== null) return;
|
|
104
82
|
const tag = document.createElement("style");
|
|
105
83
|
tag.id = id;
|
|
@@ -141,7 +119,7 @@ var dshWebAuthModule = (() => {
|
|
|
141
119
|
return null;
|
|
142
120
|
}
|
|
143
121
|
}
|
|
144
|
-
function
|
|
122
|
+
function WebAuthSettingsSection(props) {
|
|
145
123
|
const { scope } = props;
|
|
146
124
|
const snapshot = (0, import_react.useSyncExternalStore)(
|
|
147
125
|
(listener) => scope.subscribe(listener),
|
|
@@ -157,7 +135,6 @@ var dshWebAuthModule = (() => {
|
|
|
157
135
|
const [listenPick, setListenPick] = (0, import_react.useState)("127.0.0.1");
|
|
158
136
|
const [listenBusy, setListenBusy] = (0, import_react.useState)(false);
|
|
159
137
|
const [listenMsg, setListenMsg] = (0, import_react.useState)(null);
|
|
160
|
-
const [open, setOpen] = (0, import_react.useState)(false);
|
|
161
138
|
(0, import_react.useEffect)(() => {
|
|
162
139
|
let alive = true;
|
|
163
140
|
void fetchStatus().then((status) => {
|
|
@@ -220,34 +197,12 @@ var dshWebAuthModule = (() => {
|
|
|
220
197
|
setListenBusy(false);
|
|
221
198
|
}
|
|
222
199
|
};
|
|
223
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dwa-
|
|
224
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
225
|
-
"
|
|
226
|
-
{
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
"aria-expanded": open,
|
|
230
|
-
"aria-label": (open ? "\u6536\u8D77" : "\u5C55\u5F00") + "\uFF1A\u8BBF\u95EE\u8BA4\u8BC1",
|
|
231
|
-
onClick: () => setOpen(!open),
|
|
232
|
-
children: [
|
|
233
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dwa-headText", children: [
|
|
234
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dwa-title", children: "\u8BBF\u95EE\u8BA4\u8BC1" }),
|
|
235
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dwa-sub", children: "Web \u8BBF\u95EE\u5BC6\u7801\u4E0E\u76D1\u542C\u5730\u5740" })
|
|
236
|
-
] }),
|
|
237
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dwa-chevron", "data-open": open ? "" : void 0, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
238
|
-
"path",
|
|
239
|
-
{
|
|
240
|
-
d: "M3.5 5.25L7 8.75L10.5 5.25",
|
|
241
|
-
stroke: "currentColor",
|
|
242
|
-
strokeWidth: "1.5",
|
|
243
|
-
strokeLinecap: "round",
|
|
244
|
-
strokeLinejoin: "round"
|
|
245
|
-
}
|
|
246
|
-
) }) })
|
|
247
|
-
]
|
|
248
|
-
}
|
|
249
|
-
),
|
|
250
|
-
open && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dwa-body", children: [
|
|
200
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dwa-page", children: [
|
|
201
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "dwa-page-head", children: [
|
|
202
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "dwa-page-title", children: "\u8BBF\u95EE\u8BA4\u8BC1" }),
|
|
203
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dwa-page-sub", children: "Web \u8BBF\u95EE\u5BC6\u7801\u4E0E\u76D1\u542C\u5730\u5740" })
|
|
204
|
+
] }),
|
|
205
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dwa-body", children: [
|
|
251
206
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dwa-row", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dwa-status", "data-on": configured ? void 0 : "", "data-off": configured ? "" : void 0, children: configured === null ? "\u52A0\u8F7D\u4E2D\u2026" : configured ? "\u8BBF\u95EE\u8BA4\u8BC1\uFF1A\u5DF2\u542F\u7528" : "\u8BBF\u95EE\u8BA4\u8BC1\uFF1A\u672A\u8BBE\u7F6E\u5BC6\u7801\uFF08\u5F53\u524D\u672A\u542F\u7528\uFF09" }) }),
|
|
252
207
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dwa-hint", children: "\u8BBE\u7F6E\u540E\uFF0C\u8BBF\u95EE\u672C\u670D\u52A1\u9700\u8981\u8F93\u5165\u5BC6\u7801\uFF1B\u4F1A\u8BDD\u6709\u6548\u671F 7 \u5929\uFF08\u6D3B\u52A8\u81EA\u52A8\u7EED\u671F\uFF09\u3002\u4FEE\u6539\u5BC6\u7801\u4F1A\u4F7F\u6240\u6709\u5DF2\u767B\u5F55\u4F1A\u8BDD\u7ACB\u5373\u5931\u6548\u3002" }),
|
|
253
208
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { className: "dwa-divider" }),
|
|
@@ -334,14 +289,16 @@ var dshWebAuthModule = (() => {
|
|
|
334
289
|
const services = ctx;
|
|
335
290
|
const scope = services.settingsScope.bind({ namespace: WEB_AUTH_NS });
|
|
336
291
|
services.slots.inject(
|
|
337
|
-
"settings.
|
|
292
|
+
"settings.section",
|
|
338
293
|
() => services.slots.register(
|
|
339
294
|
{
|
|
340
|
-
name: "settings.
|
|
341
|
-
|
|
295
|
+
name: "settings.section",
|
|
296
|
+
id: "web-auth",
|
|
297
|
+
order: 120,
|
|
298
|
+
label: "\u8BBF\u95EE\u8BA4\u8BC1",
|
|
342
299
|
inject: () => ({ scope })
|
|
343
300
|
},
|
|
344
|
-
|
|
301
|
+
WebAuthSettingsSection
|
|
345
302
|
)
|
|
346
303
|
);
|
|
347
304
|
}
|
package/lib/handlers.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dsh-web-auth — request handlers: the gate decision (whitelist + session
|
|
3
3
|
* check), the /login page + form processing (including first-password setup),
|
|
4
|
-
* /logout, and the JSON change-password API for the settings
|
|
4
|
+
* /logout, and the JSON change-password API for the settings page.
|
|
5
5
|
*/
|
|
6
6
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
7
7
|
import { type RateLimiter } from './auth-core.ts';
|
|
@@ -45,7 +45,7 @@ export interface HandlerEnv {
|
|
|
45
45
|
loopbackAuthority?: string;
|
|
46
46
|
/**
|
|
47
47
|
* Listen-address controller (bound by the host half when the profile
|
|
48
|
-
* patch layer is reachable). Absent = the settings
|
|
48
|
+
* patch layer is reachable). Absent = the settings page cannot change
|
|
49
49
|
* the bind host.
|
|
50
50
|
*/
|
|
51
51
|
listen?: ListenController;
|
package/lib/handlers.js
CHANGED
|
@@ -353,7 +353,7 @@ export function handleLogout(req, res, env) {
|
|
|
353
353
|
clearCookie(res, isSecureRequest(req));
|
|
354
354
|
redirect(res, '/login?loggedOut=1');
|
|
355
355
|
}
|
|
356
|
-
// ── GET /api/web-auth/status — settings
|
|
356
|
+
// ── GET /api/web-auth/status — settings page state ─────────────────────────
|
|
357
357
|
export function handleStatus(req, res, env) {
|
|
358
358
|
const body = { configured: env.state.readHash() !== undefined };
|
|
359
359
|
const current = env.listen?.current();
|
package/lib/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Host half: wraps the webserver's HTTP/upgrade listeners so every request
|
|
5
5
|
* (routes, static fallback, WebSocket upgrades) must carry a valid session
|
|
6
6
|
* cookie, serves the /login page (including first-password setup), /logout,
|
|
7
|
-
* and the JSON change-password API used by the settings
|
|
7
|
+
* and the JSON change-password API used by the settings page (client half).
|
|
8
8
|
*
|
|
9
9
|
* State lives under $DSH_HOME/web-auth/ (password.hash, secret) — see
|
|
10
10
|
* state.ts. Until a password is configured the gate is OPEN and a boot
|
|
@@ -86,10 +86,10 @@ export function apply(ctx, config) {
|
|
|
86
86
|
// away; failures are logged, never thrown — the client was confirmed.
|
|
87
87
|
listen: buildListenController(ctx)
|
|
88
88
|
};
|
|
89
|
-
// The plugin's own
|
|
90
|
-
// `web-auth` settings namespace:
|
|
91
|
-
//
|
|
92
|
-
// when a settings service exists (CLI/headless profiles skip it).
|
|
89
|
+
// The plugin's own settings page (设置 → 侧栏「访问认证」) rides the
|
|
90
|
+
// `web-auth` settings namespace: binding the namespace is how the page reads
|
|
91
|
+
// and writes it, and committed listenHost changes are applied here. Attaches
|
|
92
|
+
// only when a settings service exists (CLI/headless profiles skip it).
|
|
93
93
|
installWebAuthSettings(ctx, env.listen);
|
|
94
94
|
if (state.readHash() === undefined) {
|
|
95
95
|
// console.error, not ctx.logger.warn: the cordis default logger level
|
package/lib/settings.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dsh-web-auth — the `web-auth` settings namespace.
|
|
3
3
|
*
|
|
4
|
-
* The namespace exists so the plugin's browser half can
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The namespace exists so the plugin's browser half can read and write its
|
|
5
|
+
* own settings page: the page registers on the `settings.section` slot (the
|
|
6
|
+
* 「访问认证」 entry in the settings sidebar) and binds this namespace through
|
|
7
|
+
* the client settings scope, which is the only write path to `listenHost`.
|
|
8
8
|
* The namespace carries the one genuine configuration value the plugin owns:
|
|
9
9
|
* the web server's bind host (`listenHost`). The password is deliberately
|
|
10
10
|
* NOT here — it is a secret stored hashed under `$DSH_HOME/web-auth/` and
|
package/lib/settings.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dsh-web-auth — the `web-auth` settings namespace.
|
|
3
3
|
*
|
|
4
|
-
* The namespace exists so the plugin's browser half can
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The namespace exists so the plugin's browser half can read and write its
|
|
5
|
+
* own settings page: the page registers on the `settings.section` slot (the
|
|
6
|
+
* 「访问认证」 entry in the settings sidebar) and binds this namespace through
|
|
7
|
+
* the client settings scope, which is the only write path to `listenHost`.
|
|
8
8
|
* The namespace carries the one genuine configuration value the plugin owns:
|
|
9
9
|
* the web server's bind host (`listenHost`). The password is deliberately
|
|
10
10
|
* NOT here — it is a secret stored hashed under `$DSH_HOME/web-auth/` and
|
package/package.json
CHANGED