import os from flask import ( Blueprint, abort, current_app, flash, jsonify, redirect, render_template, request, send_file, url_for, ) from flask_login import current_user, login_required from app import db from app.auth_utils import photo_owner_or_admin from app.folder_utils import can_edit_folder from app.models import Folder, Photo from app.settings_service import get_settings from app.storage_service import delete_photo_file, get_photo_stream from app.upload_service import process_uploads bp = Blueprint("main", __name__) @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 settings = get_settings() 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), max_bulk_upload=settings.max_bulk_upload, ) @bp.route("/upload", methods=["POST"]) @login_required def upload(): folder_id = request.form.get("folder_id", type=int) folder = None if folder_id: folder = Folder.query.get_or_404(folder_id) if not can_edit_folder(folder): abort(403) result = process_uploads( request.files, current_user, folder, current_app.config["ALLOWED_EXTENSIONS"], ) if result["uploaded"] == 0 and result["errors"]: flash(result["errors"][0], "error") elif result["uploaded"] == 1: flash("Фото успешно загружено", "success") elif result["uploaded"] > 1: flash(f"Загружено {result['uploaded']} фото", "success") for err in result["errors"]: if result["uploaded"] > 0: flash(err, "error") if result["uploaded"] > 0: from app.email_service import send_upload_notification send_upload_notification( current_user, result["uploaded"], folder.name if folder else None, ) if folder: return redirect(url_for("folders.view_folder", folder_id=folder.id)) return redirect(request.referrer or 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): photo = Photo.query.filter_by(filename=filename).first() storage_backend = photo.storage_backend if photo else "local" stream = get_photo_stream(filename, storage_backend) if stream is None: abort(404) mimetype = photo.mime_type if photo else "application/octet-stream" return send_file(stream, mimetype=mimetype) @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) delete_photo_file(photo.filename, photo.storage_backend) 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(): from app.folder_utils import process_pending_invites from app.quota_utils import quota_status process_pending_invites(current_user) photos = ( Photo.query.filter_by(user_id=current_user.id, folder_id=None) .order_by(Photo.created_at.desc()) .all() ) folders = Folder.query.filter_by(owner_id=current_user.id).order_by(Folder.created_at.desc()).limit(6).all() total_size = sum(p.file_size for p in photos) quota = quota_status(current_user) settings = get_settings() return render_template( "cabinet/index.html", photos=photos, folders=folders, total_photos=len(photos), total_size=total_size, quota=quota, max_upload_mb=current_app.config["MAX_CONTENT_LENGTH"] // (1024 * 1024), max_bulk_upload=settings.max_bulk_upload, ) @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")