SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
4.0 KB · 80 lines typescript
Raw Blame History
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