Sprint 0: Backend, Docker, KI-Layer mit Free/Premium-Trennung
This commit is contained in:
parent
84f49fafcf
commit
00be2bbcd5
17 changed files with 1107 additions and 0 deletions
237
backend/database.py
Normal file
237
backend/database.py
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
"""
|
||||
BAN YARO — Datenbank
|
||||
SQLite mit WAL-Modus (bewährt von akku-werkstatt).
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
import os
|
||||
import logging
|
||||
from contextlib import contextmanager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
DB_PATH = os.getenv("DB_PATH", "/data/banyaro.db")
|
||||
|
||||
|
||||
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")
|
||||
return conn
|
||||
|
||||
|
||||
@contextmanager
|
||||
def db():
|
||||
conn = get_connection()
|
||||
try:
|
||||
yield conn
|
||||
conn.commit()
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def init_db():
|
||||
"""Erstellt alle Tabellen falls nicht vorhanden."""
|
||||
logger.info(f"Initialisiere Datenbank: {DB_PATH}")
|
||||
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
|
||||
|
||||
with db() as conn:
|
||||
conn.executescript("""
|
||||
|
||||
-- USERS
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
pw_hash TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
rolle TEXT NOT NULL DEFAULT 'user',
|
||||
is_premium INTEGER NOT NULL DEFAULT 0,
|
||||
push_sub TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
last_login TEXT
|
||||
);
|
||||
|
||||
-- HUNDE
|
||||
CREATE TABLE IF NOT EXISTS dogs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
rasse TEXT,
|
||||
geburtstag TEXT,
|
||||
geschlecht TEXT,
|
||||
gewicht_kg REAL,
|
||||
chip_nr TEXT,
|
||||
foto_url TEXT,
|
||||
bio TEXT,
|
||||
is_public INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- TAGEBUCH
|
||||
CREATE TABLE IF NOT EXISTS diary (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
dog_id INTEGER NOT NULL REFERENCES dogs(id) ON DELETE CASCADE,
|
||||
datum TEXT NOT NULL DEFAULT (date('now')),
|
||||
typ TEXT NOT NULL DEFAULT 'eintrag',
|
||||
titel TEXT,
|
||||
text TEXT,
|
||||
media_url TEXT,
|
||||
tags TEXT, -- JSON Array
|
||||
gps_lat REAL,
|
||||
gps_lon REAL,
|
||||
is_milestone INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_diary_dog ON diary(dog_id, datum DESC);
|
||||
|
||||
-- GESUNDHEIT
|
||||
CREATE TABLE IF NOT EXISTS health (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
dog_id INTEGER NOT NULL REFERENCES dogs(id) ON DELETE CASCADE,
|
||||
typ TEXT NOT NULL, -- impfung | entwurmung | tierarzt | medikament | gewicht
|
||||
bezeichnung TEXT NOT NULL,
|
||||
datum TEXT NOT NULL,
|
||||
naechstes TEXT,
|
||||
notiz TEXT,
|
||||
wert REAL, -- für Gewicht o.ä.
|
||||
einheit TEXT,
|
||||
erinnerung INTEGER NOT NULL DEFAULT 1,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_health_dog ON health(dog_id, datum DESC);
|
||||
|
||||
-- GIFTKÖDER-ALARM
|
||||
CREATE TABLE IF NOT EXISTS poison (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
lat REAL NOT NULL,
|
||||
lon REAL NOT NULL,
|
||||
beschreibung TEXT,
|
||||
typ TEXT DEFAULT 'unbekannt',
|
||||
foto_url TEXT,
|
||||
bestaetigt INTEGER NOT NULL DEFAULT 0,
|
||||
bestaetigt_von INTEGER,
|
||||
geloest INTEGER NOT NULL DEFAULT 0,
|
||||
expires_at TEXT NOT NULL, -- auto-expire nach 7 Tagen
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_poison_location ON poison(lat, lon);
|
||||
CREATE INDEX IF NOT EXISTS idx_poison_active ON poison(geloest, expires_at);
|
||||
|
||||
-- ORTE (hundefreundliche Orte, Kotbeutelspender, etc.)
|
||||
CREATE TABLE IF NOT EXISTS places (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER REFERENCES users(id),
|
||||
name TEXT NOT NULL,
|
||||
typ TEXT NOT NULL, -- restaurant | shop | freilauf | kotbeutel | tierarzt | hundeschule
|
||||
lat REAL NOT NULL,
|
||||
lon REAL NOT NULL,
|
||||
adresse TEXT,
|
||||
website TEXT,
|
||||
hund_rein INTEGER,
|
||||
leine_pflicht INTEGER,
|
||||
wasser_fuer_hunde INTEGER,
|
||||
foto_url TEXT,
|
||||
bewertung REAL DEFAULT 0,
|
||||
anz_bewertungen INTEGER DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_places_location ON places(lat, lon);
|
||||
CREATE INDEX IF NOT EXISTS idx_places_typ ON places(typ);
|
||||
|
||||
-- ROUTEN
|
||||
CREATE TABLE IF NOT EXISTS routes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
name TEXT NOT NULL,
|
||||
beschreibung TEXT,
|
||||
gps_track TEXT NOT NULL, -- JSON Array von {lat, lon}
|
||||
distanz_km REAL,
|
||||
dauer_min INTEGER,
|
||||
schwierigkeit TEXT DEFAULT 'leicht',
|
||||
untergrund TEXT, -- wald | asphalt | wiese | mix
|
||||
schatten INTEGER,
|
||||
leine_empfohlen INTEGER,
|
||||
bewertung REAL DEFAULT 0,
|
||||
anz_bewertungen INTEGER DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- GASSI-TREFFEN
|
||||
CREATE TABLE IF NOT EXISTS walks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
titel TEXT NOT NULL,
|
||||
datum TEXT NOT NULL,
|
||||
uhrzeit TEXT NOT NULL,
|
||||
lat REAL NOT NULL,
|
||||
lon REAL NOT NULL,
|
||||
ort_name TEXT,
|
||||
max_teilnehmer INTEGER DEFAULT 10,
|
||||
beschreibung TEXT,
|
||||
status TEXT NOT NULL DEFAULT 'offen',
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- GASSI-TREFFEN TEILNEHMER
|
||||
CREATE TABLE IF NOT EXISTS walk_participants (
|
||||
walk_id INTEGER NOT NULL REFERENCES walks(id) ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
dog_id INTEGER REFERENCES dogs(id),
|
||||
joined_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
PRIMARY KEY (walk_id, user_id)
|
||||
);
|
||||
|
||||
-- FORUM
|
||||
CREATE TABLE IF NOT EXISTS forum_threads (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
dog_id INTEGER REFERENCES dogs(id),
|
||||
titel TEXT NOT NULL,
|
||||
kategorie TEXT NOT NULL DEFAULT 'allgemein',
|
||||
rasse_tag TEXT,
|
||||
plz TEXT,
|
||||
views INTEGER NOT NULL DEFAULT 0,
|
||||
pinned INTEGER NOT NULL DEFAULT 0,
|
||||
locked INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_threads_kat ON forum_threads(kategorie, created_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS forum_posts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
thread_id INTEGER NOT NULL REFERENCES forum_threads(id) ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
text TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
edited_at TEXT
|
||||
);
|
||||
|
||||
-- PUSH SUBSCRIPTIONS (alternativ zu users.push_sub für mehrere Geräte)
|
||||
CREATE TABLE IF NOT EXISTS push_subscriptions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
endpoint TEXT NOT NULL UNIQUE,
|
||||
p256dh TEXT NOT NULL,
|
||||
auth TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- PREMIUM-TRANSAKTIONEN (später für Zahlungsabwicklung)
|
||||
CREATE TABLE IF NOT EXISTS premium_orders (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
betrag_cent INTEGER NOT NULL,
|
||||
zahlungsart TEXT,
|
||||
valid_until TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
""")
|
||||
|
||||
logger.info("Datenbank initialisiert.")
|
||||
Loading…
Add table
Add a link
Reference in a new issue