Vehicle market intelligence platform inspired by the Sri Lanka Property Price Intelligence stack, adapted for car listings.
- Frontend: React + Vite + TypeScript + Tailwind + React Query
- Backend: FastAPI + SQLAlchemy
- Database: PostgreSQL (Supabase recommended)
src/: frontend appbackend/app/: FastAPI routes and servicesbackend/db/: SQLAlchemy models and DB session config
Copy .env.example to .env and keep:
VITE_API_URL=/api/v1
VITE_BACKEND_URL=http://127.0.0.1:8000Copy backend/.env.example to backend/.env and set Supabase:
DATABASE_URL=postgresql://postgres.<project-ref>:<password>@aws-0-ap-southeast-1.pooler.supabase.com:6543/postgres
ALLOW_SQLITE_FALLBACK=false
CORS_ORIGINS=http://localhost:8080,http://127.0.0.1:8080Notes:
- Use Supabase transaction pooler URL on port
6543. - If your URL starts with
postgres://, backend auto-converts it topostgresql://.
Frontend:
npm installBackend:
cd backend
pip install -r requirements.txtStart backend (from repo root):
cd backend
uvicorn app.main:app --reload --host 127.0.0.1 --port 8000Start frontend (from repo root):
npm run devOpen:
- Frontend:
http://localhost:8080 - API health:
http://127.0.0.1:8000/health
If you hit a blank screen, check these first:
- Frontend env has
VITE_API_URL=/api. - Frontend env has
VITE_API_URL=/api/v1. - Backend is running on
127.0.0.1:8000. - Browser console for runtime errors.
This project now includes an app-level error boundary so runtime crashes show a fallback UI instead of a silent blank page.
- Confirm
DATABASE_URLis set inbackend/.env. - Set
ALLOW_SQLITE_FALLBACK=falseto force fast failure. - Ensure IP/network policy allows your machine.
npm run test
npm run buildBoth should pass before deployment.
This repo now includes backend/Dockerfile and backend/fly.toml for Fly deployment.
From repo root:
cd backend
fly auth login
fly launch --no-deploySet required backend secrets:
fly secrets set DATABASE_URL="postgresql://..." \
CORS_ORIGINS="https://vehicle-platform-one.vercel.app" \
ALLOW_SQLITE_FALLBACK="false"Deploy:
fly deployAfter deploy, verify:
curl https://<your-fly-app>.fly.dev/health
curl https://<your-fly-app>.fly.dev/api/v1/stats/summaryIn Vercel Project Settings -> Environment Variables, set:
VITE_API_URL=https://<your-fly-app>.fly.dev/api/v1Then redeploy frontend in Vercel. Your live stats/map/listings/trends should populate once API calls target Fly.
After one-time setup, every push updates production automatically.
This repo includes deploy-backend-fly.yml. It deploys the backend whenever backend/** changes on main.
One-time setup in GitHub:
- Generate Fly token:
fly auth token- In GitHub repo -> Settings -> Secrets and variables -> Actions, add:
FLY_API_TOKEN= output offly auth token
- Confirm your Fly app is initialized once (
fly launch --no-deploy) andbackend/fly.tomlapp name matches your real Fly app.
Use Vercel Git integration:
- In Vercel project settings, connect this GitHub repo.
- Set production branch to
main. - Keep auto deploy enabled.
- Set env var:
VITE_API_URL=https://<your-fly-app>.fly.dev/api/v1Now pushes to main will:
- Auto deploy backend to Fly (if backend files changed).
- Auto deploy frontend to Vercel (if frontend files changed).
Normally not required because frontend calls the same backend URL, but if needed you can manually click "Redeploy" in Vercel Deployments.
This repo now includes .github/workflows/daily-scrape.yml to run scraping in GitHub Actions (no local data/wifi usage from your side).
It supports:
- Scheduled run: 12:10 AM and 12:10 PM Sri Lanka time
- Manual run: GitHub -> Actions -> Daily Vehicle Scraper -> Run workflow
- Independent source jobs: Ikman, Riyasewana, and Patpat run as separate jobs so one source cannot stop the others
In GitHub repo -> Settings -> Secrets and variables -> Actions:
- Add secret:
DATABASE_URL= your production PostgreSQL/Supabase URL
- (Optional) Add repository variables:
SCRAPE_MAX_PAGES= global page depth fallback (default is500)SCRAPE_MAX_PAGES_IKMAN= Ikman page depth overrideSCRAPE_MAX_PAGES_RIYASEWANA= Riyasewana page depth overrideSCRAPE_MAX_PAGES_PATPAT= Patpat page depth overrideSCRAPE_SOURCE_TIMEOUT_SECONDS_IKMAN= per-source timeout in seconds (default1800)SCRAPE_SOURCE_TIMEOUT_SECONDS_RIYASEWANA= per-source timeout in seconds (default1800)SCRAPE_SOURCE_TIMEOUT_SECONDS_PATPAT= per-source timeout in seconds (default1800)
Notes:
- Workflow forces
ALLOW_SQLITE_FALLBACK=falseso it fails fast if DB config is missing. - Playwright dependencies and Chromium are installed inside the runner automatically.
- The workflow uses
SCRAPE_ENABLED_SOURCESinternally so each job runs only one source.
This repo also includes .github/workflows/alt-sources-scrape.yml.
It currently runs this source as an independent job:
PatpatScraper(patpat.lk)
Each job sets SCRAPE_ENABLED_SOURCES so one source failure/timeout does not block the others.
Within each job, backend/run_alt_sync.py scrapes enabled source(s) and then refreshes aggregates/deal scores.
- Manual: GitHub -> Actions -> Alternate Sources Scraper -> Run workflow
Add repository variable in GitHub Actions:
SCRAPE_MAX_PAGES_ALT(default:500)SCRAPE_SOURCE_TIMEOUT_SECONDS_ALT(default:1800)SCRAPE_SOURCE_TIMEOUT_SECONDS_PATPAT(optional source-specific override)