Files
livedash-node/app/api/admin/refresh-sessions/route.ts
Kaj Kowalski 5bfd762e55 fix: comprehensive TypeScript/build fixes and modernization
- Update tsconfig to ES2024 target and bundler moduleResolution
- Add dynamic imports for chart.js and recharts (bundle optimization)
- Consolidate 17 useState into useReducer in sessions page
- Fix 18 .js extension imports across lib files
- Add type declarations for @rapideditor/country-coder
- Fix platform user types (PlatformUserRole enum)
- Fix Calendar component prop types
- Centralize next-auth type augmentation
- Add force-dynamic to all API routes (prevent build-time prerender)
- Fix Prisma JSON null handling with Prisma.DbNull
- Fix various type mismatches (SessionMessage, ImportRecord, etc.)
- Export ButtonProps from button component
- Update next-themes import path
- Replace JSX.Element with React.ReactElement
- Remove obsolete debug scripts and pnpm lockfile
- Downgrade eslint to v8 for next compatibility
2026-01-20 07:28:10 +01:00

149 lines
5.2 KiB
TypeScript

import { type NextRequest, NextResponse } from "next/server";
import { fetchAndParseCsv } from "../../../../lib/csvFetcher";
import { processQueuedImports } from "../../../../lib/importProcessor";
import { prisma } from "../../../../lib/prisma";
export const dynamic = "force-dynamic";
export async function POST(request: NextRequest) {
try {
const body = await request.json();
let { companyId } = body;
if (!companyId) {
// Try to get user from prisma based on session cookie
try {
const session = await prisma.session.findFirst({
orderBy: { createdAt: "desc" },
where: {
/* Add session check criteria here */
},
});
if (session) {
companyId = session.companyId;
}
} catch (error) {
// Log error for server-side debugging
const errorMessage =
error instanceof Error ? error.message : String(error);
// Use a server-side logging approach instead of console
process.stderr.write(`Error fetching session: ${errorMessage}\n`);
}
}
if (!companyId) {
return NextResponse.json(
{ error: "Company ID is required" },
{ status: 400 }
);
}
const company = await prisma.company.findUnique({
where: { id: companyId },
});
if (!company) {
return NextResponse.json({ error: "Company not found" }, { status: 404 });
}
// Check if company is active and can process data
if (company.status !== "ACTIVE") {
return NextResponse.json(
{
error: `Data processing is disabled for ${company.status.toLowerCase()} companies`,
companyStatus: company.status,
},
{ status: 403 }
);
}
const rawSessionData = await fetchAndParseCsv(
company.csvUrl,
company.csvUsername as string | undefined,
company.csvPassword as string | undefined
);
let importedCount = 0;
// Create SessionImport records for new data
for (const rawSession of rawSessionData) {
try {
// Use upsert to handle duplicates gracefully
await prisma.sessionImport.upsert({
where: {
companyId_externalSessionId: {
companyId: company.id,
externalSessionId: rawSession.externalSessionId,
},
},
update: {
// Update existing record with latest data
startTimeRaw: rawSession.startTimeRaw,
endTimeRaw: rawSession.endTimeRaw,
ipAddress: rawSession.ipAddress,
countryCode: rawSession.countryCode,
language: rawSession.language,
messagesSent: rawSession.messagesSent,
sentimentRaw: rawSession.sentimentRaw,
escalatedRaw: rawSession.escalatedRaw,
forwardedHrRaw: rawSession.forwardedHrRaw,
fullTranscriptUrl: rawSession.fullTranscriptUrl,
avgResponseTimeSeconds: rawSession.avgResponseTimeSeconds,
tokens: rawSession.tokens,
tokensEur: rawSession.tokensEur,
category: rawSession.category,
initialMessage: rawSession.initialMessage,
// Status tracking now handled by ProcessingStatusManager
},
create: {
companyId: company.id,
externalSessionId: rawSession.externalSessionId,
startTimeRaw: rawSession.startTimeRaw,
endTimeRaw: rawSession.endTimeRaw,
ipAddress: rawSession.ipAddress,
countryCode: rawSession.countryCode,
language: rawSession.language,
messagesSent: rawSession.messagesSent,
sentimentRaw: rawSession.sentimentRaw,
escalatedRaw: rawSession.escalatedRaw,
forwardedHrRaw: rawSession.forwardedHrRaw,
fullTranscriptUrl: rawSession.fullTranscriptUrl,
avgResponseTimeSeconds: rawSession.avgResponseTimeSeconds,
tokens: rawSession.tokens,
tokensEur: rawSession.tokensEur,
category: rawSession.category,
initialMessage: rawSession.initialMessage,
// Status tracking now handled by ProcessingStatusManager
},
});
importedCount++;
} catch (error) {
// Log individual session import errors but continue processing
process.stderr.write(
`Failed to import session ${rawSession.externalSessionId}: ${error}\n`
);
}
}
// Immediately process the queued imports to create Session records
console.log("[Refresh API] Processing queued imports...");
await processQueuedImports(100); // Process up to 100 imports immediately
// Count how many sessions were created
const sessionCount = await prisma.session.count({
where: { companyId: company.id },
});
return NextResponse.json({
ok: true,
imported: importedCount,
total: rawSessionData.length,
sessions: sessionCount,
message: `Successfully imported ${importedCount} records and processed them into sessions. Total sessions: ${sessionCount}`,
});
} catch (e) {
const error = e instanceof Error ? e.message : "An unknown error occurred";
return NextResponse.json({ error }, { status: 500 });
}
}