refactor: enhance security and performance in configuration and API routes

- Update Content Security Policy (CSP) in next.config.ts to avoid `unsafe-eval` in production, improving security against XSS attacks.
- Refactor API routes to enforce admin authentication and session validation, ensuring secure access to sensitive endpoints.
- Optimize analytics data retrieval by using database aggregation instead of loading all records into memory, improving performance and reducing memory usage.
- Implement session token creation and verification for better session management and security across the application.
- Enhance error handling and input validation in various API routes to ensure robustness and prevent potential issues.
This commit is contained in:
2026-01-11 22:44:26 +01:00
parent 9cc03bc475
commit 9072faae43
28 changed files with 433 additions and 288 deletions

View File

@@ -1,4 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
/**
* POST /api/n8n/generate-image
@@ -57,23 +58,16 @@ export async function POST(req: NextRequest) {
);
}
// Fetch project data first (needed for the new webhook format)
const projectResponse = await fetch(
`${process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000"}/api/projects/${projectId}`,
{
method: "GET",
cache: "no-store",
},
);
if (!projectResponse.ok) {
return NextResponse.json(
{ error: "Project not found" },
{ status: 404 },
);
const projectIdNum = typeof projectId === "string" ? parseInt(projectId, 10) : Number(projectId);
if (!Number.isFinite(projectIdNum)) {
return NextResponse.json({ error: "projectId must be a number" }, { status: 400 });
}
const project = await projectResponse.json();
// Fetch project data directly (avoid HTTP self-calls)
const project = await prisma.project.findUnique({ where: { id: projectIdNum } });
if (!project) {
return NextResponse.json({ error: "Project not found" }, { status: 404 });
}
// Optional: Check if project already has an image
if (!regenerate) {
@@ -83,7 +77,7 @@ export async function POST(req: NextRequest) {
success: true,
message:
"Project already has an image. Use regenerate=true to force regeneration.",
projectId: projectId,
projectId: projectIdNum,
existingImageUrl: project.imageUrl,
regenerated: false,
},
@@ -106,7 +100,7 @@ export async function POST(req: NextRequest) {
}),
},
body: JSON.stringify({
projectId: projectId,
projectId: projectIdNum,
projectData: {
title: project.title || "Unknown Project",
category: project.category || "Technology",
@@ -196,22 +190,13 @@ export async function POST(req: NextRequest) {
// If we got an image URL, we should update the project with it
if (imageUrl) {
// Update project with the new image URL
const updateResponse = await fetch(
`${process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000"}/api/projects/${projectId}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
"x-admin-request": "true",
},
body: JSON.stringify({
imageUrl: imageUrl,
}),
},
);
if (!updateResponse.ok) {
try {
await prisma.project.update({
where: { id: projectIdNum },
data: { imageUrl, updatedAt: new Date() },
});
} catch {
// Non-fatal: image URL can still be returned to caller
console.warn("Failed to update project with image URL");
}
}
@@ -220,7 +205,7 @@ export async function POST(req: NextRequest) {
{
success: true,
message: "AI image generation completed successfully",
projectId: projectId,
projectId: projectIdNum,
imageUrl: imageUrl,
generatedAt: generatedAt,
fileSize: fileSize,
@@ -257,23 +242,17 @@ export async function GET(req: NextRequest) {
);
}
// Fetch project to check image status
const projectResponse = await fetch(
`${process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000"}/api/projects/${projectId}`,
{
method: "GET",
cache: "no-store",
},
);
if (!projectResponse.ok) {
const projectIdNum = parseInt(projectId, 10);
if (!Number.isFinite(projectIdNum)) {
return NextResponse.json({ error: "projectId must be a number" }, { status: 400 });
}
const project = await prisma.project.findUnique({ where: { id: projectIdNum } });
if (!project) {
return NextResponse.json({ error: "Project not found" }, { status: 404 });
}
const project = await projectResponse.json();
return NextResponse.json({
projectId: parseInt(projectId),
projectId: projectIdNum,
title: project.title,
hasImage: !!project.imageUrl,
imageUrl: project.imageUrl || null,