import os import uuid from datetime import datetime, timezone from flask import ( Blueprint, current_app, flash, jsonify, redirect, render_template, request, send_from_directory, url_for, ) from flask_login import current_user, login_required from werkzeug.utils import secure_filename from app import db from app.auth_utils import photo_owner_or_admin from app.models import Photo bp = Blueprint("main", __name__) def allowed_file(filename): return ( "." in filename and filename.rsplit(".", 1)[1].lower() in current_app.config["ALLOWED_EXTENSIONS"] ) @bp.route("/") def index(): photos = Photo.query.order_by(Photo.created_at.desc()).limit(24).all() total_photos = Photo.query.count() total_size = db.session.query(db.func.coalesce(db.func.sum(Photo.file_size), 0)).scalar() or 0 return render_template( "index.html", photos=photos, total_photos=total_photos, total_size=int(total_size), max_upload_mb=current_app.config["MAX_CONTENT_LENGTH"] // (1024 * 1024), ) @bp.route("/upload", methods=["POST"]) @login_required def upload(): if "photo" not in request.files: flash("Файл не выбран", "error") return redirect(request.referrer or url_for("main.index")) file = request.files["photo"] if file.filename == "": flash("Файл не выбран", "error") return redirect(request.referrer or url_for("main.index")) if not allowed_file(file.filename): flash("Недопустимый формат. Разрешены: PNG, JPG, GIF, WEBP, BMP", "error") return redirect(request.referrer or url_for("main.index")) ext = file.filename.rsplit(".", 1)[1].lower() stored_name = f"{uuid.uuid4().hex}.{ext}" safe_original = secure_filename(file.filename) or f"photo.{ext}" upload_dir = current_app.config["UPLOAD_FOLDER"] filepath = os.path.join(upload_dir, stored_name) file.save(filepath) file_size = os.path.getsize(filepath) photo = Photo( filename=stored_name, original_name=safe_original, file_size=file_size, mime_type=file.content_type or f"image/{ext}", user_id=current_user.id, created_at=datetime.now(timezone.utc), ) db.session.add(photo) db.session.commit() flash("Фото успешно загружено", "success") return redirect(url_for("cabinet.index")) @bp.route("/api/photos") def api_photos(): photos = Photo.query.order_by(Photo.created_at.desc()).all() return jsonify( [ { "id": p.id, "url": p.url, "original_name": p.original_name, "file_size": p.file_size, "size_human": p.size_human, "user_id": p.user_id, "created_at": p.created_at.isoformat(), } for p in photos ] ) @bp.route("/uploads/") def uploaded_file(filename): return send_from_directory(current_app.config["UPLOAD_FOLDER"], filename) @bp.route("/delete/", methods=["POST"]) @login_required def delete_photo(photo_id): photo = Photo.query.get_or_404(photo_id) photo_owner_or_admin(photo) 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(request.referrer or url_for("main.index")) cabinet_bp = Blueprint("cabinet", __name__, url_prefix="/cabinet") @cabinet_bp.route("/") @login_required def index(): photos = ( Photo.query.filter_by(user_id=current_user.id) .order_by(Photo.created_at.desc()) .all() ) total_size = sum(p.file_size for p in photos) return render_template( "cabinet/index.html", photos=photos, total_photos=len(photos), total_size=total_size, max_upload_mb=current_app.config["MAX_CONTENT_LENGTH"] // (1024 * 1024), ) @cabinet_bp.route("/profile", methods=["GET", "POST"]) @login_required def profile(): from app.models import User if request.method == "POST": email = request.form.get("email", "").strip().lower() current_password = request.form.get("current_password", "") new_password = request.form.get("new_password", "") new_password2 = request.form.get("new_password2", "") other = User.query.filter(User.email == email, User.id != current_user.id).first() if other: flash("Этот email уже используется", "error") elif not current_user.check_password(current_password): flash("Неверный текущий пароль", "error") elif new_password and len(new_password) < 6: flash("Новый пароль — минимум 6 символов", "error") elif new_password and new_password != new_password2: flash("Новые пароли не совпадают", "error") else: current_user.email = email if new_password: current_user.set_password(new_password) db.session.commit() flash("Профиль обновлён", "success") return redirect(url_for("cabinet.profile")) return render_template("cabinet/profile.html")