vite.config.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { defineConfig, loadEnv } from "vite";
  2. // import { darkTheme } from "./src/theme.js";
  3. import vue from "@vitejs/plugin-vue";
  4. import Components from "unplugin-vue-components/vite";
  5. import { AntDesignVueResolver } from "unplugin-vue-components/resolvers";
  6. import { visualizer } from "rollup-plugin-visualizer";
  7. import svgLoader from "vite-svg-loader";
  8. import path from "path";
  9. import basicSsl from "@vitejs/plugin-basic-ssl";
  10. // DMS token 的读取口径收在 harness/tools/dms-token.mjs(与本仓库的 DMS 脚本共用一份):
  11. // **显式参数 → 环境变量 → 本机文件 `.dms-token`**。
  12. // 关键是「每个请求现读」而不是启动时读一次 —— token 约 24 小时过期,以前换一次就要重启 dev server。
  13. import { DMS_TOKEN_FILE, readDmsToken, parseJwtPayload, describeDmsToken } from "./harness/tools/dms-token.mjs";
  14. /** 同一个状态只提醒一次,避免每个请求都刷屏 */
  15. let dmsTokenWarnedFor = "";
  16. /**
  17. * token 有问题时给出**明确**提示 —— 以前 token 过期是**静默失败**(DMS 写入不生效、
  18. * 只有一条埋在别处控制台里的 warn),排查「为什么数据没存上」要花很久。
  19. */
  20. const warnDmsTokenIfNeeded = (token: string): void => {
  21. if (!token) {
  22. if (dmsTokenWarnedFor === "missing") return;
  23. dmsTokenWarnedFor = "missing";
  24. console.warn(
  25. `\n[dms] ⚠️ 没有 DMS token,DMS 读写(会话/问答记录/反馈)会失败,聊天本身不受影响。\n` +
  26. `[dms] 最省事:在浏览器里复制 token,然后跑 \`npm run dms:token\`(从剪贴板读)。\n` +
  27. `[dms] 文件位置:${DMS_TOKEN_FILE}\n`
  28. );
  29. return;
  30. }
  31. const exp = Number(parseJwtPayload(token)?.exp);
  32. if (!Number.isFinite(exp)) return;
  33. const leftMs = exp * 1000 - Date.now();
  34. const at = new Date(exp * 1000).toLocaleString("zh-CN");
  35. if (leftMs <= 0) {
  36. if (dmsTokenWarnedFor === `expired:${exp}`) return;
  37. dmsTokenWarnedFor = `expired:${exp}`;
  38. console.warn(
  39. `\n[dms] ⚠️ DMS token 已于 ${at} 过期(DMS 读写会失败)。复制新 token 后跑 \`npm run dms:token\`。\n`
  40. );
  41. return;
  42. }
  43. if (leftMs < 2 * 3600 * 1000) {
  44. const key = `soon:${exp}`;
  45. if (dmsTokenWarnedFor === key) return;
  46. dmsTokenWarnedFor = key;
  47. console.warn(`\n[dms] ⏳ DMS token 将于 ${at} 过期(还剩 ${(leftMs / 3600000).toFixed(1)} 小时)。\n`);
  48. }
  49. };
  50. // https://vitejs.dev/config/
  51. export default defineConfig(({ mode }) => {
  52. // 第三个参数传空串:不加前缀过滤,才能读到 DMS_TOKEN 这类非 VITE_ 前缀的变量
  53. // (DMS_TOKEN 只写在 .env.development.local,该文件按 .gitignore 不入库)
  54. const env = loadEnv(mode, process.cwd(), "");
  55. return {
  56. plugins: [
  57. vue(),
  58. Components({
  59. resolvers: [AntDesignVueResolver()],
  60. }),
  61. visualizer({
  62. open: false,
  63. gzipSize: true,
  64. file: "./dist/stats.html",
  65. brotliSize: true,
  66. }),
  67. svgLoader(),
  68. basicSsl(),
  69. ],
  70. server: {
  71. proxy: {
  72. //配置本地代理
  73. "/api/": {
  74. //匹配的路径
  75. target: "http://aixq.shqp.gov.cn", //目标url
  76. changeOrigin: true, //跨域
  77. rewrite: (path) => path.replace(/^\/api/, ""), //重写路径
  78. },
  79. // 新版聊天后端代理(POST /api/chat,SSE)
  80. // 开发服务器是 HTTPS 而后端是 HTTP,浏览器会拦截直连(混合内容),
  81. // 且后端未实现 CORS,因此开发环境统一走同源代理。
  82. // 对应 .env.development 中 VITE_CHAT_API="/chat-api"
  83. // target 取 VITE_CHAT_TARGET(地址收在 env 里,不再写死在配置文件中)
  84. "/chat-api/": {
  85. target: env.VITE_CHAT_TARGET || "http://192.168.2.23:8000",
  86. changeOrigin: true,
  87. rewrite: (path) => path.replace(/^\/chat-api/, ""),
  88. },
  89. // DMS 数据管理服务(会话/问答记录/反馈)
  90. // 两件事都在代理侧做,浏览器拿不到:
  91. // ① 补回 /dms 前缀(DMS 的真实路由都在 /dms/ 下)
  92. // ② 注入 token 头(DMS 的鉴权头名就叫 token,不是 Authorization)
  93. // token 来源:.env.development.local 的 DMS_TOKEN(不入库)
  94. "/dms-api/": {
  95. target: env.VITE_DMS_TARGET || "http://121.43.55.7:10081",
  96. changeOrigin: true,
  97. rewrite: (path) => path.replace(/^\/dms-api/, "/dms"),
  98. // token 在**每个请求**现读(见 readDmsToken 的说明):换 token 不用重启 dev server。
  99. // 用 configure 而不是静态 headers —— 后者的值在启动时就定死了。
  100. configure: (proxy) => {
  101. proxy.on("proxyReq", (proxyReq) => {
  102. // 现读:换 token 后不需要重启 dev server
  103. const token = readDmsToken(env.DMS_TOKEN);
  104. if (token) {
  105. // DMS 的鉴权头名就叫 token(不是 Authorization)
  106. proxyReq.setHeader("token", token);
  107. }
  108. warnDmsTokenIfNeeded(token);
  109. });
  110. },
  111. },
  112. "/asr/": {
  113. //匹配的路径
  114. target: "https://human-screen-v3.metamaker.cn", //目标url
  115. changeOrigin: true, //跨域
  116. rewrite: (path) => path.replace(/^\/asr/, ""), //重写路径
  117. ws: true,
  118. },
  119. // [已移除] "/stream/" 代理(数字人视频流):src/index.html 里没有任何代码往它发请求,
  120. // 相关 env(VITE_STREAM_SERVER / VITE_STREAM_TOKEN / VITE_HUMAN_IDLE_MP4_URL)也已删除。
  121. // 注意 "/asr/" 代理**保留** —— 它是切到讯飞 ASR 分支(VITE_XF_ASR="/asr")的退路。
  122. },
  123. },
  124. build: {
  125. rollupOptions: {
  126. input: {
  127. main: path.resolve(__dirname, "index.html"),
  128. redirect: path.resolve(__dirname, "redirect.html"),
  129. },
  130. output: {
  131. manualChunks: (id) => {
  132. console.log(id);
  133. if (id.includes("node_modules/three")) {
  134. return "three";
  135. } else if (id.includes("node_modules")) {
  136. return "vendor";
  137. }
  138. },
  139. },
  140. },
  141. assetsInlineLimit: 20480,
  142. },
  143. resolve: {
  144. alias: {
  145. "@": path.resolve("./src/"),
  146. three$: path.resolve(__dirname, "./node_modules/three/build/three.cjs"),
  147. },
  148. },
  149. base: "./",
  150. css: {
  151. preprocessorOptions: {
  152. scss: {
  153. api: "modern-compiler",
  154. },
  155. },
  156. },
  157. };
  158. });