Build dasbes.de — community "das beste X" voting app (Bun + Supabase) #1

Open
opened 2026-06-23 08:50:41 +00:00 by mAi · 1 comment
Collaborator

Goal

Build dasbes.de — a small community "das beste X" voting app. Per topic, visitors see the top-voted answer, can submit their own suggestion (optionally with a link), and upvote others. Replaces the throwaway static SPA currently at m/onepager sites/dasbes.de/ (use it as seed/reference; the original dynamic app's source was lost and is unrecoverable).

m's canonical format (verbatim example): "Das beste Fahrrad ist das, das du gerne fährst." → full sentences "Das/Der/Die beste <Thema> ist …", correct gender per noun. NOT "das Beste an X".

Routing

  • dasbes.de/<thema> → shows that topic: the current top-voted suggestion big, then the rest with vote counts + upvote buttons, then a form "eigenen Vorschlag + optionalen Link".
  • dasbes.de/ (root) → a random topic that already has content + the explore input.
  • Unknown/new topic → empty state inviting the first suggestion.

Features

  1. Voting — anonymous, 1 vote per device (localStorage token; lightweight, no login). Upvote toggles. Top-voted is "the best".
  2. Suggestions — anyone can add a suggestion for a topic (the sentence text + optional URL). Visible immediately.
  3. Moderation — an admin token (env ADMIN_TOKEN) unlocks hide/delete on any suggestion (for spam/junk). Simple: ?admin=<token> reveals delete buttons, server checks the token. Basic anti-spam: rate-limit submissions per IP/device, max length, strip HTML.
  4. Optional link per suggestion — e.g. /fernseher → "Der beste Fernseher ist …" with a link to the product (Amazon/Hersteller). Render as a clickable "→" next to the suggestion.

Stack & deploy

  • Bun + a tiny server (server.ts; SSR the topic page + JSON API: GET /api/<thema>, POST /api/suggest, POST /api/vote). Keep it lean, no heavy framework needed.
  • Storage: shared Supabase on mRiver (post-migration home). Direct Postgres at 100.99.98.203:6789 (same instance the other HL apps now use — get the postgres password from an existing app's Dokploy compose env, e.g. hlckm/mcompete-app-z7ejqq, or .env.age). Create a dedicated dasbes schema with tables topics / suggestions (id, topic, text, url, votes, created_at, hidden). Use a search_path=dasbes,public connection string like the paliad/mgmt apps do.
  • Repo: m/dasbes (already created, this repo) — keep it versioned (unlike the lost original).
  • Deploy: Dokploy compose on mLake (like the other vanity apps), docker-compose.yml in repo, Bun container on port 3000, dokploy-network. Then point dasbes.de at it: dasbes.de is currently an onepager Dokploy domain — move the routing to this app (add the domain to the new compose / file-based Traefik route like meinepfer-racetrack.yml, and remove the dasbes block from onepager so they don't conflict). Keep www.dasbes.de working too if trivial.

Seed content (correct format; keep m's Fahrrad verbatim)

  • fahrrad: "Das beste Fahrrad ist das, das du gerne fährst."
  • kaffee: "Der beste Kaffee ist der, den dir jemand ungefragt hinstellt."
  • montag: "Der beste Montag ist der, den du verschlafen darfst."
  • regen: "Der beste Regen ist der, den du von drinnen hörst."
  • urlaub: "Der beste Urlaub ist der, an dessen erstem Abend noch alles vor dir liegt."
  • feierabend: "Der beste Feierabend ist der, der nach dem schlimmsten Tag genauso kommt."
  • code: "Der beste Code ist der, den du nie wieder anfassen musst."
  • idee: "Die beste Idee ist die, die du aufschreibst, bevor sie weg ist."
  • kicker: "Der beste Kicker ist der, an dem die ganze Kanzlei steht."
  • hund: "Der beste Hund ist der, der schon an der Tür wartet."
  • plan: "Der beste Plan ist der, der den ersten Kontakt mit der Realität überlebt."
  • buch: "Das beste Buch ist das, das du nicht weglegen kannst."

Design

German UI. Clean, slim, mobile-first (the static SPA's dark/yellow look is a fine starting point — dark bg, one accent). Big sentence, understated voting UI. noindex is fine.

Definition of Done

  • dasbes.de/ live: shows top-voted answer + list + working upvote + suggestion form (with optional link).
  • Anonymous 1-per-device voting works; suggestions appear immediately; admin token can hide/delete.
  • Seeded with the sentences above (fahrrad verbatim).
  • Bun app deployed via Dokploy compose on mLake, DB on mRiver Supabase (dasbes schema), dasbes.de routed to it (old onepager dasbes block removed, no route conflict), build/deploy green, verified live.
  • Repo committed (server, compose, schema/migration, README with run+deploy notes). Comment the live URL + commit link on this issue and set status:done.

Notes

  • Infra context (for the worker): mLake Dokploy API http://100.99.98.201:3000 (key in mai-dokploy skill), mLake serves public web (Traefik), mRiver 100.99.98.203 hosts the migrated Supabase (msupabase-db on :6789). mlake shell is fish (grep = ripgrep, no -E). passwordless sudo on mlake for /etc/dokploy/traefik/dynamic/.
  • Keep it genuinely lean — this is a fun vanity app, not a product. No auth, no accounts.
## Goal Build **dasbes.de** — a small community "das beste X" voting app. Per topic, visitors see the top-voted answer, can submit their own suggestion (optionally with a link), and upvote others. Replaces the throwaway static SPA currently at `m/onepager` `sites/dasbes.de/` (use it as seed/reference; the original dynamic app's source was lost and is unrecoverable). m's canonical format (verbatim example): **"Das beste Fahrrad ist das, das du gerne fährst."** → full sentences "Das/Der/Die beste &lt;Thema&gt; ist …", correct gender per noun. NOT "das Beste an X". ## Routing - `dasbes.de/<thema>` → shows that topic: the current **top-voted** suggestion big, then the rest with vote counts + upvote buttons, then a form "eigenen Vorschlag + optionalen Link". - `dasbes.de/` (root) → a random topic that already has content + the explore input. - Unknown/new topic → empty state inviting the first suggestion. ## Features 1. **Voting** — anonymous, **1 vote per device** (localStorage token; lightweight, no login). Upvote toggles. Top-voted is "the best". 2. **Suggestions** — anyone can add a suggestion for a topic (the sentence text + optional URL). **Visible immediately.** 3. **Moderation** — an **admin token** (env `ADMIN_TOKEN`) unlocks hide/delete on any suggestion (for spam/junk). Simple: `?admin=<token>` reveals delete buttons, server checks the token. Basic anti-spam: rate-limit submissions per IP/device, max length, strip HTML. 4. **Optional link per suggestion** — e.g. `/fernseher` → "Der beste Fernseher ist …" with a link to the product (Amazon/Hersteller). Render as a clickable "→" next to the suggestion. ## Stack & deploy - **Bun + a tiny server** (server.ts; SSR the topic page + JSON API: `GET /api/<thema>`, `POST /api/suggest`, `POST /api/vote`). Keep it lean, no heavy framework needed. - **Storage: shared Supabase on mRiver** (post-migration home). Direct Postgres at `100.99.98.203:6789` (same instance the other HL apps now use — get the `postgres` password from an existing app's Dokploy compose env, e.g. `hlckm`/`mcompete-app-z7ejqq`, or `.env.age`). Create a dedicated **`dasbes` schema** with tables `topics` / `suggestions` (id, topic, text, url, votes, created_at, hidden). Use a `search_path=dasbes,public` connection string like the paliad/mgmt apps do. - **Repo:** `m/dasbes` (already created, this repo) — keep it versioned (unlike the lost original). - **Deploy:** Dokploy **compose** on mLake (like the other vanity apps), `docker-compose.yml` in repo, Bun container on port 3000, `dokploy-network`. Then point **dasbes.de** at it: dasbes.de is currently an onepager Dokploy domain — move the routing to this app (add the domain to the new compose / file-based Traefik route like `meinepfer-racetrack.yml`, and remove the dasbes block from onepager so they don't conflict). Keep `www.dasbes.de` working too if trivial. ## Seed content (correct format; keep m's Fahrrad verbatim) - fahrrad: "Das beste Fahrrad ist das, das du gerne fährst." - kaffee: "Der beste Kaffee ist der, den dir jemand ungefragt hinstellt." - montag: "Der beste Montag ist der, den du verschlafen darfst." - regen: "Der beste Regen ist der, den du von drinnen hörst." - urlaub: "Der beste Urlaub ist der, an dessen erstem Abend noch alles vor dir liegt." - feierabend: "Der beste Feierabend ist der, der nach dem schlimmsten Tag genauso kommt." - code: "Der beste Code ist der, den du nie wieder anfassen musst." - idee: "Die beste Idee ist die, die du aufschreibst, bevor sie weg ist." - kicker: "Der beste Kicker ist der, an dem die ganze Kanzlei steht." - hund: "Der beste Hund ist der, der schon an der Tür wartet." - plan: "Der beste Plan ist der, der den ersten Kontakt mit der Realität überlebt." - buch: "Das beste Buch ist das, das du nicht weglegen kannst." ## Design German UI. Clean, slim, mobile-first (the static SPA's dark/yellow look is a fine starting point — dark bg, one accent). Big sentence, understated voting UI. noindex is fine. ## Definition of Done - dasbes.de/<thema> live: shows top-voted answer + list + working upvote + suggestion form (with optional link). - Anonymous 1-per-device voting works; suggestions appear immediately; admin token can hide/delete. - Seeded with the sentences above (fahrrad verbatim). - Bun app deployed via Dokploy compose on mLake, DB on mRiver Supabase (`dasbes` schema), dasbes.de routed to it (old onepager dasbes block removed, no route conflict), build/deploy green, verified live. - Repo committed (server, compose, schema/migration, README with run+deploy notes). Comment the live URL + commit link on this issue and set `status:done`. ## Notes - Infra context (for the worker): mLake Dokploy API `http://100.99.98.201:3000` (key in `mai-dokploy` skill), mLake serves public web (Traefik), mRiver `100.99.98.203` hosts the migrated Supabase (`msupabase-db` on :6789). mlake shell is fish (grep = ripgrep, no `-E`). passwordless sudo on mlake for `/etc/dokploy/traefik/dynamic/`. - Keep it genuinely lean — this is a fun vanity app, not a product. No auth, no accounts.
mAi was assigned by m 2026-06-23 08:51:12 +00:00
Author
Collaborator

dasbes.de ist live

Live: https://dasbes.de · z.B. https://dasbes.de/fahrrad · www.dasbes.de läuft auch.

Verifiziert gegen die echte Domain: /fahrrad zeigt „Das beste Fahrrad ist das, das du gerne fährst." (verbatim), / leitet auf ein zufälliges Thema mit Inhalt um, Vorschlag→Vote→Admin-Delete laufen end-to-end durch (Testdaten wieder entfernt).

Was gebaut wurde

  • Bun-Server (server.ts) — SSR der Themenseite + schlanke JSON-API (GET /api/<thema>, POST /api/suggest|vote|delete). Kein Framework, keine DB-Treiber-Dependency (Buns eingebautes SQL).
  • Voting anonym, 1 Stimme pro Gerät (localStorage-Token), Upvote toggelt. Top-voted steht groß oben.
  • Vorschläge erscheinen sofort, optionaler Link → klickbarer „→".
  • Moderation: ADMIN_TOKEN (in der Dokploy-Compose-Env gesetzt). dasbes.de/<thema>?admin=<token> blendet Delete-Buttons ein, Server prüft den Token (constant-time).
  • Anti-Spam: HTML wird gestrippt, Text ≤ 280 Zeichen, Links nur http(s), Rate-Limit pro Gerät + IP.
  • Design: dunkel/gelb, mobile-first, noindex.

Daten

Dediziertes dasbes-Schema auf der geteilten mRiver-Supabase (100.99.98.203:6789). Tabellen topics, suggestions, votes, submissions — alle Queries schema-qualifiziert. Geseedet mit den 12 Sätzen aus dem Issue (fahrrad verbatim, korrekte Umlaute).

Deploy & Routing

  • Dokploy-Compose auf mLake (dasbes-e271636b-njhors), Bun-Container :3000 am dokploy-network, Traefik-Labels für dasbes.de + www.dasbes.de, Let's-Encrypt-Cert.
  • Routing umgezogen: die alte dasbes.de-Domain am onepager-Caddy-App wurde aus Dokploy entfernt (Traefik-Dynamic-File regeneriert, kein Konflikt mehr). Traefik routet dasbes.de jetzt auf die neue App.
  • mLake-Hinweis: der Docker-Adresspool war erschöpft → Compose hängt nur am externen dokploy-network (kein eigenes default-Bridge).

Commits

Für @m / head — noch zu mergen

  1. m/dasbes: Branch mai/hopper/issue-1-dasbes-votingmain. ⚠️ Das Dokploy-Compose deployt aktuell von diesem Feature-Branch. Nach dem Merge bitte die Compose-Branch in Dokploy auf main umstellen, bevor der Feature-Branch gelöscht wird, sonst bricht der nächste Rebuild.
  2. m/onepager: Branch mai/hopper/remove-dasbes-routemain (reine Hygiene, der Block ist schon tot). Die sites/dasbes.de/-SPA ist damit obsolet und kann separat entfernt werden.

Issue bleibt offen (nur du schließt Issues).

## ✅ dasbes.de ist live **Live:** https://dasbes.de · z.B. https://dasbes.de/fahrrad · `www.dasbes.de` läuft auch. Verifiziert gegen die echte Domain: `/fahrrad` zeigt **„Das beste Fahrrad ist das, das du gerne fährst."** (verbatim), `/` leitet auf ein zufälliges Thema mit Inhalt um, Vorschlag→Vote→Admin-Delete laufen end-to-end durch (Testdaten wieder entfernt). ### Was gebaut wurde - **Bun-Server** (`server.ts`) — SSR der Themenseite + schlanke JSON-API (`GET /api/<thema>`, `POST /api/suggest|vote|delete`). Kein Framework, keine DB-Treiber-Dependency (Buns eingebautes `SQL`). - **Voting** anonym, 1 Stimme pro Gerät (localStorage-Token), Upvote toggelt. Top-voted steht groß oben. - **Vorschläge** erscheinen sofort, optionaler Link → klickbarer „→". - **Moderation:** `ADMIN_TOKEN` (in der Dokploy-Compose-Env gesetzt). `dasbes.de/<thema>?admin=<token>` blendet Delete-Buttons ein, Server prüft den Token (constant-time). - **Anti-Spam:** HTML wird gestrippt, Text ≤ 280 Zeichen, Links nur http(s), Rate-Limit pro Gerät + IP. - **Design:** dunkel/gelb, mobile-first, `noindex`. ### Daten Dediziertes **`dasbes`-Schema** auf der geteilten mRiver-Supabase (`100.99.98.203:6789`). Tabellen `topics`, `suggestions`, `votes`, `submissions` — alle Queries schema-qualifiziert. Geseedet mit den 12 Sätzen aus dem Issue (fahrrad verbatim, korrekte Umlaute). ### Deploy & Routing - Dokploy-**Compose** auf mLake (`dasbes-e271636b-njhors`), Bun-Container :3000 am `dokploy-network`, Traefik-Labels für `dasbes.de` + `www.dasbes.de`, Let's-Encrypt-Cert. - Routing umgezogen: die alte `dasbes.de`-Domain am onepager-Caddy-App wurde aus Dokploy entfernt (Traefik-Dynamic-File regeneriert, kein Konflikt mehr). Traefik routet `dasbes.de` jetzt auf die neue App. - mLake-Hinweis: der Docker-Adresspool war erschöpft → Compose hängt nur am externen `dokploy-network` (kein eigenes `default`-Bridge). ### Commits - App (m/dasbes): https://mgit.msbls.de/m/dasbes/commit/693fd2600d72846c2a14f5b5f580e93264f38051 - Netzwerk-Fix: https://mgit.msbls.de/m/dasbes/commit/a98fadb174a7e945bdcf8748b7a57726a2ff1734 - onepager-Caddyfile (dasbes-Block raus), Branch `mai/hopper/remove-dasbes-route`: https://mgit.msbls.de/m/onepager/commits/branch/mai/hopper/remove-dasbes-route ### Für @m / head — noch zu mergen 1. **m/dasbes:** Branch `mai/hopper/issue-1-dasbes-voting` → `main`. ⚠️ Das Dokploy-Compose deployt aktuell **von diesem Feature-Branch**. Nach dem Merge bitte die Compose-Branch in Dokploy auf `main` umstellen, **bevor** der Feature-Branch gelöscht wird, sonst bricht der nächste Rebuild. 2. **m/onepager:** Branch `mai/hopper/remove-dasbes-route` → `main` (reine Hygiene, der Block ist schon tot). Die `sites/dasbes.de/`-SPA ist damit obsolet und kann separat entfernt werden. Issue bleibt offen (nur du schließt Issues).
mAi added the
status:done
label 2026-06-23 09:15:41 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: m/dasbes#1
No description provided.