Feature: Tagebuch Cover-Bild (Favorit-Funktion) für diary_media

- Migration: diary_media.is_cover (INTEGER DEFAULT 0)
- Upload: erstes Item eines Eintrags automatisch is_cover=1
- Neuer Endpoint: PATCH /diary/{id}/media/{mid}/cover
- GET-Endpoints geben is_cover + cover_url zurück
- Frontend: Stern-Button () in Gallery-Detail und Edit-Formular
- Timeline-Karte verwendet cover_url als Vorschaubild
- SW by-v212, APP_VER 186
This commit is contained in:
rene 2026-04-18 19:07:37 +02:00
parent 63ab092f5e
commit fa0fcbf8c9
7 changed files with 196 additions and 21 deletions

View file

@ -6,18 +6,27 @@ SQLite mit WAL-Modus (bewährt von akku-werkstatt).
import sqlite3
import os
import logging
import unicodedata
from contextlib import contextmanager
logger = logging.getLogger(__name__)
DB_PATH = os.getenv("DB_PATH", "/data/banyaro.db")
def _norm(s):
"""Diakritika entfernen + lowercase — für akzentunabhängige Suche."""
if not s:
return ''
return unicodedata.normalize('NFKD', str(s)).encode('ascii', 'ignore').decode('ascii').lower()
def get_connection() -> sqlite3.Connection:
conn = sqlite3.connect(DB_PATH, check_same_thread=False)
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA journal_mode=WAL")
conn.execute("PRAGMA foreign_keys=ON")
conn.execute("PRAGMA busy_timeout=5000")
conn.create_function('norm', 1, _norm)
return conn
@ -471,6 +480,8 @@ def _migrate(conn_factory):
("walks", "anz_bewertungen", "INTEGER DEFAULT 0"),
("sitters", "bewertung", "REAL DEFAULT 0"),
("sitters", "anz_bewertungen", "INTEGER DEFAULT 0"),
# Tagebuch-Medien: Cover-Bild markieren
("diary_media", "is_cover", "INTEGER NOT NULL DEFAULT 0"),
]
with conn_factory() as conn:
for table, column, col_type in migrations:
@ -774,6 +785,20 @@ def _migrate(conn_factory):
""")
logger.info("Migration: diary_media Tabelle bereit.")
# Gesundheit: mehrere Mediendateien pro Eintrag
conn.executescript("""
CREATE TABLE IF NOT EXISTS health_media (
id INTEGER PRIMARY KEY AUTOINCREMENT,
health_id INTEGER NOT NULL REFERENCES health(id) ON DELETE CASCADE,
url TEXT NOT NULL,
media_type TEXT NOT NULL DEFAULT 'image',
sort_order INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_health_media_entry ON health_media(health_id, sort_order);
""")
logger.info("Migration: health_media Tabelle bereit.")
# Walk-Einladungen (RSVP)
conn.executescript("""
CREATE TABLE IF NOT EXISTS walk_invitations (