Add user auth, personal cabinet, admin panel and first admin bootstrap
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+118
@@ -0,0 +1,118 @@
|
||||
import os
|
||||
|
||||
from flask import Blueprint, current_app, flash, redirect, render_template, request, url_for
|
||||
from flask_login import current_user
|
||||
from sqlalchemy import func
|
||||
|
||||
from app import db
|
||||
from app.auth_utils import admin_required
|
||||
from app.models import Photo, User
|
||||
|
||||
bp = Blueprint("admin", __name__, url_prefix="/admin")
|
||||
|
||||
|
||||
@bp.route("/")
|
||||
@admin_required
|
||||
def dashboard():
|
||||
stats = {
|
||||
"users": User.query.count(),
|
||||
"photos": Photo.query.count(),
|
||||
"admins": User.query.filter_by(is_admin=True).count(),
|
||||
"storage": int(
|
||||
db.session.query(func.coalesce(func.sum(Photo.file_size), 0)).scalar() or 0
|
||||
),
|
||||
}
|
||||
recent_users = User.query.order_by(User.created_at.desc()).limit(5).all()
|
||||
recent_photos = Photo.query.order_by(Photo.created_at.desc()).limit(8).all()
|
||||
return render_template(
|
||||
"admin/dashboard.html",
|
||||
stats=stats,
|
||||
recent_users=recent_users,
|
||||
recent_photos=recent_photos,
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/users")
|
||||
@admin_required
|
||||
def users():
|
||||
all_users = User.query.order_by(User.created_at.desc()).all()
|
||||
return render_template("admin/users.html", users=all_users)
|
||||
|
||||
|
||||
@bp.route("/users/<int:user_id>/toggle-admin", methods=["POST"])
|
||||
@admin_required
|
||||
def toggle_admin(user_id):
|
||||
user = User.query.get_or_404(user_id)
|
||||
if user.id == current_user.id:
|
||||
flash("Нельзя снять права администратора с самого себя", "error")
|
||||
return redirect(url_for("admin.users"))
|
||||
|
||||
admin_count = User.query.filter_by(is_admin=True).count()
|
||||
if user.is_admin and admin_count <= 1:
|
||||
flash("Нельзя удалить последнего администратора", "error")
|
||||
return redirect(url_for("admin.users"))
|
||||
|
||||
user.is_admin = not user.is_admin
|
||||
db.session.commit()
|
||||
action = "назначен администратором" if user.is_admin else "лишён прав администратора"
|
||||
flash(f"Пользователь {user.username} {action}", "success")
|
||||
return redirect(url_for("admin.users"))
|
||||
|
||||
|
||||
@bp.route("/users/<int:user_id>/toggle-active", methods=["POST"])
|
||||
@admin_required
|
||||
def toggle_active(user_id):
|
||||
user = User.query.get_or_404(user_id)
|
||||
if user.id == current_user.id:
|
||||
flash("Нельзя заблокировать самого себя", "error")
|
||||
return redirect(url_for("admin.users"))
|
||||
|
||||
user.is_active = not user.is_active
|
||||
db.session.commit()
|
||||
action = "разблокирован" if user.is_active else "заблокирован"
|
||||
flash(f"Пользователь {user.username} {action}", "success")
|
||||
return redirect(url_for("admin.users"))
|
||||
|
||||
|
||||
@bp.route("/users/<int:user_id>/delete", methods=["POST"])
|
||||
@admin_required
|
||||
def delete_user(user_id):
|
||||
user = User.query.get_or_404(user_id)
|
||||
if user.id == current_user.id:
|
||||
flash("Нельзя удалить самого себя", "error")
|
||||
return redirect(url_for("admin.users"))
|
||||
|
||||
if user.is_admin and User.query.filter_by(is_admin=True).count() <= 1:
|
||||
flash("Нельзя удалить последнего администратора", "error")
|
||||
return redirect(url_for("admin.users"))
|
||||
|
||||
for photo in user.photos.all():
|
||||
filepath = os.path.join(current_app.config["UPLOAD_FOLDER"], photo.filename)
|
||||
if os.path.exists(filepath):
|
||||
os.remove(filepath)
|
||||
db.session.delete(photo)
|
||||
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
flash(f"Пользователь {user.username} удалён", "success")
|
||||
return redirect(url_for("admin.users"))
|
||||
|
||||
|
||||
@bp.route("/photos")
|
||||
@admin_required
|
||||
def photos():
|
||||
all_photos = Photo.query.order_by(Photo.created_at.desc()).all()
|
||||
return render_template("admin/photos.html", photos=all_photos)
|
||||
|
||||
|
||||
@bp.route("/photos/<int:photo_id>/delete", methods=["POST"])
|
||||
@admin_required
|
||||
def delete_photo(photo_id):
|
||||
photo = Photo.query.get_or_404(photo_id)
|
||||
filepath = os.path.join(current_app.config["UPLOAD_FOLDER"], photo.filename)
|
||||
if os.path.exists(filepath):
|
||||
os.remove(filepath)
|
||||
db.session.delete(photo)
|
||||
db.session.commit()
|
||||
flash("Фото удалено", "success")
|
||||
return redirect(url_for("admin.photos"))
|
||||
Reference in New Issue
Block a user