Files
Michael Ramos 93e8b5dfcf Add Plannotator Workspaces waitlist (page + worker + CI) (#771)
* Add Plannotator Workspaces waitlist (page + worker + CI)

Marketing
- New /workspaces/ page: hero+form sibling layout on desktop; mobile
  reorders to hero -> form -> info -> banners so the CTA is reachable
  on the first scroll. Uses the marketing site tokens/fonts and the
  existing border-x editorial column. Theme-aware via the cookie.
- Adds a "head" slot to Base.astro for per-page <head> additions.
- Nav: new "Workspaces" link with a "soon" pill.
- Landing: small Workspaces CTA band before the final install CTA.

Waitlist worker (apps/waitlist-service)
- Cloudflare Worker + D1 (SQLite). Mirrors the paste-service layout.
- Endpoints: POST /signup (Turnstile-protected, rate-limited, honeypot),
  GET /health, GET /admin/count, GET /admin/list gated by Bearer
  ADMIN_TOKEN.
- Validation: required email + company + team_size; optional note +
  is_contributor; honeypot field; freemail-aware company auto-inference.
- D1 schema includes a non-destructive 001 migration that adds note
  and is_contributor columns. 19/19 validation unit tests pass; tsc
  clean.

CI
- Adds a deploy-waitlist job to .github/workflows/deploy.yml mirroring
  paste-service. Uses the existing CLOUDFLARE_API_TOKEN and
  CLOUDFLARE_ACCOUNT_ID secrets, so no new repo config is required.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* Force light mode on /workspaces/

The page is designed and signed off in light. Override the saved theme
cookie via a head-slot inline script that adds the `.light` class
before paint. Base.astro's cookie reader only adds the class (never
removes), so any saved-dark preference can't undo this.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-22 15:01:43 -07:00

38 lines
1.3 KiB
SQL

-- Plannotator Workspaces waitlist
--
-- Authoritative schema for from-scratch deploys. The current production
-- schema is this file PLUS the migrations under `migrations/` (already
-- applied). For a fresh database, this file alone is sufficient.
--
-- Run with: bun run db:migrate (remote) or db:migrate:local (local dev)
CREATE TABLE IF NOT EXISTS waitlist (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE COLLATE NOCASE,
-- `name` is no longer collected by the form. Kept NOT NULL with an empty
-- string default so the live D1 schema (which has NOT NULL with no default)
-- can be brought into line via the 002 migration without losing rows.
name TEXT NOT NULL DEFAULT '',
company TEXT,
company_inferred INTEGER NOT NULL DEFAULT 0,
team_size TEXT,
note TEXT,
is_contributor INTEGER NOT NULL DEFAULT 0,
ip TEXT,
country TEXT,
user_agent TEXT,
referer TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_waitlist_created_at ON waitlist(created_at);
CREATE INDEX IF NOT EXISTS idx_waitlist_company ON waitlist(company);
-- Per-IP daily rate-limit counter, prunable on a schedule.
CREATE TABLE IF NOT EXISTS rate_limit (
ip TEXT NOT NULL,
day TEXT NOT NULL,
hits INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (ip, day)
);