@choiceform/shared-auth 0.1.18 → 0.1.19
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/README.md +370 -139
- package/dist/core.d.ts +109 -66
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +3 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/init.d.ts +109 -66
- package/dist/init.d.ts.map +1 -1
- package/dist/lib/auth-client.d.ts +108 -66
- package/dist/lib/auth-client.d.ts.map +1 -1
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -0
- package/dist/services/referral-service.d.ts +54 -0
- package/dist/services/referral-service.d.ts.map +1 -0
- package/dist/services/referral-service.js +54 -0
- package/dist/store/utils.d.ts +1 -1
- package/dist/store/utils.d.ts.map +1 -1
- package/dist/types/user.d.ts +4 -0
- package/dist/types/user.d.ts.map +1 -1
- package/dist/utils/user-mapper.d.ts.map +1 -1
- package/dist/utils/user-mapper.js +2 -0
- package/package.json +11 -12
- package/dist/components/auth-sync.d.ts +0 -27
- package/dist/components/auth-sync.d.ts.map +0 -1
- package/dist/components/auth-sync.js +0 -117
- package/dist/components/protected-route.d.ts +0 -18
- package/dist/components/protected-route.d.ts.map +0 -1
- package/dist/components/protected-route.js +0 -34
- package/dist/components/sign-in-page.d.ts +0 -21
- package/dist/components/sign-in-page.d.ts.map +0 -1
- package/dist/components/sign-in-page.js +0 -31
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 邀请码服务
|
|
3
|
+
*
|
|
4
|
+
* 提供邀请码相关的业务逻辑:
|
|
5
|
+
* - 提交邀请码
|
|
6
|
+
* - 检查用户是否需要填写邀请码
|
|
7
|
+
* - 提取用户邀请码字段
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* 创建邀请码服务
|
|
11
|
+
*/
|
|
12
|
+
export function createReferralService(apiClient) {
|
|
13
|
+
async function submitReferralCode(referralCode) {
|
|
14
|
+
const response = await apiClient.post("/v1/auth/referral", { referralCode });
|
|
15
|
+
if (response.ok) {
|
|
16
|
+
return { success: true, error: undefined };
|
|
17
|
+
}
|
|
18
|
+
let error = "unknown";
|
|
19
|
+
if (response.status === 400) {
|
|
20
|
+
error = "already_referred";
|
|
21
|
+
}
|
|
22
|
+
else if (response.status === 404) {
|
|
23
|
+
error = "not_found";
|
|
24
|
+
}
|
|
25
|
+
return { success: false, error };
|
|
26
|
+
}
|
|
27
|
+
return { submitReferralCode };
|
|
28
|
+
}
|
|
29
|
+
// ===== Utilities =====
|
|
30
|
+
/**
|
|
31
|
+
* 检查用户是否需要完成邀请码步骤
|
|
32
|
+
*
|
|
33
|
+
* 仅检查用户状态,不包含 feature flag 判断(由应用层控制)
|
|
34
|
+
*/
|
|
35
|
+
export function needsReferral(user, options) {
|
|
36
|
+
if (!user)
|
|
37
|
+
return false;
|
|
38
|
+
if (user.emailVerified !== true)
|
|
39
|
+
return false;
|
|
40
|
+
const skipRoles = options?.skipRoles ?? ["admin"];
|
|
41
|
+
if (user.role && skipRoles.includes(user.role))
|
|
42
|
+
return false;
|
|
43
|
+
const { referredBy } = user;
|
|
44
|
+
return !referredBy || referredBy.trim() === "";
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 提取用户的邀请码相关字段
|
|
48
|
+
*/
|
|
49
|
+
export function getReferralFields(user) {
|
|
50
|
+
return {
|
|
51
|
+
referralCode: user?.referralCode,
|
|
52
|
+
referredBy: user?.referredBy,
|
|
53
|
+
};
|
|
54
|
+
}
|
package/dist/store/utils.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Store 工具函数
|
|
3
3
|
*/
|
|
4
4
|
import type { Observable } from "@legendapp/state";
|
|
5
|
-
import type { AuthState, SessionUser } from "../types";
|
|
6
5
|
import type { TokenStorage } from "../api";
|
|
6
|
+
import type { AuthState, SessionUser } from "../types";
|
|
7
7
|
import type { StoreActions } from "./actions";
|
|
8
8
|
export declare function getCurrentUser(authStore: Observable<AuthState>): SessionUser | null;
|
|
9
9
|
export declare function getCurrentUserId(authStore: Observable<AuthState>): string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/store/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/store/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,wBAAgB,cAAc,CAC5B,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAC/B,WAAW,GAAG,IAAI,CAEpB;AAED,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAC/B,MAAM,GAAG,IAAI,CAEf;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,OAAO,CAEzE;AAED,wBAAgB,SAAS,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,OAAO,CAEnE;AAED,wBAAgB,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,OAAO,CAElE;AAED,wBAAsB,WAAW,CAC/B,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAE1E;AAED,wBAAsB,YAAY,CAChC,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAExB;AAED,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,YAAY,GACzB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIxB;AAED,wBAAsB,cAAc,CAClC,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAEjC;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,YAAY,GACzB,QAAQ,CAKV;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC;;;EAKjE"}
|
package/dist/types/user.d.ts
CHANGED
|
@@ -28,6 +28,10 @@ export interface SessionUser {
|
|
|
28
28
|
lastLoginMethod?: string;
|
|
29
29
|
metadata: SessionUserMetadata;
|
|
30
30
|
name: string;
|
|
31
|
+
/** 用户自己的邀请码(只读,注册时自动生成) */
|
|
32
|
+
referralCode?: string;
|
|
33
|
+
/** 推荐人的用户 ID */
|
|
34
|
+
referredBy?: string;
|
|
31
35
|
role?: string;
|
|
32
36
|
updatedAt: string;
|
|
33
37
|
}
|
package/dist/types/user.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/types/user.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,kBAAkB;AAClB,MAAM,WAAW,mBAAmB;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,aAAa;AACb,MAAM,WAAW,WAAW;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,OAAO,CAAA;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gCAAgC;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,gCAAgC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kCAAkC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,0BAA0B;AAC1B,MAAM,WAAW,OAAO;IACtB,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,WAAW,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;CACf"}
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/types/user.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,kBAAkB;AAClB,MAAM,WAAW,mBAAmB;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,aAAa;AACb,MAAM,WAAW,WAAW;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,OAAO,CAAA;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gCAAgC;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,gCAAgC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kCAAkC;IAClC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,2BAA2B;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,0BAA0B;AAC1B,MAAM,WAAW,OAAO;IACtB,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,WAAW,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;CACf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-mapper.d.ts","sourceRoot":"","sources":["../../src/utils/user-mapper.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,UAAU,CAAA;AAEhE;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"user-mapper.d.ts","sourceRoot":"","sources":["../../src/utils/user-mapper.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,UAAU,CAAA;AAEhE;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CA4C5E"}
|
|
@@ -50,6 +50,8 @@ export function extractSessionUser(responseData) {
|
|
|
50
50
|
lastLoginMethod: user.lastLoginMethod,
|
|
51
51
|
metadata: user.metadata ?? {},
|
|
52
52
|
name: user.name ?? "",
|
|
53
|
+
referralCode: user.referralCode,
|
|
54
|
+
referredBy: user.referredBy,
|
|
53
55
|
role: user.role,
|
|
54
56
|
updatedAt: String(user.updatedAt ?? ""),
|
|
55
57
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@choiceform/shared-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
4
4
|
"description": "Shared authentication package for Choiceform projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,14 +16,6 @@
|
|
|
16
16
|
"dist",
|
|
17
17
|
"README.md"
|
|
18
18
|
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"build": "tsc",
|
|
21
|
-
"dev": "tsc --watch",
|
|
22
|
-
"clean": "rimraf dist",
|
|
23
|
-
"test": "vitest run",
|
|
24
|
-
"test:watch": "vitest",
|
|
25
|
-
"prepublishOnly": "pnpm run build"
|
|
26
|
-
},
|
|
27
19
|
"repository": {
|
|
28
20
|
"type": "git",
|
|
29
21
|
"url": "git+https://github.com/choiceform/automation.git",
|
|
@@ -47,13 +39,13 @@
|
|
|
47
39
|
"better-auth": "^1.4.4"
|
|
48
40
|
},
|
|
49
41
|
"peerDependencies": {
|
|
50
|
-
"@legendapp/state": "v3.0.0-beta.
|
|
42
|
+
"@legendapp/state": "v3.0.0-beta.30",
|
|
51
43
|
"better-auth": "^1.4.4",
|
|
52
44
|
"react": ">=18.0.0",
|
|
53
45
|
"react-dom": ">=18.0.0"
|
|
54
46
|
},
|
|
55
47
|
"devDependencies": {
|
|
56
|
-
"@legendapp/state": "v3.0.0-beta.
|
|
48
|
+
"@legendapp/state": "v3.0.0-beta.30",
|
|
57
49
|
"@testing-library/react": "^16.3.0",
|
|
58
50
|
"@types/react": "18.2.71",
|
|
59
51
|
"@types/react-dom": "18.2.22",
|
|
@@ -63,5 +55,12 @@
|
|
|
63
55
|
"rimraf": "^6.0.1",
|
|
64
56
|
"typescript": "^5.5.3",
|
|
65
57
|
"vitest": "^3.2.4"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsc",
|
|
61
|
+
"dev": "tsc --watch",
|
|
62
|
+
"clean": "rimraf dist",
|
|
63
|
+
"test": "vitest run",
|
|
64
|
+
"test:watch": "vitest"
|
|
66
65
|
}
|
|
67
|
-
}
|
|
66
|
+
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Better Auth 认证状态同步组件
|
|
3
|
-
*
|
|
4
|
-
* 功能说明:
|
|
5
|
-
* - 同步 better-auth 的 session 状态到 Legend State store
|
|
6
|
-
* - 在用户登录成功后自动设置组织/团队上下文
|
|
7
|
-
* - 使用响应式状态监听,确保状态同步的实时性
|
|
8
|
-
*
|
|
9
|
-
* 边缘情况处理:
|
|
10
|
-
* - Session 加载中:设置 loading 状态
|
|
11
|
-
* - Session 错误:清空用户信息,重置团队设置状态
|
|
12
|
-
* - 无 Session 但有用户:保持用户信息(可能从 token 加载)
|
|
13
|
-
* - 用户登出:重置团队设置状态,允许下次登录时重新设置
|
|
14
|
-
*/
|
|
15
|
-
import type { AuthInstance } from "../core";
|
|
16
|
-
/**
|
|
17
|
-
* AuthSync 组件 Props
|
|
18
|
-
*/
|
|
19
|
-
interface AuthSyncProps {
|
|
20
|
-
auth: AuthInstance;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* AuthSync 组件
|
|
24
|
-
*/
|
|
25
|
-
export declare function AuthSync({ auth }: AuthSyncProps): null;
|
|
26
|
-
export {};
|
|
27
|
-
//# sourceMappingURL=auth-sync.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-sync.d.ts","sourceRoot":"","sources":["../../src/components/auth-sync.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAK3C;;GAEG;AACH,UAAU,aAAa;IACrB,IAAI,EAAE,YAAY,CAAA;CACnB;AAsED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,EAAE,aAAa,QAwF/C"}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Better Auth 认证状态同步组件
|
|
3
|
-
*
|
|
4
|
-
* 功能说明:
|
|
5
|
-
* - 同步 better-auth 的 session 状态到 Legend State store
|
|
6
|
-
* - 在用户登录成功后自动设置组织/团队上下文
|
|
7
|
-
* - 使用响应式状态监听,确保状态同步的实时性
|
|
8
|
-
*
|
|
9
|
-
* 边缘情况处理:
|
|
10
|
-
* - Session 加载中:设置 loading 状态
|
|
11
|
-
* - Session 错误:清空用户信息,重置团队设置状态
|
|
12
|
-
* - 无 Session 但有用户:保持用户信息(可能从 token 加载)
|
|
13
|
-
* - 用户登出:重置团队设置状态,允许下次登录时重新设置
|
|
14
|
-
*/
|
|
15
|
-
import { useEffect, useRef, useCallback } from "react";
|
|
16
|
-
import { use$ } from "@legendapp/state/react";
|
|
17
|
-
import { mapToSessionUser } from "../utils";
|
|
18
|
-
import { setupCompanionTeam } from "../services";
|
|
19
|
-
/**
|
|
20
|
-
* 将 Better Auth session 数据映射为 SessionUser
|
|
21
|
-
*/
|
|
22
|
-
function mapBetterAuthSession(user, session) {
|
|
23
|
-
return mapToSessionUser({
|
|
24
|
-
banExpires: user.banExpires,
|
|
25
|
-
banReason: user.banReason,
|
|
26
|
-
banned: user.banned,
|
|
27
|
-
createdAt: user.createdAt,
|
|
28
|
-
email: user.email,
|
|
29
|
-
emailVerified: user.emailVerified,
|
|
30
|
-
id: user.id,
|
|
31
|
-
image: user.image,
|
|
32
|
-
name: user.name,
|
|
33
|
-
role: user.role,
|
|
34
|
-
updatedAt: user.updatedAt,
|
|
35
|
-
}, {
|
|
36
|
-
activeOrganizationId: session?.activeOrganizationId,
|
|
37
|
-
activeTeamId: session?.activeTeamId,
|
|
38
|
-
createdAt: session?.createdAt,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* AuthSync 组件
|
|
43
|
-
*/
|
|
44
|
-
export function AuthSync({ auth }) {
|
|
45
|
-
const { authClient, authActions, tokenStorage, authStore } = auth;
|
|
46
|
-
// 监听 authStore 中的用户状态(响应式)
|
|
47
|
-
const user = use$(authStore.user);
|
|
48
|
-
const isAuthenticated = use$(authStore.isAuthenticated);
|
|
49
|
-
const isLoaded = use$(authStore.isLoaded);
|
|
50
|
-
// 监听 better-auth 的 session
|
|
51
|
-
const sessionResult = authClient.useSession();
|
|
52
|
-
const { data: session, isPending, error, refetch } = sessionResult || {
|
|
53
|
-
data: null,
|
|
54
|
-
isPending: false,
|
|
55
|
-
error: null,
|
|
56
|
-
refetch: undefined,
|
|
57
|
-
};
|
|
58
|
-
// 确保 companion team 设置只执行一次
|
|
59
|
-
const teamSetupRef = useRef(false);
|
|
60
|
-
// 处理 session 更新
|
|
61
|
-
const handleSessionUpdate = useCallback((sessionData) => {
|
|
62
|
-
if (sessionData?.user) {
|
|
63
|
-
try {
|
|
64
|
-
const mappedUser = mapBetterAuthSession(sessionData.user, sessionData.session);
|
|
65
|
-
authActions.initialize(mappedUser, true);
|
|
66
|
-
}
|
|
67
|
-
catch (mappingError) {
|
|
68
|
-
console.error("[AuthSync] Failed to map session user:", mappingError);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}, [authActions]);
|
|
72
|
-
// Effect 1: 同步 better-auth session 到 store
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
if (isPending) {
|
|
75
|
-
authActions.setLoading(true);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
authActions.setLoading(false);
|
|
79
|
-
if (error) {
|
|
80
|
-
const errorMessage = error instanceof Error
|
|
81
|
-
? error.message
|
|
82
|
-
: typeof error === "string"
|
|
83
|
-
? error
|
|
84
|
-
: "Authentication error";
|
|
85
|
-
authActions.setError(errorMessage);
|
|
86
|
-
authActions.initialize(null, true);
|
|
87
|
-
teamSetupRef.current = false;
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
if (session) {
|
|
91
|
-
handleSessionUpdate(session);
|
|
92
|
-
}
|
|
93
|
-
}, [session, isPending, error, authActions, handleSessionUpdate]);
|
|
94
|
-
// Effect 2: 设置 companion team
|
|
95
|
-
useEffect(() => {
|
|
96
|
-
// 只有在用户已认证且已加载完成时才设置
|
|
97
|
-
if (isLoaded && isAuthenticated && user?.id && !teamSetupRef.current) {
|
|
98
|
-
teamSetupRef.current = true;
|
|
99
|
-
const token = tokenStorage.get();
|
|
100
|
-
if (token && typeof token === "string" && token.trim().length > 0) {
|
|
101
|
-
setupCompanionTeam(auth, token, {
|
|
102
|
-
onComplete: () => refetch?.(),
|
|
103
|
-
onError: () => {
|
|
104
|
-
// 失败后重置,允许重试
|
|
105
|
-
teamSetupRef.current = false;
|
|
106
|
-
},
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
// 用户登出:重置 ref
|
|
111
|
-
if (isLoaded && !isAuthenticated) {
|
|
112
|
-
teamSetupRef.current = false;
|
|
113
|
-
}
|
|
114
|
-
}, [user, isAuthenticated, isLoaded, auth, tokenStorage, refetch]);
|
|
115
|
-
// 不渲染任何内容
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { AuthInstance } from "../core";
|
|
3
|
-
interface ProtectedRouteProps {
|
|
4
|
-
auth: AuthInstance;
|
|
5
|
-
children: React.ReactNode;
|
|
6
|
-
loadingComponent?: React.ComponentType<{
|
|
7
|
-
message?: string;
|
|
8
|
-
}>;
|
|
9
|
-
loadingMessage?: string;
|
|
10
|
-
onUnauthorized: () => void;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* 路由保护组件
|
|
14
|
-
* 等待认证初始化完成,根据认证状态进行路由保护
|
|
15
|
-
*/
|
|
16
|
-
export declare const ProtectedRoute: React.FC<ProtectedRouteProps>;
|
|
17
|
-
export default ProtectedRoute;
|
|
18
|
-
//# sourceMappingURL=protected-route.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protected-route.d.ts","sourceRoot":"","sources":["../../src/components/protected-route.tsx"],"names":[],"mappings":"AACA,OAAO,KAAoB,MAAM,OAAO,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3C,UAAU,mBAAmB;IAC3B,IAAI,EAAE,YAAY,CAAA;IAClB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,gBAAgB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC5D,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,cAAc,EAAE,MAAM,IAAI,CAAA;CAC3B;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAgCxD,CAAA;AAED,eAAe,cAAc,CAAA"}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { observer } from "@legendapp/state/react";
|
|
3
|
-
import { useEffect } from "react";
|
|
4
|
-
/**
|
|
5
|
-
* 路由保护组件
|
|
6
|
-
* 等待认证初始化完成,根据认证状态进行路由保护
|
|
7
|
-
*/
|
|
8
|
-
export const ProtectedRoute = observer(({ children, auth, loadingComponent, loadingMessage, onUnauthorized }) => {
|
|
9
|
-
const { authStore, authComputed } = auth;
|
|
10
|
-
// 检查各种状态
|
|
11
|
-
const isInitializing = authComputed.isInitializing.get();
|
|
12
|
-
const isAuthenticated = authStore.isAuthenticated.get();
|
|
13
|
-
// 当未认证时调用回调
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
if (!isInitializing && !isAuthenticated) {
|
|
16
|
-
onUnauthorized();
|
|
17
|
-
}
|
|
18
|
-
}, [isInitializing, isAuthenticated, onUnauthorized]);
|
|
19
|
-
// 检查初始化状态
|
|
20
|
-
if (isInitializing) {
|
|
21
|
-
if (loadingComponent) {
|
|
22
|
-
const LoadingComponent = loadingComponent;
|
|
23
|
-
return _jsx(LoadingComponent, { message: loadingMessage });
|
|
24
|
-
}
|
|
25
|
-
return _jsx("div", { children: loadingMessage || "Checking authentication..." });
|
|
26
|
-
}
|
|
27
|
-
// 检查认证状态
|
|
28
|
-
if (!isAuthenticated) {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
// 所有检查通过,渲染子组件
|
|
32
|
-
return _jsx(_Fragment, { children: children });
|
|
33
|
-
});
|
|
34
|
-
export default ProtectedRoute;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { AuthInstance } from "../core";
|
|
3
|
-
interface SignInPageProps {
|
|
4
|
-
afterElement?: React.ReactNode;
|
|
5
|
-
auth: AuthInstance;
|
|
6
|
-
beforeElement?: React.ReactNode;
|
|
7
|
-
className?: string;
|
|
8
|
-
description?: string;
|
|
9
|
-
footerText?: React.ReactNode;
|
|
10
|
-
githubButton?: (isSigningIn: boolean) => React.ReactNode;
|
|
11
|
-
onAuthSuccess?: () => void;
|
|
12
|
-
provider?: string;
|
|
13
|
-
redirectUrl?: string;
|
|
14
|
-
title?: string;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* 登录页面组件
|
|
18
|
-
*/
|
|
19
|
-
export declare function SignInPage({ afterElement, auth, beforeElement, onAuthSuccess, redirectUrl, provider, title, description, githubButton, className, footerText, }: SignInPageProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
-
export {};
|
|
21
|
-
//# sourceMappingURL=sign-in-page.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sign-in-page.d.ts","sourceRoot":"","sources":["../../src/components/sign-in-page.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3C,UAAU,eAAe;IACvB,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,IAAI,EAAE,YAAY,CAAA;IAClB,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B,YAAY,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,KAAK,CAAC,SAAS,CAAA;IACxD,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,EACzB,YAAY,EACZ,IAAI,EACJ,aAAa,EACb,aAAa,EACb,WAA0B,EAC1B,QAAmB,EACnB,KAAK,EACL,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,GACX,EAAE,eAAe,2CA+CjB"}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Slot } from "@choiceform/design-system";
|
|
3
|
-
import { use$ } from "@legendapp/state/react";
|
|
4
|
-
import { useEffect, useState } from "react";
|
|
5
|
-
/**
|
|
6
|
-
* 登录页面组件
|
|
7
|
-
*/
|
|
8
|
-
export function SignInPage({ afterElement, auth, beforeElement, onAuthSuccess, redirectUrl = "/community", provider = "github", title, description, githubButton, className, footerText, }) {
|
|
9
|
-
const { authStore, authActions } = auth;
|
|
10
|
-
const { isAuthenticated, error } = use$(authStore);
|
|
11
|
-
const [isSigningIn, setIsSigningIn] = useState(false);
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
if (isAuthenticated && onAuthSuccess) {
|
|
14
|
-
onAuthSuccess();
|
|
15
|
-
}
|
|
16
|
-
}, [isAuthenticated, onAuthSuccess]);
|
|
17
|
-
const handleSignIn = async () => {
|
|
18
|
-
// 设置本地 loading 状态
|
|
19
|
-
setIsSigningIn(true);
|
|
20
|
-
try {
|
|
21
|
-
await authActions.signIn(provider, `${window.location.origin}${redirectUrl}`);
|
|
22
|
-
// OAuth 会重定向,所以这里的代码可能不会执行
|
|
23
|
-
// loading 状态会在页面刷新后重置
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
// 如果出错,重置 loading 状态
|
|
27
|
-
setIsSigningIn(false);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
return (_jsxs("div", { className: className, children: [beforeElement, title && (_jsxs("div", { className: "flex flex-col gap-2", children: [title && _jsx("p", { className: "text-heading-large", children: title }), description && (_jsx("p", { className: "text-secondary-foreground text-body-large", children: description }))] })), provider === "github" && (_jsx(Slot, { onClick: handleSignIn, children: githubButton?.(isSigningIn) })), error && _jsx("p", { className: "text-danger-foreground mt-2", children: error }), footerText, afterElement] }));
|
|
31
|
-
}
|