# LocaleScope LocaleScope is a React + Vite app for researching local markets, saving business results in Postgres, and reviewing them in dashboard and map views. ## Stack - React 19 + Vite - Local Fastify API + pg-boss worker - PostgreSQL + PostGIS - Google Maps Platform for browser maps and Places search ## Local App Setup 1. Install dependencies: `npm install` 2. Copy `.env.example` to `.env.local` and fill in at least: - `VITE_GOOGLE_MAPS_PLATFORM_KEY` - `DATABASE_URL` - `COOKIE_SECRET` - `GOOGLE_MAPS_SERVER_KEY` - Stripe env vars below if you want to test billing locally 3. Run the frontend: `npm run dev:web` The local backend and scripts load both `.env` and `.env.local`, with `.env.local` taking precedence, so you can keep the full local setup in one file during development. If you open the app from another machine on your LAN, set `VITE_API_BASE_URL` and `APP_ORIGIN` to that host instead of `localhost`. In development, the frontend also auto-rewrites a `localhost` API URL to the current browser hostname to make local-network testing easier. ## Local API Setup 1. Ensure PostgreSQL is running locally with PostGIS available. 2. Apply the local database migrations: `npm run migrate` 3. Start the API: `npm run dev:api` 4. Start the worker: `npm run dev:worker` ## Stripe Billing Setup Stripe is now the active payments integration for self-serve subscriptions and one-time export packs. Configure these server-side env vars to enable billing routes: - `STRIPE_SECRET_KEY` - `STRIPE_WEBHOOK_SECRET` - `STRIPE_PRICE_STARTER_MONTHLY` - `STRIPE_PRICE_STARTER_ANNUAL` - `STRIPE_PRICE_GROWTH_MONTHLY` - `STRIPE_PRICE_GROWTH_ANNUAL` - `STRIPE_PRICE_PRO_MONTHLY` - `STRIPE_PRICE_PRO_ANNUAL` - `STRIPE_PRICE_EXPORT_PACK_10K` - `STRIPE_PRICE_EXPORT_PACK_50K` - `STRIPE_BILLING_PORTAL_CONFIGURATION_ID` optional - `ADMIN_EMAILS` optional comma-separated allowlist for internal app-admin access (preferred) - `BILLING_ADMIN_EMAILS` optional deprecated fallback allowlist used when `ADMIN_EMAILS` is unset Notes: - The internal catalog in `shared/billing/plans.ts` and `shared/billing/addons.ts` remains canonical. Stripe price IDs are environment-specific mappings only. - Apply migrations before testing Stripe webhooks so `billing_webhook_events` exists: `npm run migrate` - Enterprise stays on a manual sales/invoicing path and does not use self-serve checkout. - Stripe lifecycle hardening now treats `past_due` subscriptions as a grace-window state before chargeable actions hard-block. The default grace window is 7 days. - Billing return notices now appear on the account page for completed and canceled checkout flows. - Internal billing support visibility is available through `/api/admin/billing/workspaces` for allowlisted admin emails. ## Docker Deployment 1. Copy `.env.example` to `.env` and set at least: - `DATABASE_URL` to the Compose database host, for example `postgres://postgres:YOUR_PASSWORD@db:5432/leads4less` - `POSTGRES_DB` - `POSTGRES_USER` - `POSTGRES_PASSWORD` - `WEB_PORT` - `APP_PORT` - `COOKIE_SECRET` - `GOOGLE_MAPS_SERVER_KEY` - `VITE_API_BASE_URL` - `VITE_GOOGLE_MAPS_PLATFORM_KEY` 2. Build and start the full stack: `docker compose up --build` The Compose stack starts PostGIS, waits for the database to become healthy, runs migrations automatically, and then starts the API, worker, and web containers. With the default `.env.example` values, the app is exposed on `http://localhost:3000` and the API on `http://localhost:4000`. ## Database Layout - `db/migrations/0001_local_core.sql` creates the local-first schema. - `db/scripts/migrate.ts` applies migrations in order and records them in `schema_migrations`. - `db/scripts/import-postal-areas.ts` imports US ZIP/ZCTA and Canada FSA polygons from local GeoJSON files. - `db/scripts/build-postal-neighbors.ts` builds adjacency links for deep research propagation. - `db/datasets/README.md` documents the expected postal dataset locations and property names. ## Postal Dataset Setup 1. Place your GeoJSON files in: - `db/datasets/postal/us_zcta.geojson` - `db/datasets/postal/ca_fsa.geojson` 2. Or override them with: - `POSTAL_US_DATASET_PATH` - `POSTAL_CA_DATASET_PATH` 3. Import and build adjacency: `npm run seed:postal` Notes: - Raw postal source files and generated GeoJSON datasets under `db/rawdata/` and `db/datasets/postal/*.geojson` are ignored by git. - The importer expects WGS84 lon/lat GeoJSON. If a source file is projected, re-export it to `EPSG:4326` before importing. Observability commands: - `npm run import:postal` logs progress every 500 features by default. - `npm run build:postal-neighbors` logs per-country progress and final adjacency totals. - `npm run check:postal` prints current postal area and neighbor counts from the database. You can change the import log interval with `POSTAL_IMPORT_LOG_EVERY`, for example: `POSTAL_IMPORT_LOG_EVERY=1000 npm run import:postal` If you want to validate the current postal tables after an import or neighbor build, run: `npm run check:postal` ## Google Maps Requirements Enable these Google Cloud APIs for the keys you use: - Maps JavaScript API - Places API - Geocoding API Use a browser-restricted key for `VITE_GOOGLE_MAPS_PLATFORM_KEY` and a server-side key for `GOOGLE_MAPS_SERVER_KEY`. ## Current Runtime Flow 1. The React app authenticates through the local Fastify API using cookie-backed sessions. 2. Search requests run through the local API and persist results in PostgreSQL. 3. The worker foundation is available for asynchronous job execution and deep research expansion. 4. The target architecture is fully local auth + Fastify routes + pg-boss workers + PostgreSQL/PostGIS.