- 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
105 lines
3 KiB
TypeScript
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);
|
|
});
|