mirror of
https://github.com/codeflash-ai/codeflash-internal.git
synced 2026-05-04 18:25:18 +00:00
- Add auth0.getSession() to unauthenticated observability endpoints (llm-call-debug, llm-export, observability chat) - Remove hardcoded JWT_SECRET fallback; require env var - Sanitize markdown HTML with DOMPurify before innerHTML assignment - Escape user data in Intercom boot snippet via JSON.stringify - Add security headers (CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) via next.config.mjs - Move OAuth params from sessionStorage to signed HttpOnly cookie - Add input validation: clamp page/pageSize bounds, allowlist sort fields - Stop leaking error.message to clients in API responses - Remove ~40 console.log/error statements that logged user IDs, org IDs, PKCE params, and OAuth flow details - Delete unused api-client.ts (NEXT_PUBLIC_CF_API_KEY never imported)
212 lines
7.3 KiB
JavaScript
212 lines
7.3 KiB
JavaScript
import bundleAnalyzer from "@next/bundle-analyzer"
|
|
import { dirname, resolve } from "path"
|
|
import { fileURLToPath } from "url"
|
|
|
|
const withBundleAnalyzer = bundleAnalyzer({
|
|
enabled: process.env.ANALYZE === "true",
|
|
})
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
|
|
/** @type {import("next").NextConfig} */
|
|
const nextConfig = {
|
|
async headers() {
|
|
return [
|
|
{
|
|
source: "/(.*)",
|
|
headers: [
|
|
{ key: "X-Frame-Options", value: "DENY" },
|
|
{ key: "X-Content-Type-Options", value: "nosniff" },
|
|
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
|
|
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
|
|
{
|
|
key: "Strict-Transport-Security",
|
|
value: "max-age=63072000; includeSubDomains; preload",
|
|
},
|
|
{
|
|
key: "Content-Security-Policy",
|
|
value: [
|
|
"default-src 'self'",
|
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://widget.intercom.io https://js.intercomcdn.com https://client.crisp.chat https://settings.crisp.chat",
|
|
"style-src 'self' 'unsafe-inline' https://client.crisp.chat",
|
|
"img-src 'self' data: blob: https://avatars.githubusercontent.com https://github.com https://*.intercomcdn.com https://*.crisp.chat https://image.crisp.chat",
|
|
"font-src 'self' data: https://client.crisp.chat",
|
|
"connect-src 'self' https://*.intercom.io https://api-iam.intercom.io wss://*.intercom.io https://*.crisp.chat wss://*.crisp.chat https://*.sentry.io https://*.ingest.us.sentry.io https://us.i.posthog.com https://us.posthog.com",
|
|
"frame-src 'self' https://intercom-sheets.com https://game.crisp.chat",
|
|
"media-src 'self' https://*.intercomcdn.com",
|
|
"worker-src 'self' blob:",
|
|
].join("; "),
|
|
},
|
|
],
|
|
},
|
|
]
|
|
},
|
|
cacheComponents: true,
|
|
cacheLife: {
|
|
dashboard: {
|
|
stale: 60, // 1 minute — serve stale while revalidating
|
|
revalidate: 300, // 5 minutes — background revalidation interval
|
|
expire: 3600, // 1 hour — hard expiry
|
|
},
|
|
frequent: {
|
|
stale: 30, // 30 seconds
|
|
revalidate: 60, // 1 minute
|
|
expire: 600, // 10 minutes
|
|
},
|
|
},
|
|
transpilePackages: ["@codeflash-ai/common"],
|
|
webpack: (config, { isServer }) => {
|
|
config.watchOptions = {
|
|
poll: 1000,
|
|
aggregateTimeout: 300,
|
|
}
|
|
|
|
// Suppress known-harmless "Critical dependency" warnings from OpenTelemetry
|
|
// and require-in-the-middle. These packages use dynamic require() for runtime
|
|
// monkey-patching — webpack can't statically analyze them but they work fine.
|
|
// Root cause: @sentry/nextjs → @sentry/node → @opentelemetry/instrumentation.
|
|
config.ignoreWarnings = [
|
|
...(config.ignoreWarnings || []),
|
|
{ module: /@opentelemetry\/instrumentation/ },
|
|
{ module: /require-in-the-middle/ },
|
|
]
|
|
|
|
// Handle web-tree-sitter's Node.js module imports in browser.
|
|
// fallback handles static require(); alias handles dynamic import()
|
|
if (!isServer) {
|
|
config.resolve.fallback = {
|
|
...config.resolve.fallback,
|
|
fs: false,
|
|
"fs/promises": false,
|
|
path: false,
|
|
module: false,
|
|
}
|
|
config.resolve.alias = {
|
|
...config.resolve.alias,
|
|
module: false,
|
|
}
|
|
}
|
|
|
|
return config
|
|
},
|
|
turbopack: {
|
|
root: __dirname,
|
|
resolveAlias: {
|
|
// Stub Node.js built-ins that web-tree-sitter tries to import in the browser.
|
|
// Uses { browser: ... } so aliases only apply to client bundles, not SSR.
|
|
'fs': { browser: './src/lib/empty-shim.js' },
|
|
'fs/promises': { browser: './src/lib/empty-shim.js' },
|
|
'path': { browser: './src/lib/empty-shim.js' },
|
|
'module': { browser: './src/lib/empty-shim.js' },
|
|
},
|
|
},
|
|
serverExternalPackages: [
|
|
"@anthropic-ai/sdk",
|
|
"sharp",
|
|
"posthog-node",
|
|
"@opentelemetry/api",
|
|
"@opentelemetry/sdk-node",
|
|
"@opentelemetry/auto-instrumentations-node",
|
|
"@opentelemetry/instrumentation",
|
|
"@prisma/instrumentation",
|
|
"@sentry/opentelemetry",
|
|
"@sentry/node",
|
|
"require-in-the-middle",
|
|
"@fastify/otel",
|
|
],
|
|
experimental: {
|
|
// Tree-shake barrel exports for these heavy packages. Without this,
|
|
// importing a single icon from lucide-react or a single component from
|
|
// chart.js pulls the entire library into the bundle.
|
|
optimizePackageImports: [
|
|
"lucide-react",
|
|
"date-fns",
|
|
"react-syntax-highlighter",
|
|
"chart.js",
|
|
"react-chartjs-2",
|
|
"motion",
|
|
"zod",
|
|
"react-hook-form",
|
|
"@hookform/resolvers",
|
|
"react-markdown",
|
|
"remark-gfm",
|
|
"sonner",
|
|
"react-resizable-panels",
|
|
"@radix-ui/react-dialog",
|
|
"@radix-ui/react-select",
|
|
"@radix-ui/react-tabs",
|
|
"@radix-ui/react-tooltip",
|
|
"@radix-ui/react-toast",
|
|
"chartjs-plugin-datalabels",
|
|
"marked",
|
|
"prism-react-renderer",
|
|
],
|
|
serverActions: {
|
|
allowedOrigins: ["app.codeflash.ai", "localhost:3000"],
|
|
bodySizeLimit: '5mb', // Increased from default 1mb to handle large PR creation payloads
|
|
},
|
|
// NOTE: turbopackRemoveUnused{Imports,Exports} are NOT enabled — they
|
|
// break @opentelemetry/api barrel re-exports and Next.js internal ESM
|
|
// modules (same class of bug as turbopackTreeShaking + @sentry/core below).
|
|
// turbopackRemoveUnusedImports requires turbopackRemoveUnusedExports.
|
|
turbopackInferModuleSideEffects: true,
|
|
// Scope hoisting: collapses module wrappers for smaller output
|
|
turbopackScopeHoisting: true,
|
|
// NOTE: turbopackTreeShaking is NOT enabled — it fragments modules into
|
|
// "internal parts" which breaks @sentry/core's ESM cross-references
|
|
// (withScope, withErrorInstrumentation exports disappear). Re-test when
|
|
// Turbopack or Sentry fixes the incompatibility.
|
|
// Persist compiled artifacts between CI builds
|
|
turbopackFileSystemCacheForBuild: true,
|
|
// Client-side router cache: avoid refetching on back-navigation
|
|
staleTimes: {
|
|
dynamic: 30,
|
|
static: 180,
|
|
},
|
|
},
|
|
typescript: {
|
|
// Type-checking is split into a separate `npm run type-check` step.
|
|
// This cuts ~16s off `next build` (was 60% of build time).
|
|
ignoreBuildErrors: true,
|
|
},
|
|
// Optimize for production stability
|
|
poweredByHeader: false,
|
|
compress: true,
|
|
images: {
|
|
remotePatterns: [
|
|
{
|
|
protocol: "https",
|
|
hostname: "avatars.githubusercontent.com",
|
|
},
|
|
{
|
|
protocol: "https",
|
|
hostname: "github.com",
|
|
},
|
|
],
|
|
formats: ['image/avif', 'image/webp'],
|
|
},
|
|
}
|
|
|
|
import { withSentryConfig } from "@sentry/nextjs"
|
|
|
|
// Only upload source maps when SENTRY_AUTH_TOKEN is set (CI/deploy).
|
|
// Skipping this shaves significant time off local builds.
|
|
const withSentry = process.env.SENTRY_AUTH_TOKEN
|
|
? (config) => withSentryConfig(
|
|
config,
|
|
{
|
|
silent: true,
|
|
org: "codeflash-ai",
|
|
project: "webapp",
|
|
},
|
|
{
|
|
widenClientFileUpload: true,
|
|
tunnelRoute: "/monitoring",
|
|
hideSourceMaps: true,
|
|
disableLogger: true,
|
|
automaticVercelMonitors: false,
|
|
},
|
|
)
|
|
: (config) => config
|
|
|
|
export default withBundleAnalyzer(withSentry(nextConfig))
|