Ctrl K
ComunidadGuías…

dashboard/next.config.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
const path = require('node:path');
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

function getCspHeader() {
  switch (process.env.CSP_MODE) {
    case 'disabled':
      return null;
    case 'custom':
      return process.env.CSP_HEADER || null;
    default:
      return `${[
        "default-src 'self' *.nhost.run wss://*.nhost.run nhost.run wss://nhost.run",
        "script-src 'self' 'unsafe-eval' cdn.segment.com js.stripe.com challenges.cloudflare.com googletagmanager.com",
        "connect-src 'self' *.nhost.run wss://*.nhost.run nhost.run wss://nhost.run discord.com api.segment.io api.segment.com cdn.segment.com nhost.zendesk.com api.github.com",
        "style-src 'self' 'unsafe-inline'",
        "img-src 'self' blob: data: github.com *.githubusercontent.com *.gravatar.com *.nhost.run nhost.run",
        "font-src 'self' data:",
        "object-src 'none'",
        "base-uri 'self'",
        "form-action 'self'",
        "frame-ancestors 'none'",
        "frame-src 'self' js.stripe.com challenges.cloudflare.com",
        'block-all-mixed-content',
        'upgrade-insecure-requests',
      ].join('; ')};`;
  }
}

const PROJECT_PATH = '/orgs/:orgSlug/projects/:appSubdomain';

// Old project URLs from before the area-based project layout, mapped to the
// page that now hosts their content. Incoming query strings are preserved by
// Next.js and merged into the destination query.
const legacyProjectRedirects = [
  // Project settings
  ['/settings/compute-resources', '/settings?tab=compute-resources'],
  ['/settings/environment-variables', '/settings?tab=environment-variables'],
  ['/settings/secrets', '/settings?tab=secrets'],
  ['/settings/editor', '/settings?tab=editor'],

  // Auth
  ['/settings/sign-in-methods', '/auth/settings?tab=sign-in-methods'],
  ['/settings/oauth2-provider', '/auth/settings?tab=oauth2-provider'],
  ['/settings/smtp', '/auth/settings?tab=smtp'],
  ['/settings/authentication', '/auth/settings?tab=authentication'],
  [
    '/settings/roles-and-permissions',
    '/auth/settings?tab=roles-and-permissions',
  ],
  ['/settings/jwt', '/auth/settings?tab=jwt'],
  // Custom domains and rate limiting were split across the area settings
  // pages; Auth was the first section on both old pages.
  ['/settings/custom-domains', '/auth/settings?tab=custom-domain'],
  ['/settings/rate-limiting', '/auth/settings?tab=rate-limiting'],

  // Database
  ['/settings/database', '/database/settings'],
  ['/backups', '/database/backups'],
  [
    '/database/browser/:dataSourceSlug/editor',
    '/database/console/:dataSourceSlug',
  ],

  // GraphQL
  ['/hasura', '/graphql/console'],
  ['/settings/hasura', '/graphql/settings'],

  // Storage
  ['/storage', '/storage/buckets'],
  ['/storage/bucket/:bucketId*', '/storage/buckets/:bucketId*'],
  ['/settings/storage', '/storage/settings'],

  // Functions
  ['/functions', '/functions/browser'],
  // `browser` and `settings` are the new subpages and must not be redirected.
  [
    '/functions/:functionSlug((?!browser(?:/|$)|settings$).+)',
    '/functions/browser/:functionSlug',
  ],

  // Other area settings
  ['/settings/ai', '/ai/settings'],
  ['/settings/deployments', '/deployments/settings'],
  ['/settings/metrics', '/metrics/settings'],
].map(([source, destination]) => ({
  source: `${PROJECT_PATH}${source}`,
  destination: `${PROJECT_PATH}${destination}`,
  permanent: true,
}));

module.exports = withBundleAnalyzer({
  turbopack: {},
  experimental: {
    // Next.js 16.4 enables this by default and inlines each route's chunk
    // bootstrap into the HTML, which our CSP (no 'unsafe-inline') blocks.
    turbopackSharedRuntime: false,
  },
  reactStrictMode: false,
  output: 'standalone',
  outputFileTracingRoot: path.join(__dirname, '../'),
  async headers() {
    const cspHeader = getCspHeader();

    if (!cspHeader) {
      return []; // No CSP headers
    }

    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: cspHeader,
          },
          {
            key: 'X-Frame-Options',
            value: 'DENY',
          },
        ],
      },
    ];
  },
  async redirects() {
    return [
      {
        source: '/login',
        destination: '/signin',
        permanent: true,
      },
      {
        source: '/orgs/:orgSlug/projects/:appSubdomain/database/browser',
        destination:
          '/orgs/:orgSlug/projects/:appSubdomain/database/browser/default',
        permanent: true,
      },
      ...legacyProjectRedirects,
    ];
  },
});