update
This commit is contained in:
@@ -21,6 +21,7 @@ from . import (
|
||||
features,
|
||||
ki,
|
||||
search,
|
||||
spenden,
|
||||
streamproxy,
|
||||
suggestions,
|
||||
sync,
|
||||
@@ -98,6 +99,7 @@ async def startup():
|
||||
clips.init()
|
||||
wishlist.init()
|
||||
features.init()
|
||||
spenden.init()
|
||||
# Erst die Sicherung, dann der Sync — so liegt vor jedem Katalog-Update
|
||||
# ein sauberer Stand
|
||||
if not HOST_SCHLUESSEL:
|
||||
@@ -714,6 +716,60 @@ def info_setzen(
|
||||
return {"text": db.get_meta("info_text")}
|
||||
|
||||
|
||||
# ── Spendenbaum ─────────────────────────────────────────────────────────
|
||||
@app.get("/api/spenden")
|
||||
def spenden_liste():
|
||||
return spenden.alle()
|
||||
|
||||
|
||||
@app.post("/api/spenden")
|
||||
def spende_eintragen(
|
||||
response: Response,
|
||||
user_id: int = Body(..., embed=True),
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
if not users.ist_host(sid):
|
||||
raise HTTPException(status_code=403, detail="Nur für den Host")
|
||||
spenden.eintragen(user_id)
|
||||
events.senden("spenden")
|
||||
return spenden.alle()
|
||||
|
||||
|
||||
@app.get("/api/kontostand")
|
||||
def kontostand():
|
||||
"""Was auf dem Filmabend-Konto liegt — der Host trägt die Zahl ein."""
|
||||
wert = db.get_meta("kontostand")
|
||||
return {"betrag": float(wert) if wert else None}
|
||||
|
||||
|
||||
@app.put("/api/kontostand")
|
||||
def kontostand_setzen(
|
||||
response: Response,
|
||||
betrag: float | None = Body(None, embed=True),
|
||||
filmabend_sid: str | None = Cookie(None),
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
if not users.ist_host(sid):
|
||||
raise HTTPException(status_code=403, detail="Nur für den Host")
|
||||
with db.connect() as conn:
|
||||
db.set_meta(conn, "kontostand", "" if betrag is None else round(betrag, 2))
|
||||
events.senden("spenden")
|
||||
return kontostand()
|
||||
|
||||
|
||||
@app.delete("/api/spenden/{user_id}")
|
||||
def spende_entfernen(
|
||||
user_id: int, response: Response, filmabend_sid: str | None = Cookie(None)
|
||||
):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
if not users.ist_host(sid):
|
||||
raise HTTPException(status_code=403, detail="Nur für den Host")
|
||||
if spenden.entfernen(user_id):
|
||||
events.senden("spenden")
|
||||
return spenden.alle()
|
||||
|
||||
|
||||
@app.get("/api/features")
|
||||
def wuensche(response: Response, filmabend_sid: str | None = Cookie(None)):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
@@ -915,7 +971,9 @@ def schutz_setzen(
|
||||
@app.get("/api/host")
|
||||
def host_stand(response: Response, filmabend_sid: str | None = Cookie(None)):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
return {"host": users.ist_host(sid)}
|
||||
# Ob es einen Host-Film gibt, darf jeder wissen — nur nicht welchen.
|
||||
# Ohne Film hat die Tastenkombination nichts zu fragen.
|
||||
return {"host": users.ist_host(sid), "film": users.host_film() is not None}
|
||||
|
||||
|
||||
@app.post("/api/host")
|
||||
@@ -930,7 +988,8 @@ def host_modus(
|
||||
|
||||
Freischalten geht mit dem Schlüssel aus der .env, einmal über
|
||||
/?host=<schlüssel>, oder mit dem Host-Film: die Tastenkombination
|
||||
fragt danach, wie beim Anmeldefilm eines Namens.
|
||||
fragt danach, wie beim Anmeldefilm eines Namens. Ist kein Host-Film
|
||||
gesetzt, reicht die Tastenkombination allein.
|
||||
|
||||
Die Freischaltung bleibt, bis jemand sie abgibt. Ob die Hostknöpfe
|
||||
gerade sichtbar sind, entscheidet der Browser für sich — dafür ist
|
||||
@@ -944,7 +1003,8 @@ def host_modus(
|
||||
darf = (
|
||||
users.ist_host(sid)
|
||||
or (bool(HOST_SCHLUESSEL) and secrets.compare_digest(schluessel or "", HOST_SCHLUESSEL))
|
||||
or (film is not None and movie_id == film)
|
||||
or film is None
|
||||
or movie_id == film
|
||||
)
|
||||
if not darf:
|
||||
raise HTTPException(status_code=403, detail="Kein Hostschlüssel")
|
||||
|
||||
44
backend/app/spenden.py
Normal file
44
backend/app/spenden.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""Der Spendenbaum: wer schon einmal etwas überwiesen hat.
|
||||
|
||||
Ein Blatt je Person, ohne Betrag. Der Host trägt ein und nimmt weg.
|
||||
"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from .db import connect
|
||||
|
||||
SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS spenden (
|
||||
user_id INTEGER PRIMARY KEY,
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
"""
|
||||
|
||||
|
||||
def init():
|
||||
with connect() as conn:
|
||||
conn.executescript(SCHEMA)
|
||||
|
||||
|
||||
def alle():
|
||||
"""Die Blätter, das neueste zuerst."""
|
||||
with connect() as conn:
|
||||
rows = conn.execute(
|
||||
"SELECT s.user_id, u.name, s.created_at FROM spenden s "
|
||||
"JOIN users u ON u.id = s.user_id ORDER BY s.created_at DESC, s.user_id DESC"
|
||||
).fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
|
||||
def eintragen(user_id):
|
||||
"""Ein zweites Mal ändert nichts — ein Blatt je Person."""
|
||||
with connect() as conn:
|
||||
conn.execute(
|
||||
"INSERT OR IGNORE INTO spenden (user_id, created_at) VALUES (?, ?)",
|
||||
(user_id, datetime.now(timezone.utc).isoformat(timespec="seconds")),
|
||||
)
|
||||
|
||||
|
||||
def entfernen(user_id):
|
||||
with connect() as conn:
|
||||
return conn.execute("DELETE FROM spenden WHERE user_id = ?", (user_id,)).rowcount > 0
|
||||
Reference in New Issue
Block a user