fix(部署): 补齐直连前端时的 API 反代
This commit is contained in:
@@ -18,6 +18,8 @@ REDIS_URL=redis://redis:6379
|
|||||||
|
|
||||||
# Web
|
# Web
|
||||||
NEXT_PUBLIC_API_URL=/api/v1
|
NEXT_PUBLIC_API_URL=/api/v1
|
||||||
|
# Fallback used by the Next.js server if the web container is exposed directly.
|
||||||
|
NEXT_API_PROXY_TARGET=http://server:3001
|
||||||
NEXTAUTH_URL=http://localhost:8080
|
NEXTAUTH_URL=http://localhost:8080
|
||||||
NEXTAUTH_SECRET=local-server-change-me
|
NEXTAUTH_SECRET=local-server-change-me
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ REDIS_URL=redis://redis:6379
|
|||||||
# Web
|
# Web
|
||||||
# For same-domain Nginx deployment, keep this relative path so browser requests go to /api/v1.
|
# For same-domain Nginx deployment, keep this relative path so browser requests go to /api/v1.
|
||||||
NEXT_PUBLIC_API_URL=/api/v1
|
NEXT_PUBLIC_API_URL=/api/v1
|
||||||
|
# Fallback used by the Next.js server if the web container is exposed directly.
|
||||||
|
NEXT_API_PROXY_TARGET=http://server:3001
|
||||||
NEXTAUTH_URL=https://pm.example.com
|
NEXTAUTH_URL=https://pm.example.com
|
||||||
NEXTAUTH_SECRET=change-me-random-32-byte-secret
|
NEXTAUTH_SECRET=change-me-random-32-byte-secret
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ RUN pnpm config set fetch-retries 5 \
|
|||||||
|
|
||||||
FROM deps AS builder
|
FROM deps AS builder
|
||||||
ARG NEXT_PUBLIC_API_URL=/api/v1
|
ARG NEXT_PUBLIC_API_URL=/api/v1
|
||||||
|
ARG NEXT_API_PROXY_TARGET=
|
||||||
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
|
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
|
||||||
|
ENV NEXT_API_PROXY_TARGET=$NEXT_API_PROXY_TARGET
|
||||||
ENV NEXT_TELEMETRY_DISABLED=1
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
COPY packages/shared packages/shared
|
COPY packages/shared packages/shared
|
||||||
@@ -28,7 +30,9 @@ RUN pnpm --filter web build
|
|||||||
|
|
||||||
FROM base AS runner
|
FROM base AS runner
|
||||||
ARG NEXT_PUBLIC_API_URL=/api/v1
|
ARG NEXT_PUBLIC_API_URL=/api/v1
|
||||||
|
ARG NEXT_API_PROXY_TARGET=
|
||||||
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
|
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
|
||||||
|
ENV NEXT_API_PROXY_TARGET=$NEXT_API_PROXY_TARGET
|
||||||
ENV NEXT_TELEMETRY_DISABLED=1
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
COPY --from=builder /app/package.json ./package.json
|
COPY --from=builder /app/package.json ./package.json
|
||||||
|
|||||||
@@ -20,3 +20,45 @@ test('next dev keeps watch ignored entries compatible with webpack schema', () =
|
|||||||
|
|
||||||
assert.deepEqual(ignoredList, ['**/.tmp-test/**']);
|
assert.deepEqual(ignoredList, ['**/.tmp-test/**']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('next config proxies API requests when the web container is exposed directly', async () => {
|
||||||
|
const previousTarget = process.env.NEXT_API_PROXY_TARGET;
|
||||||
|
process.env.NEXT_API_PROXY_TARGET = 'http://server:3001';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const rewrites = await nextConfig.rewrites();
|
||||||
|
assert.deepEqual(rewrites, [
|
||||||
|
{
|
||||||
|
source: '/api/v1/:path*',
|
||||||
|
destination: 'http://server:3001/api/v1/:path*',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
if (previousTarget === undefined) {
|
||||||
|
delete process.env.NEXT_API_PROXY_TARGET;
|
||||||
|
} else {
|
||||||
|
process.env.NEXT_API_PROXY_TARGET = previousTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('next config accepts API proxy targets that already include the API prefix', async () => {
|
||||||
|
const previousTarget = process.env.NEXT_API_PROXY_TARGET;
|
||||||
|
process.env.NEXT_API_PROXY_TARGET = 'http://server:3001/api/v1/';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const rewrites = await nextConfig.rewrites();
|
||||||
|
assert.deepEqual(rewrites, [
|
||||||
|
{
|
||||||
|
source: '/api/v1/:path*',
|
||||||
|
destination: 'http://server:3001/api/v1/:path*',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
if (previousTarget === undefined) {
|
||||||
|
delete process.env.NEXT_API_PROXY_TARGET;
|
||||||
|
} else {
|
||||||
|
process.env.NEXT_API_PROXY_TARGET = previousTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -7,8 +7,25 @@ function toIgnoredList(ignored) {
|
|||||||
return entries.filter((entry) => typeof entry === 'string' && entry.length > 0);
|
return entries.filter((entry) => typeof entry === 'string' && entry.length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toApiProxyDestination(target) {
|
||||||
|
const cleanTarget = target.trim().replace(/\/+$/, '');
|
||||||
|
if (!cleanTarget) return null;
|
||||||
|
const apiBase = cleanTarget.endsWith('/api/v1') ? cleanTarget : `${cleanTarget}/api/v1`;
|
||||||
|
return `${apiBase}/:path*`;
|
||||||
|
}
|
||||||
|
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
transpilePackages: ['@ftb/shared'],
|
transpilePackages: ['@ftb/shared'],
|
||||||
|
async rewrites() {
|
||||||
|
const destination = toApiProxyDestination(process.env.NEXT_API_PROXY_TARGET || '');
|
||||||
|
if (!destination) return [];
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: '/api/v1/:path*',
|
||||||
|
destination,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
webpack(config, { dev }) {
|
webpack(config, { dev }) {
|
||||||
if (dev) {
|
if (dev) {
|
||||||
const ignored = toIgnoredList(config.watchOptions?.ignored);
|
const ignored = toIgnoredList(config.watchOptions?.ignored);
|
||||||
|
|||||||
@@ -68,10 +68,12 @@ services:
|
|||||||
dockerfile: Dockerfile.web
|
dockerfile: Dockerfile.web
|
||||||
args:
|
args:
|
||||||
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-/api/v1}
|
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-/api/v1}
|
||||||
|
NEXT_API_PROXY_TARGET: ${NEXT_API_PROXY_TARGET:-http://server:3001}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-/api/v1}
|
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-/api/v1}
|
||||||
|
NEXT_API_PROXY_TARGET: ${NEXT_API_PROXY_TARGET:-http://server:3001}
|
||||||
NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:8080}
|
NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:8080}
|
||||||
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-local-server-change-me}
|
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-local-server-change-me}
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@@ -68,10 +68,12 @@ services:
|
|||||||
dockerfile: Dockerfile.web
|
dockerfile: Dockerfile.web
|
||||||
args:
|
args:
|
||||||
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-/api/v1}
|
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-/api/v1}
|
||||||
|
NEXT_API_PROXY_TARGET: ${NEXT_API_PROXY_TARGET:-http://server:3001}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-/api/v1}
|
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-/api/v1}
|
||||||
|
NEXT_API_PROXY_TARGET: ${NEXT_API_PROXY_TARGET:-http://server:3001}
|
||||||
NEXTAUTH_URL: ${NEXTAUTH_URL:-}
|
NEXTAUTH_URL: ${NEXTAUTH_URL:-}
|
||||||
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-}
|
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-}
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
Reference in New Issue
Block a user