Stage M2
This commit is contained in:
@@ -12,6 +12,20 @@ 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*` }];
|
||||
},
|
||||
|
||||
/**
|
||||
* Workspace packages ship TypeScript source rather than a build artefact, so
|
||||
* Next compiles them with the app. No watch-and-rebuild step during local
|
||||
@@ -23,13 +37,35 @@ const nextConfig: NextConfig = {
|
||||
typedRoutes: true,
|
||||
|
||||
images: {
|
||||
// Media is served from R2/CDN. Locally that is MinIO.
|
||||
/**
|
||||
* 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' },
|
||||
{ protocol: 'https', hostname: '**.r2.dev' },
|
||||
{ protocol: 'https', hostname: 'cdn.sport-store.local' },
|
||||
{ protocol: 'http', hostname: 'localhost', port: '9000', pathname: '/**', search: '' },
|
||||
{ protocol: 'https', hostname: '**.r2.dev', pathname: '/**', search: '' },
|
||||
{ protocol: 'https', hostname: 'cdn.sport-store.local', pathname: '/**', search: '' },
|
||||
],
|
||||
formats: ['image/avif', 'image/webp'],
|
||||
|
||||
/**
|
||||
* DEVELOPMENT ONLY.
|
||||
*
|
||||
* Next 16 refuses to fetch an upstream image whose hostname resolves to a
|
||||
* private IP — an SSRF precaution — and reports it as `"url" parameter is
|
||||
* not allowed`, the same message it uses for an unmatched remote pattern.
|
||||
* That shared message is what makes this so easy to misdiagnose.
|
||||
*
|
||||
* Local MinIO lives on `localhost:9000`, so the guard blocks every product
|
||||
* image in development. It stays ON in production, where media is served
|
||||
* from a public CDN host and the protection is exactly what we want.
|
||||
*/
|
||||
dangerouslyAllowLocalIP: process.env.NODE_ENV !== 'production',
|
||||
},
|
||||
|
||||
// Standalone output keeps the production image small (no node_modules copy).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createApiClient, type RequestOptions } from '@sport/api-client';
|
||||
|
||||
import { clientEnv, getServerEnv } from './env';
|
||||
import { getServerEnv } from './env';
|
||||
|
||||
/**
|
||||
* Two clients, because the two runtimes have different needs:
|
||||
@@ -22,9 +22,21 @@ export function getServerApi() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Browser client.
|
||||
*
|
||||
* `baseUrl: ''` means same-origin: requests go through this app's own host and
|
||||
* are proxied to the API (Next rewrite in development, Nginx in production).
|
||||
* That is what makes the refresh cookie first-party — see ADR-0015 and the
|
||||
* `rewrites()` comment in next.config.ts.
|
||||
*
|
||||
* Pointing this at the API host directly would work for anonymous catalog reads
|
||||
* and then break the moment customer sign-in lands in M8, which is precisely
|
||||
* the kind of latent inconsistency worth removing now.
|
||||
*/
|
||||
export const browserApi = createApiClient({
|
||||
baseUrl: clientEnv.NEXT_PUBLIC_API_URL,
|
||||
getAccessToken: () => null, // wired to the auth store in the auth milestone
|
||||
baseUrl: '',
|
||||
getAccessToken: () => null, // wired to the customer auth store in M8
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user