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>
This commit is contained in:
2026-06-01 10:58:58 +02:00
parent c904e3e2db
commit be8dbb4902
5 changed files with 130 additions and 5 deletions
+18 -3
View File
@@ -137,13 +137,28 @@ Mindestens `content` oder `file` muss vorhanden sein.
### `GET /entry/:pid/interactions/:mode`
Abstimmung auf einen Beitrag. (*Aktuell nicht implementiert.*)
Abstimmungsstand lesen und optional die eigene Stimme umschalten.
**Parameter:**
| Name | Typ | Beschreibung |
|------|-----|--------------|
| `pid` | int | Post-ID |
| `mode` | string | `left` oder `right` |
| `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.
---
@@ -234,7 +249,7 @@ CREATE TABLE vote (
## Offene Punkte
- `/entry/:pid/interactions/:mode` ist auskommentiert — Voting-Logik muss neu designed werden
- 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