Leads / docs /guide.md
abc1181's picture
Mappls: support static REST key (MAPPLS_API_KEY) as well as OAuth
0b4f70e
|
Raw History Blame Contribute Delete
9.05 kB
# LeadGen β€” Step-by-Step Guide (Personal Tool by AJ)
> Single-owner India SMB finder: Maps β†’ no-website hunt β†’ scoring β†’ AI icebreakers β†’ Sheets. No bulk sending from this server.
---
## 1) What you need before you start
- A Gmail account (owner β€” this will be the *only* login allowed via `APP_ALLOW_EMAIL`)
- A Hugging Face account (for the Space at `https://abc1181-leads.hf.space`)
- Optional but recommended: a Google Cloud project (for Places + OAuth) β€” free tier is enough
---
## 2) Create the Hugging Face Space
The code is already pushed to `abc1181/Leads` (Docker). Just configure envs:
1. HF β†’ Space `abc1181/Leads` β†’ **Settings β†’ Variables** (non-secrets) and **Secrets** (tokens)
2. Set the essentials below (Section 3). The Space auto-restarts on save.
3. Check `https://abc1181-leads.hf.space/api/health` β†’ should be `{"ok":true,"db":"ok","persistence":"on"}`. If `persistence:off`, the Space has no `/data` bucket β€” it will still run on ephemeral `/tmp` (banner shows), but add a bucket at `/data` in Space Settings for durability.
---
## 3) Env vars β€” what to create
### 3.1 Essentials (app won't work without these)
Set in HF **Variables** unless marked **Secret**:
| Key | Where | Value / How |
|---|---|---|
| `APP_ALLOW_EMAIL` | Variables | Your Gmail exactly β€” only this can sign in (`middleware.ts:39`, `lib/auth.ts:14`) |
| `GOOGLE_CLIENT_ID` | Variables | From Google Cloud OAuth (see 4.1) |
| `GOOGLE_CLIENT_SECRET` | **Secrets** | Same OAuth client |
| `NEXTAUTH_SECRET` | **Secrets** | Random 32b hex: `openssl rand -hex 32` (already set) |
| `NEXTAUTH_URL` | Variables | `https://abc1181-leads.hf.space` (already set) |
| `PLACES_API_KEY` | **Secrets** | Google Places API key (see 4.2) β€” *or leave empty and use the free OSM/Ola rail (Section 4.3)* |
Also needed (already set, don't change): `DATABASE_URL=file:/data/leads.db`, `CRON_SECRET`, `ENCRYPTION_KEY`
### 3.2 Optional β€” add when you want more coverage
| Key | Where | Free tier | Notes |
|---|---|---|---|
| `OLA_API_KEY` | Secrets | 100k/API/mo free | India best free β€” `docs/maps-providers.md#5` |
| `MAPPLS_CLIENT_ID` + `MAPPLS_CLIENT_SECRET` | Secrets | Free eval, no card | India best address hit β€” `docs/maps-providers.md#4` |
| `FOURSQUARE_API_KEY` | Secrets | 500 free calls | Global fallback |
| `HERE_API_KEY` | Secrets | ~5k-250k free (needs card) | Global fallback |
| `ENRICH_PROVIDER=hunter` + `ENRICH_API_KEY` | Secrets | 25-50/mo free | Else phone is spine, emails stay `unknown` |
| `LLM_BASE_URL` + `LLM_API_KEY` + `LLM_MODEL=mistral-large-latest` | Secrets | Your gateway | Else fallback templates (`lib/ai/fallback.ts`) |
| `SMTP_HOST/PORT/USER/PASS` **or** `RESEND_API_KEY` + `DIGEST_TO` | Secrets/Vars | 100/d free | Else digest is in-app only |
| `MOCK_SOURCES=1` | Variables | β€” | Use for demo with 50 Delhi fixtures, no keys |
> **No billing?** Leave `PLACES_API_KEY` empty β€” the 6-provider rail (`lib/sources/maps.ts`) runs on **OSM Overpass (free, no key)** + `OLA_API_KEY`/`MAPPLS_*` if you set them. Add Google later and it stacks β€” no rebuild.
---
## 4) How to get each credential
### 4.1 Google OAuth (for Sign-in + Sheets)
1. https://console.cloud.google.com β†’ Create project β†’ **APIs & Services β†’ OAuth consent screen** β†’ External β†’ fill App name, Support email β†’ Save
2. **Credentials β†’ Create Credentials β†’ OAuth client ID** β†’ Web application β†’ Authorized redirect URI β†’ add: `https://abc1181-leads.hf.space/api/auth/callback/google` β†’ Create β†’ copy **Client ID** and **Client Secret** β†’ paste to HF as above.
### 4.2 Google Places API (New) β€” $200 free, needs billing
1. Same Cloud project β†’ **APIs & Services β†’ Enable APIs** β†’ search **Places API (New)** β†’ Enable
2. **Credentials β†’ Create Credentials β†’ API key** β†’ Restrict β†’ API restrictions β†’ **Places API (New)** only β†’ Save β†’ copy to `PLACES_API_KEY`.
3. Enable billing (Billing β†’ Link account) to get the $200 credit β€” you won't be charged inside free. Our use β‰ˆ $5-10 (`lib/sources/places.ts:50` caps to 60 + Details only for no-website). Quota bar turns amber at 80% and degrades to `partial`.
### 4.3 Free India maps (no billing) β€” Mappls + Ola
- **Mappls:** https://www.mapmyindia.com/api β†’ Sign up β†’ Dashboard β†’ Create project β†’ If you have a **Static/REST key** (your case), set `MAPPLS_API_KEY`; if you have OAuth pair, set `MAPPLS_CLIENT_ID` + `MAPPLS_CLIENT_SECRET` β€” either works. Free eval, no card.
- **Ola Maps:** https://maps.olakrutrim.com β†’ Sign up (Krutrim Cloud) β†’ Credentials β†’ API Key β†’ `OLA_API_KEY`. 100k/API/mo free from 2026-09-01. `GET https://api.olamaps.io/places/v1/nearbysearch?location=lat,lng&radius=&keyword=` header `X-API-KEY`.
### 4.4 Other free maps (optional)
- **Foursquare:** foursquare.com/developers β†’ My Apps β†’ Create App β†’ API Key β†’ `FOURSQUARE_API_KEY` (500 free).
- **HERE:** developer.here.com β†’ Project β†’ API Key β†’ `HERE_API_KEY` (needs card, pay-as-you-grow).
- **OSM Overpass:** No key. Always on at `https://overpass-api.de/api/interpreter`.
Full per-provider table: `docs/maps-providers.md`.
### 4.5 Enrich (emails, optional)
- Hunter: hunter.io β†’ API key (25 free) β†’ `ENRICH_API_KEY` with `ENRICH_PROVIDER=hunter`
- Apollo: app.apollo.io β†’ API key (50 free) β†’ `ENRICH_PROVIDER=apollo`
### 4.6 LLM (AI drafts, optional)
Any OpenAI-compatible gateway. Set `LLM_BASE_URL` (e.g. `https://api.mistral.ai/v1`), `LLM_API_KEY`, `LLM_MODEL=mistral-large-latest`. **Llama banned** on any provider (`lib/ai/llm.ts:44` β€” AUP). Prompts are PII-free (`lib/ai/prompts.ts:8`).
### 4.7 Email digest (optional)
- Gmail: App Password β†’ `SMTP_HOST=smtp.gmail.com`, `SMTP_PORT=587`, `SMTP_USER`, `SMTP_PASS`, `DIGEST_TO=you@gmail.com`
- Resend: resend.com β†’ API key β†’ `RESEND_API_KEY` (100/d free)
---
## 5) Run locally (optional)
```bash
cp .env.example .env.local # fill with your keys; use absolute DATABASE_URL
# MUST be absolute locally (Prisma CLI vs app resolve differently):
# DATABASE_URL="file:/abs/path/to/leads-app/data/leads.db"
npm install --registry https://registry.npmjs.org/
npx prisma migrate dev # creates data/leads.db
npm run dev # http://localhost:7860
npm test # 59 unit (lib 80% lines)
# integration (mock): INTEGRATION=1 MOCK_SOURCES=1 DATABASE_URL="file:$PWD/data/test.db" npx vitest run
```
---
## 6) How to use the app (morning loop)
1. **Searches β†’ New search** β†’ pick template (Health/Beauty, Food, B2B/Factory, MidMarket/Upgrade, Generic) β†’ city + radius (geocoded & cached, `lib/geo.ts:4`) β†’ keywords β†’ Cap 20/50/100/200 β†’ Create. Star it for nightly.
2. **Run search** β†’ progress header (per-provider ms/quota) β†’ tabs Hot/Warm/Cold (`lib/score.ts:65` threshold) β†’ table (filter by status/source/email/band/phone, sort score/rating) β†’ drawer (AI draft violet, Copy/Regenerate, notes, blocklist).
3. **Verify all** (concurrency 5, skips fresh <7d unless forced) → **Push to Sheets** (master `LeadGen Master`, tab per search, 21 cols, idempotent on email→phone→company|location via `lib/sheets.ts:rowKey`) → **CSV fallback** per run.
4. **Hot queue (`/hot`)** is the morning inbox β€” `j/k` move, `q` qualify, `c` copy icebreaker, `?` help.
5. **Dashboard** shows Hot/Warm/Cold donut, per-source yield, category intel (`Where the buyers are`), quotas ($0), recent runs. Bell shows unread Hot + quota warnings.
6. **Settings** β†’ Quotas, Keys presence (masked), Scoring weights + Re-score, Templates, Sheets connect/disconnect (OAuth sealed `lib/crypto.ts`), Blocklist (undo), Danger (purge caches).
**Nightly:** GitHub Actions `30 20 * * * UTC` (02:00 IST) pings `/api/health` 5Γ— then `POST /api/cron/nightly` with `CRON_SECRET` (`jobs/runner.ts:38` sequential, `roles` rolled if >25min). Digest top-10 Hot to `DIGEST_TO` (self only) + in-app bell.
---
## 7) Troubleshooting
- `https://abc1181-leads.hf.space/api/health` β†’ `ok:true, db:ok, persistence:on` means good. `persistence:off` = no bucket at `/data` β€” still runs on `/tmp` but data resets on restart (banner shows).
- Sign-in 500 β†’ `GOOGLE_*` or `APP_ALLOW_EMAIL` wrong, or redirect URI not set to `https://abc1181-leads.hf.space/api/auth/callback/google`.
- Search returns 0 / `partial` β†’ check quota bars in Settings; with no keys, OSM/Ola still work if those keys are set.
- Chrome "Deceptive site" β†’ fixed in `c029112` by making `/`, `/privacy`, `/terms` public (no auto-Google redirect). If still cached, request review at https://transparencyreport.google.com/safe-browsing/search?url=https://abc1181-leads.hf.space
---
## 8) Branding & cost
- Branding: **Made by AJ Β· personal tool** (sidebar/footer/landing, `components/app-shell.tsx:39`). No Vercel triangle.
- Cost: $0 target (`docs/runbook.md`). Quota bars prove it. 80% banners + auto-degrade, never spend.
Questions? See `PRD.md`/`TRD.md` checkboxes (210 ticked, 52 intentionally unticked for live/V1.1/V2/parked).