# Heygo Heygo is an independent Cloudflare React + Workers scaffold for a future shortlinks app. This repository lives at `/home/ai-bot/code/heygo` and is intentionally separate from the old Linux/Django Links repository. The old Links repository under `/home/ai-bot/code/links` is read-only reference material only and must not be modified by this project. ## Quick start (local) > Prerequisites: Node.js 18+, npm, and [just](https://github.com/casey/just) installed. ```bash git clone https://github.com/wahyd4/heygo.git cd heygo npm install just build # build React SPA into dist/client just migrate-local # apply D1 schema to local SQLite just dev # start Worker on http://localhost:8787 ``` Open `http://localhost:8787` in your browser — the SPA and API are both served by the Worker. ### Sign in as admin locally OAuth isn't available in local dev. Use the dev-only login instead: 1. Start the worker: `just dev` 2. Open `http://localhost:8787/#/dev-login` in your browser 3. Enter your email (pre-filled with `wahyd4@gmail.com`) and click "Sign in as admin" Or via CLI: ```bash just dev-admin # creates admin user + session just dev-admin other@example.com # different email just dev-logout # clears the session cookie ``` This endpoint is guarded: it only works when `PUBLIC_HOST=localhost` (or `APP_BASE_URL` contains `localhost`). In dev/prod deployments it returns 403. The dev login bypasses OAuth, finds-or-creates the user, promotes them to admin, and sets a `heygo_session` cookie — exactly what a real OAuth callback would do, minus the provider. ### Just commands Run `just` (no args) to list all recipes. The most common ones: | Command | What it does | | --- | --- | | `just build` | Type-check TypeScript + build React SPA into `dist/client` | | `just dev` | Start Cloudflare Worker locally on port 8787 (API + SPA) | | `just dev-ui` | Start Vite dev server on port 5173 with HMR (frontend only) | | `just test` | Run the full Vitest suite (134 tests) | | `just test-watch` | Run tests in watch mode | | `just smoke` | Build, migrate, start worker, curl health — quick end-to-end check | | `just typecheck` | Type-check without emitting | ### Local D1 database helpers | Command | What it does | | --- | --- | | `just migrate-local` | Apply D1 migrations to local SQLite | | `just db-tables` | List all tables in local D1 | | `just db-links` | List all links in local D1 | | `just db-query "SELECT …"` | Run arbitrary SQL on local D1 | | `just add-link ` | Insert a test public shortlink | | `just rm-link ` | Delete a link by alias | | `just db-reset` | Drop local D1 and re-apply migrations | ### First-time local setup ```bash npm install just build just migrate-local # optional: add a test shortlink to try it out just add-link claude https://claude.ai # start the Worker just dev ``` Then test it: ```bash curl -I http://localhost:8787/claude # → 302 redirect to claude.ai curl http://localhost:8787/missing # → 404 HTML page curl http://localhost:8787/api/health # → {"ok":true,"service":"heygo-worker"} ``` Open `http://localhost:8787` for the SPA management UI. ### How local dev works - `just dev` runs `wrangler dev` which starts a local Cloudflare Workers runtime (Miniflare) with a local D1 SQLite database at `.wrangler/state/`. - The Worker serves the built React SPA from `dist/client/` (hence `just build` is required first). - The Worker API handles `/api/*` routes, public shortlink redirects, and private shortlink redirects. - Local `wrangler.jsonc` sets `PUBLIC_HOST=localhost` and `PRIVATE_HOST=localhost`, so both public and private routes resolve on the same port. This is fine for testing APIs, the SPA, and public redirects. Private shortlink auth flows are covered by unit tests (`tests/private-redirect.test.ts`). - To iterate on the React frontend with hot reload, use `just dev-ui` (Vite on port 5173) in a separate terminal. Note: Vite and the Worker run on different ports, so `/api/*` calls from Vite won't reach the Worker unless you set up a proxy. ## Verification ```bash just build # tsc --noEmit && vite build just test # vitest run ``` - `just build` type-checks the TypeScript project and builds the React SPA into `dist/client` for Workers Assets. - `just test` runs Vitest tests against the Worker API handler. ## Deployment Heygo ships as a single Cloudflare Worker fronted by two custom domains: a **public** shortlink host and a **private** shortlink host. There are two environments, each with its own D1 database, KV namespace, Worker, and domains. | Environment | Public host | Private host | Worker | D1 database | | --- | --- | --- | --- | --- | | dev | `dev.heygo.cc` | `my.dev.heygo.cc` | `heygo-dev` | `heygo-shortlinks-dev` | | prod | `heygo.cc` | `my.heygo.cc` | `heygo` | `heygo-shortlinks` | The Worker reads `PUBLIC_HOST`, `PRIVATE_HOST`, `APP_BASE_URL`, and `COOKIE_DOMAIN` from env vars (set in each wrangler config), so the same code serves every environment without hardcoded hostnames. Admin users are configured with `ADMIN_EMAILS` in the matching Wrangler config: - `wrangler.dev.jsonc` for `dev.heygo.cc` - `wrangler.prod.jsonc` for `heygo.cc` - `wrangler.jsonc` for local development Use a comma, semicolon, space, or newline separated list, for example: ```jsonc "ADMIN_EMAILS": "owner@example.com, ops@example.com" ``` When a signed-in user's email matches this list, the app treats that user as `admin` even if the stored database role is still `user`. ### Prerequisites - A Cloudflare account with the `heygo.cc` zone added. - A Cloudflare API token with D1, Workers KV, Workers Scripts, and Workers Routes permissions. - Terraform >= 1.5 installed. ### 1. Provision infrastructure with Terraform The `terraform/` directory provisions the D1 database, KV namespace, and both Worker custom domains. See `terraform/README.md` for full details. ```bash export CLOUDFLARE_API_TOKEN=*** # fill account_id + zone_ids in tfvars first just infra-init just infra-plan-dev # review, then: just infra-apply-dev just infra-plan-prod # review, then: just infra-apply-prod ``` ### 2. Copy Terraform outputs into Wrangler configs After `just infra-apply-dev`, read the created resource IDs: ```bash just infra-output dev ``` Paste the printed `d1_database_id` into `wrangler.dev.jsonc` (`d1_databases[].database_id`) and `kv_namespace_id` into `wrangler.dev.jsonc` (`kv_namespaces[].id`). Repeat for prod outputs into `wrangler.prod.jsonc`. The placeholder `00000000-...` IDs are there until you fill them in. ### 3. Apply D1 migrations to remote ```bash just migrate-dev just migrate-prod ``` ### 4. Deploy the Worker ```bash just deploy-dev # builds + deploys to dev.heygo.cc + my.dev.heygo.cc just deploy-prod # builds + deploys to heygo.cc + my.heygo.cc ``` ### Dev environment The dev environment uses `dev.heygo.cc` (public) and `my.dev.heygo.cc` (private), both under the `.heygo.cc` cookie domain. Session cookies set by the app UI on `dev.heygo.cc` are automatically visible to `my.dev.heygo.cc`, so private shortlink auth works out of the box — no extra configuration needed. Prod works the same way: `heygo.cc` and `my.heygo.cc` share `.heygo.cc`. ### Google sign-in Create a Google OAuth web client and add these authorized redirect URIs: - Dev: `https://dev.heygo.cc/api/auth/google/callback` - Prod: `https://heygo.cc/api/auth/google/callback` Then set the Worker secrets for the target environment: ```bash npx wrangler secret put GOOGLE_CLIENT_ID --config wrangler.dev.jsonc npx wrangler secret put GOOGLE_CLIENT_SECRET --config wrangler.dev.jsonc ``` Repeat with `wrangler.prod.jsonc` for production. The Google sign-in flow uses the `openid email profile` scopes, stores the Google account ID in `oauth_accounts`, creates a `heygo_session` cookie, and promotes matching `ADMIN_EMAILS` users to admin. ### All deployment commands | Command | What it does | | --- | --- | | `just infra-init` | `terraform init` | | `just infra-plan-dev` | Review infra changes for dev | | `just infra-apply-dev` | Apply infra for dev | | `just infra-plan-prod` | Review infra changes for prod | | `just infra-apply-prod` | Apply infra for prod | | `just infra-output ` | Show Terraform outputs (D1 ID, KV ID) | | `just migrate-dev` | Apply D1 migrations to remote dev database | | `just migrate-prod` | Apply D1 migrations to remote prod database | | `just deploy-dev` | Build + deploy Worker to dev | | `just deploy-prod` | Build + deploy Worker to prod | ## Current API skeleton - `GET /api/health` returns JSON health status from the Worker. - Unknown `/api/*` routes return JSON `404` responses.