- README.md: stack, run-locally, test/check/build, structure tree, data
model summary, anti-abuse layers, scope notes, issue origin pointer.
- docs/plans/feedback-feature.md: copied verbatim from flexsiebels for
self-containment (single source of truth in this repo from now on).
Mirrors msbls.de pattern, simplified (no mbrian-core submodule clone).
UID note: oven/bun:1-alpine has a built-in 'bun' user at UID/GID 1000 and
`addgroup -u 1000` on top of it breaks the build silently. mExDraw#14
(commit fc62b9c) lost ~4 weeks of Dokploy deploys to that. Comment in the
Dockerfile so the next person doesn't trip over the same.
Production build verified locally: vite build ✓ (4.08s).
Direct port from flexsiebels worktree. Imports getInstanceBySlug from
$lib/server/feedback (which uses fdb()) — schema rename happens at the
helper level, page code is identical.
Behaviour:
- LocalStorage: feedback:display_name (global) + feedback:session:<slug>
- 3s polling /posts?since=<latest_ts>; auto-scroll on new
- Hidden posts: '(Beitrag entfernt)' for others; own session sees body + note
- Honeypot 'company' input (CSS-hidden, aria-hidden)
- 423 → closed banner; 429 → rate-limit message; required-validation client+server
- noindex meta + no-referrer
- Question types: short_text, long_text, single_choice, multi_choice, scale, boolean
Root +layout.svelte already gives the naked shell (no sidebar/footer/bottom-nav)
so the +layout@.svelte reset trick is unnecessary here.
bun run check: 0 errors, 5 warnings (known false-positive 'data captured at
init' on $state — data from server load doesn't change client-side; same warning
pattern as flexsiebels).
Public (slug-gated, auto-allowlisted):
- GET /api/public/feedback/[slug] — instance config
- POST /api/public/feedback/[slug]/submit — form submission (honeypot, rate-limit, required-validation, 423 if closed)
- GET /api/public/feedback/[slug]/posts — chat polling (?since=, hides body of moderated posts)
- POST /api/public/feedback/[slug]/posts — new chat post (honeypot, rate-limit, 423 if closed)
Admin (requireAuth, owner-scoped):
- GET/POST /api/admin/feedback — list/create
- GET/PATCH/DELETE /api/admin/feedback/[id] — detail/update/delete (PATCH closes/reopens, sets closed_at)
- POST /api/admin/feedback/[id]/posts/[post_id]/hide — toggle hidden flag
- GET /api/admin/feedback/[id]/export?format=csv|json — single-file dump
Auth:
- POST /api/auth/sign-in — Supabase email+password, sets access+refresh cookies
- POST /api/auth/sign-out — clears cookies
bun run check: 0 errors, 0 warnings.
Bootstrap from /home/m/dev/web/msbls.de template:
- SvelteKit 2.15 + Svelte 5 + adapter-node + bun + vite 6
- Deps trimmed: @supabase/supabase-js, postgres, zod (+ dev: kit, vite-plugin-svelte, svelte-check, typescript)
- No mbrian-core submodule (irrelevant for fdbck)
- src/app.html minimal (no fonts, no theme toggler)
- src/app.d.ts declares App.Locals { userId: string | null }
- robots.txt Disallow: / (whole app is naked, per-link or auth-only)
- .env.example: Supabase + PUBLIC_SITE_URL + optional COOKIE_DOMAIN
Initial mai init scaffolding (.claude, .m, .mcp.json, AGENTS.md) bundled in
this first commit since the repo was empty before bootstrap.
Spawned from m/flexsiebels.de#63 pivot — see docs/plans/feedback-feature.md
for the full spec (copied in next commit).