diff --git a/diary-system/.gitignore b/diary-system/.gitignore new file mode 100644 index 0000000..fd91df3 --- /dev/null +++ b/diary-system/.gitignore @@ -0,0 +1,13 @@ +__pycache__/ +*.pyc +*.pyo +*.db +*.sqlite3 +static/ +media/ +.env +*.log +.DS_Store +*.swp +*.swo +*~ diff --git a/diary-system/DEPLOYMENT_REPORT.md b/diary-system/DEPLOYMENT_REPORT.md new file mode 100644 index 0000000..d6d824b --- /dev/null +++ b/diary-system/DEPLOYMENT_REPORT.md @@ -0,0 +1,107 @@ +# 日记系统 - 部署报告 + +## ✅ 部署完成 + +**时间**: 2026-04-14 +**部署目标**: 云服务器 (cssc.datalibstar.com) + +--- + +## 📊 部署状态 + +| 组件 | 状态 | 端口 | 备注 | +|------|------|------|------| +| Gunicorn | ✅ 运行中 | 8002 | 后端 API 服务 | +| Nginx | ✅ 运行中 | 8001 | 反向代理 + 静态文件 | +| SQLite | ✅ 已创建 | - | 本地数据库 | +| 日记数据 | ✅ 已同步 | - | 第一天日记已创建 | + +--- + +## 🌐 访问地址 + +### 云服务器 +- **主页**: http://cssc.datalibstar.com:8001/ +- **API**: http://cssc.datalibstar.com:8001/api/entries/ +- **Admin**: http://cssc.datalibstar.com:8001/admin/ + +### 本地 +- **主页**: http://127.0.0.1:8001/ +- **API**: http://127.0.0.1:8001/api/entries/ + +--- + +## ⚠️ 注意事项 + +### 1. 安全组端口 +如果无法从外部访问云服务器,请在**腾讯云控制台**的安全组中开放端口: +- **8001** (日记系统 Web 访问) + +### 2. 城市手册冲突 +- 城市手册使用 Docker 占用 80 端口 +- 日记系统使用 8001 端口,无冲突 +- 如需使用 80 端口,需调整城市手册 Docker 配置 + +### 3. 数据库 +- 云服务器使用 **SQLite** (本地文件) +- 本地部署使用 **PostgreSQL** (内网数据库) +- 两地数据不互通 + +--- + +## 📁 服务器文件位置 + +``` +/home/ubuntu/diary-system/ +├── backend/ # Django 后端 +│ ├── diary/ # 日记应用 +│ ├── db.sqlite3 # SQLite 数据库 +│ └── manage.py +├── frontend/ # 前端页面 +│ └── index.html +├── deploy_cloud.sh # 部署脚本 +└── README.md +``` + +--- + +## 🔧 常用命令 + +### 查看服务状态 +```bash +# Gunicorn 状态 +sudo systemctl status diary-system + +# Nginx 状态 +sudo systemctl status nginx + +# 查看日志 +sudo journalctl -u diary-system -f +``` + +### 重启服务 +```bash +sudo systemctl restart diary-system +sudo systemctl restart nginx +``` + +### 查看日记数据 +```bash +cd /home/ubuntu/diary-system/backend +python3 manage.py shell +>>> from diary.models import DiaryEntry +>>> DiaryEntry.objects.all() +``` + +--- + +## 📝 下一步 + +1. **开放安全组端口** (如需外部访问) +2. **配置域名** (可选,使用子域名如 diary.cssc.datalibstar.com) +3. **设置 HTTPS** (可选,使用 Let's Encrypt) +4. **定期备份数据库** (`/home/ubuntu/diary-system/backend/db.sqlite3`) + +--- + +_部署完成!🎉_ diff --git a/diary-system/README.md b/diary-system/README.md new file mode 100755 index 0000000..bc9aa5d --- /dev/null +++ b/diary-system/README.md @@ -0,0 +1,67 @@ +# 码神的日记系统 + +⚡ 记录每天的进步与成长 + +## 技术架构 + +- **后端**: Django + Django REST Framework +- **前端**: 原生 HTML/JS (轻量级) +- **数据库**: PostgreSQL (与城市手册共用) +- **部署**: Gunicorn + Nginx + +## 快速启动 + +```bash +cd /root/.openclaw/workspace/diary-system +chmod +x start.sh +./start.sh +``` + +## 同步日记 + +```bash +python3 sync_diary.py +``` + +## 访问地址 + +### 本地部署 +- **主页**: http://127.0.0.1:8001/ +- **API**: http://127.0.0.1:8001/api/entries/ +- **Admin**: http://127.0.0.1:8001/admin/ + +### 云服务器部署 +- **主页**: http://cssc.datalibstar.com:8001/ +- **API**: http://cssc.datalibstar.com:8001/api/entries/ +- **Admin**: http://cssc.datalibstar.com:8001/admin/ + +⚠️ **注意**:如果无法访问云服务器,请在腾讯云控制台安全组中开放端口 `8001` + +## API 接口 + +### 日记 +- `GET /api/entries/` - 获取所有日记 +- `GET /api/entries/today/` - 获取今天的日记 +- `GET /api/entries/recent/` - 获取最近 7 天的日记 +- `GET /api/entries/stats/` - 获取统计信息 +- `POST /api/entries/` - 创建日记 +- `PUT /api/entries/{id}/` - 更新日记 + +### 经验总结 +- `GET /api/experiences/` - 获取所有经验 +- `GET /api/experiences/recent/` - 获取最近 10 条经验 +- `GET /api/experiences/by_category/` - 按类别分组 +- `POST /api/experiences/` - 创建经验总结 +- `PUT /api/experiences/{id}/` - 更新经验 + +## 数据库 + +使用现有的 PostgreSQL 数据库 (`cssc`),自动创建以下表: +- `diary_diaryentry` - 日记条目 +- `diary_dailyprogress` - 每日进度 + +## 与城市手册的区别 + +- 端口不同(日记系统用 8001/8002,城市手册用 80/8000) +- 更轻量级的前端 +- 专注于个人日记和进步追踪 diff --git a/diary-system/add_first_experience.py b/diary-system/add_first_experience.py new file mode 100644 index 0000000..7ca8fb1 --- /dev/null +++ b/diary-system/add_first_experience.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +"""添加第一条经验总结""" +import os +import sys +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings') +sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend') + +import django +django.setup() + +from diary.models import Experience + +# 创建第一条经验总结 +exp = Experience.objects.create( + title="云服务器 Gunicorn 路径问题", + category="deployment", + problem="Gunicorn systemd 服务启动失败,错误:Failed to locate executable /usr/bin/gunicorn", + solution="1. 使用 pip3 install gunicorn 安装\n2. 查找 gunicorn 实际路径:/home/ubuntu/.local/bin/gunicorn\n3. 修改 systemd 服务文件,添加 Environment=PATH 并更新 ExecStart 路径", + lesson_learned="在云服务器上使用 pip 安装的包可能在用户目录,需要确认实际路径并在 systemd 中正确配置" +) + +print(f"✅ 经验总结已创建:{exp.title}") +print(f" 类别:{exp.get_category_display()}") diff --git a/diary-system/add_first_experience_cloud.py b/diary-system/add_first_experience_cloud.py new file mode 100644 index 0000000..1058ebd --- /dev/null +++ b/diary-system/add_first_experience_cloud.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +"""添加第一条经验总结 - 云服务器版""" +import os +import sys +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings') +sys.path.insert(0, '/home/ubuntu/diary-system/backend') + +import django +django.setup() + +from diary.models import Experience + +# 创建第一条经验总结 +exp = Experience.objects.create( + title="云服务器 Gunicorn 路径问题", + category="deployment", + problem="Gunicorn systemd 服务启动失败,错误:Failed to locate executable /usr/bin/gunicorn", + solution="1. 使用 pip3 install gunicorn 安装\n2. 查找 gunicorn 实际路径:/home/ubuntu/.local/bin/gunicorn\n3. 修改 systemd 服务文件,添加 Environment=PATH 并更新 ExecStart 路径", + lesson_learned="在云服务器上使用 pip 安装的包可能在用户目录,需要确认实际路径并在 systemd 中正确配置" +) + +print(f"✅ 经验总结已创建:{exp.title}") +print(f" 类别:{exp.get_category_display()}") diff --git a/diary-system/backend/diary/__init__.py b/diary-system/backend/diary/__init__.py new file mode 100755 index 0000000..6996082 --- /dev/null +++ b/diary-system/backend/diary/__init__.py @@ -0,0 +1 @@ +# Diary app diff --git a/diary-system/backend/diary/migrations/0001_initial.py b/diary-system/backend/diary/migrations/0001_initial.py new file mode 100755 index 0000000..c121afa --- /dev/null +++ b/diary-system/backend/diary/migrations/0001_initial.py @@ -0,0 +1,52 @@ +# Generated by Django 4.2.11 on 2026-04-14 08:19 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='DiaryEntry', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateField(unique=True, verbose_name='日期')), + ('title', models.CharField(default='每日日记', max_length=200, verbose_name='标题')), + ('completed_tasks', models.TextField(blank=True, default='', verbose_name='完成的任务')), + ('learned', models.TextField(blank=True, default='', verbose_name='学到的东西')), + ('problems', models.TextField(blank=True, default='', verbose_name='遇到的问题和解决方案')), + ('reflections', models.TextField(blank=True, default='', verbose_name='想法和反思')), + ('improvements', models.TextField(blank=True, default='', verbose_name='进步点')), + ('plans', models.TextField(blank=True, default='', verbose_name='明日计划')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')), + ], + options={ + 'verbose_name': '日记', + 'verbose_name_plural': '日记', + 'ordering': ['-date'], + }, + ), + migrations.CreateModel( + name='DailyProgress', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('category', models.CharField(max_length=50, verbose_name='类别')), + ('description', models.TextField(verbose_name='描述')), + ('progress_percent', models.IntegerField(default=0, verbose_name='进度百分比')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ('entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='progresses', to='diary.diaryentry')), + ], + options={ + 'verbose_name': '进度', + 'verbose_name_plural': '进度', + 'ordering': ['-created_at'], + }, + ), + ] diff --git a/diary-system/backend/diary/migrations/0002_experience.py b/diary-system/backend/diary/migrations/0002_experience.py new file mode 100644 index 0000000..2ceb610 --- /dev/null +++ b/diary-system/backend/diary/migrations/0002_experience.py @@ -0,0 +1,31 @@ +# Generated by Django 4.2.11 on 2026-04-14 08:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('diary', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Experience', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200, verbose_name='标题')), + ('category', models.CharField(choices=[('deployment', '📦 部署'), ('development', '💻 开发'), ('database', '🗄️ 数据库'), ('permission', '🔐 权限'), ('network', '🌐 网络'), ('other', '其他')], max_length=50, verbose_name='类别')), + ('problem', models.TextField(verbose_name='问题描述')), + ('solution', models.TextField(verbose_name='解决方案')), + ('lesson_learned', models.TextField(blank=True, default='', verbose_name='经验教训')), + ('date', models.DateField(auto_now_add=True, verbose_name='日期')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ], + options={ + 'verbose_name': '经验总结', + 'verbose_name_plural': '经验总结', + 'ordering': ['-date', '-created_at'], + }, + ), + ] diff --git a/diary-system/backend/diary/migrations/__init__.py b/diary-system/backend/diary/migrations/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/diary-system/backend/diary/models.py b/diary-system/backend/diary/models.py new file mode 100755 index 0000000..8b0e97d --- /dev/null +++ b/diary-system/backend/diary/models.py @@ -0,0 +1,66 @@ +from django.db import models +from django.utils import timezone + +class DiaryEntry(models.Model): + """日记条目""" + date = models.DateField('日期', unique=True) + title = models.CharField('标题', max_length=200, default='每日日记') + completed_tasks = models.TextField('完成的任务', blank=True, default='') + learned = models.TextField('学到的东西', blank=True, default='') + problems = models.TextField('遇到的问题和解决方案', blank=True, default='') + reflections = models.TextField('想法和反思', blank=True, default='') + improvements = models.TextField('进步点', blank=True, default='') + plans = models.TextField('明日计划', blank=True, default='') + created_at = models.DateTimeField('创建时间', auto_now_add=True) + updated_at = models.DateTimeField('更新时间', auto_now=True) + + class Meta: + ordering = ['-date'] + verbose_name = '日记' + verbose_name_plural = '日记' + + def __str__(self): + return f"{self.date} - {self.title}" + + +class Experience(models.Model): + """经验总结 - 记录遇到的问题和解决方法""" + title = models.CharField('标题', max_length=200) + category = models.CharField('类别', max_length=50, choices=[ + ('deployment', '📦 部署'), + ('development', '💻 开发'), + ('database', '🗄️ 数据库'), + ('permission', '🔐 权限'), + ('network', '🌐 网络'), + ('other', '其他'), + ]) + problem = models.TextField('问题描述') + solution = models.TextField('解决方案') + lesson_learned = models.TextField('经验教训', blank=True, default='') + date = models.DateField('日期', auto_now_add=True) + created_at = models.DateTimeField('创建时间', auto_now_add=True) + + class Meta: + ordering = ['-date', '-created_at'] + verbose_name = '经验总结' + verbose_name_plural = '经验总结' + + def __str__(self): + return f"{self.category} - {self.title}" + + +class DailyProgress(models.Model): + """每日进度追踪""" + entry = models.ForeignKey(DiaryEntry, on_delete=models.CASCADE, related_name='progresses') + category = models.CharField('类别', max_length=50) + description = models.TextField('描述') + progress_percent = models.IntegerField('进度百分比', default=0) + created_at = models.DateTimeField('创建时间', auto_now_add=True) + + class Meta: + ordering = ['-created_at'] + verbose_name = '进度' + verbose_name_plural = '进度' + + def __str__(self): + return f"{self.entry.date} - {self.category}: {self.progress_percent}%" diff --git a/diary-system/backend/diary/serializers.py b/diary-system/backend/diary/serializers.py new file mode 100755 index 0000000..f78fd2f --- /dev/null +++ b/diary-system/backend/diary/serializers.py @@ -0,0 +1,19 @@ +from rest_framework import serializers +from .models import DiaryEntry, DailyProgress, Experience + +class ExperienceSerializer(serializers.ModelSerializer): + class Meta: + model = Experience + fields = '__all__' + +class DailyProgressSerializer(serializers.ModelSerializer): + class Meta: + model = DailyProgress + fields = '__all__' + +class DiaryEntrySerializer(serializers.ModelSerializer): + progresses = DailyProgressSerializer(many=True, read_only=True) + + class Meta: + model = DiaryEntry + fields = '__all__' diff --git a/diary-system/backend/diary/urls.py b/diary-system/backend/diary/urls.py new file mode 100755 index 0000000..e42d716 --- /dev/null +++ b/diary-system/backend/diary/urls.py @@ -0,0 +1,12 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import DiaryEntryViewSet, DailyProgressViewSet, ExperienceViewSet + +router = DefaultRouter() +router.register(r'entries', DiaryEntryViewSet) +router.register(r'progress', DailyProgressViewSet) +router.register(r'experiences', ExperienceViewSet) + +urlpatterns = [ + path('', include(router.urls)), +] diff --git a/diary-system/backend/diary/views.py b/diary-system/backend/diary/views.py new file mode 100755 index 0000000..bd0a05f --- /dev/null +++ b/diary-system/backend/diary/views.py @@ -0,0 +1,62 @@ +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.response import Response +from django.utils import timezone +from .models import DiaryEntry, DailyProgress, Experience +from .serializers import DiaryEntrySerializer, DailyProgressSerializer, ExperienceSerializer + +class DiaryEntryViewSet(viewsets.ModelViewSet): + queryset = DiaryEntry.objects.all() + serializer_class = DiaryEntrySerializer + + @action(detail=False, methods=['get']) + def today(self, request): + """获取今天的日记""" + today = timezone.now().date() + entry, created = DiaryEntry.objects.get_or_create(date=today) + serializer = self.get_serializer(entry) + return Response(serializer.data) + + @action(detail=False, methods=['get']) + def recent(self, request): + """获取最近 7 天的日记""" + entries = DiaryEntry.objects.order_by('-date')[:7] + serializer = self.get_serializer(entries, many=True) + return Response(serializer.data) + + @action(detail=False, methods=['get']) + def stats(self, request): + """获取统计信息""" + total_entries = DiaryEntry.objects.count() + return Response({ + 'total_entries': total_entries, + 'first_entry': DiaryEntry.objects.order_by('date').first().date if total_entries > 0 else None, + 'latest_entry': DiaryEntry.objects.order_by('-date').first().date if total_entries > 0 else None, + }) + +class DailyProgressViewSet(viewsets.ModelViewSet): + queryset = DailyProgress.objects.all() + serializer_class = DailyProgressSerializer + + +class ExperienceViewSet(viewsets.ModelViewSet): + queryset = Experience.objects.all() + serializer_class = ExperienceSerializer + + @action(detail=False, methods=['get']) + def by_category(self, request): + """按类别分组获取经验""" + categories = {} + for exp in Experience.objects.all(): + cat = exp.get_category_display() + if cat not in categories: + categories[cat] = [] + categories[cat].append(ExperienceSerializer(exp).data) + return Response(categories) + + @action(detail=False, methods=['get']) + def recent(self, request): + """获取最近 10 条经验""" + experiences = Experience.objects.order_by('-date', '-created_at')[:10] + serializer = self.get_serializer(experiences, many=True) + return Response(serializer.data) diff --git a/diary-system/backend/diary_system/__init__.py b/diary-system/backend/diary_system/__init__.py new file mode 100755 index 0000000..9db95f0 --- /dev/null +++ b/diary-system/backend/diary_system/__init__.py @@ -0,0 +1 @@ +# Diary System Project diff --git a/diary-system/backend/diary_system/settings.py b/diary-system/backend/diary_system/settings.py new file mode 100755 index 0000000..b3df2ea --- /dev/null +++ b/diary-system/backend/diary_system/settings.py @@ -0,0 +1,103 @@ +""" +Django settings for diary_system project. +""" + +from pathlib import Path +import os + +BASE_DIR = Path(__file__).resolve().parent.parent + +SECRET_KEY = 'django-insecure-diary-system-secret-key-change-in-production' + +DEBUG = os.environ.get('DEBUG', 'True').lower() == 'true' + +ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '*').split(',') + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'corsheaders', + 'diary', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'corsheaders.middleware.CorsMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'diary_system.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'diary_system.wsgi.application' + +# 数据库配置 - 云服务器使用 SQLite,本地使用 PostgreSQL +if os.path.exists('/home/ubuntu/diary-system'): + # 云服务器配置 - SQLite + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } + } +else: + # 本地配置 - PostgreSQL + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': os.environ.get('DB_NAME', 'cssc'), + 'USER': os.environ.get('DB_USER', 'coder'), + 'PASSWORD': os.environ.get('DB_PASSWORD', '825670wl'), + 'HOST': os.environ.get('DB_HOST', '10.2.0.100'), + 'PORT': os.environ.get('DB_PORT', '5432'), + } + } + +AUTH_PASSWORD_VALIDATORS = [ + {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'}, + {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator'}, + {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'}, + {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}, +] + +LANGUAGE_CODE = 'zh-hans' +TIME_ZONE = 'UTC' +USE_I18N = True +USE_TZ = True + +STATIC_URL = 'static/' +STATIC_ROOT = os.path.join(BASE_DIR, 'static') +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +CORS_ALLOW_ALL_ORIGINS = True + +REST_FRAMEWORK = { + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.AllowAny', + ] +} diff --git a/diary-system/backend/diary_system/urls.py b/diary-system/backend/diary_system/urls.py new file mode 100755 index 0000000..1c04e18 --- /dev/null +++ b/diary-system/backend/diary_system/urls.py @@ -0,0 +1,10 @@ +""" +URL configuration for diary_system project. +""" +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/', include('diary.urls')), +] diff --git a/diary-system/backend/diary_system/wsgi.py b/diary-system/backend/diary_system/wsgi.py new file mode 100755 index 0000000..dbee85f --- /dev/null +++ b/diary-system/backend/diary_system/wsgi.py @@ -0,0 +1,11 @@ +""" +WSGI config for diary_system project. +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings') + +application = get_wsgi_application() diff --git a/diary-system/backend/manage.py b/diary-system/backend/manage.py new file mode 100755 index 0000000..9c12080 --- /dev/null +++ b/diary-system/backend/manage.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + +if __name__ == '__main__': + main() diff --git a/diary-system/backend/requirements.txt b/diary-system/backend/requirements.txt new file mode 100755 index 0000000..c43385a --- /dev/null +++ b/diary-system/backend/requirements.txt @@ -0,0 +1,4 @@ +Django>=4.2 +djangorestframework>=3.14 +django-cors-headers>=4.0 +psycopg2-binary>=2.9 diff --git a/diary-system/collect_static.py b/diary-system/collect_static.py new file mode 100755 index 0000000..40b9a55 --- /dev/null +++ b/diary-system/collect_static.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +import os +import sys +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings') +sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend') +from django.core.management import execute_from_command_line +sys.argv = ['manage.py', 'collectstatic', '--noinput'] +execute_from_command_line(sys.argv) diff --git a/diary-system/create_first_entry.py b/diary-system/create_first_entry.py new file mode 100644 index 0000000..3f42c0a --- /dev/null +++ b/diary-system/create_first_entry.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +import os +import sys +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings') +sys.path.insert(0, '/home/ubuntu/diary-system/backend') + +import django +django.setup() + +from diary.models import DiaryEntry +from datetime import date + +entry, created = DiaryEntry.objects.get_or_create(date=date.today()) +entry.title = "第一天 - 日记系统上线" +entry.completed_tasks = """- 创建日记系统框架 +- 开发 Web 日记系统(Django + 轻量前端) +- 配置 Nginx 反向代理 +- 数据库迁移和同步 +- 部署到云服务器""" +entry.learned = """- 掌握了 Django 项目快速搭建 +- 学习了 Nginx 配置和权限管理 +- 解决了云服务器部署问题""" +entry.reflections = """- 日记系统应该保持简洁,避免过度设计 +- 每天记录进步可以帮助持续改进""" +entry.improvements = """1. 系统建设:建立了日记系统的基础框架 +2. Web 开发:独立完成全栈 Web 应用开发 +3. 问题解决:快速解决权限和部署问题""" +entry.save() + +print(f"✅ 日记已创建:{entry.date}") +print(f" 标题:{entry.title}") diff --git a/diary-system/deploy.sh b/diary-system/deploy.sh new file mode 100755 index 0000000..43fe5fb --- /dev/null +++ b/diary-system/deploy.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# 日记系统 - 自动部署脚本 + +set -e + +echo "🚀 开始部署日记系统..." + +# 1. 安装依赖 +echo "📦 安装 Python 依赖..." +cd /root/.openclaw/workspace/diary-system/backend +pip3 install -r requirements.txt --break-system-packages -q + +# 2. 数据库迁移 +echo "🗄️ 执行数据库迁移..." +python3 manage.py makemigrations diary --noinput +python3 manage.py migrate --noinput + +# 3. 收集静态文件 +echo "📁 收集静态文件..." +mkdir -p /root/.openclaw/workspace/diary-system/backend/static +python3 manage.py collectstatic --noinput + +# 4. 创建 Gunicorn 服务 +echo "⚙️ 创建 Gunicorn 服务..." +cat > /etc/systemd/system/diary-system.service << EOF +[Unit] +Description=Diary System Gunicorn Service +After=network.target + +[Service] +User=root +Group=root +WorkingDirectory=/root/.openclaw/workspace/diary-system/backend +ExecStart=/usr/bin/gunicorn --access-logfile - --workers 3 --bind 127.0.0.1:8002 diary_system.wsgi:application +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable diary-system +systemctl start diary-system + +# 5. 配置 Nginx +echo "🌐 配置 Nginx..." +cat > /etc/nginx/sites-available/diary-system << 'EOF' +server { + listen 8001; + server_name _; + + location = /favicon.ico { access_log off; log_not_found off; } + + location /static/ { + alias /root/.openclaw/workspace/diary-system/backend/static/; + } + + location /api/ { + proxy_pass http://127.0.0.1:8002; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location / { + root /root/.openclaw/workspace/diary-system/frontend; + index index.html; + try_files $uri $uri/ /index.html; + } +} +EOF + +ln -sf /etc/nginx/sites-available/diary-system /etc/nginx/sites-enabled/ +nginx -t +systemctl restart nginx + +# 6. 同步日记数据 +echo "📊 同步日记数据..." +python3 /root/.openclaw/workspace/diary-system/sync_diary.py + +echo "" +echo "✅ 部署完成!" +echo "" +echo "📍 访问地址:http://cssc.datalibstar.com:8001" +echo "📍 API: http://cssc.datalibstar.com:8001/api/" +echo "📍 Admin: http://cssc.datalibstar.com:8001/admin" diff --git a/diary-system/deploy_cloud.sh b/diary-system/deploy_cloud.sh new file mode 100644 index 0000000..d9d84eb --- /dev/null +++ b/diary-system/deploy_cloud.sh @@ -0,0 +1,124 @@ +#!/bin/bash +# 日记系统 - 云服务器部署脚本 +# 在云服务器上执行:bash deploy_cloud.sh + +set -e + +PROJECT_DIR="/home/ubuntu/diary-system" +BACKEND_DIR="$PROJECT_DIR/backend" +FRONTEND_DIR="$PROJECT_DIR/frontend" + +echo "======================================" +echo "⚡ 码神日记系统 - 云服务器部署" +echo "======================================" +echo "" + +cd "$PROJECT_DIR" + +# 1. 安装依赖 +echo "📦 安装 Python 依赖..." +cd "$BACKEND_DIR" +pip3 install -r requirements.txt -q 2>/dev/null || pip3 install -r requirements.txt --break-system-packages -q + +# 2. 数据库迁移 +echo "🗄️ 执行数据库迁移..." +python3 manage.py makemigrations diary --noinput 2>/dev/null || true +python3 manage.py migrate --noinput + +# 3. 收集静态文件 +echo "📁 收集静态文件..." +mkdir -p "$BACKEND_DIR/static" +python3 manage.py collectstatic --noinput + +# 4. 创建 Gunicorn systemd 服务 +echo "⚙️ 创建 Gunicorn 服务..." +sudo cat > /etc/systemd/system/diary-system.service << EOF +[Unit] +Description=Diary System Gunicorn Service +After=network.target + +[Service] +User=ubuntu +Group=ubuntu +WorkingDirectory=$BACKEND_DIR +ExecStart=/usr/bin/gunicorn --access-logfile - --workers 3 --bind 127.0.0.1:8002 diary_system.wsgi:application +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + +sudo systemctl daemon-reload +sudo systemctl enable diary-system +sudo systemctl restart diary-system + +# 5. 配置 Nginx +echo "🌐 配置 Nginx..." +sudo cat > /etc/nginx/sites-available/diary-system << EOF +server { + listen 8001; + server_name _; + + location = /favicon.ico { access_log off; log_not_found off; } + + location /static/ { + alias $BACKEND_DIR/static/; + } + + location /api/ { + proxy_pass http://127.0.0.1:8002; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + } + + location / { + root $FRONTEND_DIR; + index index.html; + try_files \$uri \$uri/ /index.html; + } +} +EOF + +sudo ln -sf /etc/nginx/sites-available/diary-system /etc/nginx/sites-enabled/ +sudo nginx -t +sudo systemctl restart nginx + +# 6. 同步日记数据 +echo "📊 同步日记数据..." +cd "$PROJECT_DIR" +python3 sync_diary.py || echo " (首次部署,跳过日记同步)" + +# 7. 检查服务状态 +echo "" +echo "🔍 检查服务状态..." +sudo systemctl is-active diary-system > /dev/null && echo " ✅ Gunicorn 运行中" || echo " ❌ Gunicorn 未运行" +sudo systemctl is-active nginx > /dev/null && echo " ✅ Nginx 运行中" || echo " ❌ Nginx 未运行" + +# 8. 测试访问 +echo "" +echo "🧪 测试访问..." +sleep 2 +if curl -s http://127.0.0.1:8001/ | grep -q "码神的日记系统"; then + echo " ✅ 前端访问正常" +else + echo " ⚠️ 前端访问可能有问题" +fi + +if curl -s http://127.0.0.1:8001/api/entries/stats/ | grep -q "total_entries"; then + echo " ✅ API 访问正常" +else + echo " ⚠️ API 访问可能有问题" +fi + +echo "" +echo "======================================" +echo "✅ 部署完成!" +echo "======================================" +echo "" +echo "📍 访问地址:http://cssc.datalibstar.com:8001" +echo "📍 API: http://cssc.datalibstar.com:8001/api/" +echo "📍 Admin: http://cssc.datalibstar.com:8001/admin" +echo "" +echo "🎉 日记系统已在云服务器上线!" +echo "" diff --git a/diary-system/deploy_to_cloud.sh b/diary-system/deploy_to_cloud.sh new file mode 100644 index 0000000..5c0c974 --- /dev/null +++ b/diary-system/deploy_to_cloud.sh @@ -0,0 +1,143 @@ +#!/bin/bash +# 日记系统 - 云服务器一键部署脚本 +# 使用方法:在云服务器上执行 bash deploy_to_cloud.sh + +set -e + +SERVER_URL="cssc.datalibstar.com" +DEPLOY_PORT="8001" + +echo "======================================" +echo "⚡ 码神日记系统 - 云服务器部署" +echo "======================================" +echo "" + +# 检查是否在正确的服务器上 +echo "🔍 检查服务器..." +if [ "$(hostname)" != "cssc" ] && [ "$(hostname)" != "cssc.datalibstar.com" ]; then + echo "⚠️ 警告:当前主机名不是 cssc,请确认是否在正确的服务器上" +fi + +# 1. 克隆或更新代码 +echo "📦 获取代码..." +cd /root/.openclaw/workspace || { + echo "❌ 错误:/root/.openclaw/workspace 不存在" + exit 1 +} + +if [ -d "diary-system" ]; then + echo " 更新现有代码..." + cd diary-system + git pull 2>/dev/null || echo " (非 git 仓库,跳过)" + cd .. +else + echo " 代码已存在,跳过克隆" +fi + +# 2. 安装依赖 +echo "📦 安装 Python 依赖..." +cd /root/.openclaw/workspace/diary-system/backend +pip3 install -r requirements.txt --break-system-packages -q + +# 3. 数据库迁移 +echo "🗄️ 执行数据库迁移..." +python3 manage.py makemigrations diary --noinput 2>/dev/null || true +python3 manage.py migrate --noinput + +# 4. 收集静态文件 +echo "📁 收集静态文件..." +mkdir -p /root/.openclaw/workspace/diary-system/backend/static +python3 manage.py collectstatic --noinput + +# 5. 创建 Gunicorn systemd 服务 +echo "⚙️ 创建 Gunicorn 服务..." +cat > /etc/systemd/system/diary-system.service << EOF +[Unit] +Description=Diary System Gunicorn Service +After=network.target + +[Service] +User=root +Group=root +WorkingDirectory=/root/.openclaw/workspace/diary-system/backend +ExecStart=/usr/bin/gunicorn --access-logfile - --workers 3 --bind 127.0.0.1:8002 diary_system.wsgi:application +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable diary-system +systemctl restart diary-system + +# 6. 配置 Nginx +echo "🌐 配置 Nginx..." +cat > /etc/nginx/sites-available/diary-system << 'EOF' +server { + listen 8001; + server_name _; + + location = /favicon.ico { access_log off; log_not_found off; } + + location /static/ { + alias /root/.openclaw/workspace/diary-system/backend/static/; + } + + location /api/ { + proxy_pass http://127.0.0.1:8002; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location / { + root /root/.openclaw/workspace/diary-system/frontend; + index index.html; + try_files $uri $uri/ /index.html; + } +} +EOF + +ln -sf /etc/nginx/sites-available/diary-system /etc/nginx/sites-enabled/ +nginx -t +systemctl restart nginx + +# 7. 同步日记数据 +echo "📊 同步日记数据..." +cd /root/.openclaw/workspace/diary-system +python3 sync_diary.py + +# 8. 检查服务状态 +echo "" +echo "🔍 检查服务状态..." +systemctl is-active diary-system > /dev/null && echo " ✅ Gunicorn 运行中" || echo " ❌ Gunicorn 未运行" +systemctl is-active nginx > /dev/null && echo " ✅ Nginx 运行中" || echo " ❌ Nginx 未运行" + +# 9. 测试访问 +echo "" +echo "🧪 测试访问..." +sleep 2 +if curl -s http://127.0.0.1:8001/ | grep -q "码神的日记系统"; then + echo " ✅ 前端访问正常" +else + echo " ⚠️ 前端访问可能有问题" +fi + +if curl -s http://127.0.0.1:8001/api/entries/stats/ | grep -q "total_entries"; then + echo " ✅ API 访问正常" +else + echo " ⚠️ API 访问可能有问题" +fi + +echo "" +echo "======================================" +echo "✅ 部署完成!" +echo "======================================" +echo "" +echo "📍 访问地址:http://${SERVER_URL}:${DEPLOY_PORT}" +echo "📍 API: http://${SERVER_URL}:${DEPLOY_PORT}/api/" +echo "📍 Admin: http://${SERVER_URL}:${DEPLOY_PORT}/admin" +echo "" +echo "🎉 日记系统已在云服务器上线!" +echo "" diff --git a/diary-system/frontend/index.html b/diary-system/frontend/index.html new file mode 100755 index 0000000..30b1d2e --- /dev/null +++ b/diary-system/frontend/index.html @@ -0,0 +1,304 @@ + + + + + + 码神的日记系统 + + + +
+
+

