Anonymous, general-purpose feedback service on Cloudflare Workers + D1 + R2, with a HeroUI admin console.
- Anonymous submit:
POST /api/v1/feedbackneeds no key, has open CORS, and accepts optional attachments. - Server-side context: parsed UA, client hints, country/city/timezone/ASN, language, referer, hashed IP.
- Custom fields: free-form
extraJSON, no schema; every leaf is indexed sox.<path>=valuefilters stay fast. - Keyed reads:
ck_…keys issued in the console, scoped to one app or all, read or write. - Webhooks: per-app notification on every submission; Feishu/Lark, DingTalk, WeCom, Slack and Discord bots get a text message, anything else a signed JSON event.
- Admin console: HeroUI v3, Chinese and English UI, dark mode.
- Agent skill:
skills/cfeedcovers the read/triage API. Install withnpx skills add netaart/cfeed(also served at/skill.md), then setCFEED_BASE_URLandCFEED_KEY.
See DESIGN.md for the full API and data model.
npm install
cp .dev.vars.example .dev.vars # ADMIN_TOKEN, IP_SALT
npm run db:migrate
npm run dev # console at http://localhost:5173/dashboard/
npm run typecheckDeployed by Cloudflare Workers Builds on every push to main:
- Build command:
npm run build - Deploy command:
npx wrangler d1 migrations apply cfeed --remote && npx wrangler deploy
D1 cfeed and R2 cfeed-attachments already exist (APAC), and their IDs are in wrangler.jsonc. Secrets are set once per Worker:
npx wrangler secret put ADMIN_TOKEN
npx wrangler secret put IP_SALTcurl -X POST https://<your-worker>/api/v1/feedback \
-F app=my-app -F type=bug -F content='Save button does nothing' \
-F extra='{"version":"1.0.0"}' -F files=@screenshot.png