Initial commit: LeadFlow lead generation platform

Full-stack Next.js 16 app with three scraping pipelines:
- AirScale CSV → Anymailfinder Bulk Decision Maker search
- LinkedIn Sales Navigator → Vayne → Anymailfinder email enrichment
- Apify Google SERP → domain extraction → Anymailfinder bulk enrichment

Includes Docker multi-stage build + docker-compose for Coolify deployment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Timo Uttenweiler
2026-03-17 11:21:11 +01:00
parent 5b84001c1e
commit facf8c9f69
59 changed files with 5800 additions and 233 deletions

View File

@@ -0,0 +1,51 @@
import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/db";
import { encrypt, decrypt } from "@/lib/utils/encryption";
const SERVICES = ["anymailfinder", "apify", "vayne", "airscale"] as const;
export async function GET() {
try {
const creds = await prisma.apiCredential.findMany();
const result: Record<string, boolean> = {};
for (const svc of SERVICES) {
result[svc] = creds.some(c => c.service === svc && c.value);
}
return NextResponse.json(result);
} catch (err) {
console.error("GET /api/credentials error:", err);
return NextResponse.json({ anymailfinder: false, apify: false, vayne: false, airscale: false });
}
}
export async function POST(req: NextRequest) {
try {
const body = await req.json() as Record<string, string>;
for (const [service, value] of Object.entries(body)) {
if (!SERVICES.includes(service as typeof SERVICES[number])) continue;
if (value === null || value === undefined) continue;
await prisma.apiCredential.upsert({
where: { service },
create: { service, value: value ? encrypt(value) : "" },
update: { value: value ? encrypt(value) : "" },
});
}
return NextResponse.json({ ok: true });
} catch (err) {
console.error("POST /api/credentials error:", err);
return NextResponse.json({ error: "Failed to save credentials" }, { status: 500 });
}
}
// GET a specific decrypted value (for internal API use only)
export async function PUT(req: NextRequest) {
try {
const { service } = await req.json() as { service: string };
const cred = await prisma.apiCredential.findUnique({ where: { service } });
if (!cred) return NextResponse.json({ value: null });
return NextResponse.json({ value: decrypt(cred.value) });
} catch (err) {
console.error("PUT /api/credentials error:", err);
return NextResponse.json({ error: "Failed" }, { status: 500 });
}
}