Zero-Touch SaaS

Technical reference

Architecture, database schema, server functions, and deployment.

Architecture

TanStack Start v1 with file-based routing under src/routes/. Two layouts: _public for marketing pages and _authenticated for in-app routes (guarded by beforeLoad redirect). All app-internal server logic lives in src/lib/*.functions.ts as createServerFn calls — never edge functions.

Auth is handled by Supabase. The browser client lives in src/integrations/supabase/client.ts. Server functions use requireSupabaseAuth middleware; the bearer token is attached automatically by attachSupabaseAuth registered in src/start.ts.

Database schema

Three tables, all with RLS scoped to auth.uid():

  • profiles — id (uuid, PK = auth user id), openai_api_key, anthropic_api_key, gemini_api_key, wp_url, wp_username, wp_app_password, webhook_url, google_access_token, google_token_updated_at, last_free_generation_date, unlocked_blueprints (text[]), created_at, updated_at.
  • seo_generations — id, user_id, keyword, provider, payload (jsonb), created_at. Insert/select/delete only.
  • memory_assets — id, user_id, name, type ('text'|'url'|'file'), raw_content, created_at.

Server functions

  • seo-architect.functions.ts — runs a single generation. Input: keyword, provider, contextText, memoryId, gscProperty. Returns structured payload or FREE_LIMIT_REACHED.
  • profile.functions.ts — getProfile (incl. unlocked_blueprints), updateApiKeys, updateDeliverySettings.
  • generations.functions.ts — listGenerations, getGeneration, deleteGeneration, getDashboardStats.
  • memory.functions.ts — listMemory, createMemory, deleteMemory.
  • delivery.functions.ts + push.functions.ts — pushToWordPress, pushToWebhook.
  • gsc.functions.ts — getGscProperties, getGscTopPages.

Environment & deployment

Browser env vars (Vite-replaced at build time): VITE_SUPABASE_URL, VITE_SUPABASE_PUBLISHABLE_KEY. Server env vars (runtime, never bundled): SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, MASTER_GEMINI_KEY (freemium), Google OAuth client/secret. Read process.env inside .handler() — never at module scope.

Runtime is Cloudflare Workers (workerd) with nodejs_compat. Avoid Node-only packages, child_process, sharp, or anything requiring native bindings.