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

256 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:**
```json
{
"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:**
```json
[
{
"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:**
```json
{ "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:**
```json
{
"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)
```sql
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