Skip to content

lucent-lab/weblingo_website

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

629 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WebLingo Marketing Site

Single-tenant marketing site for WebLingo, a SaaS localization product. Built with Next.js (App Router), Tailwind CSS, and Stripe Checkout. The codebase follows a “proto-package” layout so internal modules can be promoted to standalone packages when new SaaS sites spin up. The Supabase-authenticated customer dashboard lives alongside the marketing site and communicates with the Cloudflare worker (webhooks API) for site management.

Directory Layout

app/                 # Next.js routes (localized under /[locale]) and API handlers
components/          # Reusable UI components for the site
internal/            # Proto-packages (env, billing, etc.)
  core/
  billing/
  i18n/
modules/             # Feature modules (e.g., pricing)
styles/              # Global styles (Tailwind)

Key modules today:

  • internal/core/env.ts — strict environment variable parsing
  • internal/billing/stripe.ts — Stripe client + helpers
  • internal/i18n/ — Locale config, message loaders, translation helpers
  • modules/pricing/ — Pricing tier definitions and UI
  • components/ui/ — Locally vendored shadcn/ui primitives (button, card, input, badge)
  • components/pricing-teaser.tsx — Home page pricing teaser built on shadcn/ui cards

Internationalization is handled via the /[locale] segment with English, French, and Japanese dictionaries stored under internal/i18n/messages/*.

Codex Quick Lookup

  • Env source of truth: internal/core/env.ts (client) and internal/core/env-server.ts (server).
  • Dashboard capability matrix/spec: docs/backend/DASHBOARD_SPECS.md.
  • Backend docs snapshot sync:
    • WEBLINGO_REPO_PATH=/absolute/path/to/weblingo corepack pnpm docs:sync
    • WEBLINGO_REPO_PATH=/absolute/path/to/weblingo corepack pnpm docs:sync:check
  • Minimal validation for docs/capability alignment:
    • corepack pnpm test:contracts
    • corepack pnpm check
    • WEBLINGO_REPO_PATH=/absolute/path/to/weblingo corepack pnpm check:ci

Lint Rollout

Type-aware ESLint now uses projectService: true for TypeScript files.

  • First-wave strict rules apply to app/api/**/*.ts, internal/**/*.ts, and lib/**/*.ts.
  • First-wave rules: consistent-type-imports, no-floating-promises, no-misused-promises, no-unnecessary-condition, and phased strict-boolean-expressions.
  • Broad TSX strict-boolean-expressions remains deferred for components/**, modules/**, route TSX files, and internal/**/*.tsx until ReactNode truthiness patterns are normalized.
  • corepack pnpm check:ci adds test:contracts and runs docs:sync:check when WEBLINGO_REPO_PATH is set.

Environment Variables

Create .env.local (or configure host env) with:

# App + dashboard
NEXT_PUBLIC_APP_URL=http://localhost:3000
HOME_PAGE_VARIANT=expansion # optional: classic | expansion (default expansion)
PUBLIC_PORTAL_MODE=enabled # required: enabled | disabled
# Public endpoint consumed by the dashboard UI. Protect with auth, CORS, and rate limits.
NEXT_PUBLIC_WEBHOOKS_API_BASE=https://api.weblingo.app/api
NEXT_PUBLIC_WEBHOOKS_API_TIMEOUT_MS=15000
TRY_NOW_TOKEN=preview_token_value

# Public form abuse controls
WEBSITE_WAITLIST_RATE_LIMIT_WINDOW_MS=60000
WEBSITE_WAITLIST_MAX_PER_WINDOW=20
WEBSITE_WAITLIST_MAX_BODY_BYTES=4096
WEBSITE_CONTACT_RATE_LIMIT_WINDOW_MS=60000
WEBSITE_CONTACT_MAX_PER_WINDOW=10

