Files
Kontrollverlust/notes/api.md
T
irrlicht be8dbb4902 Implement left/right voting on entries
GET /entry/{pid}/interactions/{mode} now reads the vote tally and, for
mode=left/right (auth required), toggles the user's vote: new vote, repeat
same mode removes it, different mode switches. Returns {left, right,
selected}. A UNIQUE(uid, pid) constraint on the vote table prevents
duplicate votes. The JS frontend renders Links/Rechts buttons with live
counts under each entry.

This completes the voting feature that was left commented-out and broken
in the Flask version.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 10:58:58 +02:00

5.1 KiB
Raw Blame History

kver API Definition

Abgeleitet aus dem bestehenden Flask-Code. Basis für die Go-Reimplementierung.


Authentifizierung

Sessions werden als Cookie (session) übertragen. Alle geschützten Endpunkte erwarten diesen Cookie.


Auth

POST /auth/login

Einloggen.

Request (form-encoded):

Feld Typ Beschreibung
user string Nutzername
pass string Passwort
timeout int Session-Dauer in Sekunden

Response:

  • 200 + setzt session-Cookie → Redirect auf /
  • 200 + Fehlertext bei falschen Credentials

GET /auth/login

Login-Seite anzeigen (wird in der Go-API nicht benötigt, Frontend übernimmt das).


POST /auth/newuser

Neuen Account registrieren.

Request (form-encoded):

Feld Typ Beschreibung
user string Nutzername (332 Zeichen, [a-zA-Z0-9._\s-])
pass1 string Passwort (min. 10 Zeichen)
pass2 string Passwort-Bestätigung

Response:

  • 200 bei Erfolg
  • 200 + Fehlerliste bei Validierungsfehler

GET /auth/logout

Session beenden und Cookie löschen.

Response: Redirect auf /


GET /auth/headerbar

Gibt zurück ob der Nutzer eingeloggt ist (für die UI-Headerleiste).

Response: HTML-Partial (in Go-API: JSON)


GET /auth/sessioninfo

Infos zur aktuellen Session.

Auth: erforderlich

Response:

{
  "uid": 123456,
  "created_at": 1700000000,
  "expires": 1700086400,
  "description": ""
}

Entries (Beiträge)

GET /entry/feed/:page

Paginierter Feed aller Beiträge, neueste zuerst.

Parameter:

Name Typ Beschreibung
page int Seite (0100), 20 Einträge pro Seite

Response:

[
  {
    "pid": 123456789,
    "created_at": 1700000000,
    "uid": 42,
    "content": "...",
    "filepath": "static/media/...",
    "username": "max"
  }
]

Leeres Array [] wenn keine weiteren Einträge vorhanden.


POST /entry/create

Neuen Beitrag erstellen.

Auth: erforderlich

Request (multipart/form-data):

Feld Typ Beschreibung
content string Text (max. 1000 Zeichen)
file file (optional) Bild (JPG oder GIF, wird auf max. 1024px skaliert)

Mindestens content oder file muss vorhanden sein.

Response:

  • 201 bei Erfolg
  • 400 bei leerem Beitrag
  • 401 wenn nicht eingeloggt

GET /entry/:pid/interactions/:mode

Abstimmungsstand lesen und optional die eigene Stimme umschalten.

Parameter:

Name Typ Beschreibung
pid int Post-ID
mode string none (nur lesen), left oder right (abstimmen)

Voting-Logik (nur bei left/right, Auth erforderlich):

  • keine bisherige Stimme → neue Stimme
  • gleiche Stimme erneut → Stimme zurückziehen (Toggle)
  • andere Stimme → auf neuen Modus wechseln

Response:

{ "pid": 123, "left": 4, "right": 2, "selected": "left" }

selected ist none, wenn nicht eingeloggt oder keine Stimme abgegeben. Bei left/right ohne Login → 401.

Hinweis: Mutation per GET ist aus dem ursprünglichen HTMX-Design übernommen. Sauberer wäre ein separater POST fürs Abstimmen — siehe offene Punkte.


User

GET /u/:username

Öffentliche Profilseite eines Nutzers.

Response: Nutzerprofil + Beiträge (Details noch offen)


GET /user/info

Eigene Account-Informationen.

Auth: erforderlich

Response:

{
  "uid": 42,
  "username": "max",
  "created_at": 1700000000
}

POST /user/delete

Eigenen Account dauerhaft löschen. Beiträge, Votes und Sessions werden mitgelöscht, zugehörige Mediendateien best effort entfernt.

Auth: erforderlich

Request (form-encoded):

Feld Typ Beschreibung
pass1 string Passwort zur Bestätigung

Response:

  • 200 {"status": "deleted"} + löscht den session-Cookie
  • 403 bei falschem Passwort

Datenbankschema (SQLite)

CREATE TABLE user (
    id       INTEGER PRIMARY KEY AUTOINCREMENT,
    uid      INTEGER UNIQUE,
    username TEXT UNIQUE,
    password BLOB,
    created_at INTEGER,
    last_login INTEGER
);

CREATE TABLE session (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    uid         INTEGER,
    value       TEXT UNIQUE,
    created_at  INTEGER,
    expires     INTEGER,
    description TEXT
);

CREATE TABLE entry (
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    pid        INTEGER UNIQUE,
    uid        INTEGER,
    created_at INTEGER,
    content    TEXT,
    filepath   TEXT
);

CREATE TABLE vote (
    id   INTEGER PRIMARY KEY AUTOINCREMENT,
    uid  INTEGER,
    pid  INTEGER,
    mode TEXT  -- 'left' | 'right'
);

Offene Punkte

  • Voting läuft per GET (HTMX-Erbe) — perspektivisch auf POST umstellen (CSRF/Idempotenz)
  • Kein Rate-Limiting außer dem zufälligen sleep beim Login
  • session-Token ist nur randbelow(999999999) + timestamp — sollte auf crypto/rand umgestellt werden
  • Fehler-Responses sind aktuell Plaintext/HTML — in der Go-API einheitlich JSON