diff --git a/fix_migrate.py b/fix_migrate.py new file mode 100644 index 0000000..1cfc4dc --- /dev/null +++ b/fix_migrate.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +import os, sys +sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend') +os.environ['DJANGO_SETTINGS_MODULE'] = 'diary_system.settings' + +# 先删除有问题的迁移 +import subprocess +subprocess.run(['rm', '-f', 'diary/migrations/0008_*.py'], cwd='/root/.openclaw/workspace/diary-system/backend') + +from django.core.management import execute_from_command_line +sys.argv = ['manage.py', 'migrate'] +execute_from_command_line(sys.argv) diff --git a/frontend-react/src/App.js b/frontend-react/src/App.js index 33915ea..195fb10 100644 --- a/frontend-react/src/App.js +++ b/frontend-react/src/App.js @@ -4,6 +4,8 @@ import StatsPanel from './components/StatsPanel'; import Calendar from './components/Calendar'; import DiaryDetail from './components/DiaryDetail'; import ExperienceList from './components/ExperienceList'; +import Login from './components/Login'; +import Register from './components/Register'; const API_BASE = '/api'; @@ -11,6 +13,7 @@ const API_BASE = '/api'; * ⚠️ 主应用组件 * * 核心功能: + * - 用户认证 * - 统计面板 * - 日历组件 ⭐ * - 日记详情 @@ -19,6 +22,8 @@ const API_BASE = '/api'; * ⚠️ 修改前阅读 docs/README.md */ function App() { + const [user, setUser] = useState(null); + const [authView, setAuthView] = useState('login'); // login | register const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]); const [diaryDates, setDiaryDates] = useState([]); const [entries, setEntries] = useState([]); @@ -28,9 +33,50 @@ function App() { const [error, setError] = useState(null); useEffect(() => { - loadData(); + // 检查是否已登录 + checkAuth(); }, []); + const checkAuth = async () => { + try { + const res = await axios.get(`${API_BASE}/auth/me/`); + setUser(res.data); + loadData(); + } catch (err) { + setLoading(false); + } + }; + + const handleLogin = (userData, view) => { + if (view) { + setAuthView(view); + } else if (userData) { + setUser(userData); + loadData(); + } + }; + + const handleRegister = (userData, view) => { + if (view) { + setAuthView(view); + } else if (userData) { + setUser(userData); + loadData(); + } + }; + + const handleLogout = async () => { + try { + await axios.post(`${API_BASE}/auth/logout/`); + setUser(null); + setEntries([]); + setExperiences([]); + setStats({}); + } catch (err) { + console.error('登出失败:', err); + } + }; + const loadData = async () => { try { const [statsRes, entriesRes, expRes] = await Promise.all([ @@ -50,6 +96,14 @@ function App() { } }; + // 未登录显示登录/注册界面 + if (!user) { + if (authView === 'register') { + return ; + } + return ; + } + const handleDateSelect = (date) => { setSelectedDate(date); }; @@ -67,8 +121,16 @@ function App() { return (
-

⚡ 码神的日记系统

-

记录每天的进步与成长

+
+
+

⚡ 码神的日记系统

+

记录每天的进步与成长

+
+
+ 👤 {user.username} + +
+
diff --git a/frontend-react/src/components/Login.js b/frontend-react/src/components/Login.js new file mode 100644 index 0000000..fe579f6 --- /dev/null +++ b/frontend-react/src/components/Login.js @@ -0,0 +1,77 @@ +import React, { useState } from 'react'; +import axios from 'axios'; + +const API_BASE = '/api'; + +/** + * 登录组件 + */ +function Login({ onLogin }) { + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const [loading, setLoading] = useState(false); + + const handleSubmit = async (e) => { + e.preventDefault(); + setLoading(true); + setError(''); + + try { + const res = await axios.post(`${API_BASE}/auth/login/`, { + username, + password + }); + onLogin(res.data.user); + } catch (err) { + setError(err.response?.data?.message || '登录失败'); + } finally { + setLoading(false); + } + }; + + return ( +
+
+

⚡ 码神日记系统

+

登录

+ + {error &&
{error}
} + +
+
+ + setUsername(e.target.value)} + required + placeholder="请输入用户名" + /> +
+ +
+ + setPassword(e.target.value)} + required + placeholder="请输入密码" + /> +
+ + +
+ +

