feat: DevTools调试工具 + 前端样式修复 + 管理员登录系统

DevTools (新增):
- 进程管理器: 启动/停止/重启/编译 + 端口自动释放
- 服务接管 (tryAdopt): 检测已运行服务,健康检查通过则直接接管
- 一键启动 (startAllSequential): 按 ai-core→gateway→frontend 顺序启动
- 日志布局切换: 标签页模式 ↔ 三栏并列模式
- 性能监控: CPU/内存采样 + SVG 折线图
- Web UI + WebSocket 实时推送

前端修复:
- tailwind.config.ts: 修复空配置导致 CSS 不加载 (增加 content/colors/fontFamily)
- postcss.config.js: 新建缺失的 PostCSS 配置
- App.tsx: 移除注册功能,仅保留管理员登录 (admin / cyrene-dev-admin)

后端新增:
- config.go: AdminUsername/AdminPassword/RegistrationEnabled 环境变量
- auth_handler.go: 管理员登录 + 注册邮箱验证码 + 注册开关控制
- 管理员凭据: admin / cyrene-dev-admin (默认)

其他:
- .gitignore: 新增 devtools/node_modules/ devtools/logs/ devtools/package-lock.json
- devtools.sh: DevTools 一键启动脚本
This commit is contained in:
2026-05-16 10:49:43 +08:00
parent 86b70b1613
commit cd60b01cf3
32 changed files with 4569 additions and 2845 deletions
+1 -2746
View File
File diff suppressed because it is too large Load Diff
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>昔涟 - Cyrene AI</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+2815
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
+15 -32
View File
@@ -6,24 +6,22 @@ import { useAuth } from '@/hooks/useAuth';
import { useChat } from '@/hooks/useChat';
export default function App() {
const { isLoggedIn, login, register, loading: authLoading } = useAuth();
const { isLoggedIn, login, loading: authLoading } = useAuth();
const { send } = useChat();
const [authMode, setAuthMode] = useState<'login' | 'register'>('login');
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleAuth = async () => {
const handleLogin = async () => {
setError('');
const fn = authMode === 'login' ? login : register;
const result = await fn(username, password);
const result = await login(username, password);
if (!result.success) {
setError(result.error || '操作失败');
setError(result.error || '登录失败');
}
};
// 登录页面
// 登录页面 (开发阶段禁用注册)
if (!isLoggedIn) {
return (
<div className="min-h-screen bg-[#FFFAF5] dark:bg-[#1a1a2e] flex items-center justify-center p-4">
@@ -39,27 +37,8 @@ export default function App() {
{/* 表单 */}
<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="flex rounded-lg bg-gray-100 dark:bg-gray-800 p-1">
<button
onClick={() => setAuthMode('login')}
className={`flex-1 py-2 text-sm rounded-md transition-colors ${
authMode === 'login'
? 'bg-white dark:bg-gray-700 text-pink-500 font-medium shadow-sm'
: 'text-gray-400'
}`}
>
</button>
<button
onClick={() => setAuthMode('register')}
className={`flex-1 py-2 text-sm rounded-md transition-colors ${
authMode === 'register'
? 'bg-white dark:bg-gray-700 text-pink-500 font-medium shadow-sm'
: 'text-gray-400'
}`}
>
</button>
<div className="text-center mb-2">
<span className="text-sm font-medium text-pink-500"></span>
</div>
<input
@@ -67,7 +46,7 @@ export default function App() {
placeholder="用户名"
value={username}
onChange={(e) => setUsername(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleAuth()}
onKeyDown={(e) => e.key === 'Enter' && handleLogin()}
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"
/>
@@ -76,7 +55,7 @@ export default function App() {
placeholder="密码"
value={password}
onChange={(e) => setPassword(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleAuth()}
onKeyDown={(e) => e.key === 'Enter' && handleLogin()}
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"
/>
@@ -85,12 +64,16 @@ export default function App() {
)}
<button
onClick={handleAuth}
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 ? '请稍候...' : authMode === 'login' ? '进入昔涟的世界 ♪' : '注册并开始'}
{authLoading ? '请稍候...' : '进入昔涟的世界 ♪'}
</button>
<p className="text-xs text-gray-400 text-center">
· 管理员: admin / cyrene-dev-admin
</p>
</div>
</div>
</div>
@@ -1,7 +1,7 @@
import { CyreneAvatar } from '@/components/persona/CyreneAvatar';
interface MessageBubbleProps {
role: 'user' | 'assistant';
role: 'user' | 'assistant' | 'system';
content: string;
timestamp: number;
isStreaming?: boolean;
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+31
View File
@@ -0,0 +1,31 @@
import type { Config } from 'tailwindcss';
export default {
content: [
'./index.html',
'./src/**/*.{js,ts,jsx,tsx}',
],
darkMode: 'media',
theme: {
extend: {
colors: {
pink: {
50: '#fdf2f8',
100: '#fce7f3',
200: '#fbcfe8',
300: '#f9a8d4',
400: '#f472b6',
500: '#ec4899',
600: '#db2777',
700: '#be185d',
800: '#9d174d',
900: '#831843',
},
},
fontFamily: {
sans: ['"Noto Sans SC"', '-apple-system', 'BlinkMacSystemFont', '"Segoe UI"', 'Roboto', 'sans-serif'],
},
},
},
plugins: [],
} satisfies Config;