| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- import { defineConfig } from "vite";
- // import { darkTheme } from "./src/theme.js";
- import vue from "@vitejs/plugin-vue";
- import Components from "unplugin-vue-components/vite";
- import { AntDesignVueResolver } from "unplugin-vue-components/resolvers";
- import svgLoader from "vite-svg-loader";
- import path from "path";
- import basicSsl from "@vitejs/plugin-basic-ssl";
- // DMS token 读取口(与仓库 DMS 脚本共用一份实现,harness/tools/dms-token.mjs):
- // 显式参数 → 环境变量 → 本机文件 `.dms-token`;每个请求现读,换 token 不用重启 dev server。
- import { DMS_TOKEN_FILE, readDmsToken, parseJwtPayload, describeDmsToken } from "./harness/tools/dms-token.mjs";
- /** 同一个状态只提醒一次,避免每个请求都刷屏 */
- let dmsTokenWarnedFor = "";
- /** token 缺失/过期时给出明确提示(否则 DMS 写入会静默失败,难排查) */
- const warnDmsTokenIfNeeded = (token: string): void => {
- if (!token) {
- if (dmsTokenWarnedFor === "missing") return;
- dmsTokenWarnedFor = "missing";
- console.warn(
- `\n[dms] ⚠️ 没有 DMS token,DMS 读写(会话/问答记录/反馈)会失败,聊天本身不受影响。\n` +
- `[dms] 最省事:在浏览器里复制 token,然后跑 \`npm run dms:token\`(从剪贴板读)。\n` +
- `[dms] 文件位置:${DMS_TOKEN_FILE}\n`
- );
- return;
- }
- const exp = Number(parseJwtPayload(token)?.exp);
- if (!Number.isFinite(exp)) return;
- const leftMs = exp * 1000 - Date.now();
- const at = new Date(exp * 1000).toLocaleString("zh-CN");
- if (leftMs <= 0) {
- if (dmsTokenWarnedFor === `expired:${exp}`) return;
- dmsTokenWarnedFor = `expired:${exp}`;
- console.warn(
- `\n[dms] ⚠️ DMS token 已于 ${at} 过期(DMS 读写会失败)。复制新 token 后跑 \`npm run dms:token\`。\n`
- );
- return;
- }
- if (leftMs < 2 * 3600 * 1000) {
- const key = `soon:${exp}`;
- if (dmsTokenWarnedFor === key) return;
- dmsTokenWarnedFor = key;
- console.warn(`\n[dms] ⏳ DMS token 将于 ${at} 过期(还剩 ${(leftMs / 3600000).toFixed(1)} 小时)。\n`);
- }
- };
- export default defineConfig(() => {
- return {
- plugins: [
- vue(),
- Components({
- resolvers: [AntDesignVueResolver()],
- }),
- svgLoader(),
- basicSsl(),
- ],
- server: {
- proxy: {
- // 代理总原则:前缀尽量与服务真实路径一致,只有服务端本身不带前缀时才剥。
- // /oauth/** → 121.43.55.7:10086,直通(真实路径就带 /oauth)
- // /dms/** → 121.43.55.7:10081,直通(真实路径就带 /dms),请求没带 token 时代理注入
- // /api/** → 121.43.55.7:10011,剥掉 /api(网关真实路径是 /proxy/verify/…)
- // /chat-api/** → 聊天后端,剥前缀(后端真实路径是 /api/chat,与前端前缀不同名)
- // 三台后端都只有 HTTP 而 dev server 是 HTTPS,直连会被浏览器按混合内容拦掉,
- // 必须由服务端这一跳(dev 是 vite 代理,生产是 nginx)代劳。
- // ⚠️ target 直接写在这里、不从环境变量读(所以本文件不 loadEnv),换地址就改这四处。
- // 后端网关:语音识别 token(/proxy/verify/getALYToken)与取本机 IP(/proxy/verify/getIPByBrowser)
- // 在同一台,共用这一条。
- "/api/": {
- target: "http://121.43.55.7:10011",
- changeOrigin: true,
- rewrite: (path) => path.replace(/^\/api/, ""),
- },
- // DMS 登录服务 121.43.55.7:10086(真实路径带 /oauth,所以直通、不改写):
- // 未登录访客用服务账号换 DMS token 走这里。
- "/oauth/": {
- target: "http://121.43.55.7:10086",
- changeOrigin: true,
- },
- // DMS 数据管理服务(会话/问答记录/反馈)——真实路由都在 /dms/ 下,前缀同名,直通。
- // 鉴权头名是 token(不是 Authorization):前端自己带(service-token.ts)时**代理不覆盖**,
- // 只有请求没带时才由代理兜底注入(读仓库根 `.dms-token` 或环境变量 DMS_TOKEN)。
- // 每请求现读,所以换 token 不用重启 dev server。
- "/dms/": {
- target: "http://121.43.55.7:10081",
- changeOrigin: true,
- configure: (proxy) => {
- proxy.on("proxyReq", (proxyReq) => {
- // 前端已经带了 token 就别覆盖
- const fromClient =
- typeof proxyReq.getHeader === "function" ? proxyReq.getHeader("token") : undefined;
- if (fromClient) return;
- const token = readDmsToken();
- if (token) {
- proxyReq.setHeader("token", token);
- }
- warnDmsTokenIfNeeded(token);
- });
- },
- },
- // 聊天后端(POST /api/chat,SSE):真实路径是 /api/chat,与前端前缀不同名 ⇒ 剥前缀。
- // 对应 .env.development 中 VITE_CHAT_API="/chat-api"。
- "/chat-api/": {
- target: "http://192.168.2.23:8000",
- changeOrigin: true,
- rewrite: (path) => path.replace(/^\/chat-api/, ""),
- },
- },
- },
- build: {
- rollupOptions: {
- input: {
- main: path.resolve(__dirname, "index.html"),
- },
- output: {
- manualChunks: (id) => {
- console.log(id);
- if (id.includes("node_modules/three")) {
- return "three";
- } else if (id.includes("node_modules")) {
- return "vendor";
- }
- },
- },
- },
- assetsInlineLimit: 20480,
- },
- resolve: {
- alias: {
- "@": path.resolve("./src/"),
- three$: path.resolve(__dirname, "./node_modules/three/build/three.cjs"),
- },
- },
- base: "./",
- css: {
- preprocessorOptions: {
- scss: {
- api: "modern-compiler",
- },
- },
- },
- };
- });
|