UNPKG

longcelot-sheet-db

Version:

Google Sheets-backed staging database adapter for Node.js with schema-first design

205 lines 8.43 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.seedCommand = seedCommand; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const chalk_1 = __importDefault(require("chalk")); const sheetAdapter_1 = require("../../adapter/sheetAdapter"); const actorConfig_1 = require("../../utils/actorConfig"); const cliFiles_1 = require("../../utils/cliFiles"); async function loadSeedData(seedFilePath) { const mod = require(seedFilePath); const exported = mod.default ?? mod; if (typeof exported === 'function') { return await exported(process.env); } return exported; } async function seedCommand(seedFile, opts) { console.log(chalk_1.default.blue.bold('🌱 Seeding data into Google Sheets...\n')); require('dotenv').config(); const requiredEnvVars = [ 'GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', 'GOOGLE_REDIRECT_URI', 'ADMIN_SHEET_ID', ]; for (const envVar of requiredEnvVars) { if (!process.env[envVar]) { console.error(chalk_1.default.red(`❌ Missing environment variable: ${envVar}`)); process.exit(1); } } // Load tokens const tokensPath = (0, cliFiles_1.resolveTokensPath)(); if (!fs_1.default.existsSync(tokensPath)) { console.error(chalk_1.default.red('❌ No OAuth tokens found. Run: lsdb sync')); process.exit(1); } let tokens; try { tokens = JSON.parse(fs_1.default.readFileSync(tokensPath, 'utf-8')); } catch { console.error(chalk_1.default.red('❌ Failed to read tokens file.')); process.exit(1); } // Load seed file const seedFilePath = path_1.default.resolve(process.cwd(), seedFile); if (!fs_1.default.existsSync(seedFilePath)) { console.error(chalk_1.default.red(`❌ Seed file not found: ${seedFilePath}`)); process.exit(1); } let seedData; try { seedData = await loadSeedData(seedFilePath); } catch (err) { console.error(chalk_1.default.red(`❌ Failed to load seed file: ${err}`)); process.exit(1); } let config; try { config = require((0, cliFiles_1.resolveConfigPath)()).default; } catch { console.error(chalk_1.default.red('❌ lsdb.config.ts not found. Run: lsdb init')); process.exit(1); } const adapter = (0, sheetAdapter_1.createSheetAdapter)({ adminSheetId: process.env.ADMIN_SHEET_ID, credentials: { clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, redirectUri: process.env.GOOGLE_REDIRECT_URI, }, tokens, }); // Load and register all schemas const schemasDir = path_1.default.join(process.cwd(), 'schemas'); for (const actorEntry of config.actors) { const actor = (0, actorConfig_1.resolveActorName)(actorEntry); const actorDir = path_1.default.join(schemasDir, actor); if (!fs_1.default.existsSync(actorDir)) continue; const files = fs_1.default.readdirSync(actorDir).filter((f) => f.endsWith('.ts')); for (const file of files) { try { const schema = require(path_1.default.join(actorDir, file)).default; adapter.registerSchema(schema); } catch { } } } // Use admin context for seeding const adapterWithContext = adapter.withContext({ userId: 'seed-cli', actor: 'admin', actorSheetId: process.env.ADMIN_SHEET_ID }); const isSkipExisting = opts?.skipExisting ?? false; const isUpsert = opts?.upsert ?? false; async function seedTable(ctx, tableName, records) { let inserted = 0; let skipped = 0; let updated = 0; let failed = 0; for (const record of records) { try { const row = record; if (isUpsert) { // Determine unique column(s) for upsert — use _id if present, else first provided key const whereKey = Object.keys(row)[0]; const where = { [whereKey]: row[whereKey] }; await ctx.table(tableName).upsert({ where, data: row, skipFKValidation: true }); updated++; } else if (isSkipExisting) { // Try create; skip on unique constraint violation try { await ctx.table(tableName).create(row, { skipFKValidation: true }); inserted++; } catch (err) { if (err instanceof Error && err.message.includes('Unique constraint')) { skipped++; } else { throw err; } } } else { await ctx.table(tableName).create(row, { skipFKValidation: true }); inserted++; } } catch (err) { console.error(chalk_1.default.red(` ✖ Failed: ${err}`)); failed++; } } return { inserted, skipped, updated, failed }; } let totalInserted = 0; let totalFailed = 0; if (opts?.allActors) { console.log(chalk_1.default.cyan('\nSeeding to all actor sheets (--all-actors)...')); let users = []; try { users = await adapterWithContext.table('users').findMany(); } catch { console.error(chalk_1.default.red('❌ Could not read admin users table. Make sure `users` schema is registered and admin sheet has been synced.')); process.exit(1); } if (!users || users.length === 0) { console.warn(chalk_1.default.yellow('No users found in admin users table. Nothing to seed.')); return; } let totalInsertedActors = 0; let totalFailedActors = 0; for (const user of users) { if (!user.actor_sheet_id) continue; const targetAdapter = adapter.withContext({ userId: 'seed-cli', actor: 'admin', actorSheetId: user.actor_sheet_id }); for (const [tableName, records] of Object.entries(seedData)) { if (!Array.isArray(records)) continue; const result = await seedTable(targetAdapter, tableName, records); totalInsertedActors += result.inserted + result.updated; totalFailedActors += result.failed; } } console.log(); console.log(chalk_1.default.bold(`Seed complete (all-actors): ${totalInsertedActors} inserted/updated, ${totalFailedActors} failed.`)); if (totalFailedActors > 0) process.exit(1); return; } for (const [tableName, records] of Object.entries(seedData)) { if (!Array.isArray(records)) { console.warn(chalk_1.default.yellow(` ⚠️ Skipping "${tableName}": expected an array of records`)); continue; } console.log(chalk_1.default.cyan(`\nSeeding ${tableName} (${records.length} records)...`)); const result = await seedTable(adapterWithContext, tableName, records); const status = result.failed === 0 ? chalk_1.default.green('✓') : chalk_1.default.yellow('⚠'); const parts = []; if (result.inserted > 0) parts.push(`${result.inserted} inserted`); if (result.updated > 0) parts.push(`${result.updated} upserted`); if (result.skipped > 0) parts.push(`${result.skipped} skipped`); if (result.failed > 0) parts.push(`${result.failed} failed`); console.log(` ${status} ${parts.join(', ')}`); totalInserted += result.inserted + result.updated; totalFailed += result.failed; } console.log(); console.log(chalk_1.default.bold(`Seed complete: ${totalInserted} inserted/updated, ${totalFailed} failed.`)); if (totalFailed > 0) process.exit(1); } //# sourceMappingURL=seed.js.map