Files
clientsflow/Frontend/scripts/export-dataset.mjs
2026-02-18 13:56:35 +07:00

183 lines
4.9 KiB
JavaScript

import fs from "node:fs/promises";
import fsSync from "node:fs";
import path from "node:path";
import { PrismaClient } from "@prisma/client";
function loadEnvFromDotEnv() {
const p = path.resolve(process.cwd(), ".env");
if (!fsSync.existsSync(p)) return;
const raw = fsSync.readFileSync(p, "utf8");
for (const line of raw.split("\n")) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#")) continue;
const idx = trimmed.indexOf("=");
if (idx === -1) continue;
const key = trimmed.slice(0, idx).trim();
let val = trimmed.slice(idx + 1).trim();
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
val = val.slice(1, -1);
}
if (!key) continue;
if (key === "DATABASE_URL") {
process.env[key] = val;
continue;
}
if (!process.env[key]) process.env[key] = val;
}
}
loadEnvFromDotEnv();
const prisma = new PrismaClient();
function datasetRoot() {
const teamId = process.env.TEAM_ID || "demo-team";
const userId = process.env.USER_ID || "demo-user";
return path.resolve(process.cwd(), "..", ".data", "crmfs", "teams", teamId, "users", userId);
}
async function ensureDir(p) {
await fs.mkdir(p, { recursive: true });
}
async function writeJson(p, value) {
await fs.writeFile(p, JSON.stringify(value, null, 2) + "\n", "utf8");
}
function jsonlLine(value) {
return JSON.stringify(value) + "\n";
}
async function main() {
const root = datasetRoot();
const tmp = root + ".tmp";
await fs.rm(tmp, { recursive: true, force: true });
await ensureDir(tmp);
const contactsDir = path.join(tmp, "contacts");
const notesDir = path.join(tmp, "notes");
const messagesDir = path.join(tmp, "messages");
const eventsDir = path.join(tmp, "events");
const indexDir = path.join(tmp, "index");
await Promise.all([
ensureDir(contactsDir),
ensureDir(notesDir),
ensureDir(messagesDir),
ensureDir(eventsDir),
ensureDir(indexDir),
]);
const teamId = process.env.TEAM_ID || "demo-team";
const contacts = await prisma.contact.findMany({
where: { teamId },
orderBy: { updatedAt: "desc" },
include: {
note: { select: { content: true, updatedAt: true } },
messages: {
select: {
kind: true,
direction: true,
channel: true,
content: true,
durationSec: true,
transcriptJson: true,
occurredAt: true,
},
orderBy: { occurredAt: "asc" },
},
events: {
select: { title: true, startsAt: true, endsAt: true, status: true, note: true },
orderBy: { startsAt: "asc" },
},
},
take: 5000,
});
const contactIndex = [];
for (const c of contacts) {
await writeJson(path.join(contactsDir, `${c.id}.json`), {
id: c.id,
teamId: c.teamId,
name: c.name,
company: c.company ?? null,
country: c.country ?? null,
location: c.location ?? null,
avatarUrl: c.avatarUrl ?? null,
email: c.email ?? null,
phone: c.phone ?? null,
createdAt: c.createdAt,
updatedAt: c.updatedAt,
});
await fs.writeFile(
path.join(notesDir, `${c.id}.md`),
(c.note?.content?.trim() ? c.note.content.trim() : "") + "\n",
"utf8",
);
await fs.writeFile(
path.join(messagesDir, `${c.id}.jsonl`),
c.messages
.map((m) =>
jsonlLine({
kind: m.kind,
direction: m.direction,
channel: m.channel,
occurredAt: m.occurredAt,
content: m.content,
durationSec: m.durationSec ?? null,
transcript: m.transcriptJson ?? null,
}),
)
.join(""),
"utf8",
);
await fs.writeFile(
path.join(eventsDir, `${c.id}.jsonl`),
c.events
.map((e) =>
jsonlLine({
title: e.title,
startsAt: e.startsAt,
endsAt: e.endsAt,
status: e.status ?? null,
note: e.note ?? null,
}),
)
.join(""),
"utf8",
);
const lastMessageAt = c.messages.length ? c.messages[c.messages.length - 1].occurredAt : null;
const nextEventAt = c.events.find((e) => new Date(e.startsAt).getTime() >= Date.now())?.startsAt ?? null;
contactIndex.push({
id: c.id,
name: c.name,
company: c.company ?? null,
lastMessageAt,
nextEventAt,
updatedAt: c.updatedAt,
});
}
await writeJson(path.join(indexDir, "contacts.json"), contactIndex);
await writeJson(path.join(tmp, "meta.json"), { exportedAt: new Date().toISOString(), version: 1 });
await ensureDir(path.dirname(root));
await fs.rm(root, { recursive: true, force: true });
await fs.rename(tmp, root);
console.log("exported", root);
}
await main()
.catch((e) => {
console.error(e);
process.exitCode = 1;
})
.finally(async () => {
await prisma.$disconnect();
});