Files
tokens-reef/frontend/vite.config.ts
User 5ca850700b
Some checks failed
CI / test (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
Security Scan / backend-security (push) Has been cancelled
Security Scan / frontend-security (push) Has been cancelled
feat: comprehensive review optimizations and Chinese model support
- Add fallback pricing for 10 Chinese AI providers (DeepSeek, Qwen, GLM,
  Moonshot, Doubao, MiniMax, ERNIE, Spark, Hunyuan, Yi) with 38 test cases
- Add 32 Chinese model entries to pricing JSON (qwen-long, qwq-32b,
  glm-4-air, spark-max/pro/lite, hunyuan-pro/standard/lite, etc.)
- Add 6 Chinese API domains to SSRF allowlist
- Fix XSS vulnerability in HomeView.vue with DOMPurify sanitization
- Change DB SSL default from 'disable' to 'prefer' with security comment
- Replace hardcoded admin password in config.example.yaml
- Enable @typescript-eslint/no-explicit-any as warning
- Add vite-plugin-compression for gzip production builds
- Add Prettier and EditorConfig configurations
- Fix missing context import in sticky_session_test.go
2026-04-12 22:18:35 +08:00

136 lines
3.8 KiB
TypeScript

import { defineConfig, loadEnv, Plugin } from 'vite'
import vue from '@vitejs/plugin-vue'
import checker from 'vite-plugin-checker'
import viteCompression from 'vite-plugin-compression'
import { resolve } from 'path'
/**
* Vite 插件:开发模式下注入公开配置到 index.html
* 与生产模式的后端注入行为保持一致,消除闪烁
*/
function injectPublicSettings(backendUrl: string): Plugin {
return {
name: 'inject-public-settings',
apply: 'serve',
transformIndexHtml: {
order: 'pre',
async handler(html) {
try {
const response = await fetch(`${backendUrl}/api/v1/settings/public`, {
signal: AbortSignal.timeout(2000)
})
if (response.ok) {
const data = await response.json()
if (data.code === 0 && data.data) {
const script = `<script>window.__APP_CONFIG__=${JSON.stringify(data.data)};</script>`
return html.replace('</head>', `${script}\n</head>`)
}
}
} catch (e) {
console.warn('[vite] 无法获取公开配置,将回退到 API 调用:', (e as Error).message)
}
return html
}
}
}
}
export default defineConfig(({ mode }) => {
// 加载环境变量
const env = loadEnv(mode, process.cwd(), '')
const backendUrl = env.VITE_DEV_PROXY_TARGET || 'http://localhost:8080'
const devPort = Number(env.VITE_DEV_PORT || 3000)
return {
plugins: [
vue(),
checker({
typescript: true,
vueTsc: true
}),
injectPublicSettings(backendUrl),
viteCompression({
algorithm: 'gzip',
threshold: 10240,
deleteOriginFile: false
})
],
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
// 使用 vue-i18n 运行时版本,避免 CSP unsafe-eval 问题
'vue-i18n': 'vue-i18n/dist/vue-i18n.runtime.esm-bundler.js'
}
},
define: {
// 启用 vue-i18n JIT 编译,在 CSP 环境下处理消息插值
// JIT 编译器生成 AST 对象而非 JS 代码,无需 unsafe-eval
__INTLIFY_JIT_COMPILATION__: true
},
build: {
outDir: '../backend/internal/web/dist',
emptyOutDir: true,
rollupOptions: {
output: {
/**
* 手动分包配置
* 分离第三方库并按功能合并应用代码,避免循环依赖
*/
manualChunks(id: string) {
if (id.includes('node_modules')) {
// Vue 核心库
if (
id.includes('/vue/') ||
id.includes('/vue-router/') ||
id.includes('/pinia/') ||
id.includes('/@vue/')
) {
return 'vendor-vue'
}
// UI 工具库(较大,单独分离)
if (id.includes('/@vueuse/') || id.includes('/xlsx/')) {
return 'vendor-ui'
}
// 图表库
if (id.includes('/chart.js/') || id.includes('/vue-chartjs/')) {
return 'vendor-chart'
}
// 国际化
if (id.includes('/vue-i18n/') || id.includes('/@intlify/')) {
return 'vendor-i18n'
}
// 其他小型第三方库合并
return 'vendor-misc'
}
// 应用代码:按入口点自动分包,不手动干预
// 这样可以避免循环依赖,同时保持合理的 chunk 数量
}
}
}
},
server: {
host: '0.0.0.0',
port: devPort,
proxy: {
'/api': {
target: backendUrl,
changeOrigin: true
},
'/v1': {
target: backendUrl,
changeOrigin: true
},
'/setup': {
target: backendUrl,
changeOrigin: true
}
}
}
}
})