Open-Source Cold Email & Warmup Platform
Warmbly 是开源的冷邮件平台:温暖您的邮箱、发送活动、并对每条回复进行分流。可在我们的云端运行,也可自行托管整套系统。
AI 翻译
尚未收集仓库信息。
尚未检测到更新。GitHub 发布版本后会显示在这里。
制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Find US business leads and manage them in a lightweight CRM.
LeadScout is a hosted SaaS (Next.js) plus a CLI that collects US business leads, scrapes contact details from each business's website, and exports everything to CSV or Excel.
Each account has its own lead pipeline at /app:
lib/crm.ts).backfillLegacyLeads in lib/leads.ts).Not built yet; the CRM comes first. Notes for when this becomes a priority:
email_accounts, plus email_templates,
campaigns, campaign_recipients, email_messages, and suppression_list. Sends
and replies should write to lead_activities and move leads to Contacted/Replied.Not live yet. Premium is granted by an admin on /admin; nobody can pay in the app.
The code from the credit-pack era is still in place (app/api/billing/checkout,
app/api/webhooks/paystack, lib/paystack.ts, lib/payments.ts, the payments
table, and the packs in lib/plans.ts), but credits no longer gate searches.
To do when payments become a priority:
lib/plans.ts,
the checkout route, and the webhook to grant plan = "premium" instead of credits.PAYSTACK_SECRET_KEY / NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY
in Vercel (Production only), and set the Paystack webhook URL to
https://leadscout.nocodeziggy.com/api/webhooks/paystack. The checkout callback uses
NEXT_PUBLIC_APP_URL.| Column | Description |
|---|---|
| Business Name | Name of the business |
| Niche | Friendly niche label (e.g. "Dental Practice") |
| State / City | City and state for the business |
| Phone / Website / Email | Public contact info, plus emails scraped from the website |
| Address | Best-effort full street address |
| Decision Maker | Top LinkedIn match (when SerpApi is configured) |
CRM exports also include Status, Saved, Contact Name, and Follow Up.
/admin) pick niches, states, and a limit, with no automatic daily cap.POST /api/search validates the request, inserts a queued row in searches, and
sends an Inngest event. It returns at once.lib/inngest/searchJob.ts) splits the search into state/niche
steps and runs 3 at a time (SEARCH_PARALLEL_NICHES). Each step queries Overpass,
scrapes websites, and saves its leads under a row lock on the job
(saveStep in lib/searchJobs.ts), so the daily cap and dedupe stay exact.GET /api/searches/:id. Closing the panel or the tab changes nothing.ALLOWED_COUNTRIES=NG (needs a country header from your host/CDN, e.g. Cloudflare).npm install
copy .env.example .env # then fill in values (see below)
# Create the database tables (drizzle-kit reads DATABASE_URL from .env.local)
npm run db:migrate
# Run the app, plus the Inngest dev server in a second terminal (set INNGEST_DEV=1)
npm run dev # http://localhost:3000
npx inngest-cli@latest dev -u http://localhost:3000/api/inngest
Required env vars (see .env.example for the full list):
| Variable | Where to get it |
|---|---|
DATABASE_URL | Free Postgres from neon.tech or Supabase |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY / CLERK_SECRET_KEY | Free app at clerk.com (dev can run keyless) |
PAYSTACK_SECRET_KEY / NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY | dashboard.paystack.com — use test keys first |
NEXT_PUBLIC_APP_URL | Your deployed URL (used for the Paystack callback) |
For Paystack webhooks, set the webhook URL in the Paystack dashboard to
https://<your-domain>/api/webhooks/paystack.
INNGEST_EVENT_KEY and INNGEST_SIGNING_KEY.npm run db:migrate against the production database before deploying new
migrations.master. In the Inngest dashboard, check the app is synced at
https://<your-domain>/api/inngest.Health check: GET /api/health.
# Default: dentist/salon/plumber/restaurant across all US states -> leads.xlsx
npm run cli
# Specific niches + states, CSV output
node src/cli.js --niches hvac,plumber,electrician --states US-TX,US-FL --limit 500 --out trades.csv
# See every available niche
node src/cli.js --list-niches
| Flag | Default | Description |
|---|---|---|
-n, --niches <list> | dentist,salon,plumber,restaurant | Comma-separated niche keys (--list-niches) |
-s, --states <list> | US | US/ALL for every state, or US-CA, CA, or California |
-l, --limit <number> | 2000 | Max total leads to collect |
-o, --out <path> | leads.xlsx | Output file path |
-f, --format <fmt> | inferred | csv or xlsx |
--delay <ms> | 1200 | Delay between Overpass queries (be polite) |
--no-enrich | off | Skip auto ratings enrichment even if SERPAPI_KEY is set |
The legacy localhost dashboard is still available via npm run web:legacy.
With SERPAPI_KEY in .env, the CLI adds Google ratings and review counts. The web
app keeps ratings off (SerpApi bills in USD, which doesn't fit the NGN price points)
but uses the same key for decision-maker lookups.
app/ # Next.js App Router (landing, dashboard, API routes)
api/search/ # queue a background search (returns at once)
api/searches/ # search history, job status/cancel, unseen result popup
api/inngest/ # Inngest endpoint that runs search steps
api/leads/ # CRM: list, bulk update/delete, per-lead detail, notes, export
api/billing/ # Paystack checkout
api/webhooks/ # Paystack webhook (HMAC-verified)
components/ # React components (dashboard, marketing)
lib/ # db (Drizzle), CRM (crm.ts, leads.ts), quota, paystack, plans, overpass cache
drizzle/ # generated SQL migrations
proxy.ts # Clerk auth + optional country gate
src/ # core lead engine (shared by web + CLI, plain ESM JS)
cli.js # CLI entry
runSearch.js # shared search pipeline
overpass.js # Overpass QL builder + fetch (retry, mirrors, optional cache)
parse.js # raw result -> lead record, dedupe
output.js # CSV / XLSX writers
server.js # legacy localhost dashboard (npm run web:legacy)
public/ # legacy dashboard assets