Files
web_sport/apps/admin/next.config.ts
T
2026-08-13 23:20:23 +07:00

68 lines
2.4 KiB
TypeScript

import type { NextConfig } from 'next';
import createNextIntlPlugin from 'next-intl/plugin';
/** Registers the cookie-based locale resolver in src/i18n/request.ts. */
const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
const nextConfig: NextConfig = {
reactStrictMode: true,
/**
* Proxies API calls through this app's own origin.
*
* The refresh token is a `SameSite=Lax` httpOnly cookie, so the browser only
* sends it first-party. Calling the API host directly from the browser would
* mean `SameSite=None; Secure`, which cannot work over plain HTTP in local
* development at all. Production does the same thing at the Nginx layer, so
* dev and prod share one topology instead of two.
*/
async rewrites() {
const target = process.env.API_INTERNAL_URL ?? 'http://localhost:4000';
return [{ source: '/api/:path*', destination: `${target}/api/:path*` }];
},
transpilePackages: ['@sport/ui'],
typedRoutes: true,
output: 'standalone',
images: {
/**
* Media is served from R2/CDN in production and MinIO locally.
*
* `pathname` and `search` are specified explicitly: Next 16 matches remote
* patterns strictly, and an entry without them does not authorise the URL —
* the optimizer answers `"url" parameter is not allowed` and every product
* image renders broken. Scoping to the bucket path also keeps this from
* becoming an open image proxy.
*/
remotePatterns: [
{ protocol: 'http', hostname: 'localhost', port: '9000', pathname: '/**', search: '' },
{ protocol: 'https', hostname: '**.r2.dev', pathname: '/**', search: '' },
{ protocol: 'https', hostname: 'cdn.sport-store.local', pathname: '/**', search: '' },
],
/**
* DEVELOPMENT ONLY. See the storefront config for the full explanation:
* Next 16 blocks upstream images on private IPs (SSRF guard) and reports it
* with the same message as an unmatched pattern. Local MinIO is on
* localhost, production media is on a public CDN host.
*/
dangerouslyAllowLocalIP: process.env.NODE_ENV !== 'production',
},
// The admin is an internal tool: keep it out of every index, permanently.
async headers() {
return [
{
source: '/:path*',
headers: [
{ key: 'X-Robots-Tag', value: 'noindex, nofollow' },
{ key: 'Referrer-Policy', value: 'same-origin' },
],
},
];
},
};
export default withNextIntl(nextConfig);