echos-ocr/prisma/seed-migration.ts

181 lines
5 KiB
TypeScript
Raw Permalink Normal View History

/**
* Data migration script for existing deployments upgrading to v2.
*
* Run with: npx tsx prisma/seed-migration.ts
*
* This script:
* 1. Creates a SystemConfig singleton (setup complete)
* 2. Creates a default Organization from existing data
* 3. Creates a default Location
* 4. Creates a default "Sunday Service" CollectionDay
* 5. Migrates existing Users into OrgMembers
* 6. Backfills organizationId and locationId on ResponseCards
* 7. Backfills collectionDayId using existing dates
*/
import "dotenv/config";
import { PrismaClient } from "../src/generated/prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import pg from "pg";
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
const adapter = new PrismaPg(pool);
const prisma = new PrismaClient({ adapter });
async function main() {
console.log("Starting v2 data migration...\n");
// 1. SystemConfig
const existingConfig = await prisma.systemConfig.findUnique({
where: { id: "singleton" },
});
if (existingConfig?.isSetupComplete) {
console.log("Setup already marked complete. Skipping migration.");
return;
}
await prisma.systemConfig.upsert({
where: { id: "singleton" },
update: { isSetupComplete: true, setupStep: 3 },
create: { id: "singleton", isSetupComplete: true, setupStep: 3 },
});
console.log("[1/7] SystemConfig created (setup complete)");
// 2. Default Organization
let org = await prisma.organization.findFirst();
if (!org) {
org = await prisma.organization.create({
data: {
name: "My Church",
slug: "my-church",
type: "church",
timezone: "America/Chicago",
},
});
console.log(`[2/7] Created default organization: ${org.name} (${org.id})`);
} else {
console.log(`[2/7] Organization already exists: ${org.name}`);
}
// 3. Default Location
let location = await prisma.location.findFirst({
where: { organizationId: org.id },
});
if (!location) {
location = await prisma.location.create({
data: {
name: "Main Campus",
organizationId: org.id,
},
});
console.log(`[3/7] Created default location: ${location.name} (${location.id})`);
} else {
console.log(`[3/7] Location already exists: ${location.name}`);
}
// 4. Default CollectionDay
let collectionDay = await prisma.collectionDay.findFirst({
where: { locationId: location.id },
});
if (!collectionDay) {
collectionDay = await prisma.collectionDay.create({
data: {
locationId: location.id,
name: "Sunday Service",
dayOfWeek: 0,
timeStart: "09:00",
timeEnd: "12:00",
isRecurring: true,
},
});
console.log(`[4/7] Created default collection day: ${collectionDay.name}`);
} else {
console.log(`[4/7] Collection day already exists: ${collectionDay.name}`);
}
// 5. Migrate Users to OrgMembers
const users = await prisma.user.findMany();
let memberCount = 0;
for (const user of users) {
const existing = await prisma.orgMember.findUnique({
where: {
userId_organizationId: {
userId: user.id,
organizationId: org.id,
},
},
});
if (!existing) {
await prisma.orgMember.create({
data: {
userId: user.id,
organizationId: org.id,
role: user.role || "viewer",
},
});
memberCount++;
}
}
console.log(`[5/7] Migrated ${memberCount} users to org members (${users.length} total users)`);
// 6. Backfill ResponseCard.organizationId and locationId
const cardResult = await prisma.responseCard.updateMany({
where: { organizationId: null },
data: {
organizationId: org.id,
locationId: location.id,
},
});
console.log(`[6/7] Backfilled ${cardResult.count} cards with org/location`);
// 7. Backfill collectionDayId
const cardsWithDates = await prisma.responseCard.findMany({
where: {
collectionDayId: null,
OR: [
{ firstTimeGuestDate: { not: null } },
{ createdAt: { not: undefined } },
],
},
select: { id: true, firstTimeGuestDate: true, createdAt: true },
});
let assignedCount = 0;
for (const card of cardsWithDates) {
const refDate = card.firstTimeGuestDate || card.createdAt;
const sunday = new Date(refDate);
const day = sunday.getDay();
if (day !== 0) sunday.setDate(sunday.getDate() - day);
sunday.setHours(0, 0, 0, 0);
await prisma.responseCard.update({
where: { id: card.id },
data: {
collectionDayId: collectionDay.id,
collectionDate: sunday,
},
});
assignedCount++;
}
console.log(`[7/7] Assigned ${assignedCount} cards to default collection day`);
// 8. Backfill ProcessingJob.organizationId
await prisma.processingJob.updateMany({
where: { organizationId: null },
data: { organizationId: org.id },
});
console.log("\nMigration complete!");
}
main()
.catch((e) => {
console.error("Migration failed:", e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
await pool.end();
});