echos-ocr/prisma/migrate-integrations.ts
Randall Stillwell a975043670 Add onboarding wizard, email verification, integration architecture, and settings restructure
- Auto-sign-in after registration instead of redirect to login
- Email verification system with token generation, send/confirm API routes, and persistent banner
- 7-step onboarding wizard (org, location, services, upload source, AI, integrations, complete)
- Middleware redirects owners with incomplete onboarding to /onboarding
- Integration provider plugin architecture with registry and 6 providers (Planning Center, Monday.com, Airtable, Google Sheets, Webhook, CSV Export)
- Full integration CRUD API with test, sync, fields, and OAuth authorize/callback routes
- Refactored fireIntegrationEvent to use Integration model with legacy AppSettings fallback
- Migration script for existing Monday.com/webhook config to Integration rows
- Settings page restructured from monolithic 1290-line file into focused sub-routes with section navigation
- Integration hub UI with provider tiles, connect flow, and individual config pages
- Post-onboarding contextual guidance cards on dashboard with dismissible hints
- Schema: Integration model, onboardingComplete/onboardingStep on Organization, dismissedHints on OrgMember

Made-with: Cursor
2026-04-15 01:29:13 -05:00

105 lines
3 KiB
TypeScript

/**
* One-time migration: reads Monday.com and webhook config from AppSettings
* and creates corresponding Integration rows for each organization.
*
* Usage: npx tsx prisma/migrate-integrations.ts
*/
import { PrismaClient } from "../src/generated/prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import pg from "pg";
async function main() {
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
});
const adapter = new PrismaPg(pool);
const prisma = new PrismaClient({ adapter });
const settings = await prisma.appSettings.findUnique({
where: { id: "singleton" },
});
if (!settings) {
console.log("No AppSettings found — nothing to migrate.");
await prisma.$disconnect();
return;
}
const orgs = await prisma.organization.findMany();
if (orgs.length === 0) {
console.log("No organizations found — nothing to migrate.");
await prisma.$disconnect();
return;
}
const orgId = orgs[0].id;
let created = 0;
if (settings.mondayEnabled && settings.mondayApiToken && settings.mondayBoardId) {
const existing = await prisma.integration.findFirst({
where: { organizationId: orgId, provider: "monday" },
});
if (!existing) {
await prisma.integration.create({
data: {
organizationId: orgId,
provider: "monday",
name: "Monday.com (migrated)",
enabled: settings.mondayEnabled,
config: {
apiToken: settings.mondayApiToken,
boardId: settings.mondayBoardId,
columnMap: settings.mondayColumnMap || {},
},
triggerEvents: ["card_reviewed", "card_exported"],
syncDirection: "push",
},
});
created++;
console.log("Created Monday.com integration from AppSettings.");
} else {
console.log("Monday.com integration already exists — skipping.");
}
}
if (settings.webhookEnabled && settings.webhookUrl) {
const existing = await prisma.integration.findFirst({
where: { organizationId: orgId, provider: "webhook" },
});
if (!existing) {
await prisma.integration.create({
data: {
organizationId: orgId,
provider: "webhook",
name: "Webhook (migrated)",
enabled: settings.webhookEnabled,
config: {
url: settings.webhookUrl,
secret: settings.webhookSecret,
},
triggerEvents: (settings.webhookEvents as string[]) || [
"ocr_complete",
"card_reviewed",
],
syncDirection: "push",
},
});
created++;
console.log("Created Webhook integration from AppSettings.");
} else {
console.log("Webhook integration already exists — skipping.");
}
}
console.log(`Migration complete: ${created} integration(s) created.`);
await prisma.$disconnect();
await pool.end();
}
main().catch((e) => {
console.error("Migration failed:", e);
process.exit(1);
});