+ 还没有账号? { e.preventDefault(); onLogin(null, 'register'); }}>立即注册 +

+
+
+ ); +} + +export default Login; diff --git a/frontend-react/src/components/Register.js b/frontend-react/src/components/Register.js new file mode 100644 index 0000000..818965f --- /dev/null +++ b/frontend-react/src/components/Register.js @@ -0,0 +1,112 @@ +import React, { useState } from 'react'; +import axios from 'axios'; + +const API_BASE = '/api'; + +/** + * 注册组件 + */ +function Register({ onRegister }) { + const [username, setUsername] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [error, setError] = useState(''); + const [loading, setLoading] = useState(false); + + const handleSubmit = async (e) => { + e.preventDefault(); + + if (password !== confirmPassword) { + setError('两次输入的密码不一致'); + return; + } + + if (password.length < 6) { + setError('密码至少 6 位'); + return; + } + + setLoading(true); + setError(''); + + try { + const res = await axios.post(`${API_BASE}/auth/register/`, { + username, + email, + password + }); + onRegister(res.data.user); + } catch (err) { + setError(err.response?.data?.username?.[0] || err.response?.data?.message || '注册失败'); + } finally { + setLoading(false); + } + }; + + return ( +
+
+

⚡ 码神日记系统

+

注册

+ + {error &&
{error}
} + +
+
+ + setUsername(e.target.value)} + required + placeholder="请输入用户名" + /> +
+ +
+ + setEmail(e.target.value)} + placeholder="请输入邮箱" + /> +
+ +
+ + setPassword(e.target.value)} + required + placeholder="至少 6 位" + /> +
+ +
+ + setConfirmPassword(e.target.value)} + required + placeholder="再次输入密码" + /> +
+ + +
+ +

+ 已有账号? { e.preventDefault(); onRegister(null, 'login'); }}>立即登录 +

