|
|
@@ -14,11 +14,11 @@ import basicSsl from "@vitejs/plugin-basic-ssl";
|
|
|
// 关键是「每个请求现读」而不是启动时读一次 —— token 约 24 小时过期,以前换一次就要重启 dev server。
|
|
|
import { DMS_TOKEN_FILE, readDmsToken, parseJwtPayload, describeDmsToken } from "./harness/tools/dms-token.mjs";
|
|
|
|
|
|
-// NLS(阿里云智能语音交互)token 的签名口径收在 harness/tools/nls-token.mjs。
|
|
|
-// **为什么由 dev server 换 token 而不是前端自己签**:签 token 要 AccessKeySecret,
|
|
|
-// 而前端 bundle 是公开的(谁都能拿 secret 签 token、烧账号额度)—— secret 只能待在 node 侧。
|
|
|
-// 生产环境要由后端/网关提供同样的端点,**不能把这段 dev 端点当生产方案**。
|
|
|
-import { createNlsTokenProvider, maskCredential, readNlsCredentials } from "./harness/tools/nls-token.mjs";
|
|
|
+// [已移除] dev 期自己签 NLS token 的中间件(`/nls-token/`)。
|
|
|
+// 2026-09-24 起 token 改由**后端网关**提供:`GET {VITE_ALY_TARGET}/proxy/verify/getALYToken`
|
|
|
+// → `{code:200, content:"<token>"}`,前端走同源 `/aly-api/` 代理拿(见下面的 proxy)。
|
|
|
+// 于是前端侧**不再需要 AccessKeySecret**,vite 也不必持凭据。
|
|
|
+// AK/SK 自签的能力仍留在 harness/tools/nls-token.mjs(工具侧退路,仅在没有网关时用)。
|
|
|
|
|
|
/** 同一个状态只提醒一次,避免每个请求都刷屏 */
|
|
|
let dmsTokenWarnedFor = "";
|
|
|
@@ -58,74 +58,8 @@ const warnDmsTokenIfNeeded = (token: string): void => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-/** NLS 缺凭据只提醒一次,别每个请求刷屏 */
|
|
|
-let nlsWarnedFor = "";
|
|
|
-
|
|
|
-/**
|
|
|
- * 开发期端点:`GET /nls-token/` → `{ token, expireAt }`。
|
|
|
- *
|
|
|
- * 前端只拿 token,**拿不到 AK/SK**(凭据只在 node 侧用于签名)。
|
|
|
- * token 24 小时过期,提供者内部按 5 分钟提前刷新并做并发去重 ——
|
|
|
- * 所以前端可以放心每次要用就问一次。
|
|
|
- *
|
|
|
- * ⚠️ 仅 dev(`apply: 'serve'`)。生产要后端/网关实现同一形态:
|
|
|
- * 前端仍是同源 `GET /nls-token/`,secret 仍在服务端。
|
|
|
- */
|
|
|
-const nlsTokenEndpoint = (env: Record<string, string>) => {
|
|
|
- const credentials = readNlsCredentials(env);
|
|
|
- const provider = createNlsTokenProvider({
|
|
|
- ak: credentials.ak,
|
|
|
- sk: credentials.sk,
|
|
|
- onRefresh: ({ expireAt }) =>
|
|
|
- console.log(
|
|
|
- `[nls] token 已刷新,有效期至 ${new Date(expireAt).toLocaleString("zh-CN")}(AK=${maskCredential(credentials.ak)})`
|
|
|
- ),
|
|
|
- });
|
|
|
-
|
|
|
- return {
|
|
|
- name: "nls-token-endpoint",
|
|
|
- apply: "serve" as const,
|
|
|
- configureServer(server: any) {
|
|
|
- server.middlewares.use("/nls-token", async (_req: any, res: any) => {
|
|
|
- if (!credentials.ak || !credentials.sk) {
|
|
|
- if (nlsWarnedFor !== "missing") {
|
|
|
- nlsWarnedFor = "missing";
|
|
|
- console.warn(
|
|
|
- `\n[nls] ⚠️ 没有配置 NLS 凭据,语音识别拿不到 token。\n` +
|
|
|
- `[nls] 在 .env.development.local(按 .gitignore 不入库)里加:\n` +
|
|
|
- `[nls] ALIYUN_NLS_AK=… ALIYUN_NLS_SK=… ALIYUN_NLS_APPKEY=…\n` +
|
|
|
- `[nls] 改完重启 dev server(.env 只在启动时读)。\n`
|
|
|
- );
|
|
|
- }
|
|
|
- res.statusCode = 503;
|
|
|
- res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
|
- res.end(JSON.stringify({ error: "未配置 NLS 凭据(ALIYUN_NLS_AK / ALIYUN_NLS_SK)" }));
|
|
|
- return;
|
|
|
- }
|
|
|
- try {
|
|
|
- const { token, expireAt } = await provider.get();
|
|
|
- res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
|
- // 明确禁止缓存:token 轮换后前端再来问,别被浏览器缓存住旧的
|
|
|
- res.setHeader("Cache-Control", "no-store");
|
|
|
- // appkey 一起下发:它要出现在识别请求里,但**没必要打进前端产物** ——
|
|
|
- // 从端点拿,前端就零内置凭据,将来换 AppKey 也不用重新打包
|
|
|
- res.end(JSON.stringify({ token, expireAt, appkey: credentials.appkey }));
|
|
|
- } catch (error) {
|
|
|
- const message = error instanceof Error ? error.message : String(error);
|
|
|
- console.warn("[nls] 换 token 失败:", message);
|
|
|
- res.statusCode = 502;
|
|
|
- res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
|
- res.end(JSON.stringify({ error: message }));
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
- };
|
|
|
-};
|
|
|
-
|
|
|
// https://vitejs.dev/config/
|
|
|
export default defineConfig(({ mode }) => {
|
|
|
- // 第三个参数传空串:不加前缀过滤,才能读到 DMS_TOKEN 这类非 VITE_ 前缀的变量
|
|
|
- // (DMS_TOKEN 只写在 .env.development.local,该文件按 .gitignore 不入库)
|
|
|
const env = loadEnv(mode, process.cwd(), "");
|
|
|
|
|
|
return {
|
|
|
@@ -142,7 +76,6 @@ export default defineConfig(({ mode }) => {
|
|
|
}),
|
|
|
svgLoader(),
|
|
|
basicSsl(),
|
|
|
- nlsTokenEndpoint(env),
|
|
|
],
|
|
|
server: {
|
|
|
proxy: {
|
|
|
@@ -194,6 +127,15 @@ export default defineConfig(({ mode }) => {
|
|
|
changeOrigin: true,
|
|
|
rewrite: (path) => path.replace(/^\/ip-api/, ""),
|
|
|
},
|
|
|
+ // 阿里云 NLS 的 token 端点(`/proxy/verify/getALYToken`)——**后端网关提供**,
|
|
|
+ // 与上面 `/ip-api/` 同一台机器(121.43.55.7:10011)、同样只有 HTTP:
|
|
|
+ // 页面是 HTTPS,直连会被混合内容拦掉,所以走同源代理。
|
|
|
+ // 生产要在 nginx 里做同样的事(前端调的是同源路径 `/aly-api/...`)。
|
|
|
+ "/aly-api/": {
|
|
|
+ target: env.VITE_ALY_TARGET || "http://121.43.55.7:10011",
|
|
|
+ changeOrigin: true,
|
|
|
+ rewrite: (path) => path.replace(/^\/aly-api/, ""),
|
|
|
+ },
|
|
|
"/asr/": {
|
|
|
//匹配的路径
|
|
|
target: "https://human-screen-v3.metamaker.cn", //目标url
|