feat: DevTools 数据库监看面板 + 隧道控制 + 多项 Bug 修复
**DevTools 新增功能 (Tasks 13-14):** - 首页仪表盘添加数据库实时监看卡片 (5端口状态 + 记忆数) - 侧边栏新增数据库面板,支持自动 5 秒刷新 - 数据库面板显示 PostgreSQL/Redis/Qdrant/MinIO/NATS 端口状态 - 隧道控制按钮 (启动/停止/重启/查看状态) - 新增 API 端点: GET /api/database/status, POST /api/tunnel/:action - 更新 docs/api-reference/ API 文档 **Bug 修复 (Task 15):** - 修复 pgrep -f 自匹配导致隧道状态误判 (添加 ^ssh 锚点) - devtools/src/index.js (dashboard + database/status) - scripts/tunnel.sh (is_tunnel_running + show_status) - 修复数据库面板缺少自动刷新定时器 - 修复侧边栏数据库徽章永远 display:none - 修复僵尸进程场景下按钮死锁问题 **其他改进:** - .gitignore 添加 backend/cmd, backend/iot-debug-service/main - 前端多项改进 (登录/注册/会话/流式动画等)
This commit is contained in:
+94
-15
@@ -6,12 +6,16 @@ import { useAuth } from '@/hooks/useAuth';
|
||||
import { useChat } from '@/hooks/useChat';
|
||||
|
||||
export default function App() {
|
||||
const { isLoggedIn, login, loading: authLoading } = useAuth();
|
||||
const { isLoggedIn, login, register, loading: authLoading } = useAuth();
|
||||
const { send } = useChat();
|
||||
|
||||
const [authMode, setAuthMode] = useState<'login' | 'register'>('login');
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
const [verifyCode, setVerifyCode] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [successMsg, setSuccessMsg] = useState('');
|
||||
|
||||
const handleLogin = async () => {
|
||||
setError('');
|
||||
@@ -21,7 +25,28 @@ export default function App() {
|
||||
}
|
||||
};
|
||||
|
||||
// 登录页面 (开发阶段暂时禁用注册)
|
||||
const handleRegister = async () => {
|
||||
setError('');
|
||||
setSuccessMsg('');
|
||||
if (!email) {
|
||||
setError('请输入邮箱');
|
||||
return;
|
||||
}
|
||||
const result = await register(username, password, email, verifyCode || '000000');
|
||||
if (!result.success) {
|
||||
setError(result.error || '注册失败');
|
||||
} else {
|
||||
setSuccessMsg('注册成功!正在进入...');
|
||||
}
|
||||
};
|
||||
|
||||
const switchMode = (mode: 'login' | 'register') => {
|
||||
setAuthMode(mode);
|
||||
setError('');
|
||||
setSuccessMsg('');
|
||||
};
|
||||
|
||||
// 登录/注册页面
|
||||
if (!isLoggedIn) {
|
||||
return (
|
||||
<div className="min-h-screen bg-[#FFFAF5] dark:bg-[#1a1a2e] flex items-center justify-center p-4">
|
||||
@@ -35,10 +60,30 @@ export default function App() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 登录表单 */}
|
||||
{/* 登录/注册表单 */}
|
||||
<div className="bg-white dark:bg-gray-900 rounded-2xl shadow-lg p-6 space-y-4 border border-pink-100 dark:border-pink-900">
|
||||
<div className="text-center mb-2">
|
||||
<span className="text-sm font-medium text-pink-500">登录</span>
|
||||
{/* 模式切换 */}
|
||||
<div className="flex rounded-xl bg-pink-50 dark:bg-pink-900/20 p-1">
|
||||
<button
|
||||
onClick={() => switchMode('login')}
|
||||
className={`flex-1 py-1.5 text-sm rounded-lg font-medium transition-colors ${
|
||||
authMode === 'login'
|
||||
? 'bg-white dark:bg-gray-800 text-pink-500 shadow-sm'
|
||||
: 'text-gray-400 hover:text-pink-400'
|
||||
}`}
|
||||
>
|
||||
登录
|
||||
</button>
|
||||
<button
|
||||
onClick={() => switchMode('register')}
|
||||
className={`flex-1 py-1.5 text-sm rounded-lg font-medium transition-colors ${
|
||||
authMode === 'register'
|
||||
? 'bg-white dark:bg-gray-800 text-pink-500 shadow-sm'
|
||||
: 'text-gray-400 hover:text-pink-400'
|
||||
}`}
|
||||
>
|
||||
注册
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<input
|
||||
@@ -46,33 +91,67 @@ export default function App() {
|
||||
placeholder="用户名"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleLogin()}
|
||||
onKeyDown={(e) => e.key === 'Enter' && (authMode === 'login' ? handleLogin() : handleRegister())}
|
||||
className="w-full px-4 py-2.5 rounded-xl border border-pink-200 dark:border-pink-800 bg-white dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-200 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-pink-400"
|
||||
/>
|
||||
|
||||
{authMode === 'register' && (
|
||||
<input
|
||||
type="email"
|
||||
placeholder="邮箱"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="w-full px-4 py-2.5 rounded-xl border border-pink-200 dark:border-pink-800 bg-white dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-200 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-pink-400"
|
||||
/>
|
||||
)}
|
||||
|
||||
<input
|
||||
type="password"
|
||||
placeholder="密码"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleLogin()}
|
||||
onKeyDown={(e) => e.key === 'Enter' && (authMode === 'login' ? handleLogin() : handleRegister())}
|
||||
className="w-full px-4 py-2.5 rounded-xl border border-pink-200 dark:border-pink-800 bg-white dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-200 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-pink-400"
|
||||
/>
|
||||
|
||||
{authMode === 'register' && (
|
||||
<input
|
||||
type="text"
|
||||
placeholder="验证码 (开发环境输入 000000)"
|
||||
value={verifyCode}
|
||||
onChange={(e) => setVerifyCode(e.target.value)}
|
||||
maxLength={6}
|
||||
className="w-full px-4 py-2.5 rounded-xl border border-pink-200 dark:border-pink-800 bg-white dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-200 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-pink-400"
|
||||
/>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<p className="text-xs text-red-400 text-center">{error}</p>
|
||||
)}
|
||||
{successMsg && (
|
||||
<p className="text-xs text-green-400 text-center">{successMsg}</p>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={handleLogin}
|
||||
disabled={authLoading || !username || !password}
|
||||
className="w-full py-2.5 rounded-xl bg-pink-400 hover:bg-pink-500 disabled:bg-pink-300 text-white font-medium text-sm transition-colors"
|
||||
>
|
||||
{authLoading ? '请稍候...' : '进入昔涟的世界 ♪'}
|
||||
</button>
|
||||
{authMode === 'login' ? (
|
||||
<button
|
||||
onClick={handleLogin}
|
||||
disabled={authLoading || !username || !password}
|
||||
className="w-full py-2.5 rounded-xl bg-pink-400 hover:bg-pink-500 disabled:bg-pink-300 text-white font-medium text-sm transition-colors"
|
||||
>
|
||||
{authLoading ? '请稍候...' : '进入昔涟的世界 ♪'}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleRegister}
|
||||
disabled={authLoading || !username || !password || !email}
|
||||
className="w-full py-2.5 rounded-xl bg-pink-400 hover:bg-pink-500 disabled:bg-pink-300 text-white font-medium text-sm transition-colors"
|
||||
>
|
||||
{authLoading ? '请稍候...' : '注册并进入 ♪'}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<p className="text-xs text-gray-400 text-center">
|
||||
开发阶段 · 管理员凭据: admin / cyrene-dev-admin
|
||||
{authMode === 'register' ? '开发阶段 · 验证码统一使用 000000' : '欢迎回来 ♪'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user