Add HeXi bot codebase: custom plugins, web frontends, tests
- hexi core: message handling, rate limiting, cooldown, plugin manager - Custom plugins: BF stats, daily check-in, quotes, persona cards, etc. - Community plugins vendored under hexi/plugins with local fixes - Web admin frontends (learning-chat, persona-admin), unified hexi/web - Tests for rate_limit/cooldown/memes/persona; poetry.lock Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Button, Card, Form, Input, Label, TextField, toast } from '@heroui/react'
|
||||
import { hubSettings, hubChangePassword } from '../../api/client'
|
||||
|
||||
interface SettingsData {
|
||||
ok: boolean
|
||||
user: { id: number; username: string; created_at: string }
|
||||
plugins: { id: string; name: string; icon: string }[]
|
||||
version: string
|
||||
}
|
||||
|
||||
export default function SettingsPage() {
|
||||
const [data, setData] = useState<SettingsData | null>(null)
|
||||
const [error, setError] = useState('')
|
||||
const [oldPassword, setOldPassword] = useState('')
|
||||
const [newPassword, setNewPassword] = useState('')
|
||||
const [confirm, setConfirm] = useState('')
|
||||
const [busy, setBusy] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
hubSettings()
|
||||
.then(setData)
|
||||
.catch((e: any) => setError(e.message || '加载失败'))
|
||||
}, [])
|
||||
|
||||
const changePassword = async () => {
|
||||
if (!oldPassword) { toast.warning('请输入原密码'); return }
|
||||
if (newPassword.length < 6) { toast.warning('新密码长度不能少于 6 位'); return }
|
||||
if (newPassword !== confirm) { toast.warning('两次输入的新密码不一致'); return }
|
||||
setBusy(true)
|
||||
try {
|
||||
await hubChangePassword(oldPassword, newPassword)
|
||||
toast.success('密码已修改')
|
||||
setOldPassword(''); setNewPassword(''); setConfirm('')
|
||||
} catch (e: any) {
|
||||
toast.danger(e.message || '修改失败')
|
||||
} finally { setBusy(false) }
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<h2 className="text-xl font-semibold text-slate-800">设置</h2>
|
||||
<div className="grid gap-6 lg:grid-cols-2">
|
||||
<Card className="border border-slate-200 bg-white">
|
||||
<Card.Header><Card.Title>账号信息</Card.Title></Card.Header>
|
||||
<Card.Content>
|
||||
{error ? (
|
||||
<p className="text-red-500 text-sm">{error}</p>
|
||||
) : !data ? (
|
||||
<p className="text-slate-400 text-sm">加载中…</p>
|
||||
) : (
|
||||
<div className="space-y-3 text-sm">
|
||||
<div className="flex justify-between border-b border-slate-100 pb-2">
|
||||
<span className="text-slate-500">用户名</span>
|
||||
<span className="font-medium text-slate-800">{data.user.username}</span>
|
||||
</div>
|
||||
<div className="flex justify-between border-b border-slate-100 pb-2">
|
||||
<span className="text-slate-500">用户 ID</span>
|
||||
<span className="font-medium text-slate-800">{data.user.id}</span>
|
||||
</div>
|
||||
<div className="flex justify-between border-b border-slate-100 pb-2">
|
||||
<span className="text-slate-500">创建时间</span>
|
||||
<span className="text-slate-800">{data.user.created_at || '—'}</span>
|
||||
</div>
|
||||
<div className="flex justify-between border-b border-slate-100 pb-2">
|
||||
<span className="text-slate-500">已接入插件</span>
|
||||
<span className="text-slate-800">{data.plugins.length} 个</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Card.Content>
|
||||
</Card>
|
||||
|
||||
<Card className="border border-slate-200 bg-white">
|
||||
<Card.Header><Card.Title>修改密码</Card.Title></Card.Header>
|
||||
<Card.Content>
|
||||
<Form className="space-y-4">
|
||||
<TextField><Label>原密码</Label><Input type="password" value={oldPassword} onChange={e => setOldPassword(e.target.value)} /></TextField>
|
||||
<TextField><Label>新密码</Label><Input type="password" value={newPassword} onChange={e => setNewPassword(e.target.value)} placeholder="至少 6 位" /></TextField>
|
||||
<TextField><Label>确认新密码</Label><Input type="password" value={confirm} onChange={e => setConfirm(e.target.value)} /></TextField>
|
||||
<Button isDisabled={busy} onPress={changePassword}>{busy ? '修改中…' : '保存修改'}</Button>
|
||||
</Form>
|
||||
<p className="text-xs text-slate-400 mt-3">修改后旧登录态失效,当前会话保持在线。</p>
|
||||
</Card.Content>
|
||||
</Card>
|
||||
|
||||
<Card className="border border-slate-200 bg-white lg:col-span-2">
|
||||
<Card.Header><Card.Title>关于</Card.Title></Card.Header>
|
||||
<Card.Content>
|
||||
<p className="text-sm text-slate-500">HeXi 管理系统 v{data?.version || '—'} · 统一 Web 管理台</p>
|
||||
</Card.Content>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user