|
|
import { defineConfig, loadEnv } from 'vite'
|
|
|
import vue from '@vitejs/plugin-vue'
|
|
|
import { fileURLToPath, URL } from 'node:url'
|
|
|
import dns from 'node:dns'
|
|
|
|
|
|
// 解决 Node.js 17+ 默认使用 IPv6 导致无法连接 localhost 的问题
|
|
|
try {
|
|
|
dns.setDefaultResultOrder('ipv4first')
|
|
|
} catch (e) {
|
|
|
console.warn('Failed to set DNS result order to ipv4first', e)
|
|
|
}
|
|
|
|
|
|
export default defineConfig(({ mode }) => {
|
|
|
// 加载环境变量
|
|
|
const env = loadEnv(mode, process.cwd(), '')
|
|
|
|
|
|
// 开发环境必须配置代理目标
|
|
|
if (mode === 'development') {
|
|
|
if (!env.VITE_API_PROXY_TARGET) {
|
|
|
throw new Error(
|
|
|
'❌ 错误: 开发环境必须配置 VITE_API_PROXY_TARGET 环境变量\n' +
|
|
|
'请在 .env.development 文件中添加:\n' +
|
|
|
'VITE_API_PROXY_TARGET=http://czemc.localhost\n' +
|
|
|
'\n' +
|
|
|
'示例:\n' +
|
|
|
'VITE_API_PROXY_TARGET=http://czemc.localhost\n' +
|
|
|
'或\n' +
|
|
|
'VITE_API_PROXY_TARGET=http://localhost:8000'
|
|
|
)
|
|
|
}
|
|
|
// 打印代理配置信息
|
|
|
console.log('🔧 Vite 代理配置:', {
|
|
|
target: env.VITE_API_PROXY_TARGET,
|
|
|
mode: mode
|
|
|
})
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
// 配置基础路径,使用相对路径以支持子路径部署
|
|
|
// 如果部署在根路径,可以设置为 '/'
|
|
|
// 如果部署在子路径(如 /budget/),使用相对路径 './' 更灵活
|
|
|
base: env.VITE_BASE_PATH || './',
|
|
|
plugins: [vue()],
|
|
|
resolve: {
|
|
|
alias: {
|
|
|
'@': fileURLToPath(new URL('./src', import.meta.url))
|
|
|
}
|
|
|
},
|
|
|
server: {
|
|
|
port: 3000,
|
|
|
open: true,
|
|
|
proxy: mode === 'development' ? {
|
|
|
// 代理所有 /api 请求到后端服务器
|
|
|
'/api': {
|
|
|
target: env.VITE_API_PROXY_TARGET,
|
|
|
changeOrigin: true,
|
|
|
secure: false,
|
|
|
ws: true,
|
|
|
// 添加 X-Forwarded-* 头
|
|
|
xfwd: true,
|
|
|
// 不重写路径
|
|
|
rewrite: (path) => path,
|
|
|
// 添加代理日志和错误处理
|
|
|
configure: (proxy, options) => {
|
|
|
// 如果配置了 VITE_API_PROXY_HOST_HEADER,则强制设置 Host 头
|
|
|
// 这对于后端配置了虚拟主机(Virtual Host)的情况非常有用
|
|
|
if (env.VITE_API_PROXY_HOST_HEADER) {
|
|
|
proxy.on('proxyReq', (proxyReq) => {
|
|
|
proxyReq.setHeader('Host', env.VITE_API_PROXY_HOST_HEADER)
|
|
|
})
|
|
|
}
|
|
|
|
|
|
console.log('✅ 代理中间件已配置:', {
|
|
|
target: options.target,
|
|
|
path: '/api'
|
|
|
})
|
|
|
|
|
|
proxy.on('proxyReq', (proxyReq, req, res) => {
|
|
|
const targetUrl = `${options.target}${req.url}`
|
|
|
console.log(`➡️ [代理转发] ${req.method} ${req.url}`)
|
|
|
console.log(` → 目标地址: ${targetUrl}`)
|
|
|
console.log(` → Host 头: ${proxyReq.getHeader('host')}`)
|
|
|
console.log(` → 请求头:`, JSON.stringify(proxyReq.getHeaders(), null, 2))
|
|
|
})
|
|
|
|
|
|
proxy.on('proxyRes', (proxyRes, req, res) => {
|
|
|
console.log(`⬅️ [代理响应] ${req.url} -> ${proxyRes.statusCode}`)
|
|
|
console.log(` → 响应头:`, JSON.stringify(proxyRes.headers, null, 2))
|
|
|
})
|
|
|
|
|
|
proxy.on('error', (err, req, res) => {
|
|
|
console.error(`❌ [代理错误] ${req.url}:`, err.message)
|
|
|
console.error(` 错误代码: ${err.code}`)
|
|
|
console.error(` 错误详情:`, {
|
|
|
code: err.code,
|
|
|
errno: err.errno,
|
|
|
syscall: err.syscall,
|
|
|
address: err.address,
|
|
|
port: err.port,
|
|
|
stack: err.stack
|
|
|
})
|
|
|
|
|
|
// 尝试发送错误响应
|
|
|
if (res && !res.headersSent) {
|
|
|
try {
|
|
|
res.writeHead(500, {
|
|
|
'Content-Type': 'application/json',
|
|
|
'Access-Control-Allow-Origin': '*'
|
|
|
})
|
|
|
res.end(JSON.stringify({
|
|
|
code: 500,
|
|
|
msg: '代理服务器错误',
|
|
|
error: err.message,
|
|
|
errorCode: err.code
|
|
|
}))
|
|
|
} catch (e) {
|
|
|
console.error('无法发送错误响应:', e)
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
},
|
|
|
// 保持请求路径不变,直接转发到目标服务器
|
|
|
// 例如: /api/budget/xxx -> http://czemc.localhost/api/budget/xxx
|
|
|
}
|
|
|
} : undefined
|
|
|
},
|
|
|
// 定义全局常量替换
|
|
|
define: {
|
|
|
// 确保环境变量在构建时被正确替换
|
|
|
__APP_ENV__: JSON.stringify(env.VITE_APP_ENV || mode)
|
|
|
},
|
|
|
// 构建配置
|
|
|
build: {
|
|
|
// 从环境变量读取构建输出目录,如果没有则使用默认值
|
|
|
outDir: env.VITE_BUILD_OUT_DIR || 'dist',
|
|
|
sourcemap: mode === 'development',
|
|
|
// 根据环境输出不同的构建产物名称
|
|
|
rollupOptions: {
|
|
|
output: {
|
|
|
// 可以根据环境自定义输出文件名
|
|
|
entryFileNames: `assets/[name].${mode}.js`,
|
|
|
chunkFileNames: `assets/[name].${mode}.js`,
|
|
|
assetFileNames: `assets/[name].${mode}.[ext]`
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
|