chore: migrate to PostgreSQL with Prisma
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -41,3 +41,5 @@ yarn-error.log*
|
|||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
/app/generated/prisma
|
/app/generated/prisma
|
||||||
|
|
||||||
|
/lib/generated/prisma
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export async function GET() {
|
|||||||
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const users = getUsers().map(({ id, name, email, role, domains }) => ({
|
const allUsers = await getUsers();
|
||||||
|
const users = allUsers.map(({ id, name, email, role, domains }) => ({
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
email,
|
email,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { getUsers } from "@/lib/users";
|
import { prisma } from "@/lib/prisma";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* app/api/webhooks/mail/route.ts
|
* app/api/webhooks/mail/route.ts
|
||||||
*
|
*
|
||||||
* Webhook endpoint for incoming mail notifications (e.g. from Rspamd or Mailcow).
|
* Webhook endpoint for incoming mail notifications.
|
||||||
* Sends notifications to Telegram based on the recipient email.
|
* Uses Prisma to look up user mappings in the database.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export async function POST(req: NextRequest) {
|
export async function POST(req: NextRequest) {
|
||||||
@@ -19,22 +19,15 @@ export async function POST(req: NextRequest) {
|
|||||||
|
|
||||||
console.log(`[Mail Webhook] Yeni mail geldi: ${sender} -> ${aliciMail}`);
|
console.log(`[Mail Webhook] Yeni mail geldi: ${sender} -> ${aliciMail}`);
|
||||||
|
|
||||||
// 1. Find which USER_X owns this mail address via JSON mapping
|
// 1. Find mapping in database
|
||||||
// Format: MAIL_USER_MAPPINGS='{"email1@domain.com":"USER_0", "email2@domain.com":"USER_1"}'
|
const mapping = await prisma.mailboxMapping.findUnique({
|
||||||
const mappingsRaw = process.env.MAIL_USER_MAPPINGS || "{}";
|
where: { email: aliciMail },
|
||||||
let ownerUserKey: string | undefined = undefined;
|
include: { user: true },
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
if (mapping?.user) {
|
||||||
const mappings = JSON.parse(mappingsRaw);
|
const { user } = mapping;
|
||||||
ownerUserKey = mappings[aliciMail];
|
const targetChatId = user.telegramId;
|
||||||
} catch (e) {
|
|
||||||
console.error("[Mail Webhook] MAIL_USER_MAPPINGS JSON ayrıştırma hatası:", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ownerUserKey) {
|
|
||||||
// 2. Get that USER's Telegram ID (e.g., USER_0_TELEGRAM_ID)
|
|
||||||
const tgIdKey = `${ownerUserKey}_TELEGRAM_ID`;
|
|
||||||
const targetChatId = process.env[tgIdKey];
|
|
||||||
|
|
||||||
if (targetChatId && process.env.TELEGRAM_BOT_TOKEN) {
|
if (targetChatId && process.env.TELEGRAM_BOT_TOKEN) {
|
||||||
const message = `🔔 *Yeni Mail Geldi!*\n\n📧 *Alıcı:* ${aliciMail}\n👤 *Gönderen:* ${sender}\n📝 *Konu:* ${subject}`;
|
const message = `🔔 *Yeni Mail Geldi!*\n\n📧 *Alıcı:* ${aliciMail}\n👤 *Gönderen:* ${sender}\n📝 *Konu:* ${subject}`;
|
||||||
@@ -55,7 +48,7 @@ export async function POST(req: NextRequest) {
|
|||||||
const errorText = await res.text();
|
const errorText = await res.text();
|
||||||
console.error(`[Mail Webhook] Telegram API hatası: ${res.status} ${errorText}`);
|
console.error(`[Mail Webhook] Telegram API hatası: ${res.status} ${errorText}`);
|
||||||
} else {
|
} else {
|
||||||
console.log(`[Webhook] Bildirim ${ownerUserKey} kullanıcısına (ID: ${targetChatId}) gönderildi.`);
|
console.log(`[Webhook] Bildirim ${user.email} kullanıcısına (ID: ${targetChatId}) gönderildi.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
2
auth.ts
2
auth.ts
@@ -16,7 +16,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
|
|||||||
|
|
||||||
if (!email || !password) return null;
|
if (!email || !password) return null;
|
||||||
|
|
||||||
const user = authenticateUser(email, password);
|
const user = await authenticateUser(email, password);
|
||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
15
lib/prisma.ts
Normal file
15
lib/prisma.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
|
||||||
|
const globalForPrisma = globalThis as unknown as {
|
||||||
|
prisma: PrismaClient | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const prisma =
|
||||||
|
globalForPrisma.prisma ??
|
||||||
|
new PrismaClient({
|
||||||
|
// In Prisma 7, the connection URL should ideally come from prisma.config.ts
|
||||||
|
// or passed here if not using the new config system.
|
||||||
|
// For now, let's see if it picks up the URL automatically.
|
||||||
|
});
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
|
||||||
60
lib/users.ts
60
lib/users.ts
@@ -16,54 +16,34 @@
|
|||||||
* USER_1_DOMAINS="aveminakarabudak.com"
|
* USER_1_DOMAINS="aveminakarabudak.com"
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { prisma } from "./prisma";
|
||||||
|
|
||||||
export interface AppUser {
|
export interface AppUser {
|
||||||
id: string; // "user_0", "user_1", ...
|
id: string;
|
||||||
name: string;
|
name: string | null;
|
||||||
email: string;
|
email: string;
|
||||||
password: string; // plain text — store hashed in prod or use secrets manager
|
password: string;
|
||||||
role: "SUPER_ADMIN" | "DOMAIN_ADMIN";
|
role: string;
|
||||||
domains: string[]; // ["*"] for super admin, ["domain.com"] for domain admins
|
domains: string[];
|
||||||
telegramId?: string; // Optional Telegram ID for notifications
|
telegramId?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load all users defined in environment variables */
|
/** Load all users from database */
|
||||||
export function getUsers(): AppUser[] {
|
export async function getUsers(): Promise<AppUser[]> {
|
||||||
const users: AppUser[] = [];
|
const users = await prisma.user.findMany();
|
||||||
|
return users as AppUser[];
|
||||||
let i = 0;
|
|
||||||
while (true) {
|
|
||||||
const name = process.env[`USER_${i}_NAME`];
|
|
||||||
const email = process.env[`USER_${i}_EMAIL`];
|
|
||||||
const password = process.env[`USER_${i}_PASSWORD`];
|
|
||||||
const role = process.env[`USER_${i}_ROLE`] as AppUser["role"];
|
|
||||||
const domainsRaw = process.env[`USER_${i}_DOMAINS`] ?? "";
|
|
||||||
const telegramId = process.env[`USER_${i}_TELEGRAM_ID`];
|
|
||||||
|
|
||||||
if (!name || !email || !password) break;
|
|
||||||
|
|
||||||
users.push({
|
|
||||||
id: `user_${i}`,
|
|
||||||
name,
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
role: role ?? "DOMAIN_ADMIN",
|
|
||||||
domains: domainsRaw === "*" ? ["*"] : domainsRaw.split(",").map((d) => d.trim()).filter(Boolean),
|
|
||||||
telegramId,
|
|
||||||
});
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return users;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Find user by email and validate password */
|
/** Find user by email and validate password via database */
|
||||||
export function authenticateUser(email: string, password: string): AppUser | null {
|
export async function authenticateUser(email: string, password: string): Promise<AppUser | null> {
|
||||||
const users = getUsers();
|
const user = await prisma.user.findUnique({
|
||||||
const user = users.find((u) => u.email.toLowerCase() === email.toLowerCase());
|
where: { email: email.toLowerCase() },
|
||||||
|
});
|
||||||
|
|
||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
if (user.password !== password) return null;
|
if (user.password !== password) return null;
|
||||||
return user;
|
|
||||||
|
return user as AppUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check if a user has access to a specific domain */
|
/** Check if a user has access to a specific domain */
|
||||||
|
|||||||
90
package-lock.json
generated
90
package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formatjs/intl-localematcher": "^0.8.7",
|
"@formatjs/intl-localematcher": "^0.8.7",
|
||||||
|
"@prisma/client": "^6.2.1",
|
||||||
"@tanstack/react-query": "^5.100.10",
|
"@tanstack/react-query": "^5.100.10",
|
||||||
"bcryptjs": "^3.0.3",
|
"bcryptjs": "^3.0.3",
|
||||||
"imapflow": "^1.3.3",
|
"imapflow": "^1.3.3",
|
||||||
@@ -34,6 +35,7 @@
|
|||||||
"dotenv": "^17.4.2",
|
"dotenv": "^17.4.2",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "16.2.6",
|
"eslint-config-next": "16.2.6",
|
||||||
|
"prisma": "^6.2.1",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4",
|
||||||
"tsx": "^4.21.0",
|
"tsx": "^4.21.0",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
@@ -1757,6 +1759,74 @@
|
|||||||
"integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
|
"integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@prisma/client": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-msKY2iRLISN8t5X0Tj7hU0UWet1u0KuxSPHWuf3IRkB4J95mCvGpyQBfQ6ufcmvKNOMQSq90O2iUmJEN2e5fiA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"prisma": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"prisma": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/debug": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-0KItvt39CmQxWkEw6oW+RQMD6RZ43SJWgEUnzxN8VC9ixMysa7MzZCZf22LCK5DSooiLNf8vM3LHZm/I/Ni7bQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/engines": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-lTBNLJBCxVT9iP5I7Mn6GlwqAxTpS5qMERrhebkUhtXpGVkBNd/jHnNJBZQW4kGDCKaQg/r2vlJYkzOHnAb7ZQ==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "6.2.1",
|
||||||
|
"@prisma/engines-version": "6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69",
|
||||||
|
"@prisma/fetch-engine": "6.2.1",
|
||||||
|
"@prisma/get-platform": "6.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/engines-version": {
|
||||||
|
"version": "6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69.tgz",
|
||||||
|
"integrity": "sha512-7tw1qs/9GWSX6qbZs4He09TOTg1ff3gYsB3ubaVNN0Pp1zLm9NC5C5MZShtkz7TyQjx7blhpknB7HwEhlG+PrQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/fetch-engine": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-OO7O9d6Mrx2F9i+Gu1LW+DGXXyUFkP7OE5aj9iBfA/2jjDXEJjqa9X0ZmM9NZNo8Uo7ql6zKm6yjDcbAcRrw1A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "6.2.1",
|
||||||
|
"@prisma/engines-version": "6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69",
|
||||||
|
"@prisma/get-platform": "6.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@prisma/get-platform": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-zp53yvroPl5m5/gXYLz7tGCNG33bhG+JYCm74ohxOq1pPnrL47VQYFfF3RbTZ7TzGWCrR3EtoiYMywUBw7UK6Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/debug": "6.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rtsao/scc": {
|
"node_modules/@rtsao/scc": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
||||||
@@ -6516,6 +6586,26 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prisma": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-hhyM0H13pQleQ+br4CkzGizS5I0oInoeTw3JfLw1BRZduBSQxPILlJLwi+46wZzj9Je7ndyQEMGw/n5cN2fknA==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@prisma/engines": "6.2.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"prisma": "build/index.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "2.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/process-warning": {
|
"node_modules/process-warning": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formatjs/intl-localematcher": "^0.8.7",
|
"@formatjs/intl-localematcher": "^0.8.7",
|
||||||
|
"@prisma/client": "^6.2.1",
|
||||||
"@tanstack/react-query": "^5.100.10",
|
"@tanstack/react-query": "^5.100.10",
|
||||||
"bcryptjs": "^3.0.3",
|
"bcryptjs": "^3.0.3",
|
||||||
"imapflow": "^1.3.3",
|
"imapflow": "^1.3.3",
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
"dotenv": "^17.4.2",
|
"dotenv": "^17.4.2",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "16.2.6",
|
"eslint-config-next": "16.2.6",
|
||||||
|
"prisma": "^6.2.1",
|
||||||
"tailwindcss": "^4",
|
"tailwindcss": "^4",
|
||||||
"tsx": "^4.21.0",
|
"tsx": "^4.21.0",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
|
|||||||
43
prisma/schema.prisma
Normal file
43
prisma/schema.prisma
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// This is your Prisma schema file,
|
||||||
|
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||||
|
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
email String @unique
|
||||||
|
name String?
|
||||||
|
password String
|
||||||
|
role String @default("DOMAIN_ADMIN") // SUPER_ADMIN or DOMAIN_ADMIN
|
||||||
|
domains String[] @default([]) // ["*"] or list of domains
|
||||||
|
telegramId String?
|
||||||
|
mailboxMappings MailboxMapping[]
|
||||||
|
notificationConfigs NotificationConfig[]
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
model MailboxMapping {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
email String @unique
|
||||||
|
userId String
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
model NotificationConfig {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
userId String
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
type String // e.g., "TELEGRAM", "WEBHOOK"
|
||||||
|
value String // e.g., chat_id or webhook url
|
||||||
|
active Boolean @default(true)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
66
scripts/seed.ts
Normal file
66
scripts/seed.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { prisma } from "../lib/prisma";
|
||||||
|
import { getUsers } from "../lib/users";
|
||||||
|
import "dotenv/config";
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log("Seeding database...");
|
||||||
|
|
||||||
|
// 1. Migrate Users
|
||||||
|
const users = await getUsers();
|
||||||
|
for (const user of users) {
|
||||||
|
console.log(`Migrating user: ${user.email}`);
|
||||||
|
await prisma.user.upsert({
|
||||||
|
where: { email: user.email },
|
||||||
|
update: {
|
||||||
|
name: user.name,
|
||||||
|
password: user.password,
|
||||||
|
role: user.role,
|
||||||
|
domains: user.domains,
|
||||||
|
telegramId: user.telegramId,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
email: user.email,
|
||||||
|
name: user.name,
|
||||||
|
password: user.password,
|
||||||
|
role: user.role,
|
||||||
|
domains: user.domains,
|
||||||
|
telegramId: user.telegramId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Migrate Mailbox Mappings
|
||||||
|
const mappingsRaw = process.env.MAIL_USER_MAPPINGS || "{}";
|
||||||
|
try {
|
||||||
|
const mappings = JSON.parse(mappingsRaw);
|
||||||
|
for (const [email, userKey] of Object.entries(mappings)) {
|
||||||
|
const userIndex = parseInt((userKey as string).replace("USER_", ""));
|
||||||
|
const userEmail = process.env[`USER_${userIndex}_EMAIL`];
|
||||||
|
|
||||||
|
if (userEmail) {
|
||||||
|
const dbUser = await prisma.user.findUnique({ where: { email: userEmail } });
|
||||||
|
if (dbUser) {
|
||||||
|
console.log(`Creating mapping: ${email} -> ${userEmail}`);
|
||||||
|
await prisma.mailboxMapping.upsert({
|
||||||
|
where: { email },
|
||||||
|
update: { userId: dbUser.id },
|
||||||
|
create: { email, userId: dbUser.id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Mapping migration failed:", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Seeding complete.");
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
|
})
|
||||||
|
.finally(async () => {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user