From a0956299be3960244e653070c22cc131eaab53cd Mon Sep 17 00:00:00 2001 From: DiheChen Date: Mon, 9 Nov 2026 21:22:40 +0800 Subject: [PATCH] feat: switch registration and reset account passwords in the admin page --- web/src/features/admin/accounts.tsx | 81 ++++++++++++++++++++++++ web/src/features/admin/admin-page.tsx | 4 +- web/src/features/admin/site-settings.tsx | 67 ++++++++++++++++++++ 3 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 web/src/features/admin/accounts.tsx diff --git a/web/src/features/admin/accounts.tsx b/web/src/features/admin/accounts.tsx new file mode 100644 index 0000000..0c01ce3 --- /dev/null +++ b/web/src/features/admin/accounts.tsx @@ -0,0 +1,81 @@ +import type { AdminAccount } from '@/core/realtime' +import { useState } from 'react' +import { api } from '@/core/api' +import { adminStore } from './admin-store' +import { ConfirmDialog } from './confirm-dialog' + +export function Accounts() { + const [username, setUsername] = useState('') + const [found, setFound] = useState(null) + const [password, setPassword] = useState(null) + const [confirming, setConfirming] = useState(false) + const [busy, setBusy] = useState(false) + const find = async () => { + setBusy(true) + try { + setFound(await adminStore.call(() => api.adminGetAccount(username), { NOT_FOUND: '没有这个用户名的账号。' })) + setPassword(null) + } + finally { + setBusy(false) + } + } + const reset = async (account: AdminAccount) => { + const result = await adminStore.call(() => api.adminResetAccountPassword(account.username)) + if (result !== null) + setPassword(result.password) + setConfirming(false) + } + return ( +
+
+

账号

+
+
{ + event.preventDefault() + void find() + }} + > +
+
+ + 按用户名查找。用户忘记密码时,可以在这里重置。 +
+
+ setUsername(event.target.value.trim().toLowerCase())} /> + +
+
+ {found && ( +
+
+ {found.username} + {`昵称:${found.member.name}`} +
+
+ +
+
+ )} + {found && password && ( +
+
+ + 新密码: + {password} + + 只显示这一次,请转告用户。这个账号在所有设备上都已退出登录。 +
+
+ )} +
+ {found && confirming && ( + reset(found)} onClose={() => setConfirming(false)}> + {`将为 ${found.username} 生成一个新密码,原来的密码立即失效,这个账号在所有设备上都会退出登录。`} + + )} +
+ ) +} diff --git a/web/src/features/admin/admin-page.tsx b/web/src/features/admin/admin-page.tsx index 3eaafa0..34cd288 100644 --- a/web/src/features/admin/admin-page.tsx +++ b/web/src/features/admin/admin-page.tsx @@ -2,6 +2,7 @@ import { LogOut } from 'lucide-react' import { useState } from 'react' import { brandStore } from '@/core/brand' import { useStore } from '@/shared/lib/use-store' +import { Accounts } from './accounts' import { adminStore } from './admin-store' import { PersistentRooms } from './persistent-rooms' import { SiteSettings } from './site-settings' @@ -38,7 +39,7 @@ export function AdminPage({ onEntered }: { onEntered: (ref: string) => void }) {
{brandName}

后台

-

管理品牌名、常驻房间、音源账号和已存储的媒体。

+

管理品牌名、注册、用户账号、常驻房间、音源账号和已存储的媒体。

{signedIn && ( + )} ) } + +function RegistrationSetting() { + const [open, setOpen] = useState(null) + const [failed, setFailed] = useState(false) + const [attempt, setAttempt] = useState(0) + const [busy, setBusy] = useState(false) + useEffect(() => { + let active = true + api.getSite().then((site) => { + if (active) + setOpen(site.registrationOpen) + }, () => { + if (active) + setFailed(true) + }) + return () => { + active = false + } + }, [attempt]) + const toggle = async () => { + if (open === null) + return + setBusy(true) + try { + const done = await adminStore.call(() => open ? api.adminCloseRegistration() : api.adminOpenRegistration()) + if (done === null) + return + setOpen(!open) + notificationStore.success(open ? '已关闭注册' : '已开放注册') + } + finally { + setBusy(false) + } + } + const description = failed + ? '暂时无法获取注册状态。' + : open === null + ? '正在获取注册状态…' + : open + ? '现在任何人都可以注册账号。关闭后不能再注册新账号,已有账号照常登录,游客照常使用。' + : '现在不能注册新账号。已有账号照常登录,游客照常使用。' + return ( +
+
+ 注册 + {description} +
+
+ {failed + ? ( + + ) + : } +
+
+ ) +}