SFT/vite.config.js

101 lines
4.1 KiB
JavaScript

import { fileURLToPath, URL } from 'node:url'
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '')
// 临时CORS规避方案(仅用于本地 npm run dev 调试真实后端,不用于生产构建):
// 在 .env.development.local(已被 .gitignore 的 *.local 规则忽略,不会提交)中配置
// DEV_API_PROXY_TARGET=真实后端地址 后,dev server 会把浏览器发往同源 /api 的请求
// 在 Node 端转发给真实后端;浏览器全程只与同源的 localhost:5173 通信,天然不触发
// 浏览器 CORS 检查。同时需要把该 .env.development.local 中的 VITE_API_BASE_URL
// 设为空字符串,让 axios 走相对路径(同源),否则请求不会经过下面的代理规则。
// 未配置 DEV_API_PROXY_TARGET 时,proxy 为 undefined,行为与之前完全一致
// (直接请求 VITE_API_BASE_URL 指向的地址,默认走本地 mock server)。
const proxy = env.DEV_API_PROXY_TARGET
? {
'/api': {
target: env.DEV_API_PROXY_TARGET,
changeOrigin: true,
cookieDomainRewrite: 'localhost',
configure: (proxyServer, options) => {
let seq = 0
const startTimeMap = new Map()
const LOG_MAX_LEN = 2000
// 内容过长(如文件上传/OCR的base64)时截断,避免刷屏终端
const truncate = (str) => {
if (str.length <= LOG_MAX_LEN) return str
return `${str.slice(0, LOG_MAX_LEN)}...(已截断,原文共${str.length}字符)`
}
// 请求体/响应体优先按JSON美化打印,非JSON内容按原文打印
const formatBody = (buf) => {
const text = buf.toString('utf-8')
if (!text) return ''
try {
return truncate(JSON.stringify(JSON.parse(text)))
} catch {
return truncate(text)
}
}
proxyServer.on('proxyReq', (proxyReq, req) => {
const id = ++seq
startTimeMap.set(req, { id, startAt: Date.now() })
// 强制真实后端返回未压缩内容,否则响应体是gzip二进制,日志打印会是乱码
proxyReq.setHeader('Accept-Encoding', 'identity')
const targetUrl = new URL(options.target)
const fullUrl = `${targetUrl.protocol}//${targetUrl.host}${proxyReq.path}`
// 在 http-proxy 内部把 req 自动 pipe 给 proxyReq 之前先挂上监听,
// 这样既能拿到完整请求体,又不影响正常转发流程
const chunks = []
req.on('data', (chunk) => chunks.push(chunk))
req.on('end', () => {
const body = formatBody(Buffer.concat(chunks))
console.log(`\n[REAL-API #${id}] → ${req.method} ${fullUrl}`)
if (body) console.log(` 请求体: ${body}`)
})
})
proxyServer.on('proxyRes', (proxyRes, req) => {
const meta = startTimeMap.get(req) || {}
const chunks = []
proxyRes.on('data', (chunk) => chunks.push(chunk))
proxyRes.on('end', () => {
const body = formatBody(Buffer.concat(chunks))
const cost = meta.startAt ? `${Date.now() - meta.startAt}ms` : ''
console.log(`[REAL-API #${meta.id ?? '?'}] ← ${proxyRes.statusCode} (${cost})`)
if (body) console.log(` 响应体: ${body}`)
startTimeMap.delete(req)
})
})
proxyServer.on('error', (err, req) => {
const meta = startTimeMap.get(req) || {}
console.log(`[REAL-API #${meta.id ?? '?'}] ✕ 转发异常 ${req?.method} ${req?.url}: ${err.message}`)
startTimeMap.delete(req)
})
}
}
}
: undefined
return {
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
port: 5173,
proxy
}
}
})