@windrun-huaiin/third-ui 7.1.1 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clerk/clerk-page-generator-client.d.ts +10 -0
- package/dist/clerk/clerk-page-generator-client.js +28 -0
- package/dist/clerk/clerk-page-generator-client.mjs +25 -0
- package/dist/clerk/clerk-page-generator.js +1 -0
- package/dist/clerk/clerk-page-generator.mjs +1 -0
- package/dist/clerk/client-page-generator.d.ts +10 -0
- package/dist/clerk/client-page-generator.js +28 -0
- package/dist/clerk/client-page-generator.mjs +25 -0
- package/dist/clerk/context/FingerprintProvider.d.ts +25 -0
- package/dist/clerk/context/FingerprintProvider.js +71 -0
- package/dist/clerk/context/FingerprintProvider.mjs +65 -0
- package/dist/clerk/fingerprint/fingerprint-client.d.ts +47 -0
- package/dist/clerk/fingerprint/fingerprint-client.js +183 -0
- package/dist/clerk/fingerprint/fingerprint-client.mjs +174 -0
- package/dist/clerk/fingerprint/fingerprint-provider.d.ts +25 -0
- package/dist/clerk/fingerprint/fingerprint-provider.js +71 -0
- package/dist/clerk/fingerprint/fingerprint-provider.mjs +65 -0
- package/dist/clerk/fingerprint/fingerprint-server.d.ts +22 -0
- package/dist/clerk/fingerprint/fingerprint-server.js +75 -0
- package/dist/clerk/fingerprint/fingerprint-server.mjs +71 -0
- package/dist/clerk/fingerprint/fingerprint-shared.d.ts +17 -0
- package/dist/clerk/fingerprint/fingerprint-shared.js +35 -0
- package/dist/clerk/fingerprint/fingerprint-shared.mjs +29 -0
- package/dist/clerk/fingerprint/fingerprint.d.ts +55 -0
- package/dist/clerk/fingerprint/fingerprint.js +17 -0
- package/dist/clerk/fingerprint/fingerprint.mjs +15 -0
- package/dist/clerk/fingerprint/index.d.ts +5 -0
- package/dist/clerk/fingerprint/index.js +29 -0
- package/dist/clerk/fingerprint/index.mjs +5 -0
- package/dist/clerk/fingerprint/server.d.ts +3 -0
- package/dist/clerk/fingerprint/server.js +15 -0
- package/dist/clerk/fingerprint/server.mjs +2 -0
- package/dist/clerk/fingerprint/types.d.ts +42 -0
- package/dist/clerk/fingerprint/use-fingerprint.d.ts +6 -0
- package/dist/clerk/fingerprint/use-fingerprint.js +182 -0
- package/dist/clerk/fingerprint/use-fingerprint.mjs +180 -0
- package/dist/clerk/fingerprint.d.ts +55 -0
- package/dist/clerk/fingerprint.js +237 -0
- package/dist/clerk/fingerprint.mjs +225 -0
- package/dist/clerk/hooks/useFingerprint.d.ts +6 -0
- package/dist/clerk/hooks/useFingerprint.js +182 -0
- package/dist/clerk/hooks/useFingerprint.mjs +180 -0
- package/dist/clerk/index.d.ts +3 -0
- package/dist/clerk/index.js +7 -0
- package/dist/clerk/index.mjs +3 -0
- package/dist/clerk/signin-with-fingerprint-client.d.ts +7 -0
- package/dist/clerk/signin-with-fingerprint-client.js +52 -0
- package/dist/clerk/signin-with-fingerprint-client.mjs +47 -0
- package/dist/clerk/signup-with-fingerprint-client.d.ts +7 -0
- package/dist/clerk/signup-with-fingerprint-client.js +52 -0
- package/dist/clerk/signup-with-fingerprint-client.mjs +47 -0
- package/dist/clerk/types.d.ts +42 -0
- package/dist/fuma/mdx/mermaid.js +1 -1
- package/dist/fuma/mdx/mermaid.mjs +1 -1
- package/dist/fuma/mdx/toc-base.js +1 -1
- package/dist/fuma/mdx/toc-base.mjs +1 -1
- package/dist/node_modules/.pnpm/@fingerprintjs_fingerprintjs@4.6.2/node_modules/@fingerprintjs/fingerprintjs/dist/fp.esm.js +3245 -0
- package/dist/node_modules/.pnpm/@fingerprintjs_fingerprintjs@4.6.2/node_modules/@fingerprintjs/fingerprintjs/dist/fp.esm.mjs +3218 -0
- package/dist/node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.2/node_modules/tslib/tslib.es6.js +51 -0
- package/dist/node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.2/node_modules/tslib/tslib.es6.mjs +50 -1
- package/dist/node_modules/.pnpm/cose-base@1.0.3/node_modules/cose-base/cose-base.js +1 -1
- package/dist/node_modules/.pnpm/cose-base@2.2.0/node_modules/cose-base/cose-base.js +1 -1
- package/dist/node_modules/.pnpm/layout-base@1.0.2/node_modules/layout-base/layout-base.js +1 -1
- package/dist/node_modules/.pnpm/layout-base@2.0.1/node_modules/layout-base/layout-base.js +1 -1
- package/package.json +14 -3
- package/src/clerk/clerk-page-generator-client.tsx +37 -0
- package/src/clerk/clerk-page-generator.tsx +5 -1
- package/src/clerk/fingerprint/fingerprint-client.ts +202 -0
- package/src/clerk/fingerprint/fingerprint-provider.tsx +114 -0
- package/src/clerk/fingerprint/fingerprint-server.ts +88 -0
- package/src/clerk/fingerprint/fingerprint-shared.ts +29 -0
- package/src/clerk/fingerprint/index.ts +15 -0
- package/src/clerk/fingerprint/server.ts +9 -0
- package/src/clerk/fingerprint/types.ts +48 -0
- package/src/clerk/fingerprint/use-fingerprint.ts +208 -0
- package/src/clerk/index.ts +9 -2
- package/src/clerk/server.ts +1 -0
- package/src/clerk/signin-with-fingerprint-client.tsx +57 -0
- package/src/clerk/signup-with-fingerprint-client.tsx +57 -0
- package/src/fuma/mdx/toc-base.tsx +1 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var tslib_es6 = require('../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.2/node_modules/tslib/tslib.es6.js');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fingerprint Core Utilities
|
|
7
|
+
* 基于浏览器特征生成唯一的设备指纹ID,用于匿名用户识别
|
|
8
|
+
* Core fingerprint generation logic - completely generic
|
|
9
|
+
*
|
|
10
|
+
* NOTE: This module should only be used on the client-side
|
|
11
|
+
*/
|
|
12
|
+
// Dynamic import to avoid SSR issues
|
|
13
|
+
let FingerprintJS = null;
|
|
14
|
+
// Only import in browser environment
|
|
15
|
+
if (typeof window !== 'undefined') {
|
|
16
|
+
Promise.resolve().then(function () { return require('../node_modules/.pnpm/@fingerprintjs_fingerprintjs@4.6.2/node_modules/@fingerprintjs/fingerprintjs/dist/fp.esm.js'); }).then((module) => {
|
|
17
|
+
FingerprintJS = module.default;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
// Fingerprint ID的存储键
|
|
21
|
+
const FINGERPRINT_STORAGE_KEY = 'diaomao_fingerprint_id';
|
|
22
|
+
const FINGERPRINT_HEADER_NAME = 'x-fingerprint-id';
|
|
23
|
+
const FINGERPRINT_COOKIE_NAME = 'fingerprint_id';
|
|
24
|
+
/**
|
|
25
|
+
* 生成基于真实浏览器特征的fingerprint ID
|
|
26
|
+
* 使用FingerprintJS收集浏览器特征并生成唯一标识
|
|
27
|
+
*/
|
|
28
|
+
function generateFingerprintId() {
|
|
29
|
+
return tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
// 如果在浏览器环境中,尝试使用现有的fingerprint ID
|
|
31
|
+
if (typeof window !== 'undefined') {
|
|
32
|
+
// 首先检查localStorage
|
|
33
|
+
const existingId = localStorage.getItem(FINGERPRINT_STORAGE_KEY);
|
|
34
|
+
if (existingId && isValidFingerprintId(existingId)) {
|
|
35
|
+
return existingId;
|
|
36
|
+
}
|
|
37
|
+
// 检查cookie
|
|
38
|
+
const cookieId = getCookieValue(FINGERPRINT_COOKIE_NAME);
|
|
39
|
+
if (cookieId && isValidFingerprintId(cookieId)) {
|
|
40
|
+
// 同步到localStorage
|
|
41
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, cookieId);
|
|
42
|
+
return cookieId;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
// 等待FingerprintJS加载完成
|
|
46
|
+
if (!FingerprintJS) {
|
|
47
|
+
const module = yield Promise.resolve().then(function () { return require('../node_modules/.pnpm/@fingerprintjs_fingerprintjs@4.6.2/node_modules/@fingerprintjs/fingerprintjs/dist/fp.esm.js'); });
|
|
48
|
+
FingerprintJS = module.default;
|
|
49
|
+
}
|
|
50
|
+
// 使用FingerprintJS生成基于浏览器特征的指纹
|
|
51
|
+
const fp = yield FingerprintJS.load();
|
|
52
|
+
const result = yield fp.get();
|
|
53
|
+
const fingerprintId = `fp_${result.visitorId}`;
|
|
54
|
+
// 存储到localStorage和cookie
|
|
55
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, fingerprintId);
|
|
56
|
+
setCookie(FINGERPRINT_COOKIE_NAME, fingerprintId, 365); // 365天过期
|
|
57
|
+
return fingerprintId;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.warn('Failed to generate fingerprint with FingerprintJS:', error);
|
|
61
|
+
// 降级方案:生成时间戳+随机数,但只在没有任何现有ID时使用
|
|
62
|
+
const fallbackId = `fp_fallback_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
63
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, fallbackId);
|
|
64
|
+
setCookie(FINGERPRINT_COOKIE_NAME, fallbackId, 365);
|
|
65
|
+
return fallbackId;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// 服务端环境降级方案
|
|
69
|
+
return `fp_server_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 获取当前的fingerprint ID
|
|
74
|
+
*/
|
|
75
|
+
function getFingerprintId() {
|
|
76
|
+
if (typeof window === 'undefined') {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
// 首先检查localStorage
|
|
80
|
+
const localStorageId = localStorage.getItem(FINGERPRINT_STORAGE_KEY);
|
|
81
|
+
if (localStorageId) {
|
|
82
|
+
return localStorageId;
|
|
83
|
+
}
|
|
84
|
+
// 检查cookie
|
|
85
|
+
const cookieId = getCookieValue(FINGERPRINT_COOKIE_NAME);
|
|
86
|
+
if (cookieId) {
|
|
87
|
+
// 同步到localStorage
|
|
88
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, cookieId);
|
|
89
|
+
return cookieId;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 设置fingerprint ID到存储
|
|
95
|
+
*/
|
|
96
|
+
function setFingerprintId(fingerprintId) {
|
|
97
|
+
if (typeof window === 'undefined') {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, fingerprintId);
|
|
101
|
+
setCookie(FINGERPRINT_COOKIE_NAME, fingerprintId, 365);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 清除fingerprint ID
|
|
105
|
+
*/
|
|
106
|
+
function clearFingerprintId() {
|
|
107
|
+
if (typeof window === 'undefined') {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
localStorage.removeItem(FINGERPRINT_STORAGE_KEY);
|
|
111
|
+
deleteCookie(FINGERPRINT_COOKIE_NAME);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 获取或生成fingerprint ID
|
|
115
|
+
* 如果不存在则自动生成新的
|
|
116
|
+
*/
|
|
117
|
+
function getOrGenerateFingerprintId() {
|
|
118
|
+
return tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
119
|
+
const existingId = getFingerprintId();
|
|
120
|
+
if (existingId) {
|
|
121
|
+
return existingId;
|
|
122
|
+
}
|
|
123
|
+
return yield generateFingerprintId();
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 验证fingerprint ID格式
|
|
128
|
+
*/
|
|
129
|
+
function isValidFingerprintId(fingerprintId) {
|
|
130
|
+
if (!fingerprintId)
|
|
131
|
+
return false;
|
|
132
|
+
// 支持多种格式:
|
|
133
|
+
// - fp_ + FingerprintJS visitorId (变长字符串)
|
|
134
|
+
// - fp_fallback_ + 时间戳_随机字符串 (客户端降级方案)
|
|
135
|
+
// - fp_server_ + 时间戳_随机字符串 (服务端降级)
|
|
136
|
+
return /^fp(_fallback|_server)?_[a-zA-Z0-9_]+$/.test(fingerprintId);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 创建包含fingerprint ID的fetch headers
|
|
140
|
+
*/
|
|
141
|
+
function createFingerprintHeaders() {
|
|
142
|
+
return tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
143
|
+
const fingerprintId = yield getOrGenerateFingerprintId();
|
|
144
|
+
return {
|
|
145
|
+
[FINGERPRINT_HEADER_NAME]: fingerprintId,
|
|
146
|
+
};
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Hook for generating fingerprint headers
|
|
151
|
+
*/
|
|
152
|
+
function useFingerprintHeaders() {
|
|
153
|
+
return createFingerprintHeaders;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Create a fetch wrapper that automatically includes fingerprint headers
|
|
157
|
+
*/
|
|
158
|
+
function createFingerprintFetch() {
|
|
159
|
+
return (url, init) => tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
160
|
+
const fingerprintHeaders = yield createFingerprintHeaders();
|
|
161
|
+
const headers = Object.assign(Object.assign({}, fingerprintHeaders), ((init === null || init === void 0 ? void 0 : init.headers) || {}));
|
|
162
|
+
return fetch(url, Object.assign(Object.assign({}, init), { headers }));
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* 从请求中提取fingerprint ID
|
|
167
|
+
* 优先级:header > cookie > query参数
|
|
168
|
+
*/
|
|
169
|
+
function extractFingerprintId(headers, cookies, query) {
|
|
170
|
+
// 1. 从header中获取
|
|
171
|
+
const headerValue = headers instanceof Headers
|
|
172
|
+
? headers.get(FINGERPRINT_HEADER_NAME)
|
|
173
|
+
: headers[FINGERPRINT_HEADER_NAME];
|
|
174
|
+
if (headerValue && isValidFingerprintId(headerValue)) {
|
|
175
|
+
return headerValue;
|
|
176
|
+
}
|
|
177
|
+
// 2. 从cookie中获取
|
|
178
|
+
if (cookies) {
|
|
179
|
+
const cookieValue = cookies[FINGERPRINT_COOKIE_NAME];
|
|
180
|
+
if (cookieValue && isValidFingerprintId(cookieValue)) {
|
|
181
|
+
return cookieValue;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// 3. 从query参数中获取
|
|
185
|
+
if (query) {
|
|
186
|
+
const queryValue = query.fingerprint_id || query.fp_id;
|
|
187
|
+
if (queryValue && isValidFingerprintId(queryValue)) {
|
|
188
|
+
return queryValue;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
// Cookie 辅助函数
|
|
194
|
+
function getCookieValue(name) {
|
|
195
|
+
var _a;
|
|
196
|
+
if (typeof document === 'undefined') {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
const value = `; ${document.cookie}`;
|
|
200
|
+
const parts = value.split(`; ${name}=`);
|
|
201
|
+
if (parts.length === 2) {
|
|
202
|
+
return ((_a = parts.pop()) === null || _a === void 0 ? void 0 : _a.split(';').shift()) || null;
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
function setCookie(name, value, days) {
|
|
207
|
+
if (typeof document === 'undefined') {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const expires = new Date();
|
|
211
|
+
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
|
|
212
|
+
document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/;SameSite=Lax`;
|
|
213
|
+
}
|
|
214
|
+
function deleteCookie(name) {
|
|
215
|
+
if (typeof document === 'undefined') {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/`;
|
|
219
|
+
}
|
|
220
|
+
// 常量导出
|
|
221
|
+
const FINGERPRINT_CONSTANTS = {
|
|
222
|
+
STORAGE_KEY: FINGERPRINT_STORAGE_KEY,
|
|
223
|
+
HEADER_NAME: FINGERPRINT_HEADER_NAME,
|
|
224
|
+
COOKIE_NAME: FINGERPRINT_COOKIE_NAME,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
exports.FINGERPRINT_CONSTANTS = FINGERPRINT_CONSTANTS;
|
|
228
|
+
exports.clearFingerprintId = clearFingerprintId;
|
|
229
|
+
exports.createFingerprintFetch = createFingerprintFetch;
|
|
230
|
+
exports.createFingerprintHeaders = createFingerprintHeaders;
|
|
231
|
+
exports.extractFingerprintId = extractFingerprintId;
|
|
232
|
+
exports.generateFingerprintId = generateFingerprintId;
|
|
233
|
+
exports.getFingerprintId = getFingerprintId;
|
|
234
|
+
exports.getOrGenerateFingerprintId = getOrGenerateFingerprintId;
|
|
235
|
+
exports.isValidFingerprintId = isValidFingerprintId;
|
|
236
|
+
exports.setFingerprintId = setFingerprintId;
|
|
237
|
+
exports.useFingerprintHeaders = useFingerprintHeaders;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { __awaiter } from '../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.2/node_modules/tslib/tslib.es6.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fingerprint Core Utilities
|
|
5
|
+
* 基于浏览器特征生成唯一的设备指纹ID,用于匿名用户识别
|
|
6
|
+
* Core fingerprint generation logic - completely generic
|
|
7
|
+
*
|
|
8
|
+
* NOTE: This module should only be used on the client-side
|
|
9
|
+
*/
|
|
10
|
+
// Dynamic import to avoid SSR issues
|
|
11
|
+
let FingerprintJS = null;
|
|
12
|
+
// Only import in browser environment
|
|
13
|
+
if (typeof window !== 'undefined') {
|
|
14
|
+
import('../node_modules/.pnpm/@fingerprintjs_fingerprintjs@4.6.2/node_modules/@fingerprintjs/fingerprintjs/dist/fp.esm.mjs').then((module) => {
|
|
15
|
+
FingerprintJS = module.default;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
// Fingerprint ID的存储键
|
|
19
|
+
const FINGERPRINT_STORAGE_KEY = 'diaomao_fingerprint_id';
|
|
20
|
+
const FINGERPRINT_HEADER_NAME = 'x-fingerprint-id';
|
|
21
|
+
const FINGERPRINT_COOKIE_NAME = 'fingerprint_id';
|
|
22
|
+
/**
|
|
23
|
+
* 生成基于真实浏览器特征的fingerprint ID
|
|
24
|
+
* 使用FingerprintJS收集浏览器特征并生成唯一标识
|
|
25
|
+
*/
|
|
26
|
+
function generateFingerprintId() {
|
|
27
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
28
|
+
// 如果在浏览器环境中,尝试使用现有的fingerprint ID
|
|
29
|
+
if (typeof window !== 'undefined') {
|
|
30
|
+
// 首先检查localStorage
|
|
31
|
+
const existingId = localStorage.getItem(FINGERPRINT_STORAGE_KEY);
|
|
32
|
+
if (existingId && isValidFingerprintId(existingId)) {
|
|
33
|
+
return existingId;
|
|
34
|
+
}
|
|
35
|
+
// 检查cookie
|
|
36
|
+
const cookieId = getCookieValue(FINGERPRINT_COOKIE_NAME);
|
|
37
|
+
if (cookieId && isValidFingerprintId(cookieId)) {
|
|
38
|
+
// 同步到localStorage
|
|
39
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, cookieId);
|
|
40
|
+
return cookieId;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
// 等待FingerprintJS加载完成
|
|
44
|
+
if (!FingerprintJS) {
|
|
45
|
+
const module = yield import('../node_modules/.pnpm/@fingerprintjs_fingerprintjs@4.6.2/node_modules/@fingerprintjs/fingerprintjs/dist/fp.esm.mjs');
|
|
46
|
+
FingerprintJS = module.default;
|
|
47
|
+
}
|
|
48
|
+
// 使用FingerprintJS生成基于浏览器特征的指纹
|
|
49
|
+
const fp = yield FingerprintJS.load();
|
|
50
|
+
const result = yield fp.get();
|
|
51
|
+
const fingerprintId = `fp_${result.visitorId}`;
|
|
52
|
+
// 存储到localStorage和cookie
|
|
53
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, fingerprintId);
|
|
54
|
+
setCookie(FINGERPRINT_COOKIE_NAME, fingerprintId, 365); // 365天过期
|
|
55
|
+
return fingerprintId;
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.warn('Failed to generate fingerprint with FingerprintJS:', error);
|
|
59
|
+
// 降级方案:生成时间戳+随机数,但只在没有任何现有ID时使用
|
|
60
|
+
const fallbackId = `fp_fallback_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
61
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, fallbackId);
|
|
62
|
+
setCookie(FINGERPRINT_COOKIE_NAME, fallbackId, 365);
|
|
63
|
+
return fallbackId;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// 服务端环境降级方案
|
|
67
|
+
return `fp_server_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 获取当前的fingerprint ID
|
|
72
|
+
*/
|
|
73
|
+
function getFingerprintId() {
|
|
74
|
+
if (typeof window === 'undefined') {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
// 首先检查localStorage
|
|
78
|
+
const localStorageId = localStorage.getItem(FINGERPRINT_STORAGE_KEY);
|
|
79
|
+
if (localStorageId) {
|
|
80
|
+
return localStorageId;
|
|
81
|
+
}
|
|
82
|
+
// 检查cookie
|
|
83
|
+
const cookieId = getCookieValue(FINGERPRINT_COOKIE_NAME);
|
|
84
|
+
if (cookieId) {
|
|
85
|
+
// 同步到localStorage
|
|
86
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, cookieId);
|
|
87
|
+
return cookieId;
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 设置fingerprint ID到存储
|
|
93
|
+
*/
|
|
94
|
+
function setFingerprintId(fingerprintId) {
|
|
95
|
+
if (typeof window === 'undefined') {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, fingerprintId);
|
|
99
|
+
setCookie(FINGERPRINT_COOKIE_NAME, fingerprintId, 365);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 清除fingerprint ID
|
|
103
|
+
*/
|
|
104
|
+
function clearFingerprintId() {
|
|
105
|
+
if (typeof window === 'undefined') {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
localStorage.removeItem(FINGERPRINT_STORAGE_KEY);
|
|
109
|
+
deleteCookie(FINGERPRINT_COOKIE_NAME);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 获取或生成fingerprint ID
|
|
113
|
+
* 如果不存在则自动生成新的
|
|
114
|
+
*/
|
|
115
|
+
function getOrGenerateFingerprintId() {
|
|
116
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
117
|
+
const existingId = getFingerprintId();
|
|
118
|
+
if (existingId) {
|
|
119
|
+
return existingId;
|
|
120
|
+
}
|
|
121
|
+
return yield generateFingerprintId();
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 验证fingerprint ID格式
|
|
126
|
+
*/
|
|
127
|
+
function isValidFingerprintId(fingerprintId) {
|
|
128
|
+
if (!fingerprintId)
|
|
129
|
+
return false;
|
|
130
|
+
// 支持多种格式:
|
|
131
|
+
// - fp_ + FingerprintJS visitorId (变长字符串)
|
|
132
|
+
// - fp_fallback_ + 时间戳_随机字符串 (客户端降级方案)
|
|
133
|
+
// - fp_server_ + 时间戳_随机字符串 (服务端降级)
|
|
134
|
+
return /^fp(_fallback|_server)?_[a-zA-Z0-9_]+$/.test(fingerprintId);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 创建包含fingerprint ID的fetch headers
|
|
138
|
+
*/
|
|
139
|
+
function createFingerprintHeaders() {
|
|
140
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
141
|
+
const fingerprintId = yield getOrGenerateFingerprintId();
|
|
142
|
+
return {
|
|
143
|
+
[FINGERPRINT_HEADER_NAME]: fingerprintId,
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Hook for generating fingerprint headers
|
|
149
|
+
*/
|
|
150
|
+
function useFingerprintHeaders() {
|
|
151
|
+
return createFingerprintHeaders;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Create a fetch wrapper that automatically includes fingerprint headers
|
|
155
|
+
*/
|
|
156
|
+
function createFingerprintFetch() {
|
|
157
|
+
return (url, init) => __awaiter(this, void 0, void 0, function* () {
|
|
158
|
+
const fingerprintHeaders = yield createFingerprintHeaders();
|
|
159
|
+
const headers = Object.assign(Object.assign({}, fingerprintHeaders), ((init === null || init === void 0 ? void 0 : init.headers) || {}));
|
|
160
|
+
return fetch(url, Object.assign(Object.assign({}, init), { headers }));
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* 从请求中提取fingerprint ID
|
|
165
|
+
* 优先级:header > cookie > query参数
|
|
166
|
+
*/
|
|
167
|
+
function extractFingerprintId(headers, cookies, query) {
|
|
168
|
+
// 1. 从header中获取
|
|
169
|
+
const headerValue = headers instanceof Headers
|
|
170
|
+
? headers.get(FINGERPRINT_HEADER_NAME)
|
|
171
|
+
: headers[FINGERPRINT_HEADER_NAME];
|
|
172
|
+
if (headerValue && isValidFingerprintId(headerValue)) {
|
|
173
|
+
return headerValue;
|
|
174
|
+
}
|
|
175
|
+
// 2. 从cookie中获取
|
|
176
|
+
if (cookies) {
|
|
177
|
+
const cookieValue = cookies[FINGERPRINT_COOKIE_NAME];
|
|
178
|
+
if (cookieValue && isValidFingerprintId(cookieValue)) {
|
|
179
|
+
return cookieValue;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// 3. 从query参数中获取
|
|
183
|
+
if (query) {
|
|
184
|
+
const queryValue = query.fingerprint_id || query.fp_id;
|
|
185
|
+
if (queryValue && isValidFingerprintId(queryValue)) {
|
|
186
|
+
return queryValue;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
// Cookie 辅助函数
|
|
192
|
+
function getCookieValue(name) {
|
|
193
|
+
var _a;
|
|
194
|
+
if (typeof document === 'undefined') {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
const value = `; ${document.cookie}`;
|
|
198
|
+
const parts = value.split(`; ${name}=`);
|
|
199
|
+
if (parts.length === 2) {
|
|
200
|
+
return ((_a = parts.pop()) === null || _a === void 0 ? void 0 : _a.split(';').shift()) || null;
|
|
201
|
+
}
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
function setCookie(name, value, days) {
|
|
205
|
+
if (typeof document === 'undefined') {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const expires = new Date();
|
|
209
|
+
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
|
|
210
|
+
document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/;SameSite=Lax`;
|
|
211
|
+
}
|
|
212
|
+
function deleteCookie(name) {
|
|
213
|
+
if (typeof document === 'undefined') {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/`;
|
|
217
|
+
}
|
|
218
|
+
// 常量导出
|
|
219
|
+
const FINGERPRINT_CONSTANTS = {
|
|
220
|
+
STORAGE_KEY: FINGERPRINT_STORAGE_KEY,
|
|
221
|
+
HEADER_NAME: FINGERPRINT_HEADER_NAME,
|
|
222
|
+
COOKIE_NAME: FINGERPRINT_COOKIE_NAME,
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export { FINGERPRINT_CONSTANTS, clearFingerprintId, createFingerprintFetch, createFingerprintHeaders, extractFingerprintId, generateFingerprintId, getFingerprintId, getOrGenerateFingerprintId, isValidFingerprintId, setFingerprintId, useFingerprintHeaders };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { UseFingerprintResult, FingerprintConfig } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Hook for managing fingerprint ID and anonymous user data
|
|
4
|
+
* Accepts configuration to customize API endpoint and behavior
|
|
5
|
+
*/
|
|
6
|
+
export declare function useFingerprint(config: FingerprintConfig): UseFingerprintResult;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var tslib_es6 = require('../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.2/node_modules/tslib/tslib.es6.js');
|
|
5
|
+
var React = require('react');
|
|
6
|
+
var fingerprint = require('../fingerprint.js');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hook for managing fingerprint ID and anonymous user data
|
|
10
|
+
* Accepts configuration to customize API endpoint and behavior
|
|
11
|
+
*/
|
|
12
|
+
function useFingerprint(config) {
|
|
13
|
+
const [fingerprintId, setFingerprintIdState] = React.useState(null);
|
|
14
|
+
const [anonymousUser, setAnonymousUser] = React.useState(null);
|
|
15
|
+
const [credits, setCredits] = React.useState(null);
|
|
16
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
17
|
+
const [isInitialized, setIsInitialized] = React.useState(false);
|
|
18
|
+
const [error, setError] = React.useState(null);
|
|
19
|
+
/**
|
|
20
|
+
* 第一阶段:初始化fingerprint ID
|
|
21
|
+
*/
|
|
22
|
+
const initializeFingerprintId = React.useCallback(() => tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
if (typeof window === 'undefined')
|
|
24
|
+
return null;
|
|
25
|
+
try {
|
|
26
|
+
// 优先检查现有ID
|
|
27
|
+
const existingId = fingerprint.getFingerprintId();
|
|
28
|
+
if (existingId) {
|
|
29
|
+
setFingerprintIdState(existingId);
|
|
30
|
+
return existingId;
|
|
31
|
+
}
|
|
32
|
+
// 生成新的fingerprint ID
|
|
33
|
+
const currentFingerprintId = yield fingerprint.getOrGenerateFingerprintId();
|
|
34
|
+
setFingerprintIdState(currentFingerprintId);
|
|
35
|
+
return currentFingerprintId;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error('Failed to initialize fingerprint ID:', error);
|
|
39
|
+
setError('Failed to generate fingerprint ID');
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}), []);
|
|
43
|
+
/**
|
|
44
|
+
* 第二阶段:初始化匿名用户
|
|
45
|
+
*/
|
|
46
|
+
const initializeAnonymousUser = React.useCallback(() => tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
47
|
+
if (!fingerprintId) {
|
|
48
|
+
console.warn('Cannot initialize user without fingerprint ID');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
setIsLoading(true);
|
|
53
|
+
setError(null);
|
|
54
|
+
const fingerprintHeaders = yield fingerprint.createFingerprintHeaders();
|
|
55
|
+
const response = yield fetch(config.apiEndpoint, {
|
|
56
|
+
method: 'POST',
|
|
57
|
+
headers: Object.assign({ 'Content-Type': 'application/json' }, fingerprintHeaders),
|
|
58
|
+
body: JSON.stringify({
|
|
59
|
+
fingerprintId: fingerprintId,
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
const errorData = yield response.json().catch(() => ({}));
|
|
64
|
+
throw new Error(errorData.error || 'Failed to initialize anonymous user');
|
|
65
|
+
}
|
|
66
|
+
const data = yield response.json();
|
|
67
|
+
if (data.success) {
|
|
68
|
+
setAnonymousUser(data.user);
|
|
69
|
+
setCredits(data.credits);
|
|
70
|
+
setIsInitialized(true);
|
|
71
|
+
// 确保fingerprint ID同步
|
|
72
|
+
if (data.user.fingerprintId !== fingerprintId) {
|
|
73
|
+
fingerprint.setFingerprintId(data.user.fingerprintId);
|
|
74
|
+
setFingerprintIdState(data.user.fingerprintId);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
throw new Error(data.error || 'Unknown error occurred');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
console.error('Failed to initialize anonymous user:', err);
|
|
83
|
+
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
setIsLoading(false);
|
|
87
|
+
}
|
|
88
|
+
}), [fingerprintId]);
|
|
89
|
+
/**
|
|
90
|
+
* 刷新用户数据
|
|
91
|
+
*/
|
|
92
|
+
const refreshUserData = React.useCallback(() => tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
if (!fingerprintId)
|
|
94
|
+
return;
|
|
95
|
+
try {
|
|
96
|
+
setError(null);
|
|
97
|
+
const fingerprintHeaders = yield fingerprint.createFingerprintHeaders();
|
|
98
|
+
const response = yield fetch(`${config.apiEndpoint}?fingerprintId=${fingerprintId}`, {
|
|
99
|
+
method: 'GET',
|
|
100
|
+
headers: fingerprintHeaders,
|
|
101
|
+
});
|
|
102
|
+
if (!response.ok) {
|
|
103
|
+
if (response.status === 404) {
|
|
104
|
+
// 用户不存在,需要重新初始化
|
|
105
|
+
yield initializeAnonymousUser();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
throw new Error('Failed to fetch user data');
|
|
109
|
+
}
|
|
110
|
+
const data = yield response.json();
|
|
111
|
+
if (data.success) {
|
|
112
|
+
setAnonymousUser(data.user);
|
|
113
|
+
setCredits(data.credits);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
console.error('Failed to refresh user data:', err);
|
|
118
|
+
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
119
|
+
}
|
|
120
|
+
}), [fingerprintId, initializeAnonymousUser, config.apiEndpoint]);
|
|
121
|
+
/**
|
|
122
|
+
* 检查现有用户数据(仅在有fingerprint ID时执行)
|
|
123
|
+
*/
|
|
124
|
+
const checkExistingUser = React.useCallback(() => tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
125
|
+
if (!fingerprintId)
|
|
126
|
+
return;
|
|
127
|
+
try {
|
|
128
|
+
const fingerprintHeaders = yield fingerprint.createFingerprintHeaders();
|
|
129
|
+
const response = yield fetch(`${config.apiEndpoint}?fingerprintId=${fingerprintId}`, {
|
|
130
|
+
method: 'GET',
|
|
131
|
+
headers: fingerprintHeaders,
|
|
132
|
+
});
|
|
133
|
+
if (response.ok) {
|
|
134
|
+
const data = yield response.json();
|
|
135
|
+
if (data.success) {
|
|
136
|
+
setAnonymousUser(data.user);
|
|
137
|
+
setCredits(data.credits);
|
|
138
|
+
setIsInitialized(true);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
console.error('Failed to check existing user:', err);
|
|
144
|
+
}
|
|
145
|
+
}), [fingerprintId, config.apiEndpoint]);
|
|
146
|
+
// 第一阶段:页面加载完成后生成指纹ID
|
|
147
|
+
React.useEffect(() => {
|
|
148
|
+
if (typeof window === 'undefined')
|
|
149
|
+
return;
|
|
150
|
+
const initFingerprint = () => tslib_es6.__awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
yield initializeFingerprintId();
|
|
152
|
+
setIsLoading(false); // 第一阶段完成,结束加载状态
|
|
153
|
+
});
|
|
154
|
+
initFingerprint();
|
|
155
|
+
}, [initializeFingerprintId]);
|
|
156
|
+
// 第二阶段:有指纹ID后检查现有用户
|
|
157
|
+
React.useEffect(() => {
|
|
158
|
+
if (!fingerprintId || isInitialized)
|
|
159
|
+
return;
|
|
160
|
+
checkExistingUser();
|
|
161
|
+
}, [fingerprintId, isInitialized, checkExistingUser]);
|
|
162
|
+
// 第三阶段:如果没有现有用户且自动初始化开启,则创建新用户
|
|
163
|
+
React.useEffect(() => {
|
|
164
|
+
if (!fingerprintId || isInitialized || isLoading || error)
|
|
165
|
+
return;
|
|
166
|
+
if (config.autoInitialize === false)
|
|
167
|
+
return;
|
|
168
|
+
initializeAnonymousUser();
|
|
169
|
+
}, [fingerprintId, isInitialized, isLoading, error, initializeAnonymousUser, config.autoInitialize]);
|
|
170
|
+
return {
|
|
171
|
+
fingerprintId,
|
|
172
|
+
anonymousUser,
|
|
173
|
+
credits,
|
|
174
|
+
isLoading,
|
|
175
|
+
isInitialized,
|
|
176
|
+
error,
|
|
177
|
+
initializeAnonymousUser,
|
|
178
|
+
refreshUserData,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
exports.useFingerprint = useFingerprint;
|