第一个项目
This commit is contained in:
15
check_live.sh
Normal file
15
check_live.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
SERVER_IP="192.168.3.105"
|
||||
USER="xiaji"
|
||||
|
||||
ssh $USER@$SERVER_IP << 'EOF'
|
||||
echo "=== 关键配置验证 ==="
|
||||
echo "1. Socket文件权限:"
|
||||
ls -l /home/xiaji/myproject/myproject.sock
|
||||
echo -e "\n2. Nginx代理配置:"
|
||||
sudo grep -A5 'location /' /etc/nginx/sites-available/family_rpa
|
||||
echo -e "\n3. Gunicorn服务配置:"
|
||||
sudo grep -E 'ExecStart|WorkingDirectory' /etc/systemd/system/family_rpa.service
|
||||
echo -e "\n4. 实时错误日志:"
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
EOF
|
||||
23
check_status.sh
Normal file
23
check_status.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
SERVER_IP="192.168.3.105"
|
||||
USER="xiaji"
|
||||
|
||||
echo "=== 网络连接检查 ==="
|
||||
nc -zv $SERVER_IP 80
|
||||
nc -zv $SERVER_IP 8000
|
||||
|
||||
echo -e "\n=== 服务状态检查 ==="
|
||||
ssh $USER@$SERVER_IP << 'EOF'
|
||||
echo "## Nginx状态 ##"
|
||||
sudo systemctl status nginx | head -n 5
|
||||
|
||||
echo -e "\n## Gunicorn服务状态 ##"
|
||||
sudo systemctl status family_rpa.service | head -n 5
|
||||
|
||||
echo -e "\n## 端口监听情况 ##"
|
||||
sudo netstat -tulpn | grep -E ':80|:8000'
|
||||
|
||||
echo -e "\n## 最近错误日志 ##"
|
||||
sudo tail -n 20 /var/log/nginx/error.log
|
||||
sudo journalctl -u family_rpa.service --since "5 minutes ago" | tail -n 10
|
||||
EOF
|
||||
40
deploy.sh
Normal file
40
deploy.sh
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
SERVER_IP="192.168.3.105"
|
||||
USER="xiaji"
|
||||
DEPLOY_DIR="/home/xiaji/deploy_$(date +%s)"
|
||||
LOG_FILE="$DEPLOY_DIR/deploy.log"
|
||||
|
||||
# 上传公钥
|
||||
ssh-copy-id -i ~/.ssh/id_rsa.pub $USER@$SERVER_IP
|
||||
|
||||
# 执行远程部署
|
||||
ssh $USER@$SERVER_IP << EOF
|
||||
set -e
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y python3-pip python3-venv nginx
|
||||
|
||||
mkdir -p $DEPLOY_DIR
|
||||
tar -xzvf ~/family_rpa.tar.gz -C $DEPLOY_DIR
|
||||
|
||||
python3 -m venv $DEPLOY_DIR/venv
|
||||
source $DEPLOY_DIR/venv/bin/activate
|
||||
pip install -r $DEPLOY_DIR/family_rpa/requirements.txt
|
||||
|
||||
# 生产配置检查
|
||||
if ! grep -q "DEBUG = False" $DEPLOY_DIR/family_rpa/family_rpa/settings.py; then
|
||||
echo "ERROR: 未检测到生产环境配置" | tee -a $LOG_FILE
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 迁移数据库
|
||||
python $DEPLOY_DIR/family_rpa/manage.py migrate
|
||||
python $DEPLOY_DIR/family_rpa/manage.py collectstatic --noinput
|
||||
|
||||
# 切换部署版本
|
||||
sudo systemctl stop family_rpa.service
|
||||
sudo rm -rf /home/xiaji/myproject
|
||||
mv $DEPLOY_DIR/family_rpa /home/xiaji/myproject
|
||||
sudo systemctl start family_rpa.service
|
||||
|
||||
echo "部署成功!访问地址:http://$SERVER_IP" | tee -a $LOG_FILE
|
||||
EOF
|
||||
BIN
family_rpa.tar.gz
Normal file
BIN
family_rpa.tar.gz
Normal file
Binary file not shown.
BIN
family_rpa/db.sqlite3
Normal file
BIN
family_rpa/db.sqlite3
Normal file
Binary file not shown.
12
family_rpa/family_rpa.service
Normal file
12
family_rpa/family_rpa.service
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Gunicorn instance to serve family_rpa
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=xiaji
|
||||
Group=www-data
|
||||
WorkingDirectory=/project/family_rpa
|
||||
ExecStart=/project/family_rpa/venv/bin/gunicorn --workers 3 --bind unix:/project/family_rpa/family_rpa.sock family_rpa.wsgi:application
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
0
family_rpa/family_rpa/__init__.py
Normal file
0
family_rpa/family_rpa/__init__.py
Normal file
BIN
family_rpa/family_rpa/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
family_rpa/family_rpa/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
family_rpa/family_rpa/__pycache__/settings.cpython-313.pyc
Normal file
BIN
family_rpa/family_rpa/__pycache__/settings.cpython-313.pyc
Normal file
Binary file not shown.
BIN
family_rpa/family_rpa/__pycache__/urls.cpython-313.pyc
Normal file
BIN
family_rpa/family_rpa/__pycache__/urls.cpython-313.pyc
Normal file
Binary file not shown.
BIN
family_rpa/family_rpa/__pycache__/wsgi.cpython-313.pyc
Normal file
BIN
family_rpa/family_rpa/__pycache__/wsgi.cpython-313.pyc
Normal file
Binary file not shown.
16
family_rpa/family_rpa/asgi.py
Normal file
16
family_rpa/family_rpa/asgi.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
ASGI config for family_rpa project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'family_rpa.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
152
family_rpa/family_rpa/settings.py
Normal file
152
family_rpa/family_rpa/settings.py
Normal file
@@ -0,0 +1,152 @@
|
||||
"""
|
||||
Django settings for family_rpa project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 5.1.4.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.1/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.1/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-+&-sio8(2$n#^@367@&w(ld5z024&%mamr5z$pf3kx!m^l+rxs'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django_crontab',
|
||||
'main',
|
||||
]
|
||||
|
||||
CRONJOBS = [
|
||||
('0 0 * * *', 'main.management.commands.cleanup_messages')
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'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 = 'family_rpa.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 = 'family_rpa.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
|
||||
|
||||
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',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'zh-hans'
|
||||
|
||||
TIME_ZONE = 'Asia/Shanghai'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.1/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'assets')]
|
||||
|
||||
MEDIA_URL = 'media/'
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(BASE_DIR, 'templates')],
|
||||
'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',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
LOGIN_REDIRECT_URL = 'admin:index'
|
||||
8
family_rpa/family_rpa/urls.py
Normal file
8
family_rpa/family_rpa/urls.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('main.urls')),
|
||||
path('accounts/', include('django.contrib.auth.urls')),
|
||||
]
|
||||
16
family_rpa/family_rpa/wsgi.py
Normal file
16
family_rpa/family_rpa/wsgi.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for family_rpa project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'family_rpa.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
20
family_rpa/gunicorn_config.py
Normal file
20
family_rpa/gunicorn_config.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# 绑定的地址和端口
|
||||
bind = '127.0.0.1:8000'
|
||||
# 工作进程数量,通常设置为 CPU 核心数的 2 倍 + 1
|
||||
workers = 3
|
||||
# 工作模式,使用异步的 gevent 模式
|
||||
worker_class = 'gevent'
|
||||
# 每个工作进程处理的最大请求数,超过该数后工作进程会重启
|
||||
max_requests = 1000
|
||||
# 最大请求数的抖动值,避免所有工作进程同时重启
|
||||
max_requests_jitter = 50
|
||||
# 超时时间
|
||||
timeout = 30
|
||||
# 访问日志路径
|
||||
accesslog = '/var/log/gunicorn/access.log'
|
||||
# 错误日志路径
|
||||
errorlog = '/var/log/gunicorn/error.log'
|
||||
# 日志级别
|
||||
loglevel = 'info'
|
||||
# 后台运行
|
||||
daemon = True
|
||||
0
family_rpa/main/__init__.py
Normal file
0
family_rpa/main/__init__.py
Normal file
BIN
family_rpa/main/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
family_rpa/main/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
family_rpa/main/__pycache__/admin.cpython-313.pyc
Normal file
BIN
family_rpa/main/__pycache__/admin.cpython-313.pyc
Normal file
Binary file not shown.
BIN
family_rpa/main/__pycache__/apps.cpython-313.pyc
Normal file
BIN
family_rpa/main/__pycache__/apps.cpython-313.pyc
Normal file
Binary file not shown.
BIN
family_rpa/main/__pycache__/forms.cpython-313.pyc
Normal file
BIN
family_rpa/main/__pycache__/forms.cpython-313.pyc
Normal file
Binary file not shown.
BIN
family_rpa/main/__pycache__/models.cpython-313.pyc
Normal file
BIN
family_rpa/main/__pycache__/models.cpython-313.pyc
Normal file
Binary file not shown.
BIN
family_rpa/main/__pycache__/urls.cpython-313.pyc
Normal file
BIN
family_rpa/main/__pycache__/urls.cpython-313.pyc
Normal file
Binary file not shown.
BIN
family_rpa/main/__pycache__/views.cpython-313.pyc
Normal file
BIN
family_rpa/main/__pycache__/views.cpython-313.pyc
Normal file
Binary file not shown.
16
family_rpa/main/admin.py
Normal file
16
family_rpa/main/admin.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from django.contrib import admin
|
||||
from .models import File, Message
|
||||
|
||||
@admin.register(File)
|
||||
class FileAdmin(admin.ModelAdmin):
|
||||
list_display = ('file', 'owner', 'created_at', 'is_public')
|
||||
list_filter = ('is_public', 'created_at')
|
||||
search_fields = ('file', 'description')
|
||||
date_hierarchy = 'created_at'
|
||||
|
||||
@admin.register(Message)
|
||||
class MessageAdmin(admin.ModelAdmin):
|
||||
list_display = ('author', 'content', 'created_at', 'ip_address')
|
||||
list_filter = ('created_at', 'ip_address')
|
||||
search_fields = ('author', 'content')
|
||||
date_hierarchy = 'created_at'
|
||||
6
family_rpa/main/apps.py
Normal file
6
family_rpa/main/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MainConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'main'
|
||||
18
family_rpa/main/forms.py
Normal file
18
family_rpa/main/forms.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from django import forms
|
||||
from .models import File, Message
|
||||
|
||||
class FileUploadForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = File
|
||||
fields = ['file', 'description', 'is_public']
|
||||
widgets = {
|
||||
'description': forms.Textarea(attrs={'rows': 3}),
|
||||
}
|
||||
|
||||
class MessageForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Message
|
||||
fields = ['author', 'content']
|
||||
widgets = {
|
||||
'content': forms.Textarea(attrs={'rows': 5}),
|
||||
}
|
||||
11
family_rpa/main/management/commands/cleanup_messages.py
Normal file
11
family_rpa/main/management/commands/cleanup_messages.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils import timezone
|
||||
from ...models import Message
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Clean up messages older than 7 days'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
one_week_ago = timezone.now() - timezone.timedelta(days=7)
|
||||
deleted_count, _ = Message.objects.filter(created_at__lt=one_week_ago).delete()
|
||||
self.stdout.write(self.style.SUCCESS(f'Successfully deleted {deleted_count} old messages'))
|
||||
41
family_rpa/main/migrations/0001_initial.py
Normal file
41
family_rpa/main/migrations/0001_initial.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# Generated by Django 5.1.4 on 2025-02-15 13:07
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Message',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('author', models.CharField(max_length=100)),
|
||||
('content', models.TextField()),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('ip_address', models.GenericIPAddressField(blank=True, null=True)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-created_at'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='File',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('file', models.FileField(upload_to='uploads/')),
|
||||
('description', models.TextField(blank=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('is_public', models.BooleanField(default=False)),
|
||||
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
21
family_rpa/main/migrations/0002_alter_file_owner.py
Normal file
21
family_rpa/main/migrations/0002_alter_file_owner.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# Generated by Django 5.1.4 on 2025-02-15 13:51
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('main', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='file',
|
||||
name='owner',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
0
family_rpa/main/migrations/__init__.py
Normal file
0
family_rpa/main/migrations/__init__.py
Normal file
Binary file not shown.
Binary file not shown.
BIN
family_rpa/main/migrations/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
family_rpa/main/migrations/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
25
family_rpa/main/models.py
Normal file
25
family_rpa/main/models.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
class File(models.Model):
|
||||
owner = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
|
||||
file = models.FileField(upload_to='uploads/')
|
||||
description = models.TextField(blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
is_public = models.BooleanField(default=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.file.name
|
||||
|
||||
class Message(models.Model):
|
||||
author = models.CharField(max_length=100)
|
||||
content = models.TextField()
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
ip_address = models.GenericIPAddressField(null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"来自 {self.author} 的留言"
|
||||
|
||||
class Meta:
|
||||
ordering = ['-created_at']
|
||||
3
family_rpa/main/tests.py
Normal file
3
family_rpa/main/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
9
family_rpa/main/urls.py
Normal file
9
family_rpa/main/urls.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.home, name='home'),
|
||||
path('upload/', views.upload_file, name='upload'),
|
||||
path('delete/<int:file_id>/', views.delete_file, name='delete'),
|
||||
path('message/', views.post_message, name='message'),
|
||||
]
|
||||
53
family_rpa/main/views.py
Normal file
53
family_rpa/main/views.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.http import HttpResponse
|
||||
from django.utils import timezone
|
||||
from django.core.files.storage import default_storage
|
||||
from django.core.paginator import Paginator
|
||||
from .models import File, Message
|
||||
from .forms import FileUploadForm, MessageForm
|
||||
from django.utils.timezone import timedelta
|
||||
|
||||
def home(request):
|
||||
# 获取最近一周的留言
|
||||
one_week_ago = timezone.now() - timedelta(days=7)
|
||||
messages = Message.objects.filter(created_at__gte=one_week_ago).order_by('-created_at')
|
||||
|
||||
# 获取公开文件
|
||||
files = File.objects.filter(is_public=True).order_by('-created_at')
|
||||
|
||||
return render(request, 'main/home.html', {
|
||||
'messages': messages,
|
||||
'files': files
|
||||
})
|
||||
|
||||
def upload_file(request):
|
||||
if request.method == 'POST':
|
||||
form = FileUploadForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
file = form.save(commit=False)
|
||||
if request.user.is_authenticated:
|
||||
file.owner = request.user
|
||||
file.save()
|
||||
return redirect('home')
|
||||
else:
|
||||
form = FileUploadForm()
|
||||
return render(request, 'main/upload.html', {'form': form})
|
||||
|
||||
def delete_file(request, file_id):
|
||||
file = get_object_or_404(File, id=file_id)
|
||||
if request.user == file.owner:
|
||||
file.file.delete()
|
||||
file.delete()
|
||||
return redirect('home')
|
||||
|
||||
def post_message(request):
|
||||
if request.method == 'POST':
|
||||
form = MessageForm(request.POST)
|
||||
if form.is_valid():
|
||||
message = form.save(commit=False)
|
||||
message.ip_address = request.META.get('HTTP_X_FORWARDED_FOR', request.META.get('REMOTE_ADDR'))
|
||||
message.save()
|
||||
return redirect('home')
|
||||
else:
|
||||
form = MessageForm()
|
||||
return render(request, 'main/message.html', {'form': form})
|
||||
22
family_rpa/manage.py
Normal file
22
family_rpa/manage.py
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/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', 'family_rpa.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()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
family_rpa/media/uploads/vac470lite.zip
Normal file
BIN
family_rpa/media/uploads/vac470lite.zip
Normal file
Binary file not shown.
BIN
family_rpa/media/uploads/vac470lite_0dVajUD.zip
Normal file
BIN
family_rpa/media/uploads/vac470lite_0dVajUD.zip
Normal file
Binary file not shown.
BIN
family_rpa/media/uploads/vac470lite_Tr7wrZb.zip
Normal file
BIN
family_rpa/media/uploads/vac470lite_Tr7wrZb.zip
Normal file
Binary file not shown.
BIN
family_rpa/media/uploads/vac470lite_cct4sWa.zip
Normal file
BIN
family_rpa/media/uploads/vac470lite_cct4sWa.zip
Normal file
Binary file not shown.
20
family_rpa/nginx.config
Normal file
20
family_rpa/nginx.config
Normal file
@@ -0,0 +1,20 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name 192.168.3.105;
|
||||
|
||||
location / {
|
||||
proxy_pass http://unix:/project/family_rpa/family_rpa.sock;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /static/ {
|
||||
alias /project/family_rpa/static/;
|
||||
}
|
||||
|
||||
location /media/ {
|
||||
alias /project/family_rpa/media/;
|
||||
}
|
||||
}
|
||||
0
family_rpa/requirements.txt
Normal file
0
family_rpa/requirements.txt
Normal file
107
family_rpa/static/output.css
Normal file
107
family_rpa/static/output.css
Normal file
@@ -0,0 +1,107 @@
|
||||
/*! tailwindcss v4.0.6 | MIT License | https://tailwindcss.com */
|
||||
.static {
|
||||
position: static;
|
||||
}
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
.mx-auto {
|
||||
margin-inline: auto;
|
||||
}
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
.grid-cols-1 {
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.border {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 1px;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.filter {
|
||||
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
||||
}
|
||||
.\[w\:۵qzg\] {
|
||||
w: ۵qzg;
|
||||
}
|
||||
@layer base {
|
||||
:root {
|
||||
--color-primary: 79 70 229;
|
||||
--color-secondary: 99 102 241;
|
||||
--color-accent: 249 115 22;
|
||||
--color-success: 16 185 129;
|
||||
--color-warning: 245 158 11;
|
||||
--color-error: 239 68 68;
|
||||
--color-surface: 255 255 255;
|
||||
--color-background: 249 250 251;
|
||||
}
|
||||
.dark {
|
||||
--color-primary: 99 102 241;
|
||||
--color-secondary: 129 140 248;
|
||||
--color-accent: 249 115 22;
|
||||
--color-success: 16 185 129;
|
||||
--color-warning: 245 158 11;
|
||||
--color-error: 239 68 68;
|
||||
--color-surface: 31 41 55;
|
||||
--color-background: 17 24 39;
|
||||
}
|
||||
}
|
||||
@property --tw-border-style {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: solid;
|
||||
}
|
||||
@property --tw-blur {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-brightness {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-contrast {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-grayscale {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-hue-rotate {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-invert {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-opacity {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-saturate {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-sepia {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
BIN
family_rpa/tailwindcss-windows-x64.exe
Normal file
BIN
family_rpa/tailwindcss-windows-x64.exe
Normal file
Binary file not shown.
49
family_rpa/templates/base.html
Normal file
49
family_rpa/templates/base.html
Normal file
@@ -0,0 +1,49 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-hans">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>家庭RPA系统</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="{% url 'home' %}">首页</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'upload' %}">上传文件</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'message' %}">发布留言</a>
|
||||
</li>
|
||||
{% if user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'admin:index' %}">管理后台</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'logout' %}">注销</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'login' %}">登录</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container mt-4">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
37
family_rpa/templates/main/home.html
Normal file
37
family_rpa/templates/main/home.html
Normal file
@@ -0,0 +1,37 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<h2 class="h2 mb-4">最新留言</h2>
|
||||
{% for message in messages %}
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="fw-bold">{{ message.author }}</span>
|
||||
<span class="text-muted small">{{ message.created_at|date:"Y-m-d H:i" }}</span>
|
||||
</div>
|
||||
<p class="card-text">{{ message.content }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h2 class="h2 mb-4">公开文件</h2>
|
||||
{% for file in files %}
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<a href="{{ file.file.url }}" class="fw-bold text-decoration-none">
|
||||
{{ file.file.name }}
|
||||
</a>
|
||||
<span class="text-muted small">{{ file.created_at|date:"Y-m-d H:i" }}</span>
|
||||
</div>
|
||||
<p class="card-text">{{ file.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
20
family_rpa/templates/main/message.html
Normal file
20
family_rpa/templates/main/message.html
Normal file
@@ -0,0 +1,20 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<h1 class="h2 mb-4">发布留言</h1>
|
||||
<form method="post" class="card p-4">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
{{ form.as_p }}
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
发布留言
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
33
family_rpa/templates/main/upload.html
Normal file
33
family_rpa/templates/main/upload.html
Normal file
@@ -0,0 +1,33 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<h1 class="h2 mb-4">上传文件</h1>
|
||||
<form method="post" enctype="multipart/form-data" class="card p-4">
|
||||
{% csrf_token %}
|
||||
<div class="mb-3">
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.file.id_for_label }}" class="form-label">选择文件</label>
|
||||
{{ form.file }}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.description.id_for_label }}" class="form-label">文件描述</label>
|
||||
{{ form.description }}
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
{{ form.is_public }}
|
||||
<label for="{{ form.is_public.id_for_label }}" class="form-check-label">
|
||||
公开文件(勾选后文件将对所有人可见)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
上传文件
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
15
family_rpa/templates/registration/logged_out.html
Normal file
15
family_rpa/templates/registration/logged_out.html
Normal file
@@ -0,0 +1,15 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h2 class="h2 mb-4">已注销</h2>
|
||||
|
||||
<p class="mb-4">您已成功注销。</p>
|
||||
|
||||
<a href="{% url 'login' %}" class="link-primary">重新登录</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
39
family_rpa/templates/registration/login.html
Normal file
39
family_rpa/templates/registration/login.html
Normal file
@@ -0,0 +1,39 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h2 class="h2 mb-4">登录</h2>
|
||||
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger mb-4">
|
||||
用户名或密码错误,请重试。
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">用户名</label>
|
||||
{{ form.username }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">密码</label>
|
||||
{{ form.password }}
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
登录
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="mt-4 text-center">
|
||||
<a href="#" class="link-primary">忘记密码?</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
25
final_fix.sh
Normal file
25
final_fix.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
SERVER_IP="192.168.3.105"
|
||||
USER="xiaji"
|
||||
|
||||
ssh $USER@$SERVER_IP << 'EOF'
|
||||
# 修复权限问题
|
||||
sudo chown xiaji:www-data /home/xiaji/myproject/myproject.sock
|
||||
sudo chmod 660 /home/xiaji/myproject/myproject.sock
|
||||
|
||||
# 确认配置有效性
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart family_rpa.service
|
||||
|
||||
# 验证服务状态
|
||||
echo -e "\n=== 最终服务状态 ==="
|
||||
sudo systemctl status family_rpa.service --no-pager | head -n 10
|
||||
sudo ss -nltp | grep -E ':80|.sock'
|
||||
|
||||
# 开放防火墙
|
||||
sudo ufw allow 80/tcp
|
||||
EOF
|
||||
|
||||
# 本地验证
|
||||
curl -v --connect-timeout 10 http://$SERVER_IP
|
||||
21
final_validate.sh
Normal file
21
final_validate.sh
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
SERVER_IP="192.168.3.105"
|
||||
USER="xiaji"
|
||||
|
||||
echo "=== 最终状态验证 ==="
|
||||
ssh $USER@$SERVER_IP << 'EOF'
|
||||
echo "1. 服务运行状态:"
|
||||
sudo systemctl is-active family_rpa.service nginx
|
||||
|
||||
echo -e "\n2. 端口监听情况:"
|
||||
sudo netstat -tulpn | grep -E ':80|.sock'
|
||||
|
||||
echo -e "\n3. 最新访问日志:"
|
||||
sudo tail -n 5 /var/log/nginx/access.log
|
||||
|
||||
echo -e "\n4. 资源使用情况:"
|
||||
free -h && df -h
|
||||
EOF
|
||||
|
||||
echo -e "\n=== 本地访问测试 ==="
|
||||
curl -sS -o /dev/null -w "HTTP状态码: %{http_code}\n响应时间: %{time_total}s\n" http://$SERVER_IP
|
||||
23
fix_services.sh
Normal file
23
fix_services.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
SERVER_IP="192.168.3.105"
|
||||
USER="xiaji"
|
||||
|
||||
ssh $USER@$SERVER_IP << 'EOF'
|
||||
# 修正Nginx配置
|
||||
sudo sed -i "s/proxy_pass http:\/\/127.0.0.1:8000;/proxy_pass http:\/\/unix:\/home\/xiaji\/myproject\/myproject.sock;/g" /etc/nginx/sites-available/family_rpa
|
||||
|
||||
# 修正Gunicorn绑定方式
|
||||
sudo sed -i "s/--bind 0.0.0.0:8000/--bind unix:\/home\/xiaji\/myproject\/myproject.sock/g" /etc/systemd/system/family_rpa.service
|
||||
|
||||
# 设置socket权限
|
||||
echo "UMASK=007" | sudo tee -a /etc/systemd/system/family_rpa.service
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
# 重启服务
|
||||
sudo systemctl restart family_rpa.service
|
||||
sudo systemctl restart nginx
|
||||
|
||||
# 验证配置
|
||||
sudo nginx -t
|
||||
sudo ss -alnpt | grep -E '80|.sock'
|
||||
EOF
|
||||
30
full_diagnose.sh
Normal file
30
full_diagnose.sh
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
SERVER_IP="192.168.3.105"
|
||||
USER="xiaji"
|
||||
|
||||
echo "=== 服务状态深度检查 ==="
|
||||
ssh $USER@$SERVER_IP << 'EOF'
|
||||
echo "1. Gunicorn服务状态:"
|
||||
sudo systemctl status family_rpa.service --no-pager
|
||||
|
||||
echo -e "\n2. Nginx服务状态:"
|
||||
sudo systemctl status nginx --no-pager
|
||||
|
||||
echo -e "\n3. Socket文件检查:"
|
||||
ls -l /home/xiaji/myproject/myproject.sock
|
||||
stat /home/xiaji/myproject/myproject.sock
|
||||
|
||||
echo -e "\n4. 进程关联验证:"
|
||||
sudo lsof -U | grep myproject.sock
|
||||
|
||||
echo -e "\n5. 权限配置检查:"
|
||||
groups www-data
|
||||
getent group www-data
|
||||
|
||||
echo -e "\n6. 最新错误日志:"
|
||||
sudo tail -30 /var/log/nginx/error.log
|
||||
sudo journalctl -u family_rpa.service --since "5 minutes ago" --no-pager
|
||||
EOF
|
||||
|
||||
echo -e "\n=== 本地网络验证 ==="
|
||||
curl -v --connect-timeout 10 http://$SERVER_IP
|
||||
BIN
tailwindcss-windows-x64.exe
Normal file
BIN
tailwindcss-windows-x64.exe
Normal file
Binary file not shown.
10
upload_commands.txt
Normal file
10
upload_commands.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
sudo rm -rf /project
|
||||
sudo mkdir -p /project
|
||||
sudo chown -R xiaji:xiaji /project
|
||||
rsync -avz --progress family_rpa xiaji@192.168.3.105:/project/
|
||||
sudo cp /project/family_rpa/nginx.config /etc/nginx/sites-available/family_rpa
|
||||
sudo cp /project/family_rpa/family_rpa.service /etc/systemd/system/
|
||||
sudo ln -sf /etc/nginx/sites-available/family_rpa /etc/nginx/sites-enabled/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart nginx
|
||||
sudo systemctl start family_rpa
|
||||
Reference in New Issue
Block a user