第一个项目

This commit is contained in:
2025-02-16 21:51:06 +08:00
commit 1bbb2f79dc
62 changed files with 956 additions and 0 deletions

15
check_live.sh Normal file
View 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
View 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
View 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

Binary file not shown.

BIN
family_rpa/db.sqlite3 Normal file

Binary file not shown.

View 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

View File

Binary file not shown.

Binary file not shown.

View 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()

View 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'

View 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')),
]

View 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()

View 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

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

16
family_rpa/main/admin.py Normal file
View 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
View 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
View 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}),
}

View 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'))

View 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)),
],
),
]

View 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),
),
]

View File

25
family_rpa/main/models.py Normal file
View 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
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

9
family_rpa/main/urls.py Normal file
View 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
View 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
View 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.

Binary file not shown.

20
family_rpa/nginx.config Normal file
View 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/;
}
}

View File

View 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;
}

Binary file not shown.

View 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>

View 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 %}

View 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 %}

View 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 %}

View 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 %}

View 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
View 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
View 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
View 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
View 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

Binary file not shown.

10
upload_commands.txt Normal file
View 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