feat: implement database backup and restore functionality with authentication; add deployment script and service configuration
This commit is contained in:
105
app.py
105
app.py
@@ -1,11 +1,23 @@
|
||||
from flask import Flask, request, jsonify
|
||||
from flask import Flask, request, jsonify, send_file
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_cors import CORS
|
||||
from flask_httpauth import HTTPBasicAuth
|
||||
from datetime import datetime
|
||||
import os
|
||||
import shutil
|
||||
import json
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
auth = HTTPBasicAuth()
|
||||
|
||||
# 配置认证信息
|
||||
USERS = {
|
||||
"admin": "your-secure-password" # 请修改为安全的密码
|
||||
}
|
||||
|
||||
@auth.verify_password
|
||||
def verify_password(username, password):
|
||||
if username in USERS and USERS[username] == password:
|
||||
return username
|
||||
|
||||
# 配置数据库
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///media.db'
|
||||
@@ -29,8 +41,91 @@ class Media(db.Model):
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
|
||||
# 添加数据库备份功能
|
||||
def backup_database():
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
backup_dir = 'backups'
|
||||
if not os.path.exists(backup_dir):
|
||||
os.makedirs(backup_dir)
|
||||
|
||||
backup_file = f'{backup_dir}/media_{timestamp}.db'
|
||||
shutil.copy2('media.db', backup_file)
|
||||
return backup_file
|
||||
|
||||
@app.route('/api/backup', methods=['POST'])
|
||||
@auth.login_required
|
||||
def create_backup():
|
||||
try:
|
||||
backup_file = backup_database()
|
||||
return jsonify({
|
||||
"code": 0,
|
||||
"data": {
|
||||
"backup_file": backup_file,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
},
|
||||
"message": "Backup created successfully"
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({"code": 1, "data": {}, "message": str(e)}), 500
|
||||
|
||||
@app.route('/api/backup/list', methods=['GET'])
|
||||
@auth.login_required
|
||||
def list_backups():
|
||||
try:
|
||||
backup_dir = 'backups'
|
||||
if not os.path.exists(backup_dir):
|
||||
return jsonify({
|
||||
"code": 0,
|
||||
"data": {"backups": []},
|
||||
"message": "No backups found"
|
||||
})
|
||||
|
||||
backups = []
|
||||
for file in os.listdir(backup_dir):
|
||||
if file.endswith('.db'):
|
||||
file_path = os.path.join(backup_dir, file)
|
||||
backups.append({
|
||||
"filename": file,
|
||||
"size": os.path.getsize(file_path),
|
||||
"created_at": datetime.fromtimestamp(os.path.getctime(file_path)).isoformat()
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
"code": 0,
|
||||
"data": {"backups": backups},
|
||||
"message": "Success"
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({"code": 1, "data": {}, "message": str(e)}), 500
|
||||
|
||||
@app.route('/api/backup/restore/<filename>', methods=['POST'])
|
||||
@auth.login_required
|
||||
def restore_backup(filename):
|
||||
try:
|
||||
backup_file = os.path.join('backups', filename)
|
||||
if not os.path.exists(backup_file):
|
||||
return jsonify({"code": 1, "data": {}, "message": "Backup file not found"}), 404
|
||||
|
||||
# 先备份当前数据库
|
||||
current_backup = backup_database()
|
||||
|
||||
# 恢复选定的备份
|
||||
shutil.copy2(backup_file, 'media.db')
|
||||
|
||||
return jsonify({
|
||||
"code": 0,
|
||||
"data": {
|
||||
"restored_file": filename,
|
||||
"current_backup": current_backup
|
||||
},
|
||||
"message": "Backup restored successfully"
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({"code": 1, "data": {}, "message": str(e)}), 500
|
||||
|
||||
# API 路由
|
||||
@app.route('/api/media/list', methods=['GET'])
|
||||
@auth.login_required
|
||||
def get_all_media():
|
||||
try:
|
||||
media_list = Media.query.all()
|
||||
@@ -54,6 +149,7 @@ def get_all_media():
|
||||
return jsonify({"code": 1, "data": {}, "message": str(e)}), 500
|
||||
|
||||
@app.route('/api/media/create', methods=['POST'])
|
||||
@auth.login_required
|
||||
def create_media():
|
||||
try:
|
||||
data = request.json
|
||||
@@ -89,6 +185,7 @@ def create_media():
|
||||
return jsonify({"code": 2, "data": {}, "message": str(e)}), 500
|
||||
|
||||
@app.route('/api/media/updateById/<int:media_id>', methods=['PUT'])
|
||||
@auth.login_required
|
||||
def update_media(media_id):
|
||||
try:
|
||||
media = Media.query.get_or_404(media_id)
|
||||
@@ -125,6 +222,7 @@ def update_media(media_id):
|
||||
return jsonify({"code": 2, "data": {}, "message": str(e)}), 500
|
||||
|
||||
@app.route('/api/media/deleteById/<int:media_id>', methods=['DELETE'])
|
||||
@auth.login_required
|
||||
def delete_media(media_id):
|
||||
try:
|
||||
media = Media.query.get_or_404(media_id)
|
||||
@@ -136,6 +234,7 @@ def delete_media(media_id):
|
||||
return jsonify({"code": 2, "data": {}, "message": str(e)}), 500
|
||||
|
||||
@app.route('/api/media/page', methods=['GET'])
|
||||
@auth.login_required
|
||||
def get_media_page():
|
||||
try:
|
||||
type = request.args.get('type')
|
||||
|
||||
33
deploy.sh
Normal file
33
deploy.sh
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 更新系统
|
||||
sudo apt update
|
||||
sudo apt upgrade -y
|
||||
|
||||
# 安装 Python 和依赖
|
||||
sudo apt install -y python3 python3-pip python3-venv
|
||||
|
||||
# 创建项目目录
|
||||
mkdir -p /home/ubuntu/my-score/backend
|
||||
|
||||
# 创建并激活虚拟环境
|
||||
python3 -m venv /home/ubuntu/my-score/backend/venv
|
||||
source /home/ubuntu/my-score/backend/venv/bin/activate
|
||||
|
||||
# 安装项目依赖
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 复制服务文件
|
||||
sudo cp my-score.service /etc/systemd/system/
|
||||
|
||||
# 重新加载 systemd
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
# 启动服务
|
||||
sudo systemctl start my-score
|
||||
|
||||
# 设置开机自启
|
||||
sudo systemctl enable my-score
|
||||
|
||||
# 检查服务状态
|
||||
sudo systemctl status my-score
|
||||
15
my-score.service
Normal file
15
my-score.service
Normal file
@@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=My Score Backend Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=ubuntu
|
||||
Group=ubuntu
|
||||
WorkingDirectory=/home/ubuntu/my-score/backend
|
||||
Environment="PATH=/home/ubuntu/my-score/backend/venv/bin"
|
||||
ExecStart=/home/ubuntu/my-score/backend/venv/bin/python app.py
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,5 +1,5 @@
|
||||
Flask==3.0.2
|
||||
Flask-SQLAlchemy==3.1.1
|
||||
Flask-Cors==4.0.0
|
||||
Flask-HTTPAuth==4.8.0
|
||||
python-dotenv==1.0.1
|
||||
SQLAlchemy==2.0.28
|
||||
Reference in New Issue
Block a user