alpha v0.0971

This commit is contained in:
2026-08-11 12:53:59 +03:00
parent 1d363b21a4
commit 708e6800fa

104
app.py
View File

@@ -1,5 +1,5 @@
from flask import Flask, render_template, request, redirect, url_for, flash, Response, jsonify, session, send_from_directory
from flask import Flask, render_template, request, redirect, url_for, flash, Response, jsonify, session, send_from_directory, send_file
try:
import psycopg
except ModuleNotFoundError:
@@ -16,6 +16,13 @@ import re
import os
import json
import sys
from database_maintenance import (
DatabaseMaintenanceBusy,
DatabaseMaintenanceError,
DatabaseMaintenanceManager,
InvalidBackupFile,
PostgreSqlToolNotFound,
)
from handlers import computers as computers_handlers
from handlers import projectors as projectors_handlers
from routes.projectors import _normalize_board_fields
@@ -79,6 +86,7 @@ APP_PORT = _integer_setting(APPLICATION_SETTINGS, "port", 5050)
app = Flask(__name__)
app.secret_key = "secret"
app.config["DB_INITIALIZED"] = False
database_maintenance = DatabaseMaintenanceManager(DB_CONFIG)
CLIENT_AGENT_TOKEN = "change-me-client-token"
CLIENT_TEST_OFFLINE_AFTER_SECONDS = 35
@@ -2640,6 +2648,100 @@ def logout():
return redirect(url_for("login"))
def database_admin_api(view):
@wraps(view)
def wrapper(*args, **kwargs):
if "user" not in session:
return jsonify(ok=False, error="Требуется авторизация."), 401
if session.get("role") != "admin":
return jsonify(ok=False, error="Требуются права администратора."), 403
return view(*args, **kwargs)
return wrapper
def _database_size_bytes():
conn = get_conn()
try:
cur = conn.cursor()
cur.execute("SELECT pg_database_size(current_database())")
row = cur.fetchone()
return int(row[0] or 0) if row else 0
finally:
conn.close()
@app.route("/api/database/backup/start", methods=["POST"])
@database_admin_api
def database_backup_start():
try:
try:
database_size = _database_size_bytes()
except Exception:
database_size = 0
job_id = database_maintenance.start_backup(
session.get("user"),
estimated_database_size=database_size,
)
return jsonify(ok=True, job_id=job_id), 202
except (DatabaseMaintenanceBusy, PostgreSqlToolNotFound) as exc:
return jsonify(ok=False, error=str(exc)), 409
except DatabaseMaintenanceError as exc:
return jsonify(ok=False, error=str(exc)), 400
except Exception:
app.logger.exception("Failed to start a database backup")
return jsonify(ok=False, error="Не удалось запустить резервное копирование."), 500
@app.route("/api/database/restore/start", methods=["POST"])
@database_admin_api
def database_restore_start():
uploaded_file = request.files.get("backup_file")
if not uploaded_file or not uploaded_file.filename:
return jsonify(ok=False, error="Выберите файл резервной копии."), 400
try:
job_id = database_maintenance.start_restore(
session.get("user"),
uploaded_file,
uploaded_file.filename,
)
return jsonify(ok=True, job_id=job_id), 202
except DatabaseMaintenanceBusy as exc:
return jsonify(ok=False, error=str(exc)), 409
except (InvalidBackupFile, PostgreSqlToolNotFound, DatabaseMaintenanceError) as exc:
return jsonify(ok=False, error=str(exc)), 400
except Exception:
app.logger.exception("Failed to start a database restore")
return jsonify(ok=False, error="Не удалось запустить восстановление базы данных."), 500
@app.route("/api/database/jobs/<job_id>")
@database_admin_api
def database_job_status(job_id):
job = database_maintenance.get_job(job_id, session.get("user"))
if not job:
return jsonify(ok=False, error="Операция не найдена."), 404
if job.get("kind") == "restore" and job.get("status") == "success":
app.config["DB_INITIALIZED"] = False
return jsonify(ok=True, job=job)
@app.route("/api/database/backup/<job_id>/download")
@database_admin_api
def database_backup_download(job_id):
backup = database_maintenance.get_backup_download(job_id, session.get("user"))
if not backup:
return jsonify(ok=False, error="Файл резервной копии не найден."), 404
path, filename = backup
return send_file(
path,
mimetype="application/octet-stream",
as_attachment=True,
download_name=filename,
conditional=True,
)
@app.route("/network")
@login_required
def network():