A production-grade, serverless form builder inspired by Google Forms. Built for the modern web using Cloudflare Workers, D1 (SQLite), and Cloudflare Pages. It features a secure, themeable admin panel and a lightning-fast public form renderer.
- ๐จ Powerful Builder: Drag-and-drop-style form creation with support for multiple input types (Text, Email, Radio, Checkbox, File Uploads).
- ๐จ Themeable UI: Instant dark mode support, customizable theme colors, and logo uploads.
- ๐ก๏ธ SaaS-Grade Security: JWT-based authentication, server-side validation, and Cloudflare Turnstile integration.
- ๐ Response Management: Real-time response tracking with CSV/XLSX/PDF export capabilities.
- โ๏ธ Cloud Asset Storage: Direct Cloudinary integration for handling file uploads.
- ๐ Edge-Powered: Globally distributed serverless architecture for sub-100ms response times.
- Vanilla JS & CSS3: Zero-dependency, lightweight, and blazing fast.
- Cloudflare Pages: High-performance static hosting at the edge.
- Font Awesome: Professional iconography.
- Cloudflare Workers: High-performance serverless environment.
- Cloudflare D1: SQL database for reliable data storage.
- Cloudflare KV: High-speed key-value storage for rate limiting.
- JWT Auth: Secure admin session management.
- Cloudflare Secrets: Zero-exposure secret management.
- CORS Hardening: Strict origin-based access control.
KumoFumi /
โโโ api/ # Cloudflare Worker (Backend)
โ โโโ src/ # Business logic & authentication
โ โโโ schema.sql # D1 database initialization
โ โโโ wrangler.toml # Worker configuration
โโโ css/ # Modular frontend styles
โโโ js/ # Client-side logic (Admin & Form)
โโโ index.html # Authentication Portal (Sign In & Sign Up)
โโโ builder.html # Form Builder & Dashboard
โโโ form.html # Public Form Renderer
This project follows a Backend-First Security Model. All sensitive logic is isolated in the Worker, ensuring the frontend remains thin and secure.
Important
Secrets are NEVER stored in GitHub or the source code. They are injected at runtime via Cloudflare Environment Variables.
๐ View Production Secret Commands
# How to set production secrets
wrangler secret put ADMIN_PASSWORD
wrangler secret put JWT_SECRET
wrangler secret put TURNSTILE_SECRET- Frontend: UX-focused validation (immediate feedback).
- Backend: Strict schema validation (Zod-like logic), rate limiting, and size enforcement.
- Database: SQL-level integrity checks.
Zeon Forms enforces strict server-side storage access control to ensure free/public-signup users never consume platform Cloudinary resources.
| Account Type | Allowed Storage | Backend Enforcement |
|---|---|---|
| Superadmin | Cloudinary | Server-signed upload parameters or server-proxied upload. |
| Admin (assigned by superadmin) | Cloudinary | Verified against D1 storage_provider = 'cloudinary'. |
| Admin (unassigned) | Google Drive or None | Cloudinary blocked (HTTP 403 STORAGE_PROVIDER_NOT_ALLOWED). |
| Free / Public-Signup User | Personal Google Drive only | Cloudinary blocked (HTTP 403). Must connect Drive for image/file uploads. |
| R2 Storage | Disabled | Gated behind R2_STORAGE_ENABLED !== 'true'. |
- Anonymous respondents never log into Google.
- Response attachments and payload backups are stored in the form owner's connected Google Drive (or Cloudinary if permitted) via server-side routing (
POST /api/forms/:slug/upload). - If the form owner's Drive quota or token expires, the backend returns HTTP 503
STORAGE_UNAVAILABLEwith a reconnect notification for the owner, preventing silent data loss or Cloudinary fallback. - Submissions are deduplicated via
Idempotency-Keyheaders stored insubmission_idempotency.
- Go to the Google Cloud Console.
- Create or select a Project (e.g.
Zeon Forms). - Navigate to APIs & Services > Enabled APIs & Services and enable the Google Drive API.
- Go to APIs & Services > OAuth consent screen:
- Choose External user type.
- Fill in App Name, Support Email, and Developer Contact.
- In Scopes, add
https://www.googleapis.com/auth/drive.file(per-app file access only; avoids full Drive access).
- Go to APIs & Services > Credentials:
- Click Create Credentials > OAuth client ID.
- Application type: Web application.
- Authorized JavaScript origins:
http://localhost:3000,http://127.0.0.1:3000,https://forms.ceal.in. - Authorized redirect URIs:
- Local:
http://127.0.0.1:8787/api/storage/google/callback - Production:
https://forms.ceal.in/api/storage/google/callback(or your worker domain).
- Local:
- Note down the Client ID and Client Secret.
ADMIN_PASSWORD=your_local_admin_password
JWT_SECRET=your_local_jwt_secret
DRIVE_ENCRYPTION_KEY=32_byte_random_master_key_for_aes_gcm
GOOGLE_CLIENT_ID=your_google_client_id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your_google_client_secret
GOOGLE_REDIRECT_URI=http://127.0.0.1:8787/api/storage/google/callback
CLOUDINARY_CLOUD_NAME=your_cloudinary_cloud_name
CLOUDINARY_API_KEY=your_cloudinary_api_key
CLOUDINARY_API_SECRET=your_cloudinary_api_secretnpx --prefix api wrangler secret put ADMIN_PASSWORD --env production
npx --prefix api wrangler secret put JWT_SECRET --env production
npx --prefix api wrangler secret put DRIVE_ENCRYPTION_KEY --env production
npx --prefix api wrangler secret put GOOGLE_CLIENT_ID --env production
npx --prefix api wrangler secret put GOOGLE_CLIENT_SECRET --env production
npx --prefix api wrangler secret put CLOUDINARY_CLOUD_NAME --env production
npx --prefix api wrangler secret put CLOUDINARY_API_KEY --env production
npx --prefix api wrangler secret put CLOUDINARY_API_SECRET --env productionInitialize or update the database schema:
# Local D1 database:
npx --prefix api wrangler d1 execute custom-forms-db --local --file=./schema.sql --persist-to=.wrangler/state
# Production D1 database:
npx --prefix api wrangler d1 execute custom-forms-db --remote --env production --file=./schema.sql- Existing assets for free users are marked as
storage_provider = 'legacy_cloudinary'. - Legacy Cloudinary images continue to render seamlessly.
- New Cloudinary uploads are blocked for free users.
- Users can click Migrate Assets in the Form Builder Storage settings to copy legacy Cloudinary assets to their connected Google Drive.
| Mode | Command | Frontend | Worker | Database |
|---|---|---|---|---|
| Local | npm run dev |
Local (http://127.0.0.1:3000) |
Local Wrangler (http://127.0.0.1:8787) |
Local D1 (.wrangler/state) |
| Remote Test | npm run dev:remote |
Local (http://127.0.0.1:3000) |
Remote Mode (http://127.0.0.1:8787) |
Production D1 (Cloudflare) |
| Production | npm run deploy:api |
Hosted (https://forms.ceal.in) |
Cloudflare Edge Worker | Production D1 (Cloudflare) |
-
Install dependencies:
npm install npm --prefix api install
-
Configure local development secrets: Copy the example file to
.dev.varsinside theapi/directory:cp api/.dev.vars.example api/.dev.vars
Edit
api/.dev.varswith your preferred local password (e.g.ADMIN_PASSWORD=your_local_password). -
Initialize local D1 database schema:
npm --prefix api run db:init:local
Start both the frontend static dev server and the local Cloudflare Worker simultaneously:
npm run dev- Frontend: Accessible at http://127.0.0.1:3000 (or
http://localhost:3000) - Backend Worker: Running locally via Wrangler at http://127.0.0.1:8787
- Database: Strictly local SQLite instance persisted inside
api/.wrangler/state. - Stopping: Press
Ctrl + Cin your terminal to cleanly terminate both processes concurrently.
Important
Data Isolation: Local D1 data is completely isolated from the hosted Cloudflare database. Forms and responses created locally will NEVER appear on the hosted website, and creating local records does NOT deploy them.
Query the local SQLite database without touching production:
npm --prefix api run db:query:local -- "SELECT name FROM sqlite_master WHERE type='table';"
npm --prefix api run db:query:local -- "SELECT id, slug, name, status FROM forms;"Warning
DANGER: npm run dev:remote connects your local development instance directly to PRODUCTION Cloudflare resources. Any form edits, submissions, or deletions in this mode WILL MUTATE REAL PRODUCTION DATA.
Only run this mode when intentionally verifying production integration:
npm run dev:remoteWarning
Remote queries run directly against your live production database. Never run DROP, DELETE, or untested UPDATE queries remotely.
npm --prefix api run db:query:remote -- "SELECT id, slug, name, status FROM forms LIMIT 10;"Deploy code to the Cloudflare production environment:
npm run deploy:api(Runs wrangler deploy --env production inside api/)
Production secrets must never be committed to Git. Set them securely via Wrangler:
npx --prefix api wrangler secret put ADMIN_PASSWORD --env production
npx --prefix api wrangler secret put JWT_SECRET --env production
npx --prefix api wrangler secret put TURNSTILE_SECRET --env productionBuild the minified production bundle:
npm run buildPush the repository to GitHub connected to Cloudflare Pages (Build output directory: dist/), or deploy via CLI:
npx wrangler pages deploy dist --project-name=forms- PDF Export: Generate professional PDF reports for each submission.
- Analytics: Visual charts for data distribution.
- Webhooks: Direct integration with Discord/Slack.
- Multi-Admin: RBAC support for larger teams.
- Scheduling: Auto-close forms based on timestamps.
Distributed under the MIT License. See LICENSE for more information.
Tip
This architecture is designed to scale to millions of requests at near-zero cost. Itโs not just a toolโit's a production-ready serverless platform.