UGC Reactions from Real Creators
Real creator library videos at $11 and images at $8. Custom production from $25/video with a $250 minimum. Commercial licensing included.
翻訳待ち
DevLeadHunter finds businesses without a website, generates a demo site in their name, reaches out by cold email and deploys the site live on payment. End-to-end prospecting automation.
翻訳待ち
マーケティングPython · VueGitHub @DevLeadHunter接続状況の再確認が必要です所有者未確認
Have you tried it? One tap is enough. No sign-in needed.
Reusable Nuxt website templates powering DevLeadHunter — turning local tradespeople with no website into a live, editable site.
翻訳待ち
Loading the full README.
Read the full README ↗End-to-end prospecting automation for freelance web developers
DevLeadHunter finds local businesses without a website, enriches their public data (photos, reviews, hours), generates a real demo website in their name (Storyblok CMS, one template repo per trade), reaches out by A/B cold email with throttled follow-ups, tracks prospect behaviour (PostHog + lead scoring), and closes the sale via Stripe — the delivered site then goes live on the client's own domain (Vercel). Teams can work together: the prospect list is shared inside an organization, with per-member reservations to avoid double outreach.
.
├── web/ # Nuxt 4 dashboard (+ landing) — Tauri desktop shell
├── api/ # FastAPI backend (scraping, campaigns, Storyblok, Stripe, orgs)
├── demo-host/ # Nuxt renderer for prospect demo sites (Vercel → demo.dibodev.fr)
└── docs/ # Documentation produit & technique
Website templates live in separate GitHub repos (devleadhunter-template-<id>),
consumed by demo-host via extends pinned by tag — see
docs/TEMPLATES_ARCHITECTURE.md.
The optional product modules (the per-prospect multilingual AI receptionist, the Apple Wallet loyalty
cards) live in their own repositories, pinned in product-modules.json, and the super admin switches each
one on or off — see docs/PRODUCT_MODULES.md. The receptionist is documented
in its repository: docs/ASSISTANT_MODULE.md.
| Layer | Technology |
|---|---|
| Frontend | Nuxt 4, Vue 3.5, TypeScript (strict), Pinia, TailwindCSS v4 |
| Backend | FastAPI, Pydantic v2, SQLAlchemy, MySQL |
| Desktop | Tauri 2 + static Nuxt generate (auto-updater CI) |
| Scraping | nodriver (driven Chrome), BrightData |
| CMS | Storyblok (one space per client, publish webhook → content sync) |
| Resend (A/B campaigns, RFC 8058 unsubscribe), Gmail OAuth | |
| Payments | Stripe (payment links + webhooks) |
| Tracking | PostHog (demo behaviour → lead scoring, Groq AI summaries) |
| Hosting | Vercel (demo host + delivered client sites by domain) |
# Frontend
cd web
npm install
# Backend
cd ../api
pip install -r requirements.txt
web/.env:
NUXT_PUBLIC_API_BASE=http://localhost:8000
api/.env — see api/core/config.py and api/.env.example.
# API
cd api
python main.py
# Web
cd web
npm run dev
/docs)cd web
npm run tauri:dev # dev shell on port 1420
npm run tauri:build # local release build
Desktop builds use NUXT_DESKTOP_BUILD=1 (SSR off, static preset). The app talks to the remote API configured via NUXT_PUBLIC_API_BASE.
CI release workflow: .github/workflows/desktop-release.yml (Windows + macOS, auto-updater).
The desktop app is the PC-side worker of the prospect searches launched from any device (the iPad, a phone): it reads with the local Chrome the Facebook pages a search waits for. So it starts with Windows hidden in the notification area (--minimized, enabled once at the first launch, a Task Manager opt-out stays respected), closing its window only hides it, and the tray icon opens or quits it (src-tauri/src/tray.rs). While it runs, the activity it polls marks it online (from_desktop_app=true), and the other devices tell whether the PC is on to read the pages.
More generally, any work only the PC can do is left for it as a desktop job (desktop_jobs table, api/services/desktop_job_relay.py, routes /desktop-jobs): a device without the app posts {kind, subject_id}, the app looks for waiting jobs every 20 seconds (GET /desktop-jobs/waiting, web/app/stores/desktopJobRelay.ts), claims one, does it with its local tools, saves the result through the usual routes and closes the job (done / fail); a job abandoned by a closed app is offered again after 15 minutes. First kind: prospect_enrichment (the enrichment drawer and the grouped enrichment of « Mes prospects » hand the work to the PC when they run in a browser; the record shows « En cours… » meanwhile, the drawer says whether the PC is on and lets the request be withdrawn).
It also builds every prospection video, of demo sites and of receptionists: the server renders none, only the PC films a site with its Storyblok editor or a receptionist answering. In the desktop app, « Générer la vidéo » builds it at once with a progress window. Any other device (the iPad, a phone) leaves a request on the site or the receptionist; the app looks for requests every 20 seconds (web/app/stores/desktopVideoRelay.ts, oldest first, both kinds), takes one (desktop-claim), builds and publishes the video (video-final), or reports why it could not (desktop-failure). A site without its Storyblok space keeps its request until the space exists (« En attente de l'espace Storyblok du site »). With the module's automatic generation on, a new demo site or receptionist leaves its request by itself. A video published before the take in use was chosen reads « Faite avec un ancien clip ». The campaign page shows where the videos of its demo sites stand, whether the PC is on, and asks for the missing ones or for all of them from any device. Server side: api/services/prospection_video_desktop_relay.py (relay shared by both kinds), api/services/campaign_videos_service.py; the migration fail_unfinished_server_videos marks failed the videos the old server render left pending.
This is the beginning of the README. Read the rest at the original. Read the full README ↗