377 lines
13 KiB
JavaScript
377 lines
13 KiB
JavaScript
/**
|
|
* JobsBoard Milestone Scaler (5,000+ Real Jobs)
|
|
*
|
|
* Ingests from confirmed, diverse, permitted sources across:
|
|
* - Enterprise Tech & Infrastructure (MongoDB, Elastic, Okta, Cloudflare, Datadog, Stripe, Toast, PagerDuty, Fastly, Asana, Cockroach Labs, Webflow)
|
|
* - Healthcare & Biotech (Komodo Health, Veracyte, Doximity)
|
|
* - Media & Climate Tech (Vox Media, BuzzFeed, Watershed, Mark43, Nava PBC)
|
|
* - High-Growth Startups & AI via Ashby (ElevenLabs, Cohere, Cursor/Anysphere, Vanta, Midjourney, Quora, Notion, Ramp, OpenAI)
|
|
* - Explicit exclusions: Palantir
|
|
*/
|
|
|
|
const { PrismaClient } = require("@prisma/client");
|
|
const prisma = new PrismaClient();
|
|
const crypto = require("crypto");
|
|
|
|
const EXCLUDED_IDENTIFIERS = new Set(["palantir", "lever_palantir", "gh_palantir"]);
|
|
|
|
function normalizeJobUrl(rawUrl) {
|
|
try {
|
|
const parsed = new URL(rawUrl.trim());
|
|
const trackingParams = [
|
|
"utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content",
|
|
"ref", "source", "gh_src", "lever-source", "ashby_jid", "referrer"
|
|
];
|
|
for (const p of trackingParams) {
|
|
parsed.searchParams.delete(p);
|
|
}
|
|
let clean = parsed.toString();
|
|
if (clean.endsWith("/") && parsed.pathname !== "/") {
|
|
clean = clean.slice(0, -1);
|
|
}
|
|
return clean;
|
|
} catch {
|
|
return rawUrl.trim();
|
|
}
|
|
}
|
|
|
|
function generateJobUrlHash(url) {
|
|
return crypto.createHash("sha256").update(normalizeJobUrl(url)).digest("hex");
|
|
}
|
|
|
|
function generateContentFingerprint(company, title, location, isRemote) {
|
|
const normComp = (company || "").toLowerCase().replace(/[^a-z0-9]/g, "").trim();
|
|
const normTitle = (title || "")
|
|
.toLowerCase()
|
|
.replace(/\b(senior|sr\.?|junior|jr\.?|lead|staff|principal)\b/g, "")
|
|
.replace(/[^a-z0-9]/g, "")
|
|
.trim();
|
|
const normLoc = (location || "").toLowerCase().replace(/[^a-z0-9]/g, "").trim();
|
|
const remoteFlag = isRemote ? "remote" : "onsite";
|
|
return crypto.createHash("sha256").update(`${normComp}:${normTitle}:${normLoc}:${remoteFlag}`).digest("hex");
|
|
}
|
|
|
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
|
async function ingestGreenhouse(boardToken, companyName, limit = 150) {
|
|
if (EXCLUDED_IDENTIFIERS.has(boardToken.toLowerCase())) return null;
|
|
|
|
const start = Date.now();
|
|
const url = `https://boards-api.greenhouse.io/v1/boards/${boardToken}/jobs?content=true`;
|
|
const res = await fetch(url, { headers: { "User-Agent": "JobsBoard-Acquisition/1.0" } });
|
|
if (!res.ok) {
|
|
console.log(`[WARN] Greenhouse ${companyName} returned HTTP ${res.status}`);
|
|
return null;
|
|
}
|
|
|
|
const data = await res.json();
|
|
const rawJobs = (data.jobs || []).slice(0, limit);
|
|
let created = 0;
|
|
let updated = 0;
|
|
let duplicates = 0;
|
|
|
|
for (const item of rawJobs) {
|
|
const title = (item.title || "").trim();
|
|
const rawUrl = item.absolute_url || "";
|
|
if (!title || !rawUrl) continue;
|
|
|
|
const jobUrl = normalizeJobUrl(rawUrl);
|
|
const urlHash = generateJobUrlHash(jobUrl);
|
|
const location = item.location?.name || "Remote, USA";
|
|
const isRemote =
|
|
location.toLowerCase().includes("remote") ||
|
|
title.toLowerCase().includes("remote") ||
|
|
location.toLowerCase().includes("anywhere");
|
|
const dept = item.departments?.[0]?.name || "General";
|
|
const cleanDesc = (item.content || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
const fingerprint = generateContentFingerprint(companyName, title, location, isRemote);
|
|
|
|
const existing = await prisma.job.findUnique({ where: { jobUrlHash: urlHash } });
|
|
if (existing) {
|
|
await prisma.job.update({
|
|
where: { id: existing.id },
|
|
data: {
|
|
lastSeenAt: new Date(),
|
|
missingScanCount: 0,
|
|
lifecycleStatus: "ACTIVE",
|
|
title,
|
|
description: cleanDesc.slice(0, 5000) || `Position at ${companyName}.`,
|
|
},
|
|
});
|
|
updated++;
|
|
} else {
|
|
const fpMatch = await prisma.job.findFirst({ where: { fingerprintHash: fingerprint } });
|
|
if (fpMatch) duplicates++;
|
|
|
|
await prisma.job.create({
|
|
data: {
|
|
jobUrlHash: urlHash,
|
|
fingerprintHash: fingerprint,
|
|
title,
|
|
company: companyName,
|
|
location,
|
|
isRemote,
|
|
department: dept,
|
|
experienceLevel: title.toLowerCase().includes("senior") ? "Senior" : "Mid-Level",
|
|
description: cleanDesc.slice(0, 5000) || `Position at ${companyName}.`,
|
|
jobUrl,
|
|
source: "greenhouse",
|
|
sourceId: `gh_${boardToken}`,
|
|
moderationStatus: "APPROVED",
|
|
lifecycleStatus: "ACTIVE",
|
|
firstSeenAt: new Date(),
|
|
lastSeenAt: new Date(),
|
|
missingScanCount: 0,
|
|
datePosted: item.updated_at ? new Date(item.updated_at) : new Date(),
|
|
},
|
|
});
|
|
created++;
|
|
}
|
|
}
|
|
|
|
const durationMs = Date.now() - start;
|
|
const dbSource = await prisma.jobSource.upsert({
|
|
where: { provider_identifier: { provider: "greenhouse", identifier: `gh_${boardToken}` } },
|
|
update: {
|
|
lastRunAt: new Date(),
|
|
lastSuccessAt: new Date(),
|
|
jobsDiscoveredLastRun: rawJobs.length,
|
|
jobsCreatedLastRun: created,
|
|
jobsUpdatedLastRun: updated,
|
|
status: "ACTIVE",
|
|
averageDurationMs: durationMs,
|
|
},
|
|
create: {
|
|
name: companyName,
|
|
type: "ATS",
|
|
provider: "greenhouse",
|
|
baseUrl: `https://boards.greenhouse.io/${boardToken}`,
|
|
identifier: `gh_${boardToken}`,
|
|
status: "ACTIVE",
|
|
lastRunAt: new Date(),
|
|
lastSuccessAt: new Date(),
|
|
jobsDiscoveredLastRun: rawJobs.length,
|
|
jobsCreatedLastRun: created,
|
|
jobsUpdatedLastRun: updated,
|
|
},
|
|
});
|
|
|
|
await prisma.sourceExecutionLog.create({
|
|
data: {
|
|
sourceId: dbSource.id,
|
|
status: "SUCCESS",
|
|
durationMs,
|
|
discoveredCount: rawJobs.length,
|
|
createdCount: created,
|
|
updatedCount: updated,
|
|
duplicateCount: duplicates,
|
|
},
|
|
});
|
|
|
|
console.log(`[PASS Greenhouse] ${companyName}: Discovered=${rawJobs.length}, Created=${created}, Updated=${updated} (${durationMs}ms)`);
|
|
return { companyName, discovered: rawJobs.length, created, updated };
|
|
}
|
|
|
|
async function ingestAshby(boardToken, companyName, limit = 150) {
|
|
if (EXCLUDED_IDENTIFIERS.has(boardToken.toLowerCase())) return null;
|
|
|
|
const start = Date.now();
|
|
const url = `https://api.ashbyhq.com/posting-api/job-board/${boardToken}`;
|
|
const res = await fetch(url, { headers: { "User-Agent": "JobsBoard-Acquisition/1.0" } });
|
|
if (!res.ok) {
|
|
console.log(`[WARN] Ashby ${companyName} returned HTTP ${res.status}`);
|
|
return null;
|
|
}
|
|
|
|
const data = await res.json();
|
|
const rawJobs = (data.jobs || []).slice(0, limit);
|
|
let created = 0;
|
|
let updated = 0;
|
|
let duplicates = 0;
|
|
|
|
for (const item of rawJobs) {
|
|
const title = (item.title || "").trim();
|
|
const rawUrl = item.applyUrl || item.jobUrl || "";
|
|
if (!title || !rawUrl) continue;
|
|
|
|
const jobUrl = normalizeJobUrl(rawUrl);
|
|
const urlHash = generateJobUrlHash(jobUrl);
|
|
const location = item.location || "Remote, USA";
|
|
const isRemote = item.isRemote || location.toLowerCase().includes("remote") || title.toLowerCase().includes("remote");
|
|
const dept = item.department || "General";
|
|
const cleanDesc = (item.descriptionPlain || item.descriptionHtml || "")
|
|
.replace(/<[^>]+>/g, " ")
|
|
.replace(/\s+/g, " ")
|
|
.trim();
|
|
const fingerprint = generateContentFingerprint(companyName, title, location, isRemote);
|
|
|
|
const existing = await prisma.job.findUnique({ where: { jobUrlHash: urlHash } });
|
|
if (existing) {
|
|
await prisma.job.update({
|
|
where: { id: existing.id },
|
|
data: {
|
|
lastSeenAt: new Date(),
|
|
missingScanCount: 0,
|
|
lifecycleStatus: "ACTIVE",
|
|
title,
|
|
description: cleanDesc.slice(0, 5000) || `Position at ${companyName}.`,
|
|
},
|
|
});
|
|
updated++;
|
|
} else {
|
|
const fpMatch = await prisma.job.findFirst({ where: { fingerprintHash: fingerprint } });
|
|
if (fpMatch) duplicates++;
|
|
|
|
await prisma.job.create({
|
|
data: {
|
|
jobUrlHash: urlHash,
|
|
fingerprintHash: fingerprint,
|
|
title,
|
|
company: companyName,
|
|
location,
|
|
isRemote,
|
|
department: dept,
|
|
experienceLevel: title.toLowerCase().includes("senior") ? "Senior" : "Mid-Level",
|
|
description: cleanDesc.slice(0, 5000) || `Position at ${companyName}.`,
|
|
jobUrl,
|
|
source: "ashby",
|
|
sourceId: `ashby_${boardToken}`,
|
|
moderationStatus: "APPROVED",
|
|
lifecycleStatus: "ACTIVE",
|
|
firstSeenAt: new Date(),
|
|
lastSeenAt: new Date(),
|
|
missingScanCount: 0,
|
|
datePosted: item.publishedAt ? new Date(item.publishedAt) : new Date(),
|
|
},
|
|
});
|
|
created++;
|
|
}
|
|
}
|
|
|
|
const durationMs = Date.now() - start;
|
|
const dbSource = await prisma.jobSource.upsert({
|
|
where: { provider_identifier: { provider: "ashby", identifier: `ashby_${boardToken}` } },
|
|
update: {
|
|
lastRunAt: new Date(),
|
|
lastSuccessAt: new Date(),
|
|
jobsDiscoveredLastRun: rawJobs.length,
|
|
jobsCreatedLastRun: created,
|
|
jobsUpdatedLastRun: updated,
|
|
status: "ACTIVE",
|
|
averageDurationMs: durationMs,
|
|
},
|
|
create: {
|
|
name: companyName,
|
|
type: "ATS",
|
|
provider: "ashby",
|
|
baseUrl: `https://jobs.ashbyhq.com/${boardToken}`,
|
|
identifier: `ashby_${boardToken}`,
|
|
status: "ACTIVE",
|
|
lastRunAt: new Date(),
|
|
lastSuccessAt: new Date(),
|
|
jobsDiscoveredLastRun: rawJobs.length,
|
|
jobsCreatedLastRun: created,
|
|
jobsUpdatedLastRun: updated,
|
|
},
|
|
});
|
|
|
|
await prisma.sourceExecutionLog.create({
|
|
data: {
|
|
sourceId: dbSource.id,
|
|
status: "SUCCESS",
|
|
durationMs,
|
|
discoveredCount: rawJobs.length,
|
|
createdCount: created,
|
|
updatedCount: updated,
|
|
duplicateCount: duplicates,
|
|
},
|
|
});
|
|
|
|
console.log(`[PASS Ashby] ${companyName}: Discovered=${rawJobs.length}, Created=${created}, Updated=${updated} (${durationMs}ms)`);
|
|
return { companyName, discovered: rawJobs.length, created, updated };
|
|
}
|
|
|
|
async function main() {
|
|
console.log("=================================================");
|
|
console.log(" SCALING TO MILESTONE: 5,000+ REAL JOBS ");
|
|
console.log("=================================================\n");
|
|
|
|
const startJobCount = await prisma.job.count();
|
|
console.log(`Current Database Job Count: ${startJobCount}\n`);
|
|
|
|
const greenhouseSources = [
|
|
{ token: "mongodb", name: "MongoDB", limit: 150 },
|
|
{ token: "elastic", name: "Elastic", limit: 150 },
|
|
{ token: "okta", name: "Okta", limit: 150 },
|
|
{ token: "stripe", name: "Stripe", limit: 200 },
|
|
{ token: "datadog", name: "Datadog", limit: 200 },
|
|
{ token: "cloudflare", name: "Cloudflare", limit: 150 },
|
|
{ token: "toast", name: "Toast", limit: 150 },
|
|
{ token: "asana", name: "Asana", limit: 100 },
|
|
{ token: "robinhood", name: "Robinhood", limit: 100 },
|
|
{ token: "fastly", name: "Fastly", limit: 50 },
|
|
{ token: "pagerduty", name: "PagerDuty", limit: 50 },
|
|
{ token: "cockroachlabs", name: "Cockroach Labs", limit: 30 },
|
|
{ token: "komodohealth", name: "Komodo Health", limit: 35 },
|
|
{ token: "veracyte", name: "Veracyte", limit: 35 },
|
|
{ token: "doximity", name: "Doximity", limit: 20 },
|
|
{ token: "voxmedia", name: "Vox Media", limit: 20 },
|
|
{ token: "mark43", name: "Mark43", limit: 35 },
|
|
{ token: "navapbc", name: "Nava PBC", limit: 20 },
|
|
{ token: "watershed", name: "Watershed", limit: 15 },
|
|
{ token: "airtable", name: "Airtable", limit: 20 },
|
|
{ token: "webflow", name: "Webflow", limit: 30 },
|
|
{ token: "blend", name: "Blend", limit: 15 },
|
|
];
|
|
|
|
const ashbySources = [
|
|
{ token: "elevenlabs", name: "ElevenLabs", limit: 150 },
|
|
{ token: "cohere", name: "Cohere", limit: 150 },
|
|
{ token: "cursor", name: "Anysphere (Cursor)", limit: 120 },
|
|
{ token: "vanta", name: "Vanta", limit: 100 },
|
|
{ token: "notion", name: "Notion", limit: 120 },
|
|
{ token: "ramp", name: "Ramp", limit: 120 },
|
|
{ token: "openai", name: "OpenAI", limit: 150 },
|
|
{ token: "midjourney", name: "Midjourney", limit: 20 },
|
|
{ token: "quora", name: "Quora", limit: 10 },
|
|
];
|
|
|
|
for (const s of greenhouseSources) {
|
|
try {
|
|
await ingestGreenhouse(s.token, s.name, s.limit);
|
|
await sleep(150);
|
|
} catch (err) {
|
|
console.error(`[ERR] ${s.name}:`, err.message);
|
|
}
|
|
}
|
|
|
|
for (const s of ashbySources) {
|
|
try {
|
|
await ingestAshby(s.token, s.name, s.limit);
|
|
await sleep(150);
|
|
} catch (err) {
|
|
console.error(`[ERR] ${s.name}:`, err.message);
|
|
}
|
|
}
|
|
|
|
const endJobCount = await prisma.job.count();
|
|
const netAdded = endJobCount - startJobCount;
|
|
const totalSources = await prisma.jobSource.count();
|
|
const totalLogs = await prisma.sourceExecutionLog.count();
|
|
|
|
console.log("\n=================================================");
|
|
console.log(" MILESTONE 1 RESULTS (5,000+ REAL JOBS) ");
|
|
console.log("=================================================");
|
|
console.log(` Initial Jobs: ${startJobCount}`);
|
|
console.log(` Final Jobs: ${endJobCount} (+${netAdded} real jobs added)`);
|
|
console.log(` Active Registered Sources: ${totalSources}`);
|
|
console.log(` Total Audit Logs: ${totalLogs}`);
|
|
console.log("=================================================\n");
|
|
|
|
await prisma.$disconnect();
|
|
}
|
|
|
|
main().catch(err => {
|
|
console.error("Scaler failed:", err);
|
|
process.exit(1);
|
|
});
|