Skip to content

feat: in-app Feedback button in Studio, stored in D1 via /api/feedback - #780

Merged
w1ne merged 3 commits into
developfrom
feat/studio-feedback
Sep 27, 2026
Merged

w1ne merged 3 commits into
developfrom
feat/studio-feedback

Conversation

@w1ne

@w1ne w1ne commented Sep 27, 2026

Copy link
Copy Markdown
Owner

What

In-app feedback for the Studio. It works like LabWired's feedback feature.

  • Studio header: a Feedback button that is always visible. It is in the pinned account slot, next to the user menu. The label hides on narrow screens and the icon stays.
  • Modal: category, message (10–4000 chars) and a hidden honeypot.
    • Signed out: an optional reply email field.
    • Signed in: the form shows "Sending as " and sends the Supabase user id and email.
    • Every submission also sends the route path and the app version (__APP_VERSION__+__COMMIT_HASH__).
  • Categories: general, bug, idea, modeling ("Modeling / CAD result"). modeling replaces LabWired's part.
  • Endpoint: site/functions/api/feedback.ts is a Cloudflare Pages Function beside subscribe.ts, served at POST https://kernelcad.com/api/feedback.
    • It stores every accepted submission in the D1 table feedback (binding DB, database kernelcad-subscribers).
    • Honeypot: silent 204, nothing stored.
    • Rate limit: 5 per hour per IP hash, and 5 per hour per user id when one is sent. It counts rows in the same table, so it needs no KV.
    • It does not store the raw IP, only an FNV-1a hash.
    • CORS allows only https://app.kernelcad.com, https://kernelcad.com, https://www.kernelcad.com, *.kernelcad-app.pages.dev, *.kernelcad-marketing.pages.dev and localhost/127.0.0.1.
  • Schema: site/migrations/0002_feedback.sql, idempotent (CREATE TABLE IF NOT EXISTS).
  • Endpoint override: VITE_FEEDBACK_URL (optional). The default is https://kernelcad.com/api/feedback.

The server does not verify the user id and email. The Studio client reports them, so treat them as a hint and not as an identity. The per-IP limit is the real spam guard.

Manual steps / follow-ups

  1. Apply the migration on prod D1:
    npx wrangler d1 execute kernelcad-subscribers --remote --file=site/migrations/0002_feedback.sql
  2. Pages Functions are not live on kernelcad.com today. POST https://kernelcad.com/api/subscribe returns a bare static 405 and not the function's 303.
    • Cause: deploy-kernelcad-com.yml in kernelCAD-server runs wrangler pages deploy kernelcad-web/site-deploy from the workspace root. Wrangler looks for ./functions and wrangler.toml in the current directory, so it finds neither.
    • Fix: run wrangler from inside the site directory, e.g. workingDirectory: kernelcad-web/site-deploy with pages deploy ..
    • The DB binding on the kernelcad-marketing project also needs to exist.
    • Until this is fixed, the feedback endpoint (and the existing subscribe form) will not work in prod.
  3. Email notification: not included. kernelCAD has no outbound email path today. I found none in site/, in .github/workflows or in kernelCAD-server. Supabase SMTP is only commented-out config. D1 is the source of truth. To read feedback:
    npx wrangler d1 execute kernelcad-subscribers --remote --command "SELECT * FROM feedback ORDER BY id DESC LIMIT 50"
    Follow-up: a best-effort notification to andrii@kernelcad.com, e.g. Cloudflare Email Routing send_email binding, from the same function.
  4. Deploy after merge: the Studio auto-deploys. The site needs gh workflow run deploy-kernelcad-com.yml -R w1ne/kernelCAD-server -f web_ref=develop, after step 2.

Tests

  • site/functions/api/feedback.test.ts (29 tests) covers:
    • D1 insert with all bound columns
    • defaults, and no raw IP in the stored row
    • validation errors (short/long message, bad email, bad category, bad field types, non-object JSON, malformed JSON, 413 on an oversized body)
    • honeypot: 204 and no query run
    • per-IP limit, per-user limit across IPs, and that the time window expires
    • 503 on a D1 error
    • CORS echo/deny, preflight 204/403, GET 405, origin allowlist edge cases
    • Negative control: when I disabled the rate-limit comparison, both rate-limit tests failed.
  • src/studio/components/Layout/FeedbackModal.test.tsx (9 tests) covers:
    • signed-out and signed-in payloads, including path, version and user id/email
    • Send button gating, error display, Escape, the button opening the dialog
    • postFeedback fetch and 429 mapping
  • Header.test.tsx: the Feedback button renders in the pinned account slot with auth off.
  • Local results:
    • npx vitest run src/studio/components/Layout/ site/functions/api/ plus the quality and cycle ratchets: 11 files, 94 tests passed.
    • src/studio/__tests__ and src/studio/components: 87 files, 620 tests passed.
    • tsc -b --noEmit is clean.
    • npm run lint has 0 errors. All 27 warnings were already there.

🤖 Generated with Claude Code

Public feedback endpoint for the Studio. Validates message (10-4000),
optional email, category general|bug|idea|modeling, path, app version and
client-reported Supabase user. Honeypot returns a silent 204. Rate limit
5/hour per IP hash and per user id, counted from the D1 table itself.
CORS allows app.kernelcad.com, kernelcad.com, Pages previews, localhost.

Signed-off-by: Andrii Shylenko <14119286+w1ne@users.noreply.github.com>
Always-visible Feedback button in the pinned account slot opens a modal
(category, message, optional reply email, hidden honeypot). Signed-in users
send their Supabase id and email instead of the email field. Every
submission carries the route path and app version and posts to
https://kernelcad.com/api/feedback (VITE_FEEDBACK_URL overrides).

Signed-off-by: Andrii Shylenko <14119286+w1ne@users.noreply.github.com>
… the quality ratchet

Signed-off-by: Andrii Shylenko <14119286+w1ne@users.noreply.github.com>
@w1ne
w1ne enabled auto-merge September 27, 2026 21:07
@w1ne
w1ne merged commit 8aacd80 into develop Sep 27, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant