- Node.js (v18+)
- A Cloudflare account (free tier works)
git clone https://github.com/wassertim/holler.git
cd holler
npm install
npx wrangler d1 migrations apply holler-db --local
npm run devThis starts a local server at http://localhost:8787 with a local D1 database. Migrations must be applied before the first run.
Turnstile verification is skipped when TURNSTILE_SECRET is not configured, so local development works without any setup.
Create a .dev.vars file in the project root:
ADMIN_TOKEN=test123
Then visit http://localhost:8787/?token=test123 to see admin controls (status dropdown, delete button) on each post.
If you prefer deploying manually instead of the one-click button:
npx wrangler d1 create holler-dbCopy the database_id from the output and update wrangler.toml:
[[d1_databases]]
binding = "DB"
database_name = "holler-db"
database_id = "your-database-id-here"
migrations_dir = "migrations"npm run deployThis applies D1 migrations automatically before deploying.
Turnstile is Cloudflare's free, privacy-friendly CAPTCHA alternative. It protects the feedback form from spam.
- Go to the Cloudflare dashboard and navigate to Turnstile
- Select Add widget
- Enter a widget name (e.g., "Holler Feedback")
- Under Hostname management, add the domain(s) where Holler will be deployed
- Choose a widget mode:
- Managed (recommended) -- Cloudflare decides if interaction is needed
- Non-Interactive -- no visitor interaction, challenge runs in background
- Invisible -- no visible widget at all
- Click Create
- Copy your site key and secret key
npx wrangler secret put TURNSTILE_SITE_KEY
# Paste your site key when prompted
npx wrangler secret put TURNSTILE_SECRET
# Paste your secret key when promptedThe site key is embedded in the HTML form to render the widget. The secret key is used server-side to verify tokens via the Turnstile siteverify API.
npx wrangler secret put ADMIN_TOKEN
# Enter a secure token when promptedAccess admin mode by appending ?token=YOUR_TOKEN to any page URL.
| Variable | Required | Description |
|---|---|---|
TURNSTILE_SITE_KEY |
No | Turnstile widget public key. Form submits without captcha when unset. |
TURNSTILE_SECRET |
No | Turnstile secret key for server-side verification. Verification skipped when unset. |
ADMIN_TOKEN |
No | Token for admin access. Admin routes return 503 when unset. |
For local development, put these in a .dev.vars file. For production, use npx wrangler secret put <NAME>.
| Command | Description |
|---|---|
npm run dev |
Local development with wrangler |
npm run build |
Build for production (dry-run) |
npm run deploy |
Apply migrations and deploy to Cloudflare Workers |
holler/
├── src/
│ ├── index.tsx # Worker entry + Hono routes
│ ├── db.ts # D1 queries + types
│ └── components/
│ ├── Layout.tsx # HTML shell, HTMX/CSS includes
│ ├── PostCard.tsx # Feedback post display
│ ├── PostForm.tsx # Submission form
│ └── VoteButton.tsx # Vote toggle button
├── migrations/
│ └── 0001_initial.sql # D1 schema (posts, votes, FTS5)
├── public/
│ └── styles.css # Minimal custom CSS
├── wrangler.toml
└── package.json