vite.config.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import { defineConfig, loadEnv } from 'vite';
  2. import react from '@vitejs/plugin-react';
  3. import path from 'path';
  4. import fs from 'fs';
  5. // 加载环境变量(Vite 不会自动在配置文件中加载 .env)
  6. const env = loadEnv(process.env.NODE_ENV || 'development', process.cwd(), '');
  7. const normalizeBasePath = (value?: string): string => {
  8. if (!value) {
  9. return '/';
  10. }
  11. let normalized = value.trim();
  12. if (!normalized.startsWith('/')) {
  13. normalized = `/${normalized}`;
  14. }
  15. if (!normalized.endsWith('/')) {
  16. normalized = `${normalized}/`;
  17. }
  18. return normalized.replace(/\/{2,}/g, '/');
  19. };
  20. // 后端 API 地址配置(用于代理)
  21. // 从 WebSocket URL 推导后端地址,或使用环境变量直接指定
  22. const getBackendUrl = (): string => {
  23. // 优先使用环境变量指定的后端地址
  24. if (env.VITE_API_BASE_URL) {
  25. return env.VITE_API_BASE_URL;
  26. }
  27. // 从 WebSocket URL 推导
  28. const wsUrl = env.VITE_WS_URL || 'ws://localhost:8000/ws';
  29. const backendUrl = wsUrl
  30. .replace(/^wss:\/\//, 'https://')
  31. .replace(/^ws:\/\//, 'http://')
  32. .replace(/\/ws$/, '');
  33. return backendUrl;
  34. };
  35. const backendUrl = getBackendUrl();
  36. const appBasePath = normalizeBasePath(env.VITE_APP_BASE_PATH);
  37. console.log('🔗 后端 API 代理配置:');
  38. console.log(` 目标地址: ${backendUrl}`);
  39. console.log(` 代理路径: /api -> ${backendUrl}/api`);
  40. console.log(`🌐 前端基础路径(base): ${appBasePath}`);
  41. // SSL证书配置
  42. // HTTPS开关:通过环境变量 VITE_ENABLE_HTTPS 控制(true/false)
  43. const enableHttps = env.VITE_ENABLE_HTTPS === 'true';
  44. // 优先使用环境变量指定的证书路径(生产环境)
  45. const sslKeyPath = env.VITE_SSL_KEYFILE || path.resolve(__dirname, '../ssl/key.pem');
  46. const sslCertPath = env.VITE_SSL_CERTFILE || path.resolve(__dirname, '../ssl/cert.pem');
  47. // 根据开关和证书文件决定HTTPS配置
  48. let httpsConfig: any = false;
  49. if (enableHttps) {
  50. if (fs.existsSync(sslKeyPath) && fs.existsSync(sslCertPath)) {
  51. httpsConfig = {
  52. key: fs.readFileSync(sslKeyPath),
  53. cert: fs.readFileSync(sslCertPath),
  54. };
  55. console.log('✅ HTTPS已启用,使用SSL证书:');
  56. console.log(` 证书: ${sslCertPath}`);
  57. console.log(` 私钥: ${sslKeyPath}`);
  58. } else {
  59. console.log('⚠️ HTTPS已启用但SSL证书不存在,以HTTP模式启动');
  60. console.log(` 证书路径: ${sslCertPath}`);
  61. console.log(` 私钥路径: ${sslKeyPath}`);
  62. }
  63. } else {
  64. console.log('ℹ️ HTTPS已禁用(VITE_ENABLE_HTTPS=false),以HTTP模式启动');
  65. }
  66. export default defineConfig({
  67. base: appBasePath,
  68. plugins: [react()],
  69. resolve: {
  70. alias: {
  71. '@': path.resolve(__dirname, './src'),
  72. },
  73. },
  74. assetsInclude: ['**/*.GIF'],
  75. server: {
  76. port: 3000,
  77. host: '0.0.0.0', // 允许外部访问
  78. https: httpsConfig,
  79. // 代理配置:将 /api 请求代理到后端服务器
  80. // 这样可以避免跨域和证书问题(Safari 对自签名证书的跨域请求很严格)
  81. proxy: {
  82. '/api': {
  83. target: backendUrl,
  84. changeOrigin: true,
  85. secure: false, // 允许自签名证书
  86. // 如果需要 WebSocket 代理,取消下面的注释
  87. // ws: true,
  88. },
  89. },
  90. },
  91. });