Files
portfolio/next.config.ts
denshooter 9072faae43 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.
2026-01-11 22:44:26 +01:00

148 lines
4.3 KiB
TypeScript

import type { NextConfig } from "next";
import dotenv from "dotenv";
import path from "path";
import bundleAnalyzer from "@next/bundle-analyzer";
// Load the .env file from the working directory
dotenv.config({ path: path.resolve(process.cwd(), ".env") });
const nextConfig: NextConfig = {
// Enable standalone output for Docker
output: "standalone",
outputFileTracingRoot: path.join(process.cwd()),
// Optimize for production
compress: true,
poweredByHeader: false,
// React Strict Mode
reactStrictMode: true,
// Disable ESLint during build for Docker
eslint: {
ignoreDuringBuilds: process.env.NODE_ENV === "production",
},
// Environment variables
env: {
NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL,
},
// Performance optimizations
// NOTE: `optimizePackageImports` can cause dev-time webpack runtime issues with some setups.
// Keep it enabled for production builds only.
experimental:
process.env.NODE_ENV === "production"
? {
optimizePackageImports: ["lucide-react", "framer-motion"],
}
: {},
// Image optimization
images: {
formats: ["image/webp", "image/avif"],
minimumCacheTTL: 60,
remotePatterns: [
{
protocol: "https",
hostname: "i.scdn.co",
},
{
protocol: "https",
hostname: "cdn.discordapp.com",
},
{
protocol: "https",
hostname: "media.discordapp.net",
},
],
},
// Webpack configuration
webpack: (config) => {
// Fix for module resolution issues
config.resolve.fallback = {
...config.resolve.fallback,
fs: false,
net: false,
tls: false,
};
return config;
},
// Security and cache headers
async headers() {
const csp =
process.env.NODE_ENV === "production"
? // Avoid `unsafe-eval` in production (reduces XSS impact and enables stronger CSP)
"default-src 'self'; script-src 'self' 'unsafe-inline' https://analytics.dk0.dev; script-src-elem 'self' 'unsafe-inline' https://analytics.dk0.dev; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com data:; img-src 'self' data: https:; connect-src 'self' https://analytics.dk0.dev https://api.quotable.io; frame-ancestors 'none'; base-uri 'self'; form-action 'self';"
: // Dev CSP: allow eval for tooling compatibility
"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://analytics.dk0.dev; script-src-elem 'self' 'unsafe-inline' https://analytics.dk0.dev; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com data:; img-src 'self' data: https:; connect-src 'self' https://analytics.dk0.dev https://api.quotable.io; frame-ancestors 'none'; base-uri 'self'; form-action 'self';";
return [
{
source: "/(.*)",
headers: [
{
key: "X-DNS-Prefetch-Control",
value: "on",
},
{
key: "Strict-Transport-Security",
value: "max-age=63072000; includeSubDomains; preload",
},
{
key: "X-Frame-Options",
value: "DENY",
},
{
key: "X-Content-Type-Options",
value: "nosniff",
},
{
key: "X-XSS-Protection",
value: "1; mode=block",
},
{
key: "Referrer-Policy",
value: "strict-origin-when-cross-origin",
},
{
key: "Permissions-Policy",
value: "camera=(), microphone=(), geolocation=()",
},
{
key: "Content-Security-Policy",
value: csp,
},
],
},
{
source: "/api/(.*)",
headers: [
{
key: "Cache-Control",
value: "no-store, no-cache, must-revalidate, proxy-revalidate",
},
],
},
{
source: "/_next/static/(.*)",
headers: [
{
key: "Cache-Control",
value: "public, max-age=31536000, immutable",
},
],
},
];
},
};
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === "true",
});
export default withBundleAnalyzer(nextConfig);