diff --git a/Yi.Ai.Vue3/package.json b/Yi.Ai.Vue3/package.json index 36be0a0b..9b0ca60d 100644 --- a/Yi.Ai.Vue3/package.json +++ b/Yi.Ai.Vue3/package.json @@ -30,6 +30,7 @@ }, "dependencies": { "@element-plus/icons-vue": "^2.3.1", + "@fingerprintjs/fingerprintjs": "^3.4.2", "@floating-ui/core": "^1.7.1", "@floating-ui/dom": "^1.7.1", "@floating-ui/vue": "^1.1.6", @@ -37,6 +38,7 @@ "@vueuse/core": "^13.3.0", "@vueuse/integrations": "^13.3.0", "element-plus": "^2.9.11", + "fingerprintjs": "^0.5.3", "hook-fetch": "^1.1.3", "nprogress": "^0.2.0", "pinia": "^3.0.3", @@ -52,6 +54,7 @@ "@antfu/eslint-config": "^4.13.3", "@changesets/cli": "^2.29.4", "@commitlint/config-conventional": "^19.8.1", + "@types/fingerprintjs__fingerprintjs": "^3.0.2", "@vitejs/plugin-vue": "^5.2.4", "@vue/tsconfig": "^0.7.0", "commitlint": "^19.8.1", diff --git a/Yi.Ai.Vue3/pnpm-lock.yaml b/Yi.Ai.Vue3/pnpm-lock.yaml index 49c232a1..9a6b12b7 100644 --- a/Yi.Ai.Vue3/pnpm-lock.yaml +++ b/Yi.Ai.Vue3/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@element-plus/icons-vue': specifier: ^2.3.1 version: 2.3.1(vue@3.5.16(typescript@5.8.3)) + '@fingerprintjs/fingerprintjs': + specifier: ^3.4.2 + version: 3.4.2 '@floating-ui/core': specifier: ^1.7.1 version: 1.7.1 @@ -32,6 +35,9 @@ importers: element-plus: specifier: ^2.9.11 version: 2.9.11(vue@3.5.16(typescript@5.8.3)) + fingerprintjs: + specifier: ^0.5.3 + version: 0.5.3 hook-fetch: specifier: ^1.1.3 version: 1.1.3(react@19.1.0)(typescript-api-pro@0.0.7)(vue@3.5.16(typescript@5.8.3)) @@ -72,6 +78,9 @@ importers: '@commitlint/config-conventional': specifier: ^19.8.1 version: 19.8.1 + '@types/fingerprintjs__fingerprintjs': + specifier: ^3.0.2 + version: 3.0.2 '@vitejs/plugin-vue': specifier: ^5.2.4 version: 5.2.4(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)) @@ -110,7 +119,7 @@ importers: version: 6.0.0(stylelint@16.20.0(typescript@5.8.3)) stylelint-config-recommended-scss: specifier: ^15.0.1 - version: 15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3)) + version: 15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3)) stylelint-config-recommended-vue: specifier: ^1.6.0 version: 1.6.0(postcss-html@1.8.0)(stylelint@16.20.0(typescript@5.8.3)) @@ -119,7 +128,7 @@ importers: version: 38.0.0(stylelint@16.20.0(typescript@5.8.3)) stylelint-config-standard-scss: specifier: ^15.0.1 - version: 15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3)) + version: 15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3)) typescript: specifier: ~5.8.3 version: 5.8.3 @@ -128,7 +137,7 @@ importers: version: 0.0.7 unocss: specifier: 66.1.3 - version: 66.1.3(postcss@8.5.4)(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)) + version: 66.1.3(postcss@5.2.18)(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)) unplugin-auto-import: specifier: ^19.3.0 version: 19.3.0(@nuxt/kit@3.17.5)(@vueuse/core@13.3.0(vue@3.5.16(typescript@5.8.3))) @@ -648,6 +657,12 @@ packages: resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@fingerprintjs/fingerprintjs@3.4.2': + resolution: {integrity: sha512-3Ncze6JsJpB7BpYhqIgvBpfvEX1jsEKrad5hQBpyRQxtoAp6hx3+R46zqfsuQG4D9egQZ+xftQ0u4LPFMB7Wmg==} + + '@fingerprintjs/fingerprintjs@4.6.2': + resolution: {integrity: sha512-g8mXuqcFKbgH2CZKwPfVtsUJDHyvcgIABQI7Y0tzWEFXpGxJaXuAuzlifT2oTakjDBLTK4Gaa9/5PERDhqUjtw==} + '@floating-ui/core@1.7.1': resolution: {integrity: sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==} @@ -976,6 +991,10 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/fingerprintjs__fingerprintjs@3.0.2': + resolution: {integrity: sha512-jjQ03OYO/7qKtI/vOKZ9hSXtHxHWJkwsMSMSFszY+Y+JcOnjbRlvodk+zxXSxKNQweiDTJo4PfsXtOJLC9+auQ==} + deprecated: This is a stub types definition. @fingerprintjs/fingerprintjs provides its own type definitions, so you do not need this installed. + '@types/geojson@7946.0.16': resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} @@ -2575,6 +2594,10 @@ packages: resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} engines: {node: '>=18'} + fingerprintjs@0.5.3: + resolution: {integrity: sha512-LPW+iJ9yaV+FIXbmPkbyp7n6UoxV1euVozieaq3ckuvPEAyJRRB0JxbBsShHh5h1jc9yn/EZ95EEb78109zuKg==} + deprecated: Package has been renamed to @fingerprintjs/fingerprintjs. Install @fingerprintjs/fingerprintjs to get updates. + flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -5555,6 +5578,14 @@ snapshots: '@eslint/core': 0.14.0 levn: 0.4.1 + '@fingerprintjs/fingerprintjs@3.4.2': + dependencies: + tslib: 2.8.1 + + '@fingerprintjs/fingerprintjs@4.6.2': + dependencies: + tslib: 2.8.1 + '@floating-ui/core@1.7.1': dependencies: '@floating-ui/utils': 0.2.9 @@ -5916,6 +5947,10 @@ snapshots: '@types/estree@1.0.8': {} + '@types/fingerprintjs__fingerprintjs@3.0.2': + dependencies: + '@fingerprintjs/fingerprintjs': 4.6.2 + '@types/geojson@7946.0.16': {} '@types/json-schema@7.0.15': {} @@ -6091,13 +6126,13 @@ snapshots: transitivePeerDependencies: - vue - '@unocss/postcss@66.1.3(postcss@8.5.4)': + '@unocss/postcss@66.1.3(postcss@5.2.18)': dependencies: '@unocss/config': 66.1.3 '@unocss/core': 66.1.3 '@unocss/rule-utils': 66.1.3 css-tree: 3.1.0 - postcss: 8.5.4 + postcss: 5.2.18 tinyglobby: 0.2.14 '@unocss/preset-attributify@66.1.3': @@ -7805,6 +7840,8 @@ snapshots: path-exists: 5.0.0 unicorn-magic: 0.1.0 + fingerprintjs@0.5.3: {} + flat-cache@4.0.1: dependencies: flatted: 3.3.3 @@ -9213,9 +9250,9 @@ snapshots: dependencies: postcss: 8.5.4 - postcss-scss@4.0.9(postcss@8.5.4): + postcss-scss@4.0.9(postcss@5.2.18): dependencies: - postcss: 8.5.4 + postcss: 5.2.18 postcss-selector-parser@6.1.2: dependencies: @@ -9806,14 +9843,14 @@ snapshots: stylelint: 16.20.0(typescript@5.8.3) stylelint-order: 6.0.4(stylelint@16.20.0(typescript@5.8.3)) - stylelint-config-recommended-scss@15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3)): + stylelint-config-recommended-scss@15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3)): dependencies: - postcss-scss: 4.0.9(postcss@8.5.4) + postcss-scss: 4.0.9(postcss@5.2.18) stylelint: 16.20.0(typescript@5.8.3) stylelint-config-recommended: 16.0.0(stylelint@16.20.0(typescript@5.8.3)) stylelint-scss: 6.12.0(stylelint@16.20.0(typescript@5.8.3)) optionalDependencies: - postcss: 8.5.4 + postcss: 5.2.18 stylelint-config-recommended-vue@1.6.0(postcss-html@1.8.0)(stylelint@16.20.0(typescript@5.8.3)): dependencies: @@ -9827,13 +9864,13 @@ snapshots: dependencies: stylelint: 16.20.0(typescript@5.8.3) - stylelint-config-standard-scss@15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3)): + stylelint-config-standard-scss@15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3)): dependencies: stylelint: 16.20.0(typescript@5.8.3) - stylelint-config-recommended-scss: 15.0.1(postcss@8.5.4)(stylelint@16.20.0(typescript@5.8.3)) + stylelint-config-recommended-scss: 15.0.1(postcss@5.2.18)(stylelint@16.20.0(typescript@5.8.3)) stylelint-config-standard: 38.0.0(stylelint@16.20.0(typescript@5.8.3)) optionalDependencies: - postcss: 8.5.4 + postcss: 5.2.18 stylelint-config-standard@38.0.0(stylelint@16.20.0(typescript@5.8.3)): dependencies: @@ -10189,12 +10226,12 @@ snapshots: universalify@2.0.1: {} - unocss@66.1.3(postcss@8.5.4)(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)): + unocss@66.1.3(postcss@5.2.18)(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)): dependencies: '@unocss/astro': 66.1.3(vite@6.3.5(@types/node@22.15.30)(jiti@2.4.2)(sass-embedded@1.89.1)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)) '@unocss/cli': 66.1.3 '@unocss/core': 66.1.3 - '@unocss/postcss': 66.1.3(postcss@8.5.4) + '@unocss/postcss': 66.1.3(postcss@5.2.18) '@unocss/preset-attributify': 66.1.3 '@unocss/preset-icons': 66.1.3 '@unocss/preset-mini': 66.1.3 diff --git a/Yi.Ai.Vue3/src/utils/request.ts b/Yi.Ai.Vue3/src/utils/request.ts index 0d5157f3..92ef1822 100644 --- a/Yi.Ai.Vue3/src/utils/request.ts +++ b/Yi.Ai.Vue3/src/utils/request.ts @@ -1,10 +1,21 @@ import type { HookFetchPlugin } from 'hook-fetch'; +import FingerprintJS from '@fingerprintjs/fingerprintjs'; // 新增指纹库 import { ElMessage } from 'element-plus'; import hookFetch from 'hook-fetch'; import { sseTextDecoderPlugin } from 'hook-fetch/plugins'; import router from '@/routers'; import { useUserStore } from '@/stores'; -// 标准响应格式 + +// 初始化指纹(单例模式) +const fpPromise = FingerprintJS.load(); + +// 获取浏览器指纹(缓存结果) +async function getFingerprint(): Promise { + const fp = await fpPromise; + const { visitorId } = await fp.get(); + return visitorId; +} + // 标准响应格式 interface BaseResponse { code: number; @@ -12,13 +23,12 @@ interface BaseResponse { msg: string; } -// 扩展请求函数类型声明 declare module 'hook-fetch' { interface HookFetchDefaults { - // 允许响应是裸数据(自动会被插件包装) response: any; } } + export const request = hookFetch.create({ baseURL: import.meta.env.VITE_WEB_BASE_API, headers: { @@ -26,20 +36,32 @@ export const request = hookFetch.create({ }, plugins: [ sseTextDecoderPlugin({ json: true, prefix: 'data:' }), + { + name: 'fingerprint-plugin', // 新增指纹插件 + beforeRequest: async (config) => { + try { + const fingerprint = await getFingerprint(); + config.headers = new Headers(config.headers); + config.headers.set('X-Fingerprint', fingerprint); + } + catch (error) { + console.error('Failed to generate fingerprint:', error); + } + return config; + }, + }, { name: 'adapt-array-response', afterResponse: async (response) => { - // 已经是标准格式(包含 code 字段) if (typeof response.result?.code === 'number') { return response; } - // 非标准格式 → 包装为标准格式 return { ...response, result: { - code: 200, // 默认成功码 - data: response.result, // 原始数据放入 data - msg: 'success', // 默认消息 + code: 200, + data: response.result, + msg: 'success', }, }; }, @@ -47,35 +69,33 @@ export const request = hookFetch.create({ ], }); +// JWT插件(保持原有逻辑) function jwtPlugin(): HookFetchPlugin { const userStore = useUserStore(); return { name: 'jwt', beforeRequest: async (config) => { - config.headers = new Headers(config.headers); - config.headers.set('authorization', `Bearer ${userStore.token}`); + if (userStore.token) { + config.headers = new Headers(config.headers); + config.headers.set('authorization', `Bearer ${userStore.token}`); + } return config; }, afterResponse: async (response) => { - // console.log(response); - if (response.result?.code === 200) { + if (response.result?.code === 200) return response; - } - // 处理403逻辑 + if (response.result?.code === 403) { - // 跳转到403页面(确保路由已配置) - router.replace({ - name: '403', - }); + router.replace({ name: '403' }); ElMessage.error(response.result?.msg); return Promise.reject(response); } - // 处理401逻辑 + if (response.result?.code === 401) { - // 如果没有权限,退出,且弹框提示登录 userStore.logout(); userStore.openLoginDialog(); } + ElMessage.error(response.result?.msg); return Promise.reject(response); }, @@ -84,12 +104,9 @@ function jwtPlugin(): HookFetchPlugin { request.use(jwtPlugin()); +// 导出方法(保持原有) export const post = request.post; - export const get = request.get; - export const put = request.put; - export const del = request.delete; - export default request;