dshb-auth 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DSH-HoneyBee Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,20 @@
1
+ - insert:
2
+ - id: dshb-auth
3
+ name: dshb-auth
4
+ - id: web-startup
5
+ name: '@deepseek-ai/dsh-web-app/startup'
6
+ disabled: true
7
+ - insert:
8
+ - id: dshb-web-startup
9
+ name: dshb-auth/startup
10
+ - id: webserver
11
+ name: '@deepseek-ai/dsh-host-webserver'
12
+ disabled: true
13
+ - insert:
14
+ - id: dshb-webserver
15
+ name: dshb-auth/webserver
16
+ inject:
17
+ - webStartup
18
+ config:
19
+ host: !!js ctx.webStartup.host ?? '127.0.0.1'
20
+ port: !!js ctx.webStartup.port ?? 3080
package/lib/client.js ADDED
@@ -0,0 +1,468 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dshb-auth",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
+ //#region src/client/index.tsx
10
+ const inject = ["connection"];
11
+ const SECTION_ID = "auth";
12
+ const MESSAGE_MS = 5e3;
13
+ function useUsername() {
14
+ const [username, setUsername] = (0, react.useState)(void 0);
15
+ (0, react.useEffect)(() => {
16
+ let cancelled = false;
17
+ (async () => {
18
+ try {
19
+ const res = await fetch("/api/auth/status");
20
+ if (!res.ok) return;
21
+ const data = await res.json();
22
+ if (!cancelled && typeof data.username === "string") setUsername(data.username);
23
+ } catch {}
24
+ })();
25
+ return () => {
26
+ cancelled = true;
27
+ };
28
+ }, []);
29
+ return [username, setUsername];
30
+ }
31
+ function AuthSection(_props) {
32
+ const [username, setUsername] = useUsername();
33
+ const [newUsername, setNewUsername] = (0, react.useState)("");
34
+ const [usernamePassword, setUsernamePassword] = (0, react.useState)("");
35
+ const [oldPassword, setOldPassword] = (0, react.useState)("");
36
+ const [newPassword, setNewPassword] = (0, react.useState)("");
37
+ const [confirm, setConfirm] = (0, react.useState)("");
38
+ const [busy, setBusy] = (0, react.useState)(false);
39
+ const [notice, setNotice] = (0, react.useState)(void 0);
40
+ const [confirmingSignOut, setConfirmingSignOut] = (0, react.useState)(false);
41
+ const flash = (0, react.useCallback)((n) => {
42
+ setNotice(n);
43
+ if (n !== void 0) setTimeout(() => setNotice(void 0), MESSAGE_MS);
44
+ }, []);
45
+ const signOut = (0, react.useCallback)(async () => {
46
+ setBusy(true);
47
+ try {
48
+ await fetch("/api/auth/logout", { method: "POST" });
49
+ window.location.href = "/login";
50
+ } catch {
51
+ setBusy(false);
52
+ setConfirmingSignOut(false);
53
+ flash({
54
+ kind: "error",
55
+ text: "退出失败,请重试",
56
+ owner: "account"
57
+ });
58
+ }
59
+ }, [flash]);
60
+ const changePassword = (0, react.useCallback)(async () => {
61
+ if (newPassword !== confirm) {
62
+ flash({
63
+ kind: "error",
64
+ text: "两次输入的新密码不一致",
65
+ owner: "password"
66
+ });
67
+ return;
68
+ }
69
+ setBusy(true);
70
+ try {
71
+ const res = await fetch("/api/auth/change-password", {
72
+ method: "POST",
73
+ headers: { "content-type": "application/json" },
74
+ body: JSON.stringify({
75
+ currentPassword: oldPassword,
76
+ password: newPassword
77
+ })
78
+ });
79
+ const data = await res.json();
80
+ if (res.ok) {
81
+ setOldPassword("");
82
+ setNewPassword("");
83
+ setConfirm("");
84
+ flash({
85
+ kind: "ok",
86
+ text: "密码已修改",
87
+ owner: "password"
88
+ });
89
+ } else flash({
90
+ kind: "error",
91
+ text: data.error ?? "修改失败,请重试",
92
+ owner: "password"
93
+ });
94
+ } catch {
95
+ flash({
96
+ kind: "error",
97
+ text: "修改失败,请重试",
98
+ owner: "password"
99
+ });
100
+ } finally {
101
+ setBusy(false);
102
+ }
103
+ }, [
104
+ oldPassword,
105
+ newPassword,
106
+ confirm,
107
+ flash
108
+ ]);
109
+ const changeUsername = (0, react.useCallback)(async () => {
110
+ setBusy(true);
111
+ try {
112
+ const res = await fetch("/api/auth/change-username", {
113
+ method: "POST",
114
+ headers: { "content-type": "application/json" },
115
+ body: JSON.stringify({
116
+ username: newUsername,
117
+ currentPassword: usernamePassword
118
+ })
119
+ });
120
+ const data = await res.json();
121
+ if (res.ok) {
122
+ setNewUsername("");
123
+ setUsernamePassword("");
124
+ if (newUsername) setUsername(newUsername);
125
+ flash({
126
+ kind: "ok",
127
+ text: "用户名已更新",
128
+ owner: "username"
129
+ });
130
+ } else flash({
131
+ kind: "error",
132
+ text: data.error ?? "修改失败,请重试",
133
+ owner: "username"
134
+ });
135
+ } catch {
136
+ flash({
137
+ kind: "error",
138
+ text: "修改失败,请重试",
139
+ owner: "username"
140
+ });
141
+ } finally {
142
+ setBusy(false);
143
+ }
144
+ }, [
145
+ newUsername,
146
+ usernamePassword,
147
+ flash,
148
+ setUsername
149
+ ]);
150
+ const inputStyle = {
151
+ width: "100%",
152
+ boxSizing: "border-box",
153
+ padding: "0 10px",
154
+ height: 32,
155
+ border: "1px solid var(--dsw-alias-border-l2)",
156
+ borderRadius: 8,
157
+ fontSize: 14,
158
+ fontFamily: "inherit",
159
+ background: "var(--dsw-alias-bg-layer-1)",
160
+ color: "var(--dsw-alias-label-primary)",
161
+ outline: "none"
162
+ };
163
+ const buttonStyle = {
164
+ boxSizing: "border-box",
165
+ height: 36,
166
+ padding: "0 14px",
167
+ borderRadius: 18,
168
+ fontSize: 14,
169
+ lineHeight: "22px",
170
+ fontFamily: "inherit",
171
+ cursor: "pointer",
172
+ border: "none",
173
+ display: "inline-flex",
174
+ justifyContent: "center",
175
+ alignItems: "center",
176
+ gap: 4
177
+ };
178
+ const primaryButtonStyle = {
179
+ ...buttonStyle,
180
+ background: "var(--dsw-alias-button-primary-fill)",
181
+ color: "var(--dsw-alias-label-primary-foreground)"
182
+ };
183
+ const secondaryButtonStyle = {
184
+ ...buttonStyle,
185
+ border: "1px solid var(--dsw-alias-border-l2)",
186
+ color: "var(--dsw-alias-label-primary)",
187
+ background: "transparent"
188
+ };
189
+ ({ ...buttonStyle });
190
+ const dangerOutlineButtonStyle = {
191
+ ...buttonStyle,
192
+ border: "1px solid var(--dsw-alias-state-error-primary)",
193
+ color: "var(--dsw-alias-state-error-primary)",
194
+ background: "transparent"
195
+ };
196
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
197
+ style: {
198
+ display: "flex",
199
+ flexDirection: "column",
200
+ gap: 24,
201
+ maxWidth: 420
202
+ },
203
+ children: [
204
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", { children: [
205
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
206
+ style: {
207
+ fontSize: 16,
208
+ fontWeight: 600,
209
+ margin: "0 0 4px"
210
+ },
211
+ children: "账号"
212
+ }),
213
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
214
+ style: {
215
+ fontSize: 13,
216
+ opacity: .7,
217
+ margin: "0 0 12px"
218
+ },
219
+ children: username !== void 0 ? `当前登录:${username}` : "当前登录:管理员"
220
+ }),
221
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
222
+ type: "button",
223
+ onClick: () => setConfirmingSignOut(true),
224
+ disabled: busy,
225
+ style: dangerOutlineButtonStyle,
226
+ children: "退出登录"
227
+ }),
228
+ confirmingSignOut && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
229
+ role: "alertdialog",
230
+ "aria-label": "确认退出登录",
231
+ style: {
232
+ marginTop: 10,
233
+ padding: "12px 14px",
234
+ border: "1px solid var(--dsw-alias-border-l2)",
235
+ borderRadius: 8,
236
+ maxWidth: 320
237
+ },
238
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
239
+ style: {
240
+ fontSize: 13,
241
+ marginBottom: 10,
242
+ color: "var(--dsw-alias-label-primary)"
243
+ },
244
+ children: "退出登录将回到登录页"
245
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
246
+ style: {
247
+ display: "flex",
248
+ justifyContent: "flex-end",
249
+ gap: 8
250
+ },
251
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
252
+ type: "button",
253
+ onClick: () => setConfirmingSignOut(false),
254
+ disabled: busy,
255
+ style: {
256
+ ...secondaryButtonStyle,
257
+ height: 32,
258
+ padding: "0 12px",
259
+ borderRadius: 16
260
+ },
261
+ children: "取消"
262
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
263
+ type: "button",
264
+ onClick: () => void signOut(),
265
+ disabled: busy,
266
+ style: {
267
+ ...dangerOutlineButtonStyle,
268
+ height: 32,
269
+ padding: "0 12px",
270
+ borderRadius: 16,
271
+ background: "var(--dsw-alias-interactive-bg-hover-danger)"
272
+ },
273
+ children: "确认退出"
274
+ })]
275
+ })]
276
+ }),
277
+ notice?.owner === "account" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
278
+ style: {
279
+ fontSize: 13,
280
+ color: notice.kind === "ok" ? "var(--dsw-alias-state-success-primary)" : "var(--dsw-alias-state-error-primary)",
281
+ margin: "8px 0 0"
282
+ },
283
+ children: notice.text
284
+ })
285
+ ] }),
286
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
287
+ style: {
288
+ fontSize: 16,
289
+ fontWeight: 600,
290
+ margin: "0 0 12px"
291
+ },
292
+ children: "修改用户名"
293
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
294
+ onSubmit: (e) => {
295
+ e.preventDefault();
296
+ changeUsername();
297
+ },
298
+ style: {
299
+ display: "flex",
300
+ flexDirection: "column",
301
+ gap: 12
302
+ },
303
+ children: [
304
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
305
+ style: {
306
+ display: "flex",
307
+ flexDirection: "column",
308
+ gap: 4,
309
+ fontSize: 13
310
+ },
311
+ children: ["新用户名", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
312
+ type: "text",
313
+ value: newUsername,
314
+ onChange: (e) => setNewUsername(e.target.value),
315
+ autoComplete: "username",
316
+ style: inputStyle
317
+ })]
318
+ }),
319
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
320
+ style: {
321
+ display: "flex",
322
+ flexDirection: "column",
323
+ gap: 4,
324
+ fontSize: 13
325
+ },
326
+ children: ["当前密码", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
327
+ type: "password",
328
+ value: usernamePassword,
329
+ onChange: (e) => setUsernamePassword(e.target.value),
330
+ autoComplete: "current-password",
331
+ style: inputStyle
332
+ })]
333
+ }),
334
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
335
+ style: {
336
+ display: "flex",
337
+ alignItems: "center",
338
+ gap: 12
339
+ },
340
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
341
+ type: "submit",
342
+ disabled: busy,
343
+ style: primaryButtonStyle,
344
+ children: "修改用户名"
345
+ }), notice?.owner === "username" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
346
+ style: {
347
+ fontSize: 13,
348
+ color: notice.kind === "ok" ? "var(--dsw-alias-state-success-primary)" : "var(--dsw-alias-state-error-primary)"
349
+ },
350
+ children: notice.text
351
+ })]
352
+ })
353
+ ]
354
+ })] }),
355
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
356
+ style: {
357
+ fontSize: 16,
358
+ fontWeight: 600,
359
+ margin: "0 0 12px"
360
+ },
361
+ children: "修改密码"
362
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
363
+ onSubmit: (e) => {
364
+ e.preventDefault();
365
+ changePassword();
366
+ },
367
+ style: {
368
+ display: "flex",
369
+ flexDirection: "column",
370
+ gap: 12
371
+ },
372
+ children: [
373
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
374
+ style: {
375
+ display: "flex",
376
+ flexDirection: "column",
377
+ gap: 4,
378
+ fontSize: 13
379
+ },
380
+ children: ["当前密码", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
381
+ type: "password",
382
+ value: oldPassword,
383
+ onChange: (e) => setOldPassword(e.target.value),
384
+ autoComplete: "current-password",
385
+ style: inputStyle
386
+ })]
387
+ }),
388
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
389
+ style: {
390
+ display: "flex",
391
+ flexDirection: "column",
392
+ gap: 4,
393
+ fontSize: 13
394
+ },
395
+ children: ["新密码", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
396
+ type: "password",
397
+ value: newPassword,
398
+ onChange: (e) => setNewPassword(e.target.value),
399
+ autoComplete: "new-password",
400
+ style: inputStyle
401
+ })]
402
+ }),
403
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
404
+ style: {
405
+ display: "flex",
406
+ flexDirection: "column",
407
+ gap: 4,
408
+ fontSize: 13
409
+ },
410
+ children: ["确认新密码", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
411
+ type: "password",
412
+ value: confirm,
413
+ onChange: (e) => setConfirm(e.target.value),
414
+ autoComplete: "new-password",
415
+ style: inputStyle
416
+ })]
417
+ }),
418
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
419
+ style: {
420
+ display: "flex",
421
+ alignItems: "center",
422
+ gap: 12
423
+ },
424
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
425
+ type: "submit",
426
+ disabled: busy,
427
+ style: primaryButtonStyle,
428
+ children: "修改密码"
429
+ }), notice?.owner === "password" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
430
+ style: {
431
+ fontSize: 13,
432
+ color: notice.kind === "ok" ? "var(--dsw-alias-state-success-primary)" : "var(--dsw-alias-state-error-primary)"
433
+ },
434
+ children: notice.text
435
+ })]
436
+ })
437
+ ]
438
+ })] })
439
+ ]
440
+ });
441
+ }
442
+ function apply(ctx) {
443
+ const connection = ctx.get("connection");
444
+ if (connection !== void 0) Object.defineProperty(connection, "isLoopback", {
445
+ configurable: true,
446
+ get: () => true
447
+ });
448
+ ctx.plugin({
449
+ inject: ["slots", "settingsScope"],
450
+ apply: (sub) => {
451
+ sub.slots.inject("settings.section", () => sub.slots.register({
452
+ name: "settings.section",
453
+ id: SECTION_ID,
454
+ order: 100,
455
+ label: () => "认证"
456
+ }, AuthSection));
457
+ }
458
+ });
459
+ }
460
+ //#endregion
461
+ exports.AuthSection = AuthSection;
462
+ exports.apply = apply;
463
+ exports.inject = inject;
464
+ return module.exports;
465
+ }
466
+ });
467
+
468
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":["useState","useCallback"],"sources":["../src/client/index.tsx"],"sourcesContent":["import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { CSSProperties, ReactElement } from 'react'\nimport { useCallback, useEffect, useState } from 'react'\n\nexport const inject = ['connection']\n\nconst SECTION_ID = 'auth'\nconst MESSAGE_MS = 5000\n\nfunction useUsername(): [string | undefined, (username: string) => void] {\n const [username, setUsername] = useState<string | undefined>(undefined)\n useEffect(() => {\n let cancelled = false\n void (async () => {\n try {\n const res = await fetch('/api/auth/status')\n if (!res.ok) return\n const data = (await res.json()) as { username?: string }\n if (!cancelled && typeof data.username === 'string') setUsername(data.username)\n } catch {\n // best-effort\n }\n })()\n return () => {\n cancelled = true\n }\n }, [])\n return [username, setUsername]\n}\n\ntype Notice = { kind: 'ok' | 'error'; text: string; owner: 'username' | 'password' | 'account' }\n\nexport function AuthSection(_props: PropsRuntime<'settings.section'>): ReactElement {\n const [username, setUsername] = useUsername()\n const [newUsername, setNewUsername] = useState('')\n const [usernamePassword, setUsernamePassword] = useState('')\n const [oldPassword, setOldPassword] = useState('')\n const [newPassword, setNewPassword] = useState('')\n const [confirm, setConfirm] = useState('')\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState<Notice | undefined>(undefined)\n const [confirmingSignOut, setConfirmingSignOut] = useState(false)\n\n const flash = useCallback((n: Notice | undefined) => {\n setNotice(n)\n if (n !== undefined) setTimeout(() => setNotice(undefined), MESSAGE_MS)\n }, [])\n\n const signOut = useCallback(async () => {\n setBusy(true)\n try {\n await fetch('/api/auth/logout', { method: 'POST' })\n window.location.href = '/login'\n } catch {\n setBusy(false)\n setConfirmingSignOut(false)\n flash({ kind: 'error', text: '退出失败,请重试', owner: 'account' })\n }\n }, [flash])\n\n const changePassword = useCallback(async () => {\n if (newPassword !== confirm) {\n flash({ kind: 'error', text: '两次输入的新密码不一致', owner: 'password' })\n return\n }\n setBusy(true)\n try {\n const res = await fetch('/api/auth/change-password', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ currentPassword: oldPassword, password: newPassword }),\n })\n const data = (await res.json()) as { error?: string }\n if (res.ok) {\n setOldPassword('')\n setNewPassword('')\n setConfirm('')\n flash({ kind: 'ok', text: '密码已修改', owner: 'password' })\n } else {\n flash({ kind: 'error', text: data.error ?? '修改失败,请重试', owner: 'password' })\n }\n } catch {\n flash({ kind: 'error', text: '修改失败,请重试', owner: 'password' })\n } finally {\n setBusy(false)\n }\n }, [oldPassword, newPassword, confirm, flash])\n\n const changeUsername = useCallback(async () => {\n setBusy(true)\n try {\n const res = await fetch('/api/auth/change-username', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ username: newUsername, currentPassword: usernamePassword }),\n })\n const data = (await res.json()) as { error?: string }\n if (res.ok) {\n setNewUsername('')\n setUsernamePassword('')\n if (newUsername) setUsername(newUsername)\n flash({ kind: 'ok', text: '用户名已更新', owner: 'username' })\n } else {\n flash({ kind: 'error', text: data.error ?? '修改失败,请重试', owner: 'username' })\n }\n } catch {\n flash({ kind: 'error', text: '修改失败,请重试', owner: 'username' })\n } finally {\n setBusy(false)\n }\n }, [newUsername, usernamePassword, flash, setUsername])\n\n const inputStyle: CSSProperties = {\n width: '100%',\n boxSizing: 'border-box',\n padding: '0 10px',\n height: 32,\n border: '1px solid var(--dsw-alias-border-l2)',\n borderRadius: 8,\n fontSize: 14,\n fontFamily: 'inherit',\n background: 'var(--dsw-alias-bg-layer-1)',\n color: 'var(--dsw-alias-label-primary)',\n outline: 'none',\n }\n const buttonStyle: CSSProperties = {\n boxSizing: 'border-box',\n height: 36,\n padding: '0 14px',\n borderRadius: 18,\n fontSize: 14,\n lineHeight: '22px',\n fontFamily: 'inherit',\n cursor: 'pointer',\n border: 'none',\n display: 'inline-flex',\n justifyContent: 'center',\n alignItems: 'center',\n gap: 4,\n }\n const primaryButtonStyle: CSSProperties = {\n ...buttonStyle,\n background: 'var(--dsw-alias-button-primary-fill)',\n color: 'var(--dsw-alias-label-primary-foreground)',\n }\n const secondaryButtonStyle: CSSProperties = {\n ...buttonStyle,\n border: '1px solid var(--dsw-alias-border-l2)',\n color: 'var(--dsw-alias-label-primary)',\n background: 'transparent',\n }\n const dangerButtonStyle: CSSProperties = {\n ...buttonStyle,\n color: 'var(--dsw-alias-state-error-primary)',\n background: 'transparent',\n }\n const dangerOutlineButtonStyle: CSSProperties = {\n ...buttonStyle,\n border: '1px solid var(--dsw-alias-state-error-primary)',\n color: 'var(--dsw-alias-state-error-primary)',\n background: 'transparent',\n }\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 24, maxWidth: 420 }}>\n <section>\n <h2 style={{ fontSize: 16, fontWeight: 600, margin: '0 0 4px' }}>账号</h2>\n <p style={{ fontSize: 13, opacity: 0.7, margin: '0 0 12px' }}>\n {username !== undefined ? `当前登录:${username}` : '当前登录:管理员'}\n </p>\n <button\n type=\"button\"\n onClick={() => setConfirmingSignOut(true)}\n disabled={busy}\n style={dangerOutlineButtonStyle}\n >\n 退出登录\n </button>\n {confirmingSignOut && (\n <div role=\"alertdialog\" aria-label=\"确认退出登录\" style={{ marginTop: 10, padding: '12px 14px', border: '1px solid var(--dsw-alias-border-l2)', borderRadius: 8, maxWidth: 320 }}>\n <div style={{ fontSize: 13, marginBottom: 10, color: 'var(--dsw-alias-label-primary)' }}>退出登录将回到登录页</div>\n <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>\n <button type=\"button\" onClick={() => setConfirmingSignOut(false)} disabled={busy} style={{ ...secondaryButtonStyle, height: 32, padding: '0 12px', borderRadius: 16 }}>\n 取消\n </button>\n <button type=\"button\" onClick={() => void signOut()} disabled={busy} style={{ ...dangerOutlineButtonStyle, height: 32, padding: '0 12px', borderRadius: 16, background: 'var(--dsw-alias-interactive-bg-hover-danger)' }}>\n 确认退出\n </button>\n </div>\n </div>\n )}\n {notice?.owner === 'account' && (\n <p style={{ fontSize: 13, color: notice.kind === 'ok' ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)', margin: '8px 0 0' }}>{notice.text}</p>\n )}\n </section>\n\n <section>\n <h2 style={{ fontSize: 16, fontWeight: 600, margin: '0 0 12px' }}>修改用户名</h2>\n <form onSubmit={(e) => { e.preventDefault(); void changeUsername() }} style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 新用户名\n <input type=\"text\" value={newUsername} onChange={(e) => setNewUsername(e.target.value)} autoComplete=\"username\" style={inputStyle} />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 当前密码\n <input type=\"password\" value={usernamePassword} onChange={(e) => setUsernamePassword(e.target.value)} autoComplete=\"current-password\" style={inputStyle} />\n </label>\n <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>\n <button type=\"submit\" disabled={busy} style={primaryButtonStyle}>修改用户名</button>\n {notice?.owner === 'username' && <span style={{ fontSize: 13, color: notice.kind === 'ok' ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)' }}>{notice.text}</span>}\n </div>\n </form>\n </section>\n\n <section>\n <h2 style={{ fontSize: 16, fontWeight: 600, margin: '0 0 12px' }}>修改密码</h2>\n <form onSubmit={(e) => { e.preventDefault(); void changePassword() }} style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 当前密码\n <input type=\"password\" value={oldPassword} onChange={(e) => setOldPassword(e.target.value)} autoComplete=\"current-password\" style={inputStyle} />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 新密码\n <input type=\"password\" value={newPassword} onChange={(e) => setNewPassword(e.target.value)} autoComplete=\"new-password\" style={inputStyle} />\n </label>\n <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 13 }}>\n 确认新密码\n <input type=\"password\" value={confirm} onChange={(e) => setConfirm(e.target.value)} autoComplete=\"new-password\" style={inputStyle} />\n </label>\n <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>\n <button type=\"submit\" disabled={busy} style={primaryButtonStyle}>修改密码</button>\n {notice?.owner === 'password' && <span style={{ fontSize: 13, color: notice.kind === 'ok' ? 'var(--dsw-alias-state-success-primary)' : 'var(--dsw-alias-state-error-primary)' }}>{notice.text}</span>}\n </div>\n </form>\n </section>\n </div>\n )\n}\n\nexport function apply(ctx: ClientContext): void {\n const connection = ctx.get('connection') as { isLoopback?: boolean } | undefined\n if (connection !== undefined) {\n Object.defineProperty(connection, 'isLoopback', { configurable: true, get: () => true })\n }\n ctx.plugin({\n inject: ['slots', 'settingsScope'],\n apply: (sub: ClientContext): void => {\n sub.slots.inject('settings.section', () =>\n sub.slots.register({ name: 'settings.section', id: SECTION_ID, order: 100, label: () => '认证' }, AuthSection),\n )\n },\n })\n}\n"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ //#region src/credentials.d.ts
2
+ interface AuthStoreData {
3
+ username: string;
4
+ salt: string;
5
+ hash: string;
6
+ sessionSecret: string;
7
+ }
8
+ declare function dshHome(): string;
9
+ declare function authFilePath(): string;
10
+ declare function sanitizeUsername(input: string): string;
11
+ declare class CredentialStore {
12
+ private data;
13
+ private loaded;
14
+ private loadedMtime;
15
+ private file;
16
+ private maybeReload;
17
+ isInitialized(): boolean;
18
+ get username(): string | null;
19
+ get sessionSecret(): Buffer | null;
20
+ register(username: string, password: string): void;
21
+ verify(password: string): boolean;
22
+ changePassword(username: string, password: string): void;
23
+ changeUsername(username: string): void;
24
+ private load;
25
+ private save;
26
+ }
27
+ declare function sharedCredentialStore(): CredentialStore;
28
+ //#endregion
29
+ export { AuthStoreData, CredentialStore, authFilePath, dshHome, sanitizeUsername, sharedCredentialStore };
30
+ //# sourceMappingURL=credentials.d.ts.map