188 lines
6.5 KiB
JavaScript
188 lines
6.5 KiB
JavaScript
import "dotenv/config"
|
|
import { ConvexHttpClient } from "convex/browser"
|
|
|
|
import { createPrismaClient } from "./utils/prisma-client.mjs"
|
|
|
|
const prisma = createPrismaClient()
|
|
|
|
function toMillis(date) {
|
|
return date instanceof Date ? date.getTime() : date ? new Date(date).getTime() : undefined
|
|
}
|
|
|
|
function normalizeString(value, fallback = "") {
|
|
if (!value) return fallback
|
|
return value.trim()
|
|
}
|
|
|
|
function slugify(value) {
|
|
return normalizeString(value)
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9\s-]/g, "")
|
|
.replace(/\s+/g, "-")
|
|
.replace(/-+/g, "-") || undefined
|
|
}
|
|
|
|
function normalizePrismaSlaSnapshot(snapshot) {
|
|
if (!snapshot || typeof snapshot !== "object") return undefined
|
|
const record = snapshot
|
|
const pauseStatuses =
|
|
Array.isArray(record.pauseStatuses) && record.pauseStatuses.length > 0
|
|
? record.pauseStatuses.filter((value) => typeof value === "string")
|
|
: undefined
|
|
|
|
const normalized = {
|
|
categoryId: typeof record.categoryId === "string" ? record.categoryId : undefined,
|
|
categoryName: typeof record.categoryName === "string" ? record.categoryName : undefined,
|
|
priority: typeof record.priority === "string" ? record.priority : undefined,
|
|
responseTargetMinutes: typeof record.responseTargetMinutes === "number" ? record.responseTargetMinutes : undefined,
|
|
responseMode: typeof record.responseMode === "string" ? record.responseMode : undefined,
|
|
solutionTargetMinutes: typeof record.solutionTargetMinutes === "number" ? record.solutionTargetMinutes : undefined,
|
|
solutionMode: typeof record.solutionMode === "string" ? record.solutionMode : undefined,
|
|
alertThreshold: typeof record.alertThreshold === "number" ? record.alertThreshold : undefined,
|
|
pauseStatuses,
|
|
}
|
|
|
|
return Object.values(normalized).some((value) => value !== undefined) ? normalized : undefined
|
|
}
|
|
|
|
async function main() {
|
|
const tenantId = process.env.SYNC_TENANT_ID || "tenant-atlas"
|
|
const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL || "http://127.0.0.1:3210"
|
|
const secret = process.env.CONVEX_SYNC_SECRET
|
|
|
|
if (!secret) {
|
|
console.error("CONVEX_SYNC_SECRET não configurado. Configure no .env.")
|
|
process.exit(1)
|
|
}
|
|
|
|
const [users, queues, tickets, companies] = await Promise.all([
|
|
prisma.user.findMany({
|
|
include: {
|
|
teams: {
|
|
include: { team: true },
|
|
},
|
|
company: true,
|
|
},
|
|
}),
|
|
prisma.queue.findMany(),
|
|
prisma.ticket.findMany({
|
|
include: {
|
|
requester: true,
|
|
assignee: true,
|
|
queue: true,
|
|
company: true,
|
|
comments: {
|
|
include: {
|
|
author: true,
|
|
},
|
|
},
|
|
events: true,
|
|
},
|
|
orderBy: { createdAt: "asc" },
|
|
}),
|
|
prisma.company.findMany(),
|
|
])
|
|
|
|
const userSnapshot = users.map((user) => ({
|
|
email: user.email,
|
|
name: normalizeString(user.name, user.email),
|
|
role: user.role,
|
|
avatarUrl: user.avatarUrl ?? undefined,
|
|
teams: user.teams
|
|
.map((membership) => membership.team?.name)
|
|
.filter((name) => Boolean(name) && typeof name === "string"),
|
|
companySlug: user.company?.slug ?? undefined,
|
|
}))
|
|
|
|
const queueSnapshot = queues.map((queue) => ({
|
|
name: normalizeString(queue.name, queue.slug ?? queue.id),
|
|
slug: queue.slug ? queue.slug : normalizeString(queue.name, queue.id).toLowerCase().replace(/\s+/g, "-"),
|
|
}))
|
|
|
|
const referenceFallbackStart = 41000
|
|
let referenceCounter = referenceFallbackStart
|
|
|
|
const ticketSnapshot = tickets.map((ticket) => {
|
|
const reference = ticket.reference && ticket.reference > 0 ? ticket.reference : ++referenceCounter
|
|
const requesterEmail = ticket.requester?.email ?? userSnapshot[0]?.email ?? "unknown@example.com"
|
|
const assigneeEmail = ticket.assignee?.email ?? undefined
|
|
const queueSlug = ticket.queue?.slug ?? slugify(ticket.queue?.name)
|
|
const companySlug = ticket.company?.slug ?? ticket.requester?.company?.slug ?? undefined
|
|
|
|
return {
|
|
reference,
|
|
subject: normalizeString(ticket.subject, `Ticket ${reference}`),
|
|
summary: ticket.summary ?? undefined,
|
|
status: ticket.status,
|
|
priority: ticket.priority,
|
|
channel: ticket.channel,
|
|
queueSlug: queueSlug ?? undefined,
|
|
requesterEmail,
|
|
assigneeEmail,
|
|
companySlug,
|
|
dueAt: toMillis(ticket.dueAt) ?? undefined,
|
|
firstResponseAt: toMillis(ticket.firstResponseAt) ?? undefined,
|
|
resolvedAt: toMillis(ticket.resolvedAt) ?? undefined,
|
|
closedAt: toMillis(ticket.closedAt) ?? undefined,
|
|
createdAt: toMillis(ticket.createdAt) ?? Date.now(),
|
|
updatedAt: toMillis(ticket.updatedAt) ?? Date.now(),
|
|
tags: Array.isArray(ticket.tags) ? ticket.tags : undefined,
|
|
slaSnapshot: normalizePrismaSlaSnapshot(ticket.slaSnapshot),
|
|
slaResponseDueAt: toMillis(ticket.slaResponseDueAt),
|
|
slaSolutionDueAt: toMillis(ticket.slaSolutionDueAt),
|
|
slaResponseStatus: ticket.slaResponseStatus ?? undefined,
|
|
slaSolutionStatus: ticket.slaSolutionStatus ?? undefined,
|
|
slaPausedAt: toMillis(ticket.slaPausedAt),
|
|
slaPausedBy: ticket.slaPausedBy ?? undefined,
|
|
slaPausedMs: typeof ticket.slaPausedMs === "number" ? ticket.slaPausedMs : undefined,
|
|
comments: ticket.comments.map((comment) => ({
|
|
authorEmail: comment.author?.email ?? requesterEmail,
|
|
visibility: comment.visibility,
|
|
body: comment.body,
|
|
createdAt: toMillis(comment.createdAt) ?? Date.now(),
|
|
updatedAt: toMillis(comment.updatedAt) ?? Date.now(),
|
|
})),
|
|
events: ticket.events.map((event) => ({
|
|
type: event.type,
|
|
payload: event.payload ?? {},
|
|
createdAt: toMillis(event.createdAt) ?? Date.now(),
|
|
})),
|
|
}
|
|
})
|
|
|
|
const companySnapshot = companies.map((company) => ({
|
|
slug: company.slug ?? slugify(company.name),
|
|
name: company.name,
|
|
cnpj: company.cnpj ?? undefined,
|
|
domain: company.domain ?? undefined,
|
|
phone: company.phone ?? undefined,
|
|
description: company.description ?? undefined,
|
|
address: company.address ?? undefined,
|
|
createdAt: toMillis(company.createdAt) ?? Date.now(),
|
|
updatedAt: toMillis(company.updatedAt) ?? Date.now(),
|
|
}))
|
|
|
|
const client = new ConvexHttpClient(convexUrl)
|
|
|
|
const result = await client.mutation("migrations:importPrismaSnapshot", {
|
|
secret,
|
|
snapshot: {
|
|
tenantId,
|
|
companies: companySnapshot,
|
|
users: userSnapshot,
|
|
queues: queueSnapshot,
|
|
tickets: ticketSnapshot,
|
|
},
|
|
})
|
|
|
|
console.log("Sincronização concluída:", result)
|
|
}
|
|
|
|
main()
|
|
.catch((error) => {
|
|
console.error(error)
|
|
process.exitCode = 1
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect()
|
|
})
|