TypeScript 97.5%
SQL 1.4%
Python 0.8%
1import { getDb, closeDb } from "./index";2import { providerConfigs, featureFlags, organizations, signupAllowlist, users } from "./schema";3import { and, eq, ne, sql } from "drizzle-orm";45/** Emails from `ADMIN_EMAILS` (comma-separated, case-insensitive). */6function adminEmails(): string[] {7 return Array.from(8 new Set(9 (process.env.ADMIN_EMAILS ?? "")10 .split(",")11 .map((s) => s.trim().toLowerCase())12 .filter(Boolean),13 ),14 );15}1617async function main() {18 const db = getDb();19 const providers: Array<{ id: string; label: string; networks: string[]; pricePerGbUsd: Record<string, number>; weight: number; maxConcurrency: number }> = [20 { id: "oxylabs", label: "Network A (residential)", networks: ["residential"], pricePerGbUsd: { residential: 8 }, weight: 1, maxConcurrency: 300 },21 { id: "decodo", label: "Network B (residential)", networks: ["residential"], pricePerGbUsd: { residential: 7 }, weight: 1, maxConcurrency: 300 },22 { id: "soax", label: "Network C (residential, mobile)", networks: ["residential", "mobile"], pricePerGbUsd: { residential: 9, mobile: 15 }, weight: 0.8, maxConcurrency: 200 },23 ];24 for (const p of providers) {25 await db26 .insert(providerConfigs)27 .values(p)28 .onConflictDoUpdate({ target: providerConfigs.id, set: { label: p.label, networks: p.networks, pricePerGbUsd: p.pricePerGbUsd, updatedAt: sql`now()` } });29 }3031 const flags = [32 { key: "browser_enabled", enabled: true, description: "Managed browser rendering (live)" },33 { key: "extract_enabled", enabled: false, description: "Structured extraction endpoint /v1/extract" },34 { key: "mobile_proxy_enabled", enabled: false, description: "Mobile network class" },35 { key: "organizations_enabled", enabled: false, description: "Team organizations UI" },36 { key: "billing_enabled", enabled: false, description: "Stripe checkout & subscriptions" },37 ];38 for (const f of flags) {39 await db.insert(featureFlags).values(f).onConflictDoNothing();40 }4142 // Single-plan platform: defensively normalize any legacy plan value (migration 0001 already does this).43 const normalized = await db.update(organizations).set({ plan: "unlimited", updatedAt: sql`now()` }).where(ne(organizations.plan, "unlimited")).returning({ id: organizations.id });44 if (normalized.length) console.log(`[db] ${normalized.length} organization(s) moved to plan=unlimited`);4546 // Administrators: allowlisted + promoted (idempotent).47 const admins = adminEmails();48 if (!admins.length) {49 console.warn("[db] ADMIN_EMAILS is empty — no administrator allowlisted or promoted");50 }51 for (const email of admins) {52 await db.insert(signupAllowlist).values({ email, note: "administrator" }).onConflictDoNothing();53 const promoted = await db54 .update(users)55 .set({ role: "admin", updatedAt: sql`now()` })56 .where(and(sql`lower(${users.email}) = ${email}`, ne(users.role, "admin")))57 .returning({ id: users.id });58 if (promoted.length) console.log(`[db] promoted ${email} to admin`);59 const [existing] = await db.select({ id: users.id, createdAt: users.createdAt }).from(users).where(sql`lower(${users.email}) = ${email}`).limit(1);60 if (existing) {61 await db62 .update(signupAllowlist)63 .set({ userId: existing.id, usedAt: sql`coalesce(${signupAllowlist.usedAt}, ${existing.createdAt})` })64 .where(eq(signupAllowlist.email, email));65 }66 console.log(`[db] admin ${email}: allowlisted${existing ? ", account linked" : ", no account yet"}`);67 }6869 // v0.2: the managed browser is live. Rows seeded by v0.1 (still carrying the old description) are70 // switched on once; an admin toggling the flag off afterwards is respected on later seeds.71 await db.execute(sql`update feature_flags set enabled = true, description = 'Managed browser rendering (live)', updated_at = now() where key = 'browser_enabled' and description = 'Managed browser execution (/v1/fetch browser=true)'`);72 console.log("[db] seed ok");73 await closeDb();74}7576main().catch((e) => {77 console.error(e);78 process.exit(1);79});80