@windrun-huaiin/third-ui 7.1.2 → 7.2.1
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 +177 -0
- package/dist/clerk/fingerprint/fingerprint-client.mjs +168 -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 +44 -0
- package/dist/clerk/fingerprint/use-fingerprint.d.ts +6 -0
- package/dist/clerk/fingerprint/use-fingerprint.js +176 -0
- package/dist/clerk/fingerprint/use-fingerprint.mjs +174 -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/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 +3212 -0
- package/dist/node_modules/.pnpm/@fingerprintjs_fingerprintjs@4.6.2/node_modules/@fingerprintjs/fingerprintjs/dist/fp.esm.mjs +3187 -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 +194 -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 +50 -0
- package/src/clerk/fingerprint/use-fingerprint.ts +200 -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
|
@@ -17,6 +17,17 @@ PERFORMANCE OF THIS SOFTWARE.
|
|
|
17
17
|
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
exports.__assign = function() {
|
|
21
|
+
exports.__assign = Object.assign || function __assign(t) {
|
|
22
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
23
|
+
s = arguments[i];
|
|
24
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
25
|
+
}
|
|
26
|
+
return t;
|
|
27
|
+
};
|
|
28
|
+
return exports.__assign.apply(this, arguments);
|
|
29
|
+
};
|
|
30
|
+
|
|
20
31
|
function __rest(s, e) {
|
|
21
32
|
var t = {};
|
|
22
33
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -39,10 +50,50 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
39
50
|
});
|
|
40
51
|
}
|
|
41
52
|
|
|
53
|
+
function __generator(thisArg, body) {
|
|
54
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
55
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
56
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
57
|
+
function step(op) {
|
|
58
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
59
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
60
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
61
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
62
|
+
switch (op[0]) {
|
|
63
|
+
case 0: case 1: t = op; break;
|
|
64
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
65
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
66
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
67
|
+
default:
|
|
68
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
69
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
70
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
71
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
72
|
+
if (t[2]) _.ops.pop();
|
|
73
|
+
_.trys.pop(); continue;
|
|
74
|
+
}
|
|
75
|
+
op = body.call(thisArg, _);
|
|
76
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
77
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function __spreadArray(to, from, pack) {
|
|
82
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
83
|
+
if (ar || !(i in from)) {
|
|
84
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
85
|
+
ar[i] = from[i];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
89
|
+
}
|
|
90
|
+
|
|
42
91
|
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
43
92
|
var e = new Error(message);
|
|
44
93
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
45
94
|
};
|
|
46
95
|
|
|
47
96
|
exports.__awaiter = __awaiter;
|
|
97
|
+
exports.__generator = __generator;
|
|
48
98
|
exports.__rest = __rest;
|
|
99
|
+
exports.__spreadArray = __spreadArray;
|
|
@@ -15,6 +15,17 @@ PERFORMANCE OF THIS SOFTWARE.
|
|
|
15
15
|
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
var __assign = function() {
|
|
19
|
+
__assign = Object.assign || function __assign(t) {
|
|
20
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
21
|
+
s = arguments[i];
|
|
22
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
23
|
+
}
|
|
24
|
+
return t;
|
|
25
|
+
};
|
|
26
|
+
return __assign.apply(this, arguments);
|
|
27
|
+
};
|
|
28
|
+
|
|
18
29
|
function __rest(s, e) {
|
|
19
30
|
var t = {};
|
|
20
31
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -37,9 +48,47 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
37
48
|
});
|
|
38
49
|
}
|
|
39
50
|
|
|
51
|
+
function __generator(thisArg, body) {
|
|
52
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
53
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
54
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
55
|
+
function step(op) {
|
|
56
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
57
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
58
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
59
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
60
|
+
switch (op[0]) {
|
|
61
|
+
case 0: case 1: t = op; break;
|
|
62
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
63
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
64
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
65
|
+
default:
|
|
66
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
67
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
68
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
69
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
70
|
+
if (t[2]) _.ops.pop();
|
|
71
|
+
_.trys.pop(); continue;
|
|
72
|
+
}
|
|
73
|
+
op = body.call(thisArg, _);
|
|
74
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
75
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function __spreadArray(to, from, pack) {
|
|
80
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
81
|
+
if (ar || !(i in from)) {
|
|
82
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
83
|
+
ar[i] = from[i];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
87
|
+
}
|
|
88
|
+
|
|
40
89
|
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
41
90
|
var e = new Error(message);
|
|
42
91
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
43
92
|
};
|
|
44
93
|
|
|
45
|
-
export { __awaiter, __rest };
|
|
94
|
+
export { __assign, __awaiter, __generator, __rest, __spreadArray };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var coseBase$1 = require('../../../../../_virtual/cose-
|
|
3
|
+
var coseBase$1 = require('../../../../../_virtual/cose-base.js');
|
|
4
4
|
var layoutBase = require('../../../layout-base@1.0.2/node_modules/layout-base/layout-base.js');
|
|
5
5
|
|
|
6
6
|
var coseBase = coseBase$1.__module.exports;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var coseBase$1 = require('../../../../../_virtual/cose-
|
|
3
|
+
var coseBase$1 = require('../../../../../_virtual/cose-base2.js');
|
|
4
4
|
var layoutBase = require('../../../layout-base@2.0.1/node_modules/layout-base/layout-base.js');
|
|
5
5
|
|
|
6
6
|
var coseBase = coseBase$1.__module.exports;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windrun-huaiin/third-ui",
|
|
3
|
-
"version": "7.1
|
|
3
|
+
"version": "7.2.1",
|
|
4
4
|
"description": "Third-party integrated UI components for windrun-huaiin projects",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -16,6 +16,16 @@
|
|
|
16
16
|
"import": "./dist/clerk/server.mjs",
|
|
17
17
|
"require": "./dist/clerk/server.js"
|
|
18
18
|
},
|
|
19
|
+
"./fingerprint": {
|
|
20
|
+
"types": "./dist/clerk/fingerprint/index.d.ts",
|
|
21
|
+
"import": "./dist/clerk/fingerprint/index.mjs",
|
|
22
|
+
"require": "./dist/clerk/fingerprint/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./fingerprint/server": {
|
|
25
|
+
"types": "./dist/clerk/fingerprint/server.d.ts",
|
|
26
|
+
"import": "./dist/clerk/fingerprint/server.mjs",
|
|
27
|
+
"require": "./dist/clerk/fingerprint/server.js"
|
|
28
|
+
},
|
|
19
29
|
"./main": {
|
|
20
30
|
"types": "./dist/main/index.d.ts",
|
|
21
31
|
"import": "./dist/main/index.mjs",
|
|
@@ -55,6 +65,7 @@
|
|
|
55
65
|
"@clerk/localizations": "^3.16.0",
|
|
56
66
|
"@clerk/types": "^4.59.0",
|
|
57
67
|
"@clerk/nextjs": "^6.19.4",
|
|
68
|
+
"@fingerprintjs/fingerprintjs": "^4.5.1",
|
|
58
69
|
"fumadocs-core": "15.3.3",
|
|
59
70
|
"fumadocs-mdx": "11.6.3",
|
|
60
71
|
"fumadocs-typescript": "4.0.4",
|
|
@@ -64,8 +75,8 @@
|
|
|
64
75
|
"mermaid": "^11.6.0",
|
|
65
76
|
"react-medium-image-zoom": "^5.2.14",
|
|
66
77
|
"zod": "^3.22.4",
|
|
67
|
-
"@windrun-huaiin/
|
|
68
|
-
"@windrun-huaiin/
|
|
78
|
+
"@windrun-huaiin/lib": "^7.1.1",
|
|
79
|
+
"@windrun-huaiin/base-ui": "^8.1.1"
|
|
69
80
|
},
|
|
70
81
|
"peerDependencies": {
|
|
71
82
|
"react": "19.1.0",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Client-side page generators with fingerprint support
|
|
5
|
+
* These should only be used in client-side code
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { SignUpWithFingerprint } from './signup-with-fingerprint-client';
|
|
9
|
+
import { SignInWithFingerprint } from './signin-with-fingerprint-client';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create a SignUp page with fingerprint support
|
|
13
|
+
* Note: This must be used within a FingerprintProvider
|
|
14
|
+
*/
|
|
15
|
+
export function createSignUpPageWithFingerprint() {
|
|
16
|
+
return function SignUpPage() {
|
|
17
|
+
return (
|
|
18
|
+
<div className="flex-1 flex justify-center mt-0 mb-32">
|
|
19
|
+
<SignUpWithFingerprint />
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create a SignIn page with fingerprint support
|
|
27
|
+
* Note: This must be used within a FingerprintProvider
|
|
28
|
+
*/
|
|
29
|
+
export function createSignInPageWithFingerprint() {
|
|
30
|
+
return function SignInPage() {
|
|
31
|
+
return (
|
|
32
|
+
<div className="flex-1 flex justify-center mb-64">
|
|
33
|
+
<SignInWithFingerprint />
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { SignIn, SignUp, Waitlist } from '@clerk/nextjs';
|
|
11
11
|
|
|
12
|
+
// Legacy page generators (for backward compatibility)
|
|
12
13
|
export function createSignInPage() {
|
|
13
14
|
return function SignInPage() {
|
|
14
15
|
return (
|
|
@@ -37,4 +38,7 @@ export function createWaitlistPage() {
|
|
|
37
38
|
</div>
|
|
38
39
|
);
|
|
39
40
|
};
|
|
40
|
-
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Note: Fingerprint-aware page generators moved to client-side only
|
|
44
|
+
// Use the fingerprint components directly in your client-side code
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fingerprint Client Utilities
|
|
3
|
+
* 客户端专用的指纹生成和管理逻辑
|
|
4
|
+
* 只能在浏览器环境中使用
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
FINGERPRINT_STORAGE_KEY,
|
|
9
|
+
FINGERPRINT_COOKIE_NAME,
|
|
10
|
+
isValidFingerprintId
|
|
11
|
+
} from './fingerprint-shared';
|
|
12
|
+
import FingerprintJS from '@fingerprintjs/fingerprintjs';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 生成基于真实浏览器特征的fingerprint ID
|
|
16
|
+
* 使用FingerprintJS收集浏览器特征并生成唯一标识
|
|
17
|
+
* 只能在客户端使用
|
|
18
|
+
*/
|
|
19
|
+
export async function generateFingerprintId(): Promise<string> {
|
|
20
|
+
if (typeof window === 'undefined') {
|
|
21
|
+
throw new Error('generateFingerprintId can only be used in browser environment');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 首先检查现有ID
|
|
25
|
+
const existingId = getFingerprintId();
|
|
26
|
+
if (existingId && isValidFingerprintId(existingId)) {
|
|
27
|
+
return existingId;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 检查cookie
|
|
31
|
+
const cookieId = getCookieValue(FINGERPRINT_COOKIE_NAME);
|
|
32
|
+
if (cookieId && isValidFingerprintId(cookieId)) {
|
|
33
|
+
// 同步到localStorage
|
|
34
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, cookieId);
|
|
35
|
+
return cookieId;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// 使用FingerprintJS生成基于浏览器特征的指纹
|
|
40
|
+
const fp = await FingerprintJS.load();
|
|
41
|
+
const result = await fp.get();
|
|
42
|
+
const fingerprintId = `fp_${result.visitorId}`;
|
|
43
|
+
|
|
44
|
+
// 存储到localStorage和cookie
|
|
45
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, fingerprintId);
|
|
46
|
+
setCookie(FINGERPRINT_COOKIE_NAME, fingerprintId, 365); // 365天过期
|
|
47
|
+
|
|
48
|
+
return fingerprintId;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.warn('Failed to generate fingerprint with FingerprintJS:', error);
|
|
51
|
+
// 降级方案:生成时间戳+随机数
|
|
52
|
+
const fallbackId = `fp_fallback_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
53
|
+
|
|
54
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, fallbackId);
|
|
55
|
+
setCookie(FINGERPRINT_COOKIE_NAME, fallbackId, 365);
|
|
56
|
+
|
|
57
|
+
return fallbackId;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 获取当前的fingerprint ID
|
|
63
|
+
* 只能在客户端使用
|
|
64
|
+
*/
|
|
65
|
+
export function getFingerprintId(): string | null {
|
|
66
|
+
if (typeof window === 'undefined') {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 首先检查localStorage
|
|
71
|
+
const localStorageId = localStorage.getItem(FINGERPRINT_STORAGE_KEY);
|
|
72
|
+
if (localStorageId) {
|
|
73
|
+
return localStorageId;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 检查cookie
|
|
77
|
+
const cookieId = getCookieValue(FINGERPRINT_COOKIE_NAME);
|
|
78
|
+
if (cookieId) {
|
|
79
|
+
// 同步到localStorage
|
|
80
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, cookieId);
|
|
81
|
+
return cookieId;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 设置fingerprint ID到存储
|
|
89
|
+
* 只能在客户端使用
|
|
90
|
+
*/
|
|
91
|
+
export function setFingerprintId(fingerprintId: string): void {
|
|
92
|
+
if (typeof window === 'undefined') {
|
|
93
|
+
throw new Error('setFingerprintId can only be used in browser environment');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
localStorage.setItem(FINGERPRINT_STORAGE_KEY, fingerprintId);
|
|
97
|
+
setCookie(FINGERPRINT_COOKIE_NAME, fingerprintId, 365);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 清除fingerprint ID
|
|
102
|
+
* 只能在客户端使用
|
|
103
|
+
*/
|
|
104
|
+
export function clearFingerprintId(): void {
|
|
105
|
+
if (typeof window === 'undefined') {
|
|
106
|
+
throw new Error('clearFingerprintId can only be used in browser environment');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
localStorage.removeItem(FINGERPRINT_STORAGE_KEY);
|
|
110
|
+
deleteCookie(FINGERPRINT_COOKIE_NAME);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 获取或生成fingerprint ID
|
|
115
|
+
* 如果不存在则自动生成新的
|
|
116
|
+
* 只能在客户端使用
|
|
117
|
+
*/
|
|
118
|
+
export async function getOrGenerateFingerprintId(): Promise<string> {
|
|
119
|
+
const existingId = getFingerprintId();
|
|
120
|
+
if (existingId) {
|
|
121
|
+
return existingId;
|
|
122
|
+
}
|
|
123
|
+
return await generateFingerprintId();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 创建包含fingerprint ID的fetch headers
|
|
128
|
+
* 只能在客户端使用
|
|
129
|
+
*/
|
|
130
|
+
export async function createFingerprintHeaders(): Promise<Record<string, string>> {
|
|
131
|
+
const fingerprintId = await getOrGenerateFingerprintId();
|
|
132
|
+
return {
|
|
133
|
+
FINGERPRINT_HEADER_NAME : fingerprintId,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Hook for generating fingerprint headers
|
|
139
|
+
* 只能在客户端使用
|
|
140
|
+
*/
|
|
141
|
+
export function useFingerprintHeaders(): () => Promise<Record<string, string>> {
|
|
142
|
+
return createFingerprintHeaders;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Create a fetch wrapper that automatically includes fingerprint headers
|
|
147
|
+
* 只能在客户端使用
|
|
148
|
+
*/
|
|
149
|
+
export function createFingerprintFetch() {
|
|
150
|
+
return async (url: string | URL | Request, init?: RequestInit) => {
|
|
151
|
+
const fingerprintHeaders = await createFingerprintHeaders();
|
|
152
|
+
const headers = {
|
|
153
|
+
...fingerprintHeaders,
|
|
154
|
+
...(init?.headers || {}),
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
return fetch(url, {
|
|
158
|
+
...init,
|
|
159
|
+
headers,
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Cookie 辅助函数 (私有)
|
|
165
|
+
function getCookieValue(name: string): string | null {
|
|
166
|
+
if (typeof document === 'undefined') {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const value = `; ${document.cookie}`;
|
|
171
|
+
const parts = value.split(`; ${name}=`);
|
|
172
|
+
if (parts.length === 2) {
|
|
173
|
+
return parts.pop()?.split(';').shift() || null;
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function setCookie(name: string, value: string, days: number): void {
|
|
179
|
+
if (typeof document === 'undefined') {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const expires = new Date();
|
|
184
|
+
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
|
|
185
|
+
document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/;SameSite=Lax`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function deleteCookie(name: string): void {
|
|
189
|
+
if (typeof document === 'undefined') {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/`;
|
|
194
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useContext } from 'react';
|
|
4
|
+
import { useFingerprint } from './use-fingerprint';
|
|
5
|
+
import type {
|
|
6
|
+
FingerprintContextType,
|
|
7
|
+
FingerprintProviderProps
|
|
8
|
+
} from './types';
|
|
9
|
+
|
|
10
|
+
const FingerprintContext = createContext<FingerprintContextType | undefined>(undefined);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Fingerprint Provider Component
|
|
14
|
+
* 为应用提供fingerprint和匿名用户管理功能
|
|
15
|
+
*/
|
|
16
|
+
export function FingerprintProvider({
|
|
17
|
+
children,
|
|
18
|
+
config
|
|
19
|
+
}: FingerprintProviderProps) {
|
|
20
|
+
const fingerprintData = useFingerprint(config);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<FingerprintContext.Provider value={fingerprintData}>
|
|
24
|
+
{children}
|
|
25
|
+
</FingerprintContext.Provider>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Hook to use fingerprint context
|
|
31
|
+
*/
|
|
32
|
+
export function useFingerprintContext(): FingerprintContextType {
|
|
33
|
+
const context = useContext(FingerprintContext);
|
|
34
|
+
if (context === undefined) {
|
|
35
|
+
throw new Error('useFingerprintContext must be used within a FingerprintProvider');
|
|
36
|
+
}
|
|
37
|
+
return context;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Safe hook to use fingerprint context - returns null if no provider
|
|
42
|
+
* 安全版本的fingerprint context hook - 如果没有Provider则返回null
|
|
43
|
+
*/
|
|
44
|
+
export function useFingerprintContextSafe(): FingerprintContextType | null {
|
|
45
|
+
const context = useContext(FingerprintContext);
|
|
46
|
+
return context || null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* HOC for components that need fingerprint functionality
|
|
51
|
+
* Note: This HOC now requires config to be passed externally
|
|
52
|
+
*/
|
|
53
|
+
export function withFingerprint<P extends object>(
|
|
54
|
+
Component: React.ComponentType<P>,
|
|
55
|
+
config: FingerprintProviderProps['config']
|
|
56
|
+
) {
|
|
57
|
+
return function FingerprintWrappedComponent(props: P) {
|
|
58
|
+
return (
|
|
59
|
+
<FingerprintProvider config={config}>
|
|
60
|
+
<Component {...props} />
|
|
61
|
+
</FingerprintProvider>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 组件:显示用户状态和积分信息(用于调试)
|
|
68
|
+
*/
|
|
69
|
+
export function FingerprintDebugInfo() {
|
|
70
|
+
const {
|
|
71
|
+
fingerprintId,
|
|
72
|
+
anonymousUser,
|
|
73
|
+
credits,
|
|
74
|
+
isLoading,
|
|
75
|
+
isInitialized,
|
|
76
|
+
error
|
|
77
|
+
} = useFingerprintContext();
|
|
78
|
+
|
|
79
|
+
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'production') {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div style={{
|
|
85
|
+
position: 'fixed',
|
|
86
|
+
bottom: '10px',
|
|
87
|
+
right: '10px',
|
|
88
|
+
background: '#f0f0f0',
|
|
89
|
+
padding: '10px',
|
|
90
|
+
borderRadius: '5px',
|
|
91
|
+
fontSize: '12px',
|
|
92
|
+
fontFamily: 'monospace',
|
|
93
|
+
maxWidth: '300px',
|
|
94
|
+
zIndex: 9999,
|
|
95
|
+
border: '1px solid #ccc'
|
|
96
|
+
}}>
|
|
97
|
+
<h4 style={{ margin: '0 0 5px 0' }}>Fingerprint Debug</h4>
|
|
98
|
+
<div><strong>FP ID:</strong> {fingerprintId || 'None'}</div>
|
|
99
|
+
<div><strong>Loading:</strong> {isLoading ? 'Yes' : 'No'}</div>
|
|
100
|
+
<div><strong>Initialized:</strong> {isInitialized ? 'Yes' : 'No'}</div>
|
|
101
|
+
{error && <div style={{ color: 'red' }}><strong>Error:</strong> {error}</div>}
|
|
102
|
+
{anonymousUser && (
|
|
103
|
+
<div>
|
|
104
|
+
<strong>User ID:</strong> {anonymousUser.userId.slice(0, 8)}...
|
|
105
|
+
</div>
|
|
106
|
+
)}
|
|
107
|
+
{credits && (
|
|
108
|
+
<div>
|
|
109
|
+
<strong>Credits:</strong> {credits.balanceFree}F + {credits.balancePaid}P = {credits.totalBalance}
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fingerprint Server Utilities
|
|
3
|
+
* 服务端专用的指纹ID提取和验证逻辑
|
|
4
|
+
* 可以安全地在服务端使用,不依赖浏览器API或FingerprintJS
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
FINGERPRINT_HEADER_NAME,
|
|
9
|
+
FINGERPRINT_COOKIE_NAME,
|
|
10
|
+
isValidFingerprintId
|
|
11
|
+
} from './fingerprint-shared';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 从请求中提取fingerprint ID
|
|
15
|
+
* 优先级:header > cookie > query参数
|
|
16
|
+
* 可以安全地在服务端使用
|
|
17
|
+
*/
|
|
18
|
+
export function extractFingerprintId(
|
|
19
|
+
headers: Headers | Record<string, string>,
|
|
20
|
+
cookies?: Record<string, string>,
|
|
21
|
+
query?: Record<string, string | undefined>
|
|
22
|
+
): string | null {
|
|
23
|
+
// 1. 从header中获取
|
|
24
|
+
const headerValue = headers instanceof Headers
|
|
25
|
+
? headers.get(FINGERPRINT_HEADER_NAME)
|
|
26
|
+
: headers[FINGERPRINT_HEADER_NAME];
|
|
27
|
+
|
|
28
|
+
if (headerValue && isValidFingerprintId(headerValue)) {
|
|
29
|
+
return headerValue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 2. 从cookie中获取
|
|
33
|
+
if (cookies) {
|
|
34
|
+
const cookieValue = cookies[FINGERPRINT_COOKIE_NAME];
|
|
35
|
+
if (cookieValue && isValidFingerprintId(cookieValue)) {
|
|
36
|
+
return cookieValue;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 3. 从query参数中获取
|
|
41
|
+
if (query) {
|
|
42
|
+
const queryValue = query.fingerprint_id || query.fp_id;
|
|
43
|
+
if (queryValue && isValidFingerprintId(queryValue)) {
|
|
44
|
+
return queryValue;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 生成服务端降级fingerprint ID
|
|
53
|
+
* 当客户端无法生成fingerprint时使用
|
|
54
|
+
* 可以安全地在服务端使用
|
|
55
|
+
*/
|
|
56
|
+
export function generateServerFingerprintId(): string {
|
|
57
|
+
return `fp_server_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 从Next.js Request对象中提取fingerprint ID
|
|
62
|
+
* 便捷方法,适用于Next.js API路由
|
|
63
|
+
*/
|
|
64
|
+
export function extractFingerprintFromNextRequest(request: Request): string | null {
|
|
65
|
+
const headers = request.headers;
|
|
66
|
+
|
|
67
|
+
// 尝试从cookies获取(需要解析cookie header)
|
|
68
|
+
const cookieHeader = headers.get('cookie');
|
|
69
|
+
const cookies: Record<string, string> = {};
|
|
70
|
+
|
|
71
|
+
if (cookieHeader) {
|
|
72
|
+
cookieHeader.split(';').forEach(cookie => {
|
|
73
|
+
const [name, value] = cookie.trim().split('=');
|
|
74
|
+
if (name && value) {
|
|
75
|
+
cookies[name] = value;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 尝试从URL query参数获取
|
|
81
|
+
const url = new URL(request.url);
|
|
82
|
+
const query: Record<string, string> = {};
|
|
83
|
+
url.searchParams.forEach((value, key) => {
|
|
84
|
+
query[key] = value;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return extractFingerprintId(headers, cookies, query);
|
|
88
|
+
}
|