AI-powered GitHub PR review app with a modern React dashboard and a secure Node/Express backend. It supports GitHub OAuth login, webhook-driven review processing via a queue, realtime progress updates over WebSockets, and optional billing with Stripe.
- GitHub OAuth login: JWT access tokens + httpOnly refresh token rotation
- Webhook-first workflow: GitHub events → queue → worker → review results
- Realtime updates: WebSocket endpoint at
/ws(same port as the API) - API documentation: Swagger UI at
/api/docs - Infra-ready: MongoDB persistence, Redis + BullMQ for background jobs
- Payments (optional): Stripe subscription plumbing (PRO tier hooks)
- Frontend: React + TypeScript + Vite, React Router, Tailwind CSS
- Backend: Node.js + TypeScript, Express, WebSockets (
ws) - Data/Jobs: MongoDB (Mongoose), Redis, BullMQ
- Integrations: GitHub (OAuth + App), Groq, Stripe
.
├─ backend/ # Express API + WebSocket server + queue worker
│ ├─ src/
│ │ ├─ app.ts # Express app (CORS, routes, Swagger)
│ │ ├─ server.ts # HTTP + WS bootstrap (single port)
│ │ ├─ modules/ # auth, review, webhook, billing
│ │ ├─ queues/ # BullMQ queue + worker
│ │ └─ lib/ # mongodb, redis, swagger, stripe, github client
│ └─ .env.example
└─ frontend/ # Vite React app (dashboard UI)
└─ src/
├─ pages/ # Login, Dashboard, PR detail, Billing, etc.
├─ api/ # API client with auto refresh-on-401
└─ hooks/ # WebSocket hook for review events
- Node.js (recommended: current LTS)
- MongoDB running locally or a hosted connection string
- Redis running locally (default
redis://localhost:6379)
cd backend
npm install
copy .env.example .envFill in the values in backend/.env (see Environment variables below).
Run the API + WebSocket server:
npm run devBackend defaults:
- API:
http://localhost:5000 - WebSocket:
ws://localhost:5000/ws - Swagger UI:
http://localhost:5000/api/docs - Health:
http://localhost:5000/health
cd frontend
npm install
npm run devFrontend default:
- App:
http://localhost:5173
The frontend uses:
VITE_API_URL(if set), otherwise defaults tohttp://localhost:5000
You can set it via your shell before running Vite, for example:
set VITE_API_URL=http://localhost:5000
npm run devStart from backend/.env.example. Commonly used variables:
PORT: API/WS port (default5000)MONGODB_URI: Mongo connection stringFRONTEND_URL: frontend origin allowed by CORS (defaulthttp://localhost:5173)NODE_ENV:development/production
JWT_ACCESS_SECRET: secret used to sign access tokensJWT_REFRESH_SECRET: secret used to sign refresh tokens
GITHUB_CLIENT_IDGITHUB_CLIENT_SECRETGITHUB_CALLBACK_URL: defaults tohttp://localhost:5000/api/auth/github/callback
REDIS_URL: defaultredis://localhost:6379GITHUB_WEBHOOK_SECRET: used to validate webhook HMAC signatures
GITHUB_APP_PRIVATE_KEY: GitHub App private key (multiline string)GITHUB_APP_ID: GitHub App IDGROQ_API_KEY: Groq API key
STRIPE_SECRET_KEYSTRIPE_WEBHOOK_SECRETSTRIPE_PRO_PRICE_ID
- Login: user signs in via GitHub OAuth. The backend issues:
- a JWT access token (short-lived) used in
Authorization: Bearer … - a refresh token stored in an httpOnly cookie, rotated on use
- a JWT access token (short-lived) used in
- Webhook event: GitHub sends events to the backend webhook route.
- Queue + worker: the backend enqueues review work in BullMQ; the worker processes it.
- Realtime status: the frontend opens a WebSocket to
/wsand authenticates on the first message; progress and results stream live.
Run the backend and open Swagger UI:
http://localhost:5000/api/docs
npm run dev: start in watch mode (ts-node-dev)npm run build: compile TypeScriptnpm run start: run compiled server fromdist/
npm run dev: start Vite dev servernpm run build: typecheck + buildnpm run preview: preview production buildnpm run lint: run ESLint
- Same-origin vs cross-origin: the backend is configured for credentialed CORS so the refresh-token cookie works across origins.
- Webhooks require raw body: webhook routes are mounted before
express.json()so HMAC signature validation can use the raw bytes. - WebSocket auth: the server uses a “first message” auth handshake (browser WebSocket API can’t send custom headers on connect).
ISC (see backend/package.json).