fix(mining-admin-web): 修复 API 请求超时和路由问题

问题:
- mining-admin-web 容器与后端服务在不同 Docker 网络, 无法通过公网 IP 回连 (hairpin NAT 失败)
- next.config.js 生产模式强制走 mapi.szaiai.com Kong 网关, 容器内无法访问
- client.ts 使用 NEXT_PUBLIC_API_URL 导致外部 URL 被 build 时内联到客户端包
- mining-service controller 有 mining/ 前缀, 直连模式 rewrite 丢失该前缀

修复:
- next.config.js: 改用 API_GATEWAY_URL 判断路由模式, 未设置则直连后端服务
- next.config.js: mining rewrite 保留 mining/ 前缀匹配 controller 路由
- client.ts: baseURL 固定为 /api, 所有请求统一走 Next.js rewrite 代理
- docker-compose.2.0.yml: 添加 TRADING_SERVICE_URL 和 MINING_SERVICE_URL 环境变量

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-02 01:14:19 -08:00
parent d9d8f69562
commit 59efdb1f78
3 changed files with 16 additions and 16 deletions

View File

@ -480,6 +480,10 @@ services:
PORT: 3100
NEXT_PUBLIC_API_URL: http://mining-admin-service:3023
NEXT_PUBLIC_APP_NAME: 挖矿管理后台
# 直连后端服务 (同一 Docker 网络内, 不经 Kong 网关)
# 如需走 Kong 网关, 设置 API_GATEWAY_URL 即可 (如 https://mapi.szaiai.com)
TRADING_SERVICE_URL: http://trading-service:3022
MINING_SERVICE_URL: http://mining-service:3021
ports:
- "3100:3100"
healthcheck:

View File

@ -3,27 +3,21 @@ const nextConfig = {
reactStrictMode: true,
output: 'standalone',
async rewrites() {
// 获取 API 基础 URLKong 网关地址)
// 生产环境: https://mapi.szaiai.com
// 开发环境: http://localhost:3023 (mining-admin-service)
const apiGatewayUrl = process.env.API_GATEWAY_URL || 'https://mapi.szaiai.com';
// API 路由模式:
// 1. 设置了 API_GATEWAY_URL -> 通过 Kong 网关代理 (适用于前端与后端不在同一 Docker 网络)
// 2. 未设置 API_GATEWAY_URL -> 直连各后端服务 (适用于同一 Docker 网络, 推荐 standalone 模式)
const apiGatewayUrl = process.env.API_GATEWAY_URL || '';
const miningAdminUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3023';
const tradingServiceUrl = process.env.TRADING_SERVICE_URL || 'http://localhost:3022';
const miningServiceUrl = process.env.MINING_SERVICE_URL || 'http://localhost:3021';
// 检查是否是生产环境(使用 Kong 网关)
const isProduction = process.env.NODE_ENV === 'production';
// 移除末尾可能存在的路径避免重复
const cleanMiningAdminUrl = miningAdminUrl.replace(/\/api\/v2.*$/, '');
const cleanTradingUrl = tradingServiceUrl.replace(/\/api\/v2.*$/, '');
const cleanMiningUrl = miningServiceUrl.replace(/\/api\/v2.*$/, '');
if (isProduction) {
// 生产环境:通过 Kong 网关
// /api/trading/* -> Kong -> trading-service
// /api/mining/* -> Kong -> mining-service
// /api/* -> Kong -> mining-admin-service
if (apiGatewayUrl) {
// 通过 Kong 网关: 所有请求经 Kong 路由分发到各服务
return [
{
source: '/api/trading/:path*',
@ -39,15 +33,16 @@ const nextConfig = {
},
];
} else {
// 开发环境:直连各服务
// 直连各服务: 前端与后端在同一 Docker 网络内直接通信
return [
{
source: '/api/trading/:path*',
destination: `${cleanTradingUrl}/api/v2/:path*`,
},
{
// mining-service 的 controller 有 mining/ 前缀, 需保留
source: '/api/mining/:path*',
destination: `${cleanMiningUrl}/api/v2/:path*`,
destination: `${cleanMiningUrl}/api/v2/mining/:path*`,
},
{
source: '/api/:path*',

View File

@ -1,7 +1,8 @@
import axios from 'axios';
// 生产环境使用 NEXT_PUBLIC_API_URL开发环境使用 /api 代理
const baseURL = process.env.NEXT_PUBLIC_API_URL || '/api';
// 始终使用 /api 前缀, 通过 Next.js rewrite 代理到后端服务
// NEXT_PUBLIC_API_URL 仅供服务端使用 (next.config.js rewrites), 不在客户端使用
const baseURL = '/api';
export const apiClient = axios.create({
baseURL,