feat: comprehensive security and architecture improvements

- Add Zod validation schemas with strong password requirements (12+ chars, complexity)
- Implement rate limiting for authentication endpoints (registration, password reset)
- Remove duplicate MetricCard component, consolidate to ui/metric-card.tsx
- Update README.md to use pnpm commands consistently
- Enhance authentication security with 12-round bcrypt hashing
- Add comprehensive input validation for all API endpoints
- Fix security vulnerabilities in user registration and password reset flows

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-06-28 01:52:53 +02:00
parent 192f9497b4
commit 7f48a085bf
68 changed files with 8045 additions and 4542 deletions

View File

@ -37,12 +37,11 @@ export async function POST(request: NextRequest) {
);
}
const company = await prisma.company.findUnique({ where: { id: companyId } });
const company = await prisma.company.findUnique({
where: { id: companyId },
});
if (!company) {
return NextResponse.json(
{ error: "Company not found" },
{ status: 404 }
);
return NextResponse.json({ error: "Company not found" }, { status: 404 });
}
const rawSessionData = await fetchAndParseCsv(
@ -114,12 +113,12 @@ export async function POST(request: NextRequest) {
}
// Immediately process the queued imports to create Session records
console.log('[Refresh API] Processing queued imports...');
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 }
where: { companyId: company.id },
});
return NextResponse.json({
@ -127,7 +126,7 @@ export async function POST(request: NextRequest) {
imported: importedCount,
total: rawSessionData.length,
sessions: sessionCount,
message: `Successfully imported ${importedCount} records and processed them into sessions. Total 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";

View File

@ -45,18 +45,21 @@ export async function POST(request: NextRequest) {
const { batchSize, maxConcurrency } = body;
// Validate parameters
const validatedBatchSize = batchSize && batchSize > 0 ? parseInt(batchSize) : null;
const validatedMaxConcurrency = maxConcurrency && maxConcurrency > 0 ? parseInt(maxConcurrency) : 5;
const validatedBatchSize =
batchSize && batchSize > 0 ? parseInt(batchSize) : null;
const validatedMaxConcurrency =
maxConcurrency && maxConcurrency > 0 ? parseInt(maxConcurrency) : 5;
// Check how many sessions need AI processing using the new status system
const sessionsNeedingAI = await ProcessingStatusManager.getSessionsNeedingProcessing(
ProcessingStage.AI_ANALYSIS,
1000 // Get count only
);
const sessionsNeedingAI =
await ProcessingStatusManager.getSessionsNeedingProcessing(
ProcessingStage.AI_ANALYSIS,
1000 // Get count only
);
// Filter to sessions for this company
const companySessionsNeedingAI = sessionsNeedingAI.filter(
statusRecord => statusRecord.session.companyId === user.companyId
(statusRecord) => statusRecord.session.companyId === user.companyId
);
const unprocessedCount = companySessionsNeedingAI.length;
@ -77,10 +80,15 @@ export async function POST(request: NextRequest) {
// The processing will continue in the background
processUnprocessedSessions(validatedBatchSize, validatedMaxConcurrency)
.then(() => {
console.log(`[Manual Trigger] Processing completed for company ${user.companyId}`);
console.log(
`[Manual Trigger] Processing completed for company ${user.companyId}`
);
})
.catch((error) => {
console.error(`[Manual Trigger] Processing failed for company ${user.companyId}:`, error);
console.error(
`[Manual Trigger] Processing failed for company ${user.companyId}:`,
error
);
});
return NextResponse.json({
@ -91,7 +99,6 @@ export async function POST(request: NextRequest) {
maxConcurrency: validatedMaxConcurrency,
startedAt: new Date().toISOString(),
});
} catch (error) {
console.error("[Manual Trigger] Error:", error);
return NextResponse.json(