@choiceform/shared-auth 0.1.13 → 0.1.15
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 +106 -450
- package/dist/api/auth-api.d.ts +28 -0
- package/dist/api/auth-api.d.ts.map +1 -0
- package/dist/api/auth-api.js +133 -0
- package/dist/api/client.d.ts +34 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +104 -0
- package/dist/api/index.d.ts +12 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +7 -0
- package/dist/api/organization-api.d.ts +96 -0
- package/dist/api/organization-api.d.ts.map +1 -0
- package/dist/api/organization-api.js +228 -0
- package/dist/api/team-api.d.ts +57 -0
- package/dist/api/team-api.d.ts.map +1 -0
- package/dist/api/team-api.js +118 -0
- package/dist/config.d.ts +4 -50
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -6
- package/dist/core.d.ts +307 -1717
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +35 -17
- package/dist/index.d.ts +11 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -12
- package/dist/init.d.ts +326 -1732
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +13 -14
- package/dist/lib/auth-client.d.ts +232 -1689
- package/dist/lib/auth-client.d.ts.map +1 -1
- package/dist/lib/auth-client.js +10 -16
- package/dist/services/companion-team.d.ts +16 -0
- package/dist/services/companion-team.d.ts.map +1 -0
- package/dist/services/companion-team.js +73 -0
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +4 -0
- package/dist/store/actions.d.ts +45 -30
- package/dist/store/actions.d.ts.map +1 -1
- package/dist/store/actions.js +139 -103
- package/dist/store/index.d.ts +8 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +7 -0
- package/dist/store/state.d.ts +10 -7
- package/dist/store/state.d.ts.map +1 -1
- package/dist/store/state.js +31 -23
- package/dist/store/utils.d.ts +22 -71
- package/dist/store/utils.d.ts.map +1 -1
- package/dist/store/utils.js +28 -146
- package/dist/types/auth.d.ts +107 -0
- package/dist/types/auth.d.ts.map +1 -0
- package/dist/types/auth.js +4 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +4 -0
- package/dist/types/organization.d.ts +111 -0
- package/dist/types/organization.d.ts.map +1 -0
- package/dist/types/organization.js +4 -0
- package/dist/types/team.d.ts +52 -0
- package/dist/types/team.d.ts.map +1 -0
- package/dist/types/team.js +4 -0
- package/dist/types/user.d.ts +44 -0
- package/dist/types/user.d.ts.map +1 -0
- package/dist/types/user.js +4 -0
- package/dist/utils/date.d.ts +10 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +13 -0
- package/dist/utils/env.d.ts +20 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +23 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/user-mapper.d.ts +21 -0
- package/dist/utils/user-mapper.d.ts.map +1 -0
- package/dist/utils/user-mapper.js +55 -0
- package/package.json +3 -4
- package/dist/components/auth-sync.d.ts +0 -25
- package/dist/components/auth-sync.d.ts.map +0 -1
- package/dist/components/auth-sync.js +0 -346
- 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
- package/dist/core/init-auth-sync.d.ts +0 -7
- package/dist/core/init-auth-sync.d.ts.map +0 -1
- package/dist/core/init-auth-sync.js +0 -34
- package/dist/types.d.ts +0 -87
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-client.d.ts","sourceRoot":"","sources":["../../src/lib/auth-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE3C
|
|
1
|
+
{"version":3,"file":"auth-client.d.ts","sourceRoot":"","sources":["../../src/lib/auth-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE3C;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAsB8xE,CAAC;iBAAe,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAA42C,CAAC;;eAA2D,CAAC;;;;;gBAA8U,CAAC;mBAA8C,CAAC;iBAAmC,CAAC;iBAAmC,CAAC;YAA+B,CAAC;gBAAuC,CAAC;gBAA2C,CAAC;sBAAwC,CAAC;cAAwC,CAAC;cAA8C,CAAC;eAA+B,CAAC;mBAA0G,CAAC;yBAAuB,CAAC;;eAAyC,CAAC;;;aAA2G,CAAC;YAA+B,CAAC;;;;;;;;;;;;YAA+e,CAAC;aAAgB,CAAC;cAAiB,CAAC;cAAiB,CAAC;;aAA8F,CAAC;oBAAkE,CAAC;cAAgC,CAAC;mBAAmG,CAAC;yBAA2E,CAAC;qBAAwC,CAAC;;;uBAAkF,CAAC;+GAAuL,CAAC;;;;;;EAFp/L"}
|
package/dist/lib/auth-client.js
CHANGED
|
@@ -6,6 +6,7 @@ import { createAuthClient } from "better-auth/react";
|
|
|
6
6
|
* - 使用 Bearer Token 认证模式
|
|
7
7
|
* - Token 从 localStorage 中动态读取
|
|
8
8
|
* - 支持服务器端 Bearer Plugin 的 session 管理
|
|
9
|
+
* - basePath 设置为 /v1/auth(与后端 better-auth 配置一致)
|
|
9
10
|
*
|
|
10
11
|
* @param config - 认证配置对象
|
|
11
12
|
* @returns Better Auth 客户端实例
|
|
@@ -13,27 +14,20 @@ import { createAuthClient } from "better-auth/react";
|
|
|
13
14
|
export function createAuthClientFromConfig(config) {
|
|
14
15
|
const { baseURL, plugins = [], tokenStorageKey = "auth-token" } = config;
|
|
15
16
|
return createAuthClient({
|
|
16
|
-
baseURL
|
|
17
|
+
baseURL,
|
|
18
|
+
basePath: "/v1/auth",
|
|
17
19
|
plugins,
|
|
18
20
|
fetchOptions: {
|
|
19
21
|
auth: {
|
|
20
22
|
type: "Bearer",
|
|
21
23
|
token: () => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (typeof window === "undefined" || typeof localStorage === "undefined") {
|
|
25
|
-
return "";
|
|
24
|
+
if (typeof window === "undefined") {
|
|
25
|
+
return undefined;
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// localStorage 访问失败(如隐私模式),返回空字符串
|
|
33
|
-
return "";
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
27
|
+
return localStorage.getItem(tokenStorageKey) ?? undefined;
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
credentials: "include",
|
|
31
|
+
},
|
|
38
32
|
});
|
|
39
33
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Companion Team 服务
|
|
3
|
+
*
|
|
4
|
+
* 在用户登录成功后自动设置组织和团队上下文
|
|
5
|
+
*/
|
|
6
|
+
import type { AuthInstance } from "../core";
|
|
7
|
+
import type { CompanionTeamOptions } from "../types";
|
|
8
|
+
/**
|
|
9
|
+
* 设置 Companion Team
|
|
10
|
+
*
|
|
11
|
+
* @param auth - Auth 实例
|
|
12
|
+
* @param token - 认证 token
|
|
13
|
+
* @param options - 选项
|
|
14
|
+
*/
|
|
15
|
+
export declare function setupCompanionTeam(auth: AuthInstance, token: string, options?: CompanionTeamOptions): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=companion-team.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"companion-team.d.ts","sourceRoot":"","sources":["../../src/services/companion-team.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAIpD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAwDf"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Companion Team 服务
|
|
3
|
+
*
|
|
4
|
+
* 在用户登录成功后自动设置组织和团队上下文
|
|
5
|
+
*/
|
|
6
|
+
import { createAuthApi } from "../api";
|
|
7
|
+
import { getAuthBaseUrl } from "../utils";
|
|
8
|
+
/**
|
|
9
|
+
* 设置 Companion Team
|
|
10
|
+
*
|
|
11
|
+
* @param auth - Auth 实例
|
|
12
|
+
* @param token - 认证 token
|
|
13
|
+
* @param options - 选项
|
|
14
|
+
*/
|
|
15
|
+
export async function setupCompanionTeam(auth, token, options = {}) {
|
|
16
|
+
const { isNewUser, onComplete, onError } = options;
|
|
17
|
+
try {
|
|
18
|
+
if (!token?.trim())
|
|
19
|
+
return;
|
|
20
|
+
const baseURL = getAuthBaseUrl();
|
|
21
|
+
const authApi = createAuthApi(auth.apiClient, baseURL);
|
|
22
|
+
let session = await authApi.getSessionWithToken(token);
|
|
23
|
+
// 新用户或没有 inherent 字段时需要 onboard
|
|
24
|
+
const needsOnboard = isNewUser ||
|
|
25
|
+
(!session?.inherentOrganizationId && !session?.inherentTeamId);
|
|
26
|
+
if (needsOnboard) {
|
|
27
|
+
try {
|
|
28
|
+
await authApi.onboard(token);
|
|
29
|
+
session = await authApi.getSessionWithToken(token);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// onboard 失败不阻塞流程
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (!session)
|
|
36
|
+
return;
|
|
37
|
+
// 已有活动组织和团队,只需更新 store
|
|
38
|
+
if (session.activeOrganizationId && session.activeTeamId) {
|
|
39
|
+
updateAuthStore(auth, session);
|
|
40
|
+
onComplete?.();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// 设置活动组织
|
|
44
|
+
if (!session.activeOrganizationId && session.inherentOrganizationId) {
|
|
45
|
+
await authApi.setActiveOrganization({ organizationId: session.inherentOrganizationId }, token);
|
|
46
|
+
}
|
|
47
|
+
// 设置活动团队
|
|
48
|
+
if (!session.activeTeamId && session.inherentTeamId) {
|
|
49
|
+
await authApi.setActiveTeam({ teamId: session.inherentTeamId }, token);
|
|
50
|
+
}
|
|
51
|
+
// 刷新并更新 store
|
|
52
|
+
const updated = await authApi.getSessionWithToken(token);
|
|
53
|
+
if (updated) {
|
|
54
|
+
updateAuthStore(auth, updated);
|
|
55
|
+
}
|
|
56
|
+
onComplete?.();
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function updateAuthStore(auth, session) {
|
|
63
|
+
const user = auth.authStore.user.get();
|
|
64
|
+
if (user) {
|
|
65
|
+
auth.authStore.user.set({
|
|
66
|
+
...user,
|
|
67
|
+
activeOrganizationId: session.activeOrganizationId,
|
|
68
|
+
activeTeamId: session.activeTeamId,
|
|
69
|
+
inherentOrganizationId: session.inherentOrganizationId,
|
|
70
|
+
inherentTeamId: session.inherentTeamId,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA"}
|
package/dist/store/actions.d.ts
CHANGED
|
@@ -1,38 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 认证 Actions
|
|
3
|
+
*/
|
|
1
4
|
import type { Observable } from "@legendapp/state";
|
|
2
|
-
import type { AuthState, SessionUser } from "../types";
|
|
3
|
-
import type {
|
|
4
|
-
import type { TokenStorage } from "./state";
|
|
5
|
+
import type { AuthClientMethods, AuthConfig, AuthState, SessionUser } from "../types";
|
|
6
|
+
import type { TokenStorage } from "../api";
|
|
5
7
|
/**
|
|
6
|
-
* 创建认证
|
|
8
|
+
* 创建认证 Actions
|
|
7
9
|
*/
|
|
8
|
-
export declare function createAuthActions(authStore: Observable<AuthState>, tokenStorage: TokenStorage, config: AuthConfig, authClient: {
|
|
9
|
-
signIn: {
|
|
10
|
-
social: (options: {
|
|
11
|
-
callbackURL: string;
|
|
12
|
-
provider: string;
|
|
13
|
-
}) => Promise<unknown>;
|
|
14
|
-
};
|
|
15
|
-
signOut: () => Promise<unknown>;
|
|
16
|
-
}): {
|
|
10
|
+
export declare function createAuthActions(authStore: Observable<AuthState>, tokenStorage: TokenStorage, config: AuthConfig, authClient: AuthClientMethods): {
|
|
17
11
|
/**
|
|
18
12
|
* 初始化认证状态
|
|
19
13
|
*/
|
|
20
14
|
initialize(user: SessionUser | null, isLoaded: boolean): Promise<void>;
|
|
21
15
|
/**
|
|
22
16
|
* 使用 Bearer Token 获取 session
|
|
23
|
-
*
|
|
24
|
-
* 流程:
|
|
25
|
-
* 1. 保存 token 到 localStorage
|
|
26
|
-
* 2. 使用 Bearer Token 请求 session endpoint
|
|
27
|
-
* 3. 解析响应数据并提取用户信息
|
|
28
|
-
* 4. 更新 authStore 状态
|
|
29
|
-
*
|
|
30
|
-
* @param token - Bearer Token
|
|
31
17
|
*/
|
|
32
18
|
fetchSessionWithToken(token: string): Promise<void>;
|
|
33
19
|
/**
|
|
34
|
-
*
|
|
35
|
-
* 清理所有认证状态和本地存储
|
|
20
|
+
* 处理未授权
|
|
36
21
|
*/
|
|
37
22
|
handleUnauthorized(): void;
|
|
38
23
|
/**
|
|
@@ -44,17 +29,47 @@ export declare function createAuthActions(authStore: Observable<AuthState>, toke
|
|
|
44
29
|
*/
|
|
45
30
|
setError(error: string | null): void;
|
|
46
31
|
/**
|
|
47
|
-
*
|
|
32
|
+
* OAuth 登录
|
|
48
33
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
34
|
+
* 直接调用 better-auth 的 signIn.social,传入完整的回调 URL
|
|
35
|
+
*
|
|
36
|
+
* @param provider - OAuth 提供商(如 github)
|
|
37
|
+
* @param callbackURL - 登录成功后的完整回调 URL
|
|
38
|
+
* @param newUserCallbackURL - 新用户的回调 URL(可选)
|
|
39
|
+
* @param errorCallbackURL - 登录失败后的完整回调 URL(可选)
|
|
52
40
|
*/
|
|
53
|
-
signIn(provider?: string,
|
|
41
|
+
signIn(provider: string, callbackURL: string, newUserCallbackURL?: string, errorCallbackURL?: string): Promise<void>;
|
|
54
42
|
/**
|
|
55
|
-
*
|
|
43
|
+
* Magic Link 登录
|
|
44
|
+
*
|
|
45
|
+
* 发送 Magic Link 到用户邮箱
|
|
46
|
+
*
|
|
47
|
+
* @param email - 用户邮箱
|
|
48
|
+
* @param callbackURL - 登录成功后的完整回调 URL
|
|
49
|
+
* @param name - 用户名称(可选,用于新用户)
|
|
50
|
+
* @param newUserCallbackURL - 新用户的回调 URL(可选)
|
|
51
|
+
* @returns 是否发送成功
|
|
52
|
+
*/
|
|
53
|
+
signInWithMagicLink(email: string, callbackURL: string, name?: string, newUserCallbackURL?: string): Promise<boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Email/Password 登录
|
|
56
|
+
*
|
|
57
|
+
* @param email - 邮箱
|
|
58
|
+
* @param password - 密码
|
|
59
|
+
* @returns 是否登录成功
|
|
60
|
+
*/
|
|
61
|
+
signInWithEmail(email: string, password: string): Promise<boolean>;
|
|
62
|
+
/**
|
|
63
|
+
* Email/Password 注册
|
|
56
64
|
*
|
|
57
|
-
* @param
|
|
65
|
+
* @param email - 邮箱
|
|
66
|
+
* @param password - 密码
|
|
67
|
+
* @param name - 用户名
|
|
68
|
+
* @returns 是否注册成功
|
|
69
|
+
*/
|
|
70
|
+
signUpWithEmail(email: string, password: string, name: string): Promise<boolean>;
|
|
71
|
+
/**
|
|
72
|
+
* 登出
|
|
58
73
|
*/
|
|
59
74
|
signOut(redirectTo?: string): Promise<void>;
|
|
60
75
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/store/actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/store/actions.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AACrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAG1C;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,EAChC,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,iBAAiB;IAS3B;;OAEG;qBACoB,WAAW,GAAG,IAAI,YAAY,OAAO;IAe5D;;OAEG;iCACgC,MAAM;IAgDzC;;OAEG;;IAcH;;OAEG;wBACiB,OAAO;IAI3B;;OAEG;oBACa,MAAM,GAAG,IAAI;IAI7B;;;;;;;;;OASG;qBACoB,MAAM,eAAe,MAAM,uBAAuB,MAAM,qBAAqB,MAAM;IAsB1G;;;;;;;;;;OAUG;+BAEM,MAAM,eACA,MAAM,SACZ,MAAM,uBACQ,MAAM,GAC1B,OAAO,CAAC,OAAO,CAAC;IA+BnB;;;;;;OAMG;2BAC0B,MAAM,YAAY,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAuCxE;;;;;;;OAOG;2BAEM,MAAM,YACH,MAAM,QACV,MAAM,GACX,OAAO,CAAC,OAAO,CAAC;IAuCnB;;OAEG;yBACwB,MAAM;IA6BjC;;OAEG;qBACc,WAAW,GAAG,IAAI;IAUnC;;OAEG;eACQ,WAAW,GAAG,IAAI;EAIhC;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAA"}
|
package/dist/store/actions.js
CHANGED
|
@@ -1,67 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 认证 Actions
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
if (!date)
|
|
6
|
-
return undefined;
|
|
7
|
-
return typeof date === "string" ? date : new Date(date).toISOString();
|
|
8
|
-
}
|
|
4
|
+
import { extractSessionUser } from "../utils";
|
|
9
5
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 支持多种数据结构格式以提供更好的兼容性:
|
|
13
|
-
* - 标准格式: { user: {...}, session: {...} }
|
|
14
|
-
* - 嵌套格式: { session: { user: {...} } }
|
|
15
|
-
* - 包装格式: { data: { user: {...} } }
|
|
16
|
-
* - 扁平格式: { id, email, ... }
|
|
17
|
-
*
|
|
18
|
-
* @param sessionData - 服务器返回的 session 数据
|
|
19
|
-
* @returns 映射后的 SessionUser 对象,失败返回 null
|
|
20
|
-
*/
|
|
21
|
-
function extractUserData(sessionData) {
|
|
22
|
-
if (!sessionData || typeof sessionData !== "object")
|
|
23
|
-
return null;
|
|
24
|
-
const data = sessionData;
|
|
25
|
-
// 尝试从不同的数据结构中提取用户信息
|
|
26
|
-
const rawUserData = data.user || // 标准格式
|
|
27
|
-
data.session?.user || // 嵌套格式
|
|
28
|
-
data.data?.user || // 包装格式
|
|
29
|
-
(data.id && data.email ? data : null); // 扁平格式
|
|
30
|
-
if (!rawUserData || typeof rawUserData !== "object")
|
|
31
|
-
return null;
|
|
32
|
-
const user = rawUserData;
|
|
33
|
-
// 提取 session 相关数据(activeOrganizationId 和 activeTeamId 存储在 session 中)
|
|
34
|
-
const sessionInfo = data.session;
|
|
35
|
-
const sessionCreatedAt = sessionInfo?.createdAt
|
|
36
|
-
? toISOString(sessionInfo.createdAt)
|
|
37
|
-
: undefined;
|
|
38
|
-
const activeOrganizationId = sessionInfo?.activeOrganizationId;
|
|
39
|
-
const activeTeamId = sessionInfo?.activeTeamId;
|
|
40
|
-
return {
|
|
41
|
-
banExpires: user.banExpires,
|
|
42
|
-
banReason: user.banReason,
|
|
43
|
-
banned: user.banned,
|
|
44
|
-
createdAt: toISOString(user.createdAt) ?? "",
|
|
45
|
-
email: user.email,
|
|
46
|
-
emailVerified: user.emailVerified ?? false,
|
|
47
|
-
id: user.id,
|
|
48
|
-
image: user.image || undefined,
|
|
49
|
-
lastLoginAt: sessionCreatedAt,
|
|
50
|
-
name: user.name,
|
|
51
|
-
role: user.role,
|
|
52
|
-
updatedAt: toISOString(user.updatedAt) ?? "",
|
|
53
|
-
activeOrganizationId,
|
|
54
|
-
activeTeamId,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* 创建认证 actions
|
|
6
|
+
* 创建认证 Actions
|
|
59
7
|
*/
|
|
60
8
|
export function createAuthActions(authStore, tokenStorage, config, authClient) {
|
|
61
|
-
const { baseURL,
|
|
62
|
-
const buildCallbackURL = callbackURLBuilder || ((redirectTo, baseURL) => {
|
|
63
|
-
return `${baseURL}/v1/auth/redirect?to=${encodeURIComponent(redirectTo)}`;
|
|
64
|
-
});
|
|
9
|
+
const { baseURL, getSessionEndpoint = "/v1/auth/get-session", skipTokenCleanupOnError = false, } = config;
|
|
65
10
|
return {
|
|
66
11
|
/**
|
|
67
12
|
* 初始化认证状态
|
|
@@ -84,21 +29,12 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
|
|
|
84
29
|
},
|
|
85
30
|
/**
|
|
86
31
|
* 使用 Bearer Token 获取 session
|
|
87
|
-
*
|
|
88
|
-
* 流程:
|
|
89
|
-
* 1. 保存 token 到 localStorage
|
|
90
|
-
* 2. 使用 Bearer Token 请求 session endpoint
|
|
91
|
-
* 3. 解析响应数据并提取用户信息
|
|
92
|
-
* 4. 更新 authStore 状态
|
|
93
|
-
*
|
|
94
|
-
* @param token - Bearer Token
|
|
95
32
|
*/
|
|
96
33
|
async fetchSessionWithToken(token) {
|
|
97
34
|
try {
|
|
98
35
|
if (!token) {
|
|
99
36
|
throw new Error("Token is required");
|
|
100
37
|
}
|
|
101
|
-
// 保存 token 到 localStorage(供 authClient 使用)
|
|
102
38
|
tokenStorage.save(token);
|
|
103
39
|
const endpoint = `${baseURL}${getSessionEndpoint}`;
|
|
104
40
|
const response = await fetch(endpoint, {
|
|
@@ -112,20 +48,17 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
|
|
|
112
48
|
throw new Error(`Failed to fetch session: ${response.status}`);
|
|
113
49
|
}
|
|
114
50
|
const responseText = await response.text();
|
|
115
|
-
// 解析响应数据
|
|
116
51
|
let sessionData = null;
|
|
117
52
|
try {
|
|
118
53
|
if (responseText && responseText !== "null" && responseText !== "") {
|
|
119
54
|
sessionData = JSON.parse(responseText);
|
|
120
55
|
}
|
|
121
56
|
}
|
|
122
|
-
catch
|
|
123
|
-
console.error("[fetchSessionWithToken] Failed to parse session response:", error);
|
|
57
|
+
catch {
|
|
124
58
|
this.handleUnauthorized();
|
|
125
59
|
return;
|
|
126
60
|
}
|
|
127
|
-
|
|
128
|
-
const userData = extractUserData(sessionData);
|
|
61
|
+
const userData = extractSessionUser(sessionData);
|
|
129
62
|
if (userData) {
|
|
130
63
|
authStore.user.set(userData);
|
|
131
64
|
authStore.isAuthenticated.set(true);
|
|
@@ -133,24 +66,25 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
|
|
|
133
66
|
authStore.loading.set(false);
|
|
134
67
|
}
|
|
135
68
|
else {
|
|
136
|
-
console.error("[fetchSessionWithToken] Invalid session data received");
|
|
137
69
|
this.handleUnauthorized();
|
|
138
70
|
}
|
|
139
71
|
}
|
|
140
|
-
catch
|
|
141
|
-
console.error("[fetchSessionWithToken] Failed to fetch session:", error);
|
|
72
|
+
catch {
|
|
142
73
|
this.handleUnauthorized();
|
|
143
74
|
}
|
|
144
75
|
},
|
|
145
76
|
/**
|
|
146
|
-
*
|
|
147
|
-
* 清理所有认证状态和本地存储
|
|
77
|
+
* 处理未授权
|
|
148
78
|
*/
|
|
149
79
|
handleUnauthorized() {
|
|
150
80
|
authStore.user.set(null);
|
|
151
81
|
authStore.isAuthenticated.set(false);
|
|
152
82
|
authStore.isLoaded.set(true);
|
|
153
83
|
authStore.loading.set(false);
|
|
84
|
+
if (skipTokenCleanupOnError) {
|
|
85
|
+
console.warn("[Auth] Token cleanup skipped (skipTokenCleanupOnError is enabled)");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
154
88
|
tokenStorage.clear();
|
|
155
89
|
},
|
|
156
90
|
/**
|
|
@@ -166,37 +100,28 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
|
|
|
166
100
|
authStore.error.set(error);
|
|
167
101
|
},
|
|
168
102
|
/**
|
|
169
|
-
*
|
|
103
|
+
* OAuth 登录
|
|
104
|
+
*
|
|
105
|
+
* 直接调用 better-auth 的 signIn.social,传入完整的回调 URL
|
|
170
106
|
*
|
|
171
|
-
* @param provider OAuth
|
|
172
|
-
* @param
|
|
173
|
-
*
|
|
107
|
+
* @param provider - OAuth 提供商(如 github)
|
|
108
|
+
* @param callbackURL - 登录成功后的完整回调 URL
|
|
109
|
+
* @param newUserCallbackURL - 新用户的回调 URL(可选)
|
|
110
|
+
* @param errorCallbackURL - 登录失败后的完整回调 URL(可选)
|
|
174
111
|
*/
|
|
175
|
-
async signIn(provider
|
|
176
|
-
// 防止重复点击
|
|
112
|
+
async signIn(provider, callbackURL, newUserCallbackURL, errorCallbackURL) {
|
|
177
113
|
if (authStore.loading.get()) {
|
|
178
114
|
return;
|
|
179
115
|
}
|
|
180
116
|
try {
|
|
181
117
|
authStore.loading.set(true);
|
|
182
118
|
authStore.error.set(null);
|
|
183
|
-
if (!baseURL) {
|
|
184
|
-
throw new Error("Auth baseURL is not configured");
|
|
185
|
-
}
|
|
186
|
-
// 构建重定向地址:相对路径自动添加 origin
|
|
187
|
-
const finalRedirectTo = redirectTo
|
|
188
|
-
? redirectTo.startsWith("http")
|
|
189
|
-
? redirectTo
|
|
190
|
-
: `${window.location.origin}${redirectTo}`
|
|
191
|
-
: defaultRedirectAfterLogin
|
|
192
|
-
? `${window.location.origin}${defaultRedirectAfterLogin}`
|
|
193
|
-
: `${window.location.origin}/explore`;
|
|
194
|
-
const callbackURL = buildCallbackURL(finalRedirectTo, baseURL);
|
|
195
119
|
await authClient.signIn.social({
|
|
196
120
|
provider,
|
|
197
121
|
callbackURL,
|
|
122
|
+
newUserCallbackURL,
|
|
123
|
+
errorCallbackURL,
|
|
198
124
|
});
|
|
199
|
-
// 注意:OAuth 重定向后,loading 状态会在页面刷新时重置
|
|
200
125
|
}
|
|
201
126
|
catch (error) {
|
|
202
127
|
const message = error instanceof Error ? error.message : "Sign in failed";
|
|
@@ -205,12 +130,126 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
|
|
|
205
130
|
}
|
|
206
131
|
},
|
|
207
132
|
/**
|
|
208
|
-
*
|
|
133
|
+
* Magic Link 登录
|
|
134
|
+
*
|
|
135
|
+
* 发送 Magic Link 到用户邮箱
|
|
136
|
+
*
|
|
137
|
+
* @param email - 用户邮箱
|
|
138
|
+
* @param callbackURL - 登录成功后的完整回调 URL
|
|
139
|
+
* @param name - 用户名称(可选,用于新用户)
|
|
140
|
+
* @param newUserCallbackURL - 新用户的回调 URL(可选)
|
|
141
|
+
* @returns 是否发送成功
|
|
142
|
+
*/
|
|
143
|
+
async signInWithMagicLink(email, callbackURL, name, newUserCallbackURL) {
|
|
144
|
+
if (authStore.loading.get()) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
if (!authClient.signIn.magicLink) {
|
|
148
|
+
authStore.error.set("Magic link is not available. Please add magicLinkClient() plugin.");
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
authStore.loading.set(true);
|
|
153
|
+
authStore.error.set(null);
|
|
154
|
+
await authClient.signIn.magicLink({
|
|
155
|
+
email,
|
|
156
|
+
name,
|
|
157
|
+
callbackURL,
|
|
158
|
+
newUserCallbackURL,
|
|
159
|
+
});
|
|
160
|
+
authStore.loading.set(false);
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
const message = error instanceof Error ? error.message : "Failed to send magic link";
|
|
165
|
+
authStore.error.set(message);
|
|
166
|
+
authStore.loading.set(false);
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
/**
|
|
171
|
+
* Email/Password 登录
|
|
209
172
|
*
|
|
210
|
-
* @param
|
|
173
|
+
* @param email - 邮箱
|
|
174
|
+
* @param password - 密码
|
|
175
|
+
* @returns 是否登录成功
|
|
176
|
+
*/
|
|
177
|
+
async signInWithEmail(email, password) {
|
|
178
|
+
if (authStore.loading.get()) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
if (!authClient.signIn.email) {
|
|
182
|
+
authStore.error.set("Email sign in is not available.");
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
authStore.loading.set(true);
|
|
187
|
+
authStore.error.set(null);
|
|
188
|
+
await authClient.signIn.email({ email, password }, {
|
|
189
|
+
onSuccess: (context) => {
|
|
190
|
+
const token = context.response.headers.get("set-auth-token");
|
|
191
|
+
if (token) {
|
|
192
|
+
tokenStorage.save(token);
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
onError: (context) => {
|
|
196
|
+
authStore.error.set(context.error.message || "Login failed");
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
authStore.loading.set(false);
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
const message = error instanceof Error ? error.message : "Login failed";
|
|
204
|
+
authStore.error.set(message);
|
|
205
|
+
authStore.loading.set(false);
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
/**
|
|
210
|
+
* Email/Password 注册
|
|
211
|
+
*
|
|
212
|
+
* @param email - 邮箱
|
|
213
|
+
* @param password - 密码
|
|
214
|
+
* @param name - 用户名
|
|
215
|
+
* @returns 是否注册成功
|
|
216
|
+
*/
|
|
217
|
+
async signUpWithEmail(email, password, name) {
|
|
218
|
+
if (authStore.loading.get()) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
if (!authClient.signUp?.email) {
|
|
222
|
+
authStore.error.set("Email sign up is not available.");
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
authStore.loading.set(true);
|
|
227
|
+
authStore.error.set(null);
|
|
228
|
+
await authClient.signUp.email({ email, password, name }, {
|
|
229
|
+
onSuccess: (context) => {
|
|
230
|
+
const token = context.response.headers.get("set-auth-token");
|
|
231
|
+
if (token) {
|
|
232
|
+
tokenStorage.save(token);
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
onError: (context) => {
|
|
236
|
+
authStore.error.set(context.error.message || "Sign up failed");
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
authStore.loading.set(false);
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
const message = error instanceof Error ? error.message : "Sign up failed";
|
|
244
|
+
authStore.error.set(message);
|
|
245
|
+
authStore.loading.set(false);
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
/**
|
|
250
|
+
* 登出
|
|
211
251
|
*/
|
|
212
252
|
async signOut(redirectTo) {
|
|
213
|
-
// 防止重复调用
|
|
214
253
|
if (authStore.loading.get()) {
|
|
215
254
|
return;
|
|
216
255
|
}
|
|
@@ -218,11 +257,9 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
|
|
|
218
257
|
authStore.loading.set(true);
|
|
219
258
|
authStore.error.set(null);
|
|
220
259
|
await authClient.signOut();
|
|
221
|
-
// 清理本地状态
|
|
222
260
|
authStore.user.set(null);
|
|
223
261
|
authStore.isAuthenticated.set(false);
|
|
224
262
|
tokenStorage.clear();
|
|
225
|
-
// 如果提供了重定向地址,则执行跳转
|
|
226
263
|
if (redirectTo) {
|
|
227
264
|
window.location.href = redirectTo;
|
|
228
265
|
}
|
|
@@ -230,7 +267,6 @@ export function createAuthActions(authStore, tokenStorage, config, authClient) {
|
|
|
230
267
|
catch (error) {
|
|
231
268
|
const message = error instanceof Error ? error.message : "Sign out failed";
|
|
232
269
|
authStore.error.set(message);
|
|
233
|
-
// 即使出错,也尝试清理本地状态
|
|
234
270
|
authStore.user.set(null);
|
|
235
271
|
authStore.isAuthenticated.set(false);
|
|
236
272
|
tokenStorage.clear();
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store 导出
|
|
3
|
+
*/
|
|
4
|
+
export { createAuthStore, createTokenStorage } from "./state";
|
|
5
|
+
export { createAuthActions, type AuthActions } from "./actions";
|
|
6
|
+
export { createAuthComputed } from "./computed";
|
|
7
|
+
export { getCurrentUser, getCurrentUserId, isAuthenticated, isLoading, isLoaded, waitForAuth, getAuthToken, getAuthTokenSync, getAuthHeaders, getAuthHeadersSync, handle401Response, createUserManager, createBoundAuthUtils, } from "./utils";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAC7D,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Store 导出
|
|
3
|
+
*/
|
|
4
|
+
export { createAuthStore, createTokenStorage } from "./state";
|
|
5
|
+
export { createAuthActions } from "./actions";
|
|
6
|
+
export { createAuthComputed } from "./computed";
|
|
7
|
+
export { getCurrentUser, getCurrentUserId, isAuthenticated, isLoading, isLoaded, waitForAuth, getAuthToken, getAuthTokenSync, getAuthHeaders, getAuthHeadersSync, handle401Response, createUserManager, createBoundAuthUtils, } from "./utils";
|