+
+
+ ); +} + +export default Register; diff --git a/frontend-react/src/index.css b/frontend-react/src/index.css index 7a015f6..1c60283 100644 --- a/frontend-react/src/index.css +++ b/frontend-react/src/index.css @@ -262,6 +262,128 @@ header p { color: #999; } +/* 认证界面 */ +.auth-container { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 20px; +} + +.auth-card { + background: white; + padding: 40px; + border-radius: 10px; + box-shadow: 0 4px 20px rgba(0,0,0,0.2); + width: 100%; + max-width: 400px; +} + +.auth-card h1 { + color: #667eea; + font-size: 2em; + text-align: center; + margin-bottom: 10px; +} + +.auth-card h2 { + color: #333; + font-size: 1.5em; + text-align: center; + margin-bottom: 30px; +} + +.form-group { + margin-bottom: 20px; +} + +.form-group label { + display: block; + color: #555; + font-weight: 500; + margin-bottom: 8px; +} + +.form-group input { + width: 100%; + padding: 12px; + border: 1px solid #ddd; + border-radius: 5px; + font-size: 1em; + transition: border-color 0.2s; +} + +.form-group input:focus { + outline: none; + border-color: #667eea; +} + +.btn-primary { + width: 100%; + padding: 12px; + background: linear-gradient(135deg, #667eea, #764ba2); + color: white; + border: none; + border-radius: 5px; + font-size: 1.1em; + font-weight: 600; + cursor: pointer; + transition: transform 0.2s; +} + +.btn-primary:hover { + transform: translateY(-2px); +} + +.btn-primary:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.auth-tip { + text-align: center; + margin-top: 20px; + color: #666; +} + +.auth-tip a { + color: #667eea; + text-decoration: none; +} + +.auth-tip a:hover { + text-decoration: underline; +} + +/* 用户信息 */ +.header-content { + display: flex; + justify-content: space-between; + align-items: center; +} + +.user-info { + display: flex; + align-items: center; + gap: 15px; + color: white; +} + +.btn-logout { + padding: 8px 16px; + background: rgba(255,255,255,0.2); + color: white; + border: 1px solid rgba(255,255,255,0.5); + border-radius: 5px; + cursor: pointer; + transition: background 0.2s; +} + +.btn-logout:hover { + background: rgba(255,255,255,0.3); +} + /* 移动端 */ @media (max-width: 768px) { .grid-2 { diff --git a/migrate_data.py b/migrate_data.py new file mode 100644 index 0000000..85fcfdf --- /dev/null +++ b/migrate_data.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +""" +创建默认用户并迁移现有数据 +""" +import os, sys +sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend') +os.environ['DJANGO_SETTINGS_MODULE'] = 'diary_system.settings' + +import django +django.setup() + +from django.contrib.auth.models import User +from diary.models import DiaryEntry, Experience, Task, Comment + +# 1. 创建默认用户(北极星) +print("📝 创建默认用户...") +default_user, created = User.objects.get_or_create( + username='beijixing', + defaults={'email': 'beijixing@example.com'} +) +if created: + default_user.set_password('beijixing123') + default_user.save() + print(f"✅ 创建默认用户:beijixing / beijixing123") +else: + print(f"✅ 默认用户已存在:beijixing") + +# 2. 迁移现有数据到默认用户 +print("\n📦 迁移现有数据...") + +# 迁移日记 +diary_count = DiaryEntry.objects.filter(user__isnull=True).count() +DiaryEntry.objects.filter(user__isnull=True).update(user=default_user) +print(f" 日记:{diary_count} 条") + +# 迁移经验 +exp_count = Experience.objects.filter(user__isnull=True).count() +Experience.objects.filter(user__isnull=True).update(user=default_user) +print(f" 经验:{exp_count} 条") + +# 迁移任务 +task_count = Task.objects.filter(user__isnull=True).count() +Task.objects.filter(user__isnull=True).update(user=default_user) +print(f" 任务:{task_count} 条") + +# 迁移评论 +comment_count = Comment.objects.filter(user__isnull=True).count() +Comment.objects.filter(user__isnull=True).update(user=default_user) +print(f" 评论:{comment_count} 条") + +print("\n✅ 数据迁移完成!") +print(f"\n📝 默认用户:beijixing") +print(f"🔑 密码:beijixing123") diff --git a/run_syncdb.py b/run_syncdb.py new file mode 100644 index 0000000..03f5504 --- /dev/null +++ b/run_syncdb.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 +import os, sys +sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend') +os.environ['DJANGO_SETTINGS_MODULE'] = 'diary_system.settings' +from django.core.management import execute_from_command_line +sys.argv = ['manage.py', 'migrate', '--run-syncdb'] +execute_from_command_line(sys.argv) diff --git a/setup_auth_tables.py b/setup_auth_tables.py new file mode 100644 index 0000000..c651761 --- /dev/null +++ b/setup_auth_tables.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +""" +手动创建 auth 相关表 +""" +import os, sys +sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend') +os.environ['DJANGO_SETTINGS_MODULE'] = 'diary_system.settings' + +import django +django.setup() + +from django.core.management import call_command +from django.db import connection + +# 先迁移 auth 和 contenttypes +print("📦 创建 auth 相关表...") +call_command('migrate', 'contenttypes', '--run-syncdb') +call_command('migrate', 'auth', '--run-syncdb') +call_command('migrate', 'admin', '--run-syncdb') +call_command('migrate', 'sessions', '--run-syncdb') + +print("✅ auth 表创建完成") + +# 再运行 diary 迁移 +print("\n📦 运行 diary 迁移...") +call_command('migrate', 'diary', '--run-syncdb') +print("✅ diary 迁移完成")