# Preview abuse controls (required when TRY_NOW_TOKEN is set)
WEBSITE_PREVIEW_RATE_LIMIT_WINDOW_MS=60000
WEBSITE_PREVIEW_CREATE_MAX_PER_WINDOW=20
WEBSITE_PREVIEW_CREATE_MAX_PER_SOURCE_HOST_PER_WINDOW=10
WEBSITE_PREVIEW_STATUS_MAX_PER_WINDOW=120
WEBSITE_PREVIEW_STREAM_MAX_PER_WINDOW=30
WEBSITE_PREVIEW_MAX_BODY_BYTES=16384
WEBSITE_PREVIEW_UPSTREAM_CREATE_TIMEOUT_MS=15000
WEBSITE_PREVIEW_UPSTREAM_STATUS_TIMEOUT_MS=15000
WEBSITE_PREVIEW_UPSTREAM_STREAM_CONNECT_TIMEOUT_MS=15000

# Redis (required; preferred Vercel/Upstash naming)
UPSTASH_REDIS__KV_REST_API_URL=https://<db>.upstash.io
UPSTASH_REDIS__KV_REST_API_TOKEN=upstash_token

# Stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_PRICING_TABLE_ID=prctbl_default
STRIPE_PRICING_TABLE_ID_EN=prctbl_for_en
STRIPE_PRICING_TABLE_ID_FR=prctbl_for_fr
STRIPE_PRICING_TABLE_ID_JA=prctbl_for_ja

# Supabase (auth + logging)
NEXT_PUBLIC_SUPABASE_URL=https://<project>.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=public-anon-key
SUPABASE_SECRET_KEY=service-role-key
SUPABASE_AUTH_TIMEOUT_MS=15000

# Analytics (optional)
NEXT_PUBLIC_POSTHOG_KEY=phc_...
# Required at build time. Use https://metrics.weblingo.app in production/preview.
NEXT_PUBLIC_POSTHOG_BROWSER_HOST=http://localhost:3000/_analytics/posthog
# Kill switch for browser and server analytics capture.
NEXT_PUBLIC_POSTHOG_CAPTURE=enabled
# Upstream ingestion host for server analytics and the local fallback proxy route.
NEXT_PUBLIC_POSTHOG_HOST=https://eu.i.posthog.com
# Session replay is disabled unless explicitly sampled on allowlisted public routes.
NEXT_PUBLIC_POSTHOG_REPLAY_CAPTURE=disabled
NEXT_PUBLIC_POSTHOG_REPLAY_SAMPLE_RATE=0

PUBLIC_PORTAL_MODE=disabled hides the login CTA, disables signup/login actions, blocks checkout, and returns 404 for the login and dashboard onboarding screens. Set it to enabled to expose the portal.

