vite.config.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import { defineConfig } 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 svgLoader from "vite-svg-loader";
  7. import path from "path";
  8. import basicSsl from "@vitejs/plugin-basic-ssl";
  9. // DMS token 读取口(与仓库 DMS 脚本共用一份实现,harness/tools/dms-token.mjs):
  10. // 显式参数 → 环境变量 → 本机文件 `.dms-token`;每个请求现读,换 token 不用重启 dev server。
  11. import { DMS_TOKEN_FILE, readDmsToken, parseJwtPayload, describeDmsToken } from "./harness/tools/dms-token.mjs";
  12. /** 同一个状态只提醒一次,避免每个请求都刷屏 */
  13. let dmsTokenWarnedFor = "";
  14. /** token 缺失/过期时给出明确提示(否则 DMS 写入会静默失败,难排查) */
  15. const warnDmsTokenIfNeeded = (token: string): void => {
  16. if (!token) {
  17. if (dmsTokenWarnedFor === "missing") return;
  18. dmsTokenWarnedFor = "missing";
  19. console.warn(
  20. `\n[dms] ⚠️ 没有 DMS token,DMS 读写(会话/问答记录/反馈)会失败,聊天本身不受影响。\n` +
  21. `[dms] 最省事:在浏览器里复制 token,然后跑 \`npm run dms:token\`(从剪贴板读)。\n` +
  22. `[dms] 文件位置:${DMS_TOKEN_FILE}\n`
  23. );
  24. return;
  25. }
  26. const exp = Number(parseJwtPayload(token)?.exp);
  27. if (!Number.isFinite(exp)) return;
  28. const leftMs = exp * 1000 - Date.now();
  29. const at = new Date(exp * 1000).toLocaleString("zh-CN");
  30. if (leftMs <= 0) {
  31. if (dmsTokenWarnedFor === `expired:${exp}`) return;
  32. dmsTokenWarnedFor = `expired:${exp}`;
  33. console.warn(
  34. `\n[dms] ⚠️ DMS token 已于 ${at} 过期(DMS 读写会失败)。复制新 token 后跑 \`npm run dms:token\`。\n`
  35. );
  36. return;
  37. }
  38. if (leftMs < 2 * 3600 * 1000) {
  39. const key = `soon:${exp}`;
  40. if (dmsTokenWarnedFor === key) return;
  41. dmsTokenWarnedFor = key;
  42. console.warn(`\n[dms] ⏳ DMS token 将于 ${at} 过期(还剩 ${(leftMs / 3600000).toFixed(1)} 小时)。\n`);
  43. }
  44. };
  45. export default defineConfig(() => {
  46. return {
  47. plugins: [
  48. vue(),
  49. Components({
  50. resolvers: [AntDesignVueResolver()],
  51. }),
  52. svgLoader(),
  53. basicSsl(),
  54. ],
  55. server: {
  56. proxy: {
  57. // 代理总原则:前缀尽量与服务真实路径一致,只有服务端本身不带前缀时才剥。
  58. // /oauth/** → 121.43.55.7:10086,直通(真实路径就带 /oauth)
  59. // /dms/** → 121.43.55.7:10081,直通(真实路径就带 /dms),请求没带 token 时代理注入
  60. // /api/** → 121.43.55.7:10011,剥掉 /api(网关真实路径是 /proxy/verify/…)
  61. // /chat-api/** → 聊天后端,剥前缀(后端真实路径是 /api/chat,与前端前缀不同名)
  62. // 三台后端都只有 HTTP 而 dev server 是 HTTPS,直连会被浏览器按混合内容拦掉,
  63. // 必须由服务端这一跳(dev 是 vite 代理,生产是 nginx)代劳。
  64. // ⚠️ target 直接写在这里、不从环境变量读(所以本文件不 loadEnv),换地址就改这四处。
  65. // 后端网关:语音识别 token(/proxy/verify/getALYToken)与取本机 IP(/proxy/verify/getIPByBrowser)
  66. // 在同一台,共用这一条。
  67. "/api/": {
  68. target: "http://121.43.55.7:10011",
  69. changeOrigin: true,
  70. rewrite: (path) => path.replace(/^\/api/, ""),
  71. },
  72. // DMS 登录服务 121.43.55.7:10086(真实路径带 /oauth,所以直通、不改写):
  73. // 未登录访客用服务账号换 DMS token 走这里。
  74. "/oauth/": {
  75. target: "http://121.43.55.7:10086",
  76. changeOrigin: true,
  77. },
  78. // DMS 数据管理服务(会话/问答记录/反馈)——真实路由都在 /dms/ 下,前缀同名,直通。
  79. // 鉴权头名是 token(不是 Authorization):前端自己带(service-token.ts)时**代理不覆盖**,
  80. // 只有请求没带时才由代理兜底注入(读仓库根 `.dms-token` 或环境变量 DMS_TOKEN)。
  81. // 每请求现读,所以换 token 不用重启 dev server。
  82. "/dms/": {
  83. target: "http://121.43.55.7:10081",
  84. changeOrigin: true,
  85. configure: (proxy) => {
  86. proxy.on("proxyReq", (proxyReq) => {
  87. // 前端已经带了 token 就别覆盖
  88. const fromClient =
  89. typeof proxyReq.getHeader === "function" ? proxyReq.getHeader("token") : undefined;
  90. if (fromClient) return;
  91. const token = readDmsToken();
  92. if (token) {
  93. proxyReq.setHeader("token", token);
  94. }
  95. warnDmsTokenIfNeeded(token);
  96. });
  97. },
  98. },
  99. // 聊天后端(POST /api/chat,SSE):真实路径是 /api/chat,与前端前缀不同名 ⇒ 剥前缀。
  100. // 对应 .env.development 中 VITE_CHAT_API="/chat-api"。
  101. "/chat-api/": {
  102. target: "http://192.168.2.23:8000",
  103. changeOrigin: true,
  104. rewrite: (path) => path.replace(/^\/chat-api/, ""),
  105. },
  106. },
  107. },
  108. build: {
  109. rollupOptions: {
  110. input: {
  111. main: path.resolve(__dirname, "index.html"),
  112. },
  113. output: {
  114. manualChunks: (id) => {
  115. console.log(id);
  116. if (id.includes("node_modules/three")) {
  117. return "three";
  118. } else if (id.includes("node_modules")) {
  119. return "vendor";
  120. }
  121. },
  122. },
  123. },
  124. assetsInlineLimit: 20480,
  125. },
  126. resolve: {
  127. alias: {
  128. "@": path.resolve("./src/"),
  129. three$: path.resolve(__dirname, "./node_modules/three/build/three.cjs"),
  130. },
  131. },
  132. base: "./",
  133. css: {
  134. preprocessorOptions: {
  135. scss: {
  136. api: "modern-compiler",
  137. },
  138. },
  139. },
  140. };
  141. });