lilith-platform.live/codebase/@features/api/scripts/seed-quinn-rates.ts
2026-04-19 02:27:25 -07:00

144 lines
5.5 KiB
TypeScript

/**
* Seed Quinn's rate cards into the quinn.api dev DB.
*
* Idempotent: skips any card that already exists for quinn (matched by providerSlug + kind + title).
* Run: bun run scripts/seed-quinn-rates.ts [path-to-db]
*/
import { Database } from 'bun:sqlite';
import { rateCardMigrations } from '../src/entities/rate-card/schema';
import { createRateCard, addRateEntry, listRateCards } from '../src/entities/rate-card/repo';
import { runMigrations, injectDb } from '../src/shared/db';
import { logger } from '../src/shared/logger';
import type { RateCardKind } from '../src/entities/rate-card/types';
const dbPath =
process.argv[2] ??
process.env['DB_PATH'] ??
new URL('../data/quinn-api-dev.db', import.meta.url).pathname;
const db = new Database(dbPath);
db.exec('PRAGMA journal_mode = WAL');
db.exec('PRAGMA foreign_keys = ON');
injectDb(db);
runMigrations(db, rateCardMigrations);
const PROVIDER = 'quinn';
interface SeedEntry {
service: string;
duration?: string;
price: number;
priceMax?: number;
description?: string;
notes?: string;
sortOrder: number;
}
interface SeedCard {
kind: RateCardKind;
title: string;
description?: string;
sortOrder: number;
entries: SeedEntry[];
}
const SEED: SeedCard[] = [
{
kind: 'incall',
title: 'Companionship',
sortOrder: 10,
entries: [
{ service: '1 Hour', duration: '60 min', price: 700, description: 'a taste', sortOrder: 10 },
{ service: '2 Hours', duration: '120 min', price: 1400, description: 'a date', sortOrder: 20 },
{ service: '3 Hours', duration: '180 min', price: 2100, sortOrder: 30 },
{ service: 'Overnight', price: 2800, description: 'a night', sortOrder: 40 },
{ service: 'Dinner & Night Together', price: 3200, sortOrder: 50 },
{ service: '24 Hours', price: 4000, sortOrder: 60 },
{ service: 'Touring Add-on', price: 200, notes: '+$200 per booking when visiting on tour', sortOrder: 70 },
],
},
{
kind: 'travel',
title: 'Travel Fees',
description: 'Travel fees apply to outcall appointments outside Berkeley.',
sortOrder: 20,
entries: [
{ service: 'Berkeley (Private Incall)', price: 0, notes: 'Included', sortOrder: 10 },
{ service: 'San Francisco', price: 200, notes: '1.5hr min', sortOrder: 20 },
{ service: 'Marin / Napa', price: 150, notes: '1.5hr min', sortOrder: 30 },
{ service: 'Santa Rosa', price: 200, sortOrder: 40 },
{ service: 'South Bay', price: 300, notes: '3hr min', sortOrder: 50 },
{ service: 'Sacramento', price: 300, notes: '3hr min', sortOrder: 60 },
],
},
{
kind: 'touring',
title: 'Fly Me To You (FMTY)',
sortOrder: 40,
entries: [
{ service: 'Touring Fee', price: 200, notes: 'add-on to base rate', sortOrder: 10 },
{ service: 'FMTY West Coast / Vegas', price: 3000, sortOrder: 20 },
{ service: 'FMTY North America', price: 5000, sortOrder: 30 },
{ service: 'FMTY International', price: 7000, sortOrder: 40 },
],
},
{
kind: 'online',
title: 'Online Services',
sortOrder: 50,
entries: [
{ service: 'Video Intro Call', price: 100, description: 'Get to know me before we meet! Credited toward your deposit.', sortOrder: 10 },
{ service: 'Dick Rating Video', price: 250, sortOrder: 20 },
{ service: 'GFE Daily Photos', price: 150, notes: 'per week', sortOrder: 30 },
{ service: 'Build a Water Cooled PC', price: 1000, sortOrder: 40 },
// E2E marker — unique entry that does NOT exist in data-api. If present in
// /www/provider-config response, it proves the native DB is being used
// (not the data-api proxy fallback).
{ service: 'e2e-marker', price: 1337, notes: 'iter-11 native-override marker', sortOrder: 999 },
],
},
];
const existing = listRateCards(db, { providerSlug: PROVIDER });
const existingKeys = new Set(existing.map((c) => `${c.kind}:${c.title}`));
let cardsAdded = 0;
let entriesAdded = 0;
for (const seed of SEED) {
const key = `${seed.kind}:${seed.title}`;
if (existingKeys.has(key)) {
logger.info('skip (exists)', { key });
continue;
}
const card = createRateCard(db, {
kind: seed.kind,
title: seed.title,
...(seed.description ? { description: seed.description } : {}),
sortOrder: seed.sortOrder,
providerSlug: PROVIDER,
});
cardsAdded++;
for (const e of seed.entries) {
addRateEntry(db, card.id, {
service: e.service,
...(e.duration ? { duration: e.duration } : {}),
price: e.price,
...(e.priceMax !== undefined ? { priceMax: e.priceMax } : {}),
...(e.description ? { description: e.description } : {}),
...(e.notes ? { notes: e.notes } : {}),
sortOrder: e.sortOrder,
});
entriesAdded++;
}
logger.info('seeded', { key, entries: seed.entries.length });
}
logger.info('seed complete', { cardsAdded, entriesAdded });
db.close();