@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,346 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useCallback } from "react";
|
|
2
|
-
import { use$ } from "@legendapp/state/react";
|
|
3
|
-
/**
|
|
4
|
-
* 将日期转换为 ISO 字符串
|
|
5
|
-
*/
|
|
6
|
-
function toISOString(date) {
|
|
7
|
-
if (!date)
|
|
8
|
-
return undefined;
|
|
9
|
-
return typeof date === "string" ? date : new Date(date).toISOString();
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* 将 Better Auth session 用户数据映射为 SessionUser
|
|
13
|
-
*/
|
|
14
|
-
function mapSessionUserToSessionUser(sessionUser, sessionData) {
|
|
15
|
-
return {
|
|
16
|
-
banExpires: sessionUser.banExpires,
|
|
17
|
-
banReason: sessionUser.banReason,
|
|
18
|
-
banned: sessionUser.banned,
|
|
19
|
-
createdAt: toISOString(sessionUser.createdAt) ?? "",
|
|
20
|
-
email: sessionUser.email,
|
|
21
|
-
emailVerified: sessionUser.emailVerified,
|
|
22
|
-
id: sessionUser.id,
|
|
23
|
-
image: sessionUser.image || undefined,
|
|
24
|
-
lastLoginAt: toISOString(sessionData?.createdAt),
|
|
25
|
-
name: sessionUser.name,
|
|
26
|
-
role: sessionUser.role,
|
|
27
|
-
updatedAt: toISOString(sessionUser.updatedAt) ?? "",
|
|
28
|
-
activeOrganizationId: sessionData?.activeOrganizationId,
|
|
29
|
-
activeTeamId: sessionData?.activeTeamId,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* 检查是否为开发环境
|
|
34
|
-
*/
|
|
35
|
-
const isDev = import.meta.env?.DEV ?? false;
|
|
36
|
-
/**
|
|
37
|
-
* 安全获取环境变量
|
|
38
|
-
*/
|
|
39
|
-
function getEnvVar(key) {
|
|
40
|
-
return import.meta.env?.[key];
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* 解析 API 错误响应
|
|
44
|
-
*/
|
|
45
|
-
async function parseErrorResponse(response) {
|
|
46
|
-
try {
|
|
47
|
-
const text = await response.text();
|
|
48
|
-
if (text) {
|
|
49
|
-
try {
|
|
50
|
-
const json = JSON.parse(text);
|
|
51
|
-
if (typeof json === "object" && json !== null) {
|
|
52
|
-
const data = json;
|
|
53
|
-
if (typeof data.error === "object" && data.error !== null) {
|
|
54
|
-
const errorObj = data.error;
|
|
55
|
-
if (typeof errorObj.message === "string") {
|
|
56
|
-
return errorObj.message;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
else if (typeof data.message === "string") {
|
|
60
|
-
return data.message;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// 如果不是 JSON,返回原始文本(限制长度)
|
|
66
|
-
return text.length < 200 ? text : `${text.substring(0, 200)}...`;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
// 忽略解析错误
|
|
72
|
-
}
|
|
73
|
-
return `HTTP ${response.status}: ${response.statusText}`;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* 获取或创建伴生团队
|
|
77
|
-
*
|
|
78
|
-
* 功能说明:
|
|
79
|
-
* - 在用户登录成功后自动获取组织信息
|
|
80
|
-
* - 设置活动组织和团队,确保用户拥有正确的权限上下文
|
|
81
|
-
* - 使用 fetch API 直接调用 oneauth 和 better-auth 的 API
|
|
82
|
-
* - 所有错误都会被捕获,不会影响登录流程
|
|
83
|
-
*
|
|
84
|
-
* 边缘情况处理:
|
|
85
|
-
* - 环境变量未配置:静默跳过
|
|
86
|
-
* - Token 缺失:静默跳过
|
|
87
|
-
* - 网络错误:记录错误但不抛出
|
|
88
|
-
* - API 返回错误:记录详细错误信息
|
|
89
|
-
* - 无效响应数据:记录错误但不影响登录
|
|
90
|
-
*/
|
|
91
|
-
async function setupCompanionTeam(auth, token, refetchSession) {
|
|
92
|
-
try {
|
|
93
|
-
// 1. 验证环境变量配置
|
|
94
|
-
const oneAuthBaseUrl = getEnvVar("VITE_AUTH_API_URL") || getEnvVar("VITE_CORE_AI_API_URL");
|
|
95
|
-
if (!oneAuthBaseUrl) {
|
|
96
|
-
if (isDev) {
|
|
97
|
-
console.warn("[AuthSync] VITE_AUTH_API_URL is not configured, skipping companion team setup");
|
|
98
|
-
}
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
// 2. 验证 token
|
|
102
|
-
if (!token || typeof token !== "string" || token.trim().length === 0) {
|
|
103
|
-
if (isDev) {
|
|
104
|
-
console.warn("[AuthSync] Token is missing or invalid, skipping companion team setup");
|
|
105
|
-
}
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
// 3. 获取 token(优先使用 oneauth_token,如果没有则使用传入的 token)
|
|
109
|
-
const oneAuthTokenFromStorage = localStorage.getItem("oneauth_token");
|
|
110
|
-
const oneAuthToken = oneAuthTokenFromStorage ?? token;
|
|
111
|
-
if (!oneAuthToken || oneAuthToken.trim().length === 0) {
|
|
112
|
-
if (isDev) {
|
|
113
|
-
console.warn("[AuthSync] No valid token available, skipping companion team setup");
|
|
114
|
-
}
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
// 5. 获取组织信息
|
|
118
|
-
const myOrganizationUrl = `${oneAuthBaseUrl}/v1/organizations/me`;
|
|
119
|
-
if (isDev) {
|
|
120
|
-
console.log("[AuthSync] Fetching organization from:", myOrganizationUrl);
|
|
121
|
-
}
|
|
122
|
-
const orgResponse = await fetch(myOrganizationUrl, {
|
|
123
|
-
headers: {
|
|
124
|
-
Authorization: `Bearer ${oneAuthToken}`,
|
|
125
|
-
"Content-Type": "application/json",
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
if (!orgResponse.ok) {
|
|
129
|
-
const errorMessage = await parseErrorResponse(orgResponse);
|
|
130
|
-
throw new Error(`Failed to fetch organization: ${errorMessage}`);
|
|
131
|
-
}
|
|
132
|
-
// 6. 解析组织数据
|
|
133
|
-
let orgResponseData;
|
|
134
|
-
try {
|
|
135
|
-
orgResponseData = await orgResponse.json();
|
|
136
|
-
}
|
|
137
|
-
catch (parseError) {
|
|
138
|
-
throw new Error(`Failed to parse organization response: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
139
|
-
}
|
|
140
|
-
// 7. 提取组织对象(支持 {data: {...}} 或直接是组织对象)
|
|
141
|
-
const organization = orgResponseData?.data ||
|
|
142
|
-
orgResponseData;
|
|
143
|
-
// 8. 验证组织数据
|
|
144
|
-
if (!organization || typeof organization !== "object" || !organization.id || typeof organization.id !== "string") {
|
|
145
|
-
throw new Error(`Invalid organization data received: missing or invalid organization ID`);
|
|
146
|
-
}
|
|
147
|
-
// 9. 获取 auth baseURL
|
|
148
|
-
const authBaseURL = getEnvVar("VITE_AUTH_API_URL") ||
|
|
149
|
-
getEnvVar("VITE_CORE_AI_API_URL") ||
|
|
150
|
-
"http://localhost:4320";
|
|
151
|
-
// 10. 设置活动组织
|
|
152
|
-
const setActiveOrgUrl = `${authBaseURL}/v1/auth/organization/set-active`;
|
|
153
|
-
if (isDev) {
|
|
154
|
-
console.log("[AuthSync] Setting active organization:", { organizationId: organization.id });
|
|
155
|
-
}
|
|
156
|
-
const setActiveOrgResponse = await fetch(setActiveOrgUrl, {
|
|
157
|
-
method: "POST",
|
|
158
|
-
headers: {
|
|
159
|
-
Authorization: `Bearer ${oneAuthToken}`,
|
|
160
|
-
"Content-Type": "application/json",
|
|
161
|
-
},
|
|
162
|
-
body: JSON.stringify({ organizationId: organization.id }),
|
|
163
|
-
});
|
|
164
|
-
if (!setActiveOrgResponse.ok) {
|
|
165
|
-
const errorMessage = await parseErrorResponse(setActiveOrgResponse);
|
|
166
|
-
throw new Error(`Failed to set active organization: ${errorMessage}`);
|
|
167
|
-
}
|
|
168
|
-
// 11. 设置活动团队(如果有团队)
|
|
169
|
-
if (organization.teams && Array.isArray(organization.teams) && organization.teams.length > 0) {
|
|
170
|
-
const firstTeam = organization.teams[0];
|
|
171
|
-
if (firstTeam && firstTeam.id && typeof firstTeam.id === "string") {
|
|
172
|
-
const setActiveTeamUrl = `${authBaseURL}/v1/auth/organization/set-active-team`;
|
|
173
|
-
if (isDev) {
|
|
174
|
-
console.log("[AuthSync] Setting active team:", { teamId: firstTeam.id });
|
|
175
|
-
}
|
|
176
|
-
const setActiveTeamResponse = await fetch(setActiveTeamUrl, {
|
|
177
|
-
method: "POST",
|
|
178
|
-
headers: {
|
|
179
|
-
Authorization: `Bearer ${oneAuthToken}`,
|
|
180
|
-
"Content-Type": "application/json",
|
|
181
|
-
},
|
|
182
|
-
body: JSON.stringify({ teamId: firstTeam.id }),
|
|
183
|
-
});
|
|
184
|
-
if (!setActiveTeamResponse.ok) {
|
|
185
|
-
const errorMessage = await parseErrorResponse(setActiveTeamResponse);
|
|
186
|
-
throw new Error(`Failed to set active team: ${errorMessage}`);
|
|
187
|
-
}
|
|
188
|
-
if (isDev) {
|
|
189
|
-
console.log("[AuthSync] Successfully set active team");
|
|
190
|
-
}
|
|
191
|
-
// 刷新 session 并更新 authStore
|
|
192
|
-
if (refetchSession) {
|
|
193
|
-
refetchSession();
|
|
194
|
-
}
|
|
195
|
-
// 使用 fetch 直接调用 get-session 获取最新数据
|
|
196
|
-
try {
|
|
197
|
-
const getSessionUrl = `${authBaseURL}/v1/auth/get-session`;
|
|
198
|
-
const sessionResponse = await fetch(getSessionUrl, {
|
|
199
|
-
headers: {
|
|
200
|
-
Authorization: `Bearer ${oneAuthToken}`,
|
|
201
|
-
"Content-Type": "application/json",
|
|
202
|
-
},
|
|
203
|
-
});
|
|
204
|
-
if (sessionResponse.ok) {
|
|
205
|
-
const sessionData = await sessionResponse.json();
|
|
206
|
-
const data = sessionData;
|
|
207
|
-
if (data.user && data.session) {
|
|
208
|
-
const currentUser = auth.authStore.user.get();
|
|
209
|
-
if (currentUser) {
|
|
210
|
-
auth.authStore.user.set({
|
|
211
|
-
...currentUser,
|
|
212
|
-
activeOrganizationId: data.session.activeOrganizationId,
|
|
213
|
-
activeTeamId: data.session.activeTeamId,
|
|
214
|
-
});
|
|
215
|
-
if (isDev) {
|
|
216
|
-
console.log("[AuthSync] Updated authStore with activeOrganizationId and activeTeamId");
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
catch (err) {
|
|
223
|
-
if (isDev) {
|
|
224
|
-
console.error("[AuthSync] Failed to refresh session:", err);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
else if (isDev) {
|
|
229
|
-
console.warn("[AuthSync] First team has invalid ID, skipping team setup");
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
else if (isDev) {
|
|
233
|
-
console.log("[AuthSync] No teams found in organization");
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
catch (error) {
|
|
237
|
-
// 记录错误但不抛出,避免影响登录流程
|
|
238
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
239
|
-
console.error("[AuthSync] Failed to setup companion team:", errorMessage);
|
|
240
|
-
// 在开发环境下输出更详细的错误信息
|
|
241
|
-
if (isDev && error instanceof Error) {
|
|
242
|
-
console.error("[AuthSync] Error details:", {
|
|
243
|
-
message: error.message,
|
|
244
|
-
stack: error.stack,
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Better Auth 认证状态同步组件
|
|
251
|
-
*
|
|
252
|
-
* 功能说明:
|
|
253
|
-
* - 使用 better-auth 的 useSession hook 同步认证状态到 Legend State
|
|
254
|
-
* - 监听 authStore 状态,在用户登录成功后自动设置组织/团队上下文
|
|
255
|
-
* - 确保登录流程的完整性和权限上下文的正确性
|
|
256
|
-
*
|
|
257
|
-
* 技术实现:
|
|
258
|
-
* - 使用 Legend State 的 use$ hook 响应式监听状态变化
|
|
259
|
-
* - 使用 useRef 确保组织/团队设置只执行一次
|
|
260
|
-
* - 使用 useCallback 缓存映射函数,避免不必要的重渲染
|
|
261
|
-
* - 分离 session 同步和团队设置的逻辑,保持代码清晰
|
|
262
|
-
*
|
|
263
|
-
* 边缘情况处理:
|
|
264
|
-
* - Session 加载中:设置 loading 状态
|
|
265
|
-
* - Session 错误:清空用户信息,重置团队设置状态
|
|
266
|
-
* - 无 Session 但有用户:保持用户信息(可能从 token 加载)
|
|
267
|
-
* - 用户登出:重置团队设置状态,允许下次登录时重新设置
|
|
268
|
-
*/
|
|
269
|
-
export function AuthSync({ auth }) {
|
|
270
|
-
const { authClient, authActions, tokenStorage, authStore } = auth;
|
|
271
|
-
// 监听 authStore 中的用户状态(响应式)
|
|
272
|
-
const user = use$(authStore.user);
|
|
273
|
-
const isAuthenticated = use$(authStore.isAuthenticated);
|
|
274
|
-
const isLoaded = use$(authStore.isLoaded);
|
|
275
|
-
// 监听 better-auth 的 session(用于同步到 store)
|
|
276
|
-
const sessionResult = authClient.useSession();
|
|
277
|
-
const { data: session, isPending, error, refetch } = sessionResult || {
|
|
278
|
-
data: null,
|
|
279
|
-
isPending: false,
|
|
280
|
-
error: null,
|
|
281
|
-
refetch: undefined,
|
|
282
|
-
};
|
|
283
|
-
// 使用 ref 确保组织/团队设置只执行一次
|
|
284
|
-
const teamSetupRef = useRef(false);
|
|
285
|
-
// 使用 useCallback 缓存 session 映射逻辑,避免不必要的重计算
|
|
286
|
-
const handleSessionUpdate = useCallback((sessionData) => {
|
|
287
|
-
if (sessionData?.user) {
|
|
288
|
-
try {
|
|
289
|
-
const mappedUser = mapSessionUserToSessionUser(sessionData.user, sessionData.session);
|
|
290
|
-
authActions.initialize(mappedUser, true);
|
|
291
|
-
}
|
|
292
|
-
catch (mappingError) {
|
|
293
|
-
console.error("[AuthSync] Failed to map session user:", mappingError);
|
|
294
|
-
// 映射失败时不清空用户信息,可能已有有效的用户数据
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}, [authActions]);
|
|
298
|
-
// Effect 1: 同步 better-auth session 到 store
|
|
299
|
-
useEffect(() => {
|
|
300
|
-
// 加载中状态
|
|
301
|
-
if (isPending) {
|
|
302
|
-
authActions.setLoading(true);
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
authActions.setLoading(false);
|
|
306
|
-
// 错误处理
|
|
307
|
-
if (error) {
|
|
308
|
-
const errorMessage = error instanceof Error ? error.message : typeof error === "string" ? error : "Authentication error";
|
|
309
|
-
authActions.setError(errorMessage);
|
|
310
|
-
authActions.initialize(null, true);
|
|
311
|
-
teamSetupRef.current = false;
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
// Session 存在:映射用户数据并更新 store
|
|
315
|
-
if (session) {
|
|
316
|
-
handleSessionUpdate(session);
|
|
317
|
-
}
|
|
318
|
-
// 注意:没有 session 时不主动清空用户信息,因为可能已经从 token 加载了
|
|
319
|
-
}, [session, isPending, error, authActions, handleSessionUpdate]);
|
|
320
|
-
// Effect 2: 监听 authStore 状态,设置伴生团队
|
|
321
|
-
useEffect(() => {
|
|
322
|
-
// 条件检查:只有在用户已认证且已加载完成时才设置伴生团队
|
|
323
|
-
if (isLoaded && isAuthenticated && user?.id && !teamSetupRef.current) {
|
|
324
|
-
teamSetupRef.current = true;
|
|
325
|
-
const token = tokenStorage.get();
|
|
326
|
-
// 验证 token 存在
|
|
327
|
-
if (token && typeof token === "string" && token.trim().length > 0) {
|
|
328
|
-
setupCompanionTeam(auth, token, refetch).catch((error) => {
|
|
329
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
330
|
-
console.error("[AuthSync] Failed to setup companion team:", errorMessage);
|
|
331
|
-
// 失败后重置 ref,允许在特定条件下重试(如 token 更新后)
|
|
332
|
-
teamSetupRef.current = false;
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
else if (isDev) {
|
|
336
|
-
console.warn("[AuthSync] Token not found or invalid, cannot setup companion team");
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
// 用户登出:重置 ref,允许下次登录时重新设置
|
|
340
|
-
if (isLoaded && !isAuthenticated) {
|
|
341
|
-
teamSetupRef.current = false;
|
|
342
|
-
}
|
|
343
|
-
}, [user, isAuthenticated, isLoaded, auth, tokenStorage, refetch]);
|
|
344
|
-
// 这是一个隐形组件,不渲染任何内容
|
|
345
|
-
return null;
|
|
346
|
-
}
|
|
@@ -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,WAAwB,EACxB,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 = "/explore", 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
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"init-auth-sync.d.ts","sourceRoot":"","sources":["../../src/core/init-auth-sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3C;;;GAGG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BpE"}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 同步版本的认证初始化
|
|
3
|
-
* 可以在应用渲染前调用(非 React hook)
|
|
4
|
-
*/
|
|
5
|
-
export async function initAuthSync(auth) {
|
|
6
|
-
const { authActions, tokenStorage } = auth;
|
|
7
|
-
// 检查 URL 中的 token
|
|
8
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
9
|
-
let tokenFromUrl = urlParams.get("token");
|
|
10
|
-
if (tokenFromUrl) {
|
|
11
|
-
if (decodeURIComponent(tokenFromUrl) === tokenFromUrl) {
|
|
12
|
-
tokenFromUrl = encodeURIComponent(tokenFromUrl);
|
|
13
|
-
}
|
|
14
|
-
// 立即清理 URL 中的 token 参数(避免暴露)
|
|
15
|
-
urlParams.delete("token");
|
|
16
|
-
const newUrl = urlParams.toString()
|
|
17
|
-
? `${window.location.pathname}?${urlParams.toString()}`
|
|
18
|
-
: window.location.pathname;
|
|
19
|
-
window.history.replaceState({}, "", newUrl);
|
|
20
|
-
// 使用 token 获取 session
|
|
21
|
-
await authActions.fetchSessionWithToken(tokenFromUrl);
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
// 检查 localStorage 中是否有存储的 token
|
|
25
|
-
const storedToken = tokenStorage.get();
|
|
26
|
-
if (storedToken) {
|
|
27
|
-
await authActions.fetchSessionWithToken(storedToken);
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
// 没有 token,标记为已加载
|
|
31
|
-
await authActions.initialize(null, true);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
package/dist/types.d.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 认证相关的类型定义
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* 会话用户信息
|
|
6
|
-
*
|
|
7
|
-
* 包含用户基本信息和当前活动的组织/团队上下文
|
|
8
|
-
*/
|
|
9
|
-
export type SessionUser = {
|
|
10
|
-
/** 当前活动的组织 ID */
|
|
11
|
-
activeOrganizationId?: string;
|
|
12
|
-
/** 当前活动的团队 ID */
|
|
13
|
-
activeTeamId?: string;
|
|
14
|
-
/** 禁用到期时间 */
|
|
15
|
-
banExpires?: string;
|
|
16
|
-
/** 禁用原因 */
|
|
17
|
-
banReason?: string;
|
|
18
|
-
/** 是否被禁用 */
|
|
19
|
-
banned?: boolean;
|
|
20
|
-
/** 用户创建时间 */
|
|
21
|
-
createdAt: string;
|
|
22
|
-
/** 用户邮箱 */
|
|
23
|
-
email: string;
|
|
24
|
-
/** 邮箱是否已验证 */
|
|
25
|
-
emailVerified: boolean;
|
|
26
|
-
/** 用户 ID */
|
|
27
|
-
id: string;
|
|
28
|
-
/** 用户头像 URL */
|
|
29
|
-
image?: string;
|
|
30
|
-
/** 最后登录时间 */
|
|
31
|
-
lastLoginAt?: string;
|
|
32
|
-
/** 用户名称 */
|
|
33
|
-
name: string;
|
|
34
|
-
/** 用户角色 */
|
|
35
|
-
role?: string;
|
|
36
|
-
/** 用户信息更新时间 */
|
|
37
|
-
updatedAt: string;
|
|
38
|
-
};
|
|
39
|
-
/**
|
|
40
|
-
* Better Auth Session 对象
|
|
41
|
-
*
|
|
42
|
-
* 包含会话信息和关联的用户数据
|
|
43
|
-
*/
|
|
44
|
-
export type Session = {
|
|
45
|
-
/** 当前活动的组织 ID(存储在 session 中) */
|
|
46
|
-
activeOrganizationId?: string;
|
|
47
|
-
/** 当前活动的团队 ID(存储在 session 中) */
|
|
48
|
-
activeTeamId?: string;
|
|
49
|
-
/** 会话创建时间 */
|
|
50
|
-
createdAt: string;
|
|
51
|
-
/** 会话过期时间 */
|
|
52
|
-
expiresAt: string;
|
|
53
|
-
/** 会话 ID */
|
|
54
|
-
id: string;
|
|
55
|
-
/** 客户端 IP 地址 */
|
|
56
|
-
ipAddress?: string;
|
|
57
|
-
/** 会话 Token */
|
|
58
|
-
token: string;
|
|
59
|
-
/** 会话更新时间 */
|
|
60
|
-
updatedAt: string;
|
|
61
|
-
/** 关联的用户信息 */
|
|
62
|
-
user: SessionUser;
|
|
63
|
-
/** 客户端 User-Agent */
|
|
64
|
-
userAgent?: string;
|
|
65
|
-
/** 用户 ID */
|
|
66
|
-
userId: string;
|
|
67
|
-
};
|
|
68
|
-
/**
|
|
69
|
-
* 认证状态
|
|
70
|
-
*
|
|
71
|
-
* 存储在 Legend State 中的认证状态数据
|
|
72
|
-
*/
|
|
73
|
-
export interface AuthState {
|
|
74
|
-
/** 错误信息 */
|
|
75
|
-
error: string | null;
|
|
76
|
-
/** 是否已认证 */
|
|
77
|
-
isAuthenticated: boolean;
|
|
78
|
-
/** 是否已加载完成 */
|
|
79
|
-
isLoaded: boolean;
|
|
80
|
-
/** 是否正在加载 */
|
|
81
|
-
loading: boolean;
|
|
82
|
-
/** 认证 Token */
|
|
83
|
-
token: string | null;
|
|
84
|
-
/** 当前用户信息 */
|
|
85
|
-
user: SessionUser | null;
|
|
86
|
-
}
|
|
87
|
-
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,iBAAiB;IACjB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iBAAiB;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY;IACZ,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW;IACX,KAAK,EAAE,MAAM,CAAC;IACd,cAAc;IACd,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,eAAe;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe;IACf,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG;IACpB,gCAAgC;IAChC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,gCAAgC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,gBAAgB;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe;IACf,KAAK,EAAE,MAAM,CAAC;IACd,aAAa;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc;IACd,IAAI,EAAE,WAAW,CAAC;IAClB,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY;IACZ,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,WAAW;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,YAAY;IACZ,eAAe,EAAE,OAAO,CAAA;IACxB,cAAc;IACd,QAAQ,EAAE,OAAO,CAAA;IACjB,aAAa;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,eAAe;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,aAAa;IACb,IAAI,EAAE,WAAW,GAAG,IAAI,CAAA;CACzB"}
|
package/dist/types.js
DELETED