Add support for use singe pic on post

This commit is contained in:
2026-08-30 10:29:53 +03:00
parent 844121ecb1
commit 987774a320
8 changed files with 54 additions and 10 deletions
+45 -6
View File
@@ -10,20 +10,25 @@ from functools import wraps
from pathlib import Path
from secrets import token_urlsafe
from hashlib import sha256
from uuid import uuid4
from flask import Flask, abort, flash, g, redirect, render_template, request, session, url_for
from flask import Flask, abort, flash, g, redirect, render_template, request, send_from_directory, session, url_for
from flask_wtf.csrf import CSRFProtect
from PIL import Image, ImageOps, UnidentifiedImageError
import pyotp
import qrcode
from werkzeug.security import check_password_hash, generate_password_hash
BASE_DIR = Path(__file__).resolve().parent
DATABASE = Path(os.environ.get("DATABASE_PATH", BASE_DIR / "data" / "eternity.db"))
UPLOAD_FOLDER = Path(os.environ.get("UPLOAD_FOLDER", BASE_DIR / "data" / "uploads"))
app = Flask(__name__)
app.config.update(
SECRET_KEY=os.environ.get("SECRET_KEY", "change-this-secret-before-production"),
DATABASE=DATABASE,
UPLOAD_FOLDER=UPLOAD_FOLDER,
MAX_CONTENT_LENGTH=8 * 1024 * 1024,
MAIL_HOST=os.environ.get("MAIL_HOST"),
MAIL_PORT=int(os.environ.get("MAIL_PORT", "587")),
MAIL_USERNAME=os.environ.get("MAIL_USERNAME"),
@@ -71,6 +76,7 @@ def init_db():
excerpt TEXT NOT NULL,
body TEXT NOT NULL,
category TEXT NOT NULL,
image_filename TEXT,
author_id INTEGER NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
@@ -95,9 +101,12 @@ def init_db():
"mfa_enabled": "ALTER TABLE users ADD COLUMN mfa_enabled INTEGER NOT NULL DEFAULT 0",
"email": "ALTER TABLE users ADD COLUMN email TEXT",
}
post_columns = {row["name"] for row in db.execute("PRAGMA table_info(posts)")}
for column, statement in migrations.items():
if column not in user_columns:
db.execute(statement)
if "image_filename" not in post_columns:
db.execute("ALTER TABLE posts ADD COLUMN image_filename TEXT")
db.execute("CREATE UNIQUE INDEX IF NOT EXISTS users_email_unique ON users(email)")
# Preserve access for accounts created before approvals were introduced.
@@ -159,6 +168,27 @@ def post_from_request(post_id=None):
return title, unique_slug(title, post_id), excerpt, body, category, now
def save_post_image():
image_file = request.files.get("image")
if image_file is None or not image_file.filename:
return None
try:
image = Image.open(image_file.stream)
image.verify()
source_format = image.format
image_file.stream.seek(0)
image = ImageOps.exif_transpose(Image.open(image_file.stream)).convert("RGB")
except (UnidentifiedImageError, OSError, SyntaxError) as error:
raise ValueError("Upload a valid JPEG, PNG, or WebP image.") from error
if source_format not in {"JPEG", "PNG", "WEBP"}:
raise ValueError("Upload a JPEG, PNG, or WebP image.")
image.thumbnail((2400, 2400))
filename = f"{uuid4().hex}.webp"
app.config["UPLOAD_FOLDER"].mkdir(parents=True, exist_ok=True)
image.save(app.config["UPLOAD_FOLDER"] / filename, "WEBP", quality=88, method=6)
return filename
def valid_email(email):
return re.fullmatch(r"[^\s@]+@[^\s@]+\.[^\s@]+", email) is not None
@@ -222,6 +252,11 @@ def post(slug):
return render_template("post.html", post=article)
@app.get("/uploads/<path:filename>")
def uploaded_image(filename):
return send_from_directory(app.config["UPLOAD_FOLDER"], filename)
@app.route("/register", methods=("GET", "POST"))
def register():
if request.method == "POST":
@@ -423,10 +458,11 @@ def write():
if request.method == "POST":
try:
title, slug, excerpt, body, category, now = post_from_request()
image_filename = save_post_image()
get_db().execute(
"""INSERT INTO posts (title, slug, excerpt, body, category, author_id, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
(title, slug, excerpt, body, category, session["user_id"], now, now),
"""INSERT INTO posts (title, slug, excerpt, body, category, image_filename, author_id, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(title, slug, excerpt, body, category, image_filename, session["user_id"], now, now),
)
get_db().commit()
return redirect(url_for("post", slug=slug))
@@ -446,11 +482,14 @@ def edit_post(slug):
if request.method == "POST":
try:
title, new_slug, excerpt, body, category, now = post_from_request(article["id"])
image_filename = save_post_image() or article["image_filename"]
get_db().execute(
"""UPDATE posts SET title=?, slug=?, excerpt=?, body=?, category=?, updated_at=? WHERE id=?""",
(title, new_slug, excerpt, body, category, now, article["id"]),
"""UPDATE posts SET title=?, slug=?, excerpt=?, body=?, category=?, image_filename=?, updated_at=? WHERE id=?""",
(title, new_slug, excerpt, body, category, image_filename, now, article["id"]),
)
get_db().commit()
if image_filename != article["image_filename"] and article["image_filename"]:
(app.config["UPLOAD_FOLDER"] / article["image_filename"]).unlink(missing_ok=True)
return redirect(url_for("post", slug=new_slug))
except ValueError as error:
flash(str(error), "error")