Skip to content

Add Plannotator Workspaces waitlist (page + worker + CI)#771

Merged
backnotprop merged 2 commits into
mainfrom
feat/signup
May 22, 2026
Merged

Add Plannotator Workspaces waitlist (page + worker + CI)#771
backnotprop merged 2 commits into
mainfrom
feat/signup

Conversation

@backnotprop
Copy link
Copy Markdown
Owner

Summary

Introduces Plannotator Workspaces — the hosted team layer waitlist — as a new page on the marketing site backed by a new Cloudflare Worker + D1 backend.

What's new

  • /workspaces/ marketing page: two-column conversion layout on desktop (hero + form siblings, both top-aligned); single-column on mobile reordered as hero → form → info → banners so the CTA is reachable without scrolling past the long info section.
  • apps/waitlist-service/: Cloudflare Worker that accepts signup submissions and writes to a D1 (SQLite) waitlist table.
    • POST /signup — Turnstile-verified, honeypot-protected, per-IP/day rate-limited.
    • GET /admin/{count,list} — bearer-token gated (ADMIN_TOKEN).
    • 19/19 validation unit tests pass; tsc --noEmit clean.
  • CI: deploy-waitlist job added to .github/workflows/deploy.yml, mirroring deploy-paste. Uses the existing CLOUDFLARE_API_TOKEN / CLOUDFLARE_ACCOUNT_ID repo secrets — no new config.

Already done out-of-band

The Cloudflare side has been provisioned manually so this PR's worker job won't be doing first-time setup work:

  • D1 database plannotator-waitlist created (id pinned in wrangler.toml)
  • Schema applied + 001 migration applied (note, is_contributor columns)
  • ADMIN_TOKEN and TURNSTILE_SECRET_KEY secrets uploaded via wrangler secret put
  • Worker manually deployed once at https://plannotator-waitlist.plannotator.workers.dev and end-to-end-curl-tested

The merge will trigger an idempotent wrangler deploy from CI, which is what we want for ongoing changes.

Production verification needed

Local form testing on localhost:3003 was blocked by Cloudflare Turnstile error 110200 ("domain not allowed") — the widget's allowlist only includes plannotator.ai. Adding localhost / 127.0.0.1 to the widget's hostnames in the CF dashboard would fix dev testing, but for now we're verifying directly against production after merge.

Test plan

  • After merge, browse to https://plannotator.ai/workspaces/
  • Submit a real signup (Email, Company, Team size, optional note + contributor checkbox)
  • Verify success state (receipt with ticket ID + cohort position)
  • Confirm the row landed in D1: wrangler d1 execute plannotator-waitlist --remote --command="SELECT email, company, team_size, note, is_contributor, created_at FROM waitlist ORDER BY created_at DESC LIMIT 5"
  • Re-submit with the same email → silent success (we never expose the duplicate flag)
  • Try submitting from a non-allowed origin via curl → 403
  • GET /admin/list without token → 401; with token → 200

Notes on legacy data shape

The earlier iteration of this work briefly captured name / role / tools / use_cases. Those columns remain in the waitlist table for forward-compat (zero rows in production yet), but the new validator no longer accepts them and the worker writes empty strings/NULLs to those columns. A follow-up destructive migration can drop the unused columns whenever convenient.

Generated with Devin

backnotprop and others added 2 commits May 22, 2026 14:57
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>
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>
@backnotprop backnotprop merged commit 93e8b5d into main May 22, 2026
3 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