#!/usr/bin/env tsx /** * Import churches from JSON export to Neon database * Run this LOCALLY (uses DATABASE_URL from .env pointing to Neon) */ import { PrismaClient } from '@prisma/client'; import fs from 'fs/promises'; import path from 'path'; interface ChurchExport { id: string; name: string; latitude: number; longitude: number; country: string; massSchedules?: any[]; confessionSchedules?: any[]; adorationSchedules?: any[]; [key: string]: any; } async function main() { const country = process.argv[2] || 'PL'; const mode = process.argv[3] || 'dry-run'; const dryRun = mode === 'dry-run'; console.log(`šŸ“¤ Importing ${country} data to Neon...`); console.log(`DATABASE_URL: ${process.env.DATABASE_URL?.replace(/:[^:@]+@/, ':****@')}`); if (dryRun) { console.log('šŸ” DRY RUN MODE - No data will be written'); } // Read export file const exportFile = path.join(process.cwd(), `export-${country}.json`); try { const data: ChurchExport[] = JSON.parse(await fs.readFile(exportFile, 'utf-8')); console.log(`Loaded ${data.length} churches from export file`); // Connect to Neon const prisma = new PrismaClient(); try { await prisma.$connect(); console.log('āœ… Connected to Neon database'); let inserted = 0; let updated = 0; let skipped = 0; let errors = 0; let totalMassSchedules = 0; let totalConfessionSchedules = 0; let totalAdorationSchedules = 0; for (const church of data) { try { const massSchedules = church.massSchedules || []; const confessionSchedules = church.confessionSchedules || []; const adorationSchedules = church.adorationSchedules || []; // Remove relations and ids delete church.massSchedules; delete church.confessionSchedules; delete church.adorationSchedules; delete church.id; if (!dryRun) { // Check if church already exists const existing = await prisma.churches.findFirst({ where: { latitude: church.latitude, longitude: church.longitude } }); if (existing) { // Update existing church await prisma.churches.update({ where: { id: existing.id }, data: church }); // Delete existing schedules await prisma.massSchedules.deleteMany({ where: { churchId: existing.id } }); await prisma.confessionSchedules.deleteMany({ where: { churchId: existing.id } }); await prisma.adorationSchedules.deleteMany({ where: { churchId: existing.id } }); // Insert new schedules for (const schedule of massSchedules) { delete schedule.id; await prisma.massSchedules.create({ data: { ...schedule, churchId: existing.id } }); totalMassSchedules++; } for (const schedule of confessionSchedules) { delete schedule.id; await prisma.confessionSchedules.create({ data: { ...schedule, churchId: existing.id } }); totalConfessionSchedules++; } for (const schedule of adorationSchedules) { delete schedule.id; await prisma.adorationSchedules.create({ data: { ...schedule, churchId: existing.id } }); totalAdorationSchedules++; } updated++; } else { // Create new church const result = await prisma.churches.create({ data: church }); // Insert schedules for (const schedule of massSchedules) { delete schedule.id; await prisma.massSchedules.create({ data: { ...schedule, churchId: result.id } }); totalMassSchedules++; } for (const schedule of confessionSchedules) { delete schedule.id; await prisma.confessionSchedules.create({ data: { ...schedule, churchId: result.id } }); totalConfessionSchedules++; } for (const schedule of adorationSchedules) { delete schedule.id; await prisma.adorationSchedules.create({ data: { ...schedule, churchId: result.id } }); totalAdorationSchedules++; } inserted++; } } else { // Dry run - just count inserted++; totalMassSchedules += massSchedules.length; totalConfessionSchedules += confessionSchedules.length; totalAdorationSchedules += adorationSchedules.length; } if ((inserted + updated) % 100 === 0) { console.log(`Progress: ${inserted + updated} churches processed...`); } } catch (error) { errors++; console.error(`Error importing church ${church.name}:`, error instanceof Error ? error.message : error); } } console.log('\nāœ… Import complete!'); console.log(` - ${inserted} churches inserted`); console.log(` - ${updated} churches updated`); console.log(` - ${skipped} churches skipped`); console.log(` - ${errors} errors`); console.log(` - ${totalMassSchedules} mass schedules`); console.log(` - ${totalConfessionSchedules} confession schedules`); console.log(` - ${totalAdorationSchedules} adoration schedules`); await prisma.$disconnect(); if (dryRun) { console.log('\nšŸ’” This was a DRY RUN. To actually import to Neon, run:'); console.log(` npx tsx scripts/import-to-neon.ts ${country} real-import`); } else { console.log('\nšŸŽ‰ Data successfully uploaded to Neon!'); } } catch (error) { console.error('āŒ Import failed:', error); await prisma.$disconnect(); throw error; } } catch (error) { if (error instanceof Error && 'code' in error && error.code === 'ENOENT') { console.error(`āŒ Export file not found: ${exportFile}`); console.error(`\nFirst, export data from NAS:`); console.error(` ssh albert@192.168.0.145`); console.error(` cd /volume1/docker/nearestmass`); console.error(` /usr/local/bin/docker compose --profile tools run --rm scraper npx tsx scripts/export-from-nas.ts ${country}`); console.error(`\nThen download the export:`); console.error(` scp albert@192.168.0.145:/volume1/docker/nearestmass/export-${country}.json .`); console.error(`\nFinally, run this import script again.`); } else { console.error('āŒ Process failed:', error); } process.exit(1); } } main().catch(console.error);