PostHog browser traffic uses NEXT_PUBLIC_POSTHOG_BROWSER_HOST as the SDK api_host. Because NEXT_PUBLIC_* values are baked into the Next.js client bundle at build time, set this variable in every deployed build target before deploying. Production and preview should point it at the managed proxy (https://metrics.weblingo.app); local development can keep using the first-party http://localhost:3000/_analytics/posthog route. Keep NEXT_PUBLIC_POSTHOG_HOST pointed at the upstream PostHog ingestion host (for example https://eu.i.posthog.com), not the browser-facing proxy path. NEXT_PUBLIC_POSTHOG_CAPTURE=disabled is the analytics kill switch. Session replay remains off unless NEXT_PUBLIC_POSTHOG_REPLAY_CAPTURE=sampled and NEXT_PUBLIC_POSTHOG_REPLAY_SAMPLE_RATE is a value from 0 to 1; replay is still limited by the route allowlist in internal/analytics/replay.ts.

See docs/POSTHOG_ANALYTICS.md for the event/property contract, replay allowlist, identity grouping, and PostHog MCP guardrails.

Running Locally

  1. Install dependencies: corepack pnpm install
  2. Fill .env.local with the values above (set NEXT_PUBLIC_WEBHOOKS_API_BASE for the dashboard and TRY_NOW_TOKEN for previews). Redis credentials are required for public-form and preview rate limiting (UPSTASH_REDIS__KV_REST_API_URL + UPSTASH_REDIS__KV_REST_API_TOKEN).
  3. Start dev server: corepack pnpm run dev (opens http://localhost:3000).
  4. Dashboard access: visit /dashboard, sign in via Supabase auth, then create/manage sites (calls NEXT_PUBLIC_WEBHOOKS_API_BASE).
  5. Validation: optional corepack pnpm run lint, corepack pnpm run typecheck, corepack pnpm run format before committing.

Dashboard Smoke (Mock Mode)

  • Run corepack pnpm test:e2e:smoke.
  • This command sets DASHBOARD_E2E_MOCK=1, which bypasses Supabase login and serves deterministic mocked webhook payloads for dashboard smoke paths/actions.
  • Use this for CI/local smoke coverage of dashboard routing + action feedback without depending on live auth/API infrastructure.

Backend Docs Sync

Website API docs use backend-synced snapshots under content/docs/_generated.

  • Refresh snapshots:
    • WEBLINGO_REPO_PATH=/absolute/path/to/weblingo corepack pnpm docs:sync
  • Verify snapshots are fresh (fails fast when missing/stale):
    • WEBLINGO_REPO_PATH=/absolute/path/to/weblingo corepack pnpm docs:sync:check
  • Contract/doc coverage tests:
    • corepack pnpm test:contracts

WEBLINGO_REPO_PATH is required for sync commands. There is no fallback path.

CI behavior:

  • When CROSS_REPO_CHECKOUT_TOKEN is available, .github/workflows/ci.yml checks out backend HEAD and runs:
    • WEBLINGO_REPO_PATH="$GITHUB_WORKSPACE/backend" corepack pnpm docs:sync:check
    • corepack pnpm test:contracts
  • When token access is unavailable, run this fallback locally before opening/updating a PR:
    • WEBLINGO_REPO_PATH=/absolute/path/to/weblingo corepack pnpm check:ci

Ownership:

  • Website maintainers own content/docs/_generated/*, docs pages, and dashboard capability matrix updates.
  • Backend maintainers own canonical OpenAPI/feature catalog/playbooks and must notify website maintainers on user-facing contract changes.
  • Current maintainer model: one repository maintainer currently fulfills both roles.
  • Canonical website capability matrix/spec: docs/backend/DASHBOARD_SPECS.md.
  • Backend bridge pointer to this matrix: weblingo/docs/backend/DASHBOARD_SPECS.md.

Stripe Setup

  1. Create recurring prices for the public customer plans and keep the self-serve contract aligned with the backend: one account/subscription covers one website. Additional websites require separate scoping or agency handling.
  2. Populate the matching IDs in modules/pricing/data.ts.
  3. Run stripe listen --forward-to localhost:3000/api/stripe/webhook for local development and set the STRIPE_WEBHOOK_SECRET.
  4. Configure Stripe Pricing Tables for each locale (or reuse one) and copy the IDs into STRIPE_PRICING_TABLE_ID (fallback) and the locale-specific envs (STRIPE_PRICING_TABLE_ID_EN, FR, JA). The embed renders on /[locale]/pricing, with the in-app comparison table acting as a fallback.

Deployment

  • Build: corepack pnpm run build (Next.js static + server output).
  • Hosting: Deploy to Vercel/Netlify/Fly/etc. with Node 20.9+ and set all env vars above. Ensure the hosting URL matches NEXT_PUBLIC_APP_URL.
  • Rate limiting store: Provision Upstash Redis (or compatible REST KV) and configure UPSTASH_REDIS__KV_REST_API_URL/UPSTASH_REDIS__KV_REST_API_TOKEN. This is required for waitlist/contact/preview abuse controls.
  • Supabase: Configure the site URL and redirect URLs in Supabase Auth settings. Provide NEXT_PUBLIC_SUPABASE_URL/NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY/SUPABASE_SECRET_KEY to the host.
  • Worker API: Point NEXT_PUBLIC_WEBHOOKS_API_BASE to the live webhooks worker; enable CORS for the dashboard origin. Use TRY_NOW_TOKEN for server-side preview requests.
  • Stripe: Add webhook endpoint pointing to /api/stripe/webhook and set STRIPE_WEBHOOK_SECRET on the host.
  • Dashboard: /dashboard uses Supabase session cookies; ensure the domain matches your Supabase config so auth cookies persist.
  • Smoke checks: After deploy, verify /[locale] pages load, Stripe Pricing Table renders, and the dashboard shows no-site onboarding or routes a one-site customer directly to the website workspace.

Roadmap For Additional Sites

  • Extract modules from internal/* into /packages/* when a second SaaS app appears.
  • Add authentication module (e.g., Supabase) under internal/auth once customer portals launch.
  • Extend billing module with customer portal API when needed.

About

Public websites for customer subscription

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages