Files
fotohost/app/routes.py
T
2026-06-06 22:30:00 +03:00

193 lines
6.0 KiB
Python

import os
import uuid
from datetime import datetime, timezone
from flask import (
Blueprint,
current_app,
abort,
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.folder_utils import can_edit_folder
from app.models import Folder, 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"))
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)
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,
folder_id=folder.id if folder else None,
created_at=datetime.now(timezone.utc),
)
db.session.add(photo)
db.session.commit()
flash("Фото успешно загружено", "success")
if folder:
return redirect(url_for("folders.view_folder", folder_id=folder.id))
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/<path:filename>")
def uploaded_file(filename):
return send_from_directory(current_app.config["UPLOAD_FOLDER"], filename)
@bp.route("/delete/<int:photo_id>", 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():
from app.folder_utils import process_pending_invites
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)
return render_template(
"cabinet/index.html",
photos=photos,
folders=folders,
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")