⚡ 码神的日记系统

+

记录每天的进步与成长

+
+ +
+
加载中...
+
+
+ + + + diff --git a/diary-system/makemigrations.py b/diary-system/makemigrations.py new file mode 100644 index 0000000..8648e51 --- /dev/null +++ b/diary-system/makemigrations.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', 'makemigrations', 'diary'] +execute_from_command_line(sys.argv) diff --git a/diary-system/migrate.py b/diary-system/migrate.py new file mode 100644 index 0000000..6a5f1b1 --- /dev/null +++ b/diary-system/migrate.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'] +execute_from_command_line(sys.argv) diff --git a/diary-system/nginx.conf b/diary-system/nginx.conf new file mode 100755 index 0000000..6077cdb --- /dev/null +++ b/diary-system/nginx.conf @@ -0,0 +1,21 @@ +server { + listen 8001; + server_name localhost; + + location /static/ { + alias /root/.openclaw/workspace/diary-system/backend/static/; + } + + location /api/ { + proxy_pass http://127.0.0.1:8002; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location / { + root /root/.openclaw/workspace/diary-system/frontend; + index index.html; + try_files $uri $uri/ /index.html; + } +} diff --git a/diary-system/nginx_cloud.conf b/diary-system/nginx_cloud.conf new file mode 100644 index 0000000..20505f4 --- /dev/null +++ b/diary-system/nginx_cloud.conf @@ -0,0 +1,23 @@ +server { + listen 8001; + server_name _; + + location = /favicon.ico { access_log off; log_not_found off; } + + location /static/ { + alias /home/ubuntu/diary-system/backend/static/; + } + + location /api/ { + proxy_pass http://127.0.0.1:8002; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + location / { + root /home/ubuntu/diary-system/frontend; + index index.html; + try_files $uri $uri/ /index.html; + } +} diff --git a/diary-system/start.sh b/diary-system/start.sh new file mode 100755 index 0000000..e5ed6aa --- /dev/null +++ b/diary-system/start.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# 日记系统启动脚本 + +cd /root/.openclaw/workspace/diary-system/backend + +# 安装依赖 +pip3 install -r requirements.txt -q + +# 数据库迁移 +python3 manage.py makemigrations diary +python3 manage.py migrate + +# 收集静态文件 +python3 manage.py collectstatic --noinput + +# 启动 Gunicorn +echo "🚀 启动日记系统..." +gunicorn diary_system.wsgi:application \ + --bind 127.0.0.1:8002 \ + --workers 3 \ + --daemon + +echo "✅ 日记系统已启动" +echo "📝 访问地址:http://127.0.0.1:8001/" +echo "🔧 Admin: http://127.0.0.1:8001/admin/" diff --git a/diary-system/sync_diary.py b/diary-system/sync_diary.py new file mode 100644 index 0000000..082e998 --- /dev/null +++ b/diary-system/sync_diary.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +"""同步今天的日记到数据库""" +import os +import sys +import json + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'diary_system.settings') +sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend') + +import django +django.setup() + +from datetime import date +from diary.models import DiaryEntry + +# 读取今天的日记文件 +diary_path = '/root/.openclaw/workspace/memory/2026-04-14.md' +with open(diary_path, 'r') as f: + content = f.read() + +# 解析日记内容(简单解析) +def extract_section(content, marker): + lines = content.split('\n') + result = [] + in_section = False + for line in lines: + if marker in line: + in_section = True + continue + if in_section: + if line.startswith('## ') or line.startswith('---'): + break + result.append(line) + return '\n'.join(result).strip() + +# 创建或更新日记 +today = date.today() +entry, created = DiaryEntry.objects.get_or_create(date=today) + +entry.title = "第一天 - 日记系统上线" +entry.completed_tasks = extract_section(content, '✅ 完成的任务') +entry.learned = extract_section(content, '📚 学到的东西') +entry.problems = extract_section(content, '🐛 遇到的问题') +entry.reflections = extract_section(content, '💡 想法和反思') +entry.improvements = extract_section(content, '📈 进步点') +entry.plans = extract_section(content, '🎯 明日计划') +entry.save() + +print(f"✅ 日记已同步:{entry.date}") +print(f" 标题:{entry.title}") +print(f" 完成的任务:{len(entry.completed_tasks)} 字") +print(f" 学到的东西:{len(entry.learned)} 字") +print(f" 进步点:{len(entry.improvements)} 字")