@choiceform/shared-auth 0.1.17 → 0.1.18

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.
Files changed (105) hide show
  1. package/README.md +286 -134
  2. package/dist/__tests__/auth-utils.test.d.ts +5 -0
  3. package/dist/__tests__/auth-utils.test.d.ts.map +1 -0
  4. package/dist/__tests__/auth-utils.test.js +96 -0
  5. package/dist/__tests__/store.test.d.ts +5 -0
  6. package/dist/__tests__/store.test.d.ts.map +1 -0
  7. package/dist/__tests__/store.test.js +210 -0
  8. package/dist/__tests__/user-mapper.test.d.ts +5 -0
  9. package/dist/__tests__/user-mapper.test.d.ts.map +1 -0
  10. package/dist/__tests__/user-mapper.test.js +76 -0
  11. package/dist/api/auth-api.d.ts +93 -9
  12. package/dist/api/auth-api.d.ts.map +1 -1
  13. package/dist/api/auth-api.js +219 -80
  14. package/dist/api/client.d.ts +10 -0
  15. package/dist/api/client.d.ts.map +1 -1
  16. package/dist/api/client.js +10 -0
  17. package/dist/api/organization-api.d.ts +2 -7
  18. package/dist/api/organization-api.d.ts.map +1 -1
  19. package/dist/api/organization-api.js +2 -17
  20. package/dist/api/team-api.d.ts +1 -5
  21. package/dist/api/team-api.d.ts.map +1 -1
  22. package/dist/api/team-api.js +5 -11
  23. package/dist/components/auth-sync.d.ts +27 -0
  24. package/dist/components/auth-sync.d.ts.map +1 -0
  25. package/dist/components/auth-sync.js +117 -0
  26. package/dist/components/protected-route.d.ts +18 -0
  27. package/dist/components/protected-route.d.ts.map +1 -0
  28. package/dist/components/protected-route.js +34 -0
  29. package/dist/components/sign-in-page.d.ts +21 -0
  30. package/dist/components/sign-in-page.d.ts.map +1 -0
  31. package/dist/components/sign-in-page.js +31 -0
  32. package/dist/config.js +1 -1
  33. package/dist/core.d.ts +148 -71
  34. package/dist/core.d.ts.map +1 -1
  35. package/dist/core.js +109 -28
  36. package/dist/hooks/index.d.ts +8 -0
  37. package/dist/hooks/index.d.ts.map +1 -0
  38. package/dist/hooks/index.js +7 -0
  39. package/dist/hooks/use-auth-init.d.ts +4 -0
  40. package/dist/hooks/use-auth-init.d.ts.map +1 -1
  41. package/dist/hooks/use-auth-init.js +16 -21
  42. package/dist/hooks/use-auth-sync.d.ts +60 -0
  43. package/dist/hooks/use-auth-sync.d.ts.map +1 -0
  44. package/dist/hooks/use-auth-sync.js +116 -0
  45. package/dist/hooks/use-email-verification.d.ts +85 -0
  46. package/dist/hooks/use-email-verification.d.ts.map +1 -0
  47. package/dist/hooks/use-email-verification.js +145 -0
  48. package/dist/hooks/use-protected-route.d.ts +67 -0
  49. package/dist/hooks/use-protected-route.d.ts.map +1 -0
  50. package/dist/hooks/use-protected-route.js +102 -0
  51. package/dist/index.d.ts +12 -6
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +43 -13
  54. package/dist/init.d.ts +127 -70
  55. package/dist/init.d.ts.map +1 -1
  56. package/dist/lib/auth-client.d.ts.map +1 -1
  57. package/dist/lib/auth-client.js +75 -2
  58. package/dist/services/auth-service.d.ts +101 -0
  59. package/dist/services/auth-service.d.ts.map +1 -0
  60. package/dist/services/auth-service.js +356 -0
  61. package/dist/services/callback-service.d.ts +33 -0
  62. package/dist/services/callback-service.d.ts.map +1 -0
  63. package/dist/services/callback-service.js +473 -0
  64. package/dist/services/companion-team.d.ts.map +1 -1
  65. package/dist/services/companion-team.js +41 -39
  66. package/dist/services/index.d.ts +2 -0
  67. package/dist/services/index.d.ts.map +1 -1
  68. package/dist/services/index.js +2 -0
  69. package/dist/store/actions.d.ts +54 -51
  70. package/dist/store/actions.d.ts.map +1 -1
  71. package/dist/store/actions.js +111 -243
  72. package/dist/store/computed.d.ts +72 -1
  73. package/dist/store/computed.d.ts.map +1 -1
  74. package/dist/store/computed.js +90 -3
  75. package/dist/store/index.d.ts +3 -3
  76. package/dist/store/index.d.ts.map +1 -1
  77. package/dist/store/index.js +2 -2
  78. package/dist/store/state.d.ts +10 -0
  79. package/dist/store/state.d.ts.map +1 -1
  80. package/dist/store/state.js +11 -1
  81. package/dist/store/utils.d.ts +3 -34
  82. package/dist/store/utils.d.ts.map +1 -1
  83. package/dist/store/utils.js +2 -22
  84. package/dist/types/auth.d.ts +106 -0
  85. package/dist/types/auth.d.ts.map +1 -1
  86. package/dist/types/callback.d.ts +35 -0
  87. package/dist/types/callback.d.ts.map +1 -0
  88. package/dist/types/callback.js +1 -0
  89. package/dist/types/index.d.ts +4 -3
  90. package/dist/types/index.d.ts.map +1 -1
  91. package/dist/types/organization.d.ts +19 -3
  92. package/dist/types/organization.d.ts.map +1 -1
  93. package/dist/types/team.d.ts +6 -2
  94. package/dist/types/team.d.ts.map +1 -1
  95. package/dist/types/user.d.ts +7 -3
  96. package/dist/types/user.d.ts.map +1 -1
  97. package/dist/utils/auth-utils.d.ts +60 -0
  98. package/dist/utils/auth-utils.d.ts.map +1 -0
  99. package/dist/utils/auth-utils.js +146 -0
  100. package/dist/utils/index.d.ts +1 -0
  101. package/dist/utils/index.d.ts.map +1 -1
  102. package/dist/utils/index.js +1 -0
  103. package/dist/utils/user-mapper.d.ts.map +1 -1
  104. package/dist/utils/user-mapper.js +2 -1
  105. package/package.json +10 -2
@@ -0,0 +1,473 @@
1
+ /**
2
+ * 认证回调服务
3
+ *
4
+ * 处理各种认证回调流程:
5
+ * - OAuth 回调(社交登录)
6
+ * - 邮箱验证回调
7
+ * - 删除用户回调
8
+ * - 密码重置回调
9
+ * - 团队邀请回调
10
+ * - 更改邮箱回调
11
+ */
12
+ const defaultConfig = {
13
+ lang: "us",
14
+ defaultRedirect: "/",
15
+ signInPath: "/sign-in",
16
+ linkExpiredPath: "/auth/link-expired",
17
+ deleteSuccessPath: "/auth/delete-success",
18
+ resetPasswordPath: "/reset-password",
19
+ };
20
+ /**
21
+ * 创建回调服务
22
+ */
23
+ export function createCallbackService(auth, config = {}) {
24
+ const cfg = { ...defaultConfig, ...config };
25
+ /**
26
+ * 处理 OAuth 回调
27
+ *
28
+ * 当 OAuth 登录成功后,会带着 token 跳转回来
29
+ * 需要保存 token 并获取 session
30
+ */
31
+ async function handleOAuthCallback(token, isNewUser = false) {
32
+ if (!token) {
33
+ return {
34
+ success: false,
35
+ error: "Token is required",
36
+ redirectTo: `${cfg.signInPath}?lang=${cfg.lang}`,
37
+ };
38
+ }
39
+ try {
40
+ // 保存 token 并获取 session
41
+ const session = await auth.authService.fetchAndSetSession(token);
42
+ if (!session) {
43
+ return {
44
+ success: false,
45
+ error: "Failed to get session",
46
+ redirectTo: `${cfg.signInPath}?lang=${cfg.lang}`,
47
+ };
48
+ }
49
+ // 新用户且没有默认组织,执行 onboard
50
+ if (isNewUser && !session.inherentOrganizationId) {
51
+ try {
52
+ await auth.authApi.onboard(token);
53
+ // 重新获取 session 以获取新的组织信息
54
+ await auth.authService.fetchAndSetSession(token);
55
+ }
56
+ catch {
57
+ // onboard 失败不影响登录结果
58
+ }
59
+ }
60
+ return {
61
+ success: true,
62
+ redirectTo: cfg.defaultRedirect,
63
+ data: { session, isNewUser },
64
+ };
65
+ }
66
+ catch (error) {
67
+ return {
68
+ success: false,
69
+ error: error instanceof Error ? error.message : "OAuth callback failed",
70
+ redirectTo: `${cfg.signInPath}?lang=${cfg.lang}`,
71
+ };
72
+ }
73
+ }
74
+ /**
75
+ * 处理邮箱验证回调
76
+ *
77
+ * 用户点击验证邮件中的链接后调用
78
+ */
79
+ async function handleEmailVerificationCallback(token) {
80
+ if (!token) {
81
+ return {
82
+ success: false,
83
+ error: "Token is required",
84
+ redirectTo: `${cfg.linkExpiredPath}?type=signup&lang=${cfg.lang}`,
85
+ };
86
+ }
87
+ return new Promise((resolve) => {
88
+ auth.authClient.verifyEmail({ query: { token } }, {
89
+ onSuccess: (ctx) => {
90
+ if (ctx.data?.status !== true) {
91
+ resolve({
92
+ success: false,
93
+ error: "Verification failed",
94
+ redirectTo: `${cfg.linkExpiredPath}?type=signup&lang=${cfg.lang}`,
95
+ });
96
+ return;
97
+ }
98
+ // 更新本地状态
99
+ const user = auth.authStore.user.get();
100
+ if (user) {
101
+ auth.storeActions.updateUser({ emailVerified: true });
102
+ }
103
+ resolve({
104
+ success: true,
105
+ redirectTo: cfg.defaultRedirect,
106
+ });
107
+ },
108
+ onError: (ctx) => {
109
+ const errorCode = ctx.error.code;
110
+ const isExpired = errorCode === "INVALID_TOKEN" || ctx.error.message?.includes("expired");
111
+ if (isExpired) {
112
+ // 链接过期,清理本地状态
113
+ auth.authClient.signOut().catch(() => { });
114
+ auth.storeActions.clearAuth();
115
+ }
116
+ resolve({
117
+ success: false,
118
+ error: ctx.error.message,
119
+ errorCode,
120
+ redirectTo: `${cfg.linkExpiredPath}?type=signup&lang=${cfg.lang}`,
121
+ });
122
+ },
123
+ });
124
+ });
125
+ }
126
+ /**
127
+ * 处理删除用户回调
128
+ *
129
+ * 用户点击删除确认邮件中的链接后调用
130
+ * 注意:需要用户已登录才能执行删除
131
+ */
132
+ async function handleDeleteUserCallback(token, userEmail) {
133
+ if (!token) {
134
+ return {
135
+ success: false,
136
+ error: "Token is required",
137
+ redirectTo: `${cfg.linkExpiredPath}?type=delete-user&lang=${cfg.lang}`,
138
+ };
139
+ }
140
+ // 检查是否已登录
141
+ const isAuthenticated = auth.authStore.isAuthenticated.get();
142
+ if (!isAuthenticated) {
143
+ return {
144
+ success: false,
145
+ needsLogin: true,
146
+ error: "Please login to confirm account deletion",
147
+ data: { token },
148
+ };
149
+ }
150
+ return new Promise((resolve) => {
151
+ auth.authClient.deleteUser({ token }, {
152
+ onSuccess: () => {
153
+ const email = userEmail || auth.authStore.user.get()?.email;
154
+ const emailParam = email ? `&email=${encodeURIComponent(email)}` : "";
155
+ resolve({
156
+ success: true,
157
+ redirectTo: `${cfg.deleteSuccessPath}?lang=${cfg.lang}${emailParam}`,
158
+ });
159
+ },
160
+ onError: (ctx) => {
161
+ const errorCode = ctx.error.code;
162
+ const isExpired = errorCode === "INVALID_TOKEN" || ctx.error.message?.includes("expired");
163
+ resolve({
164
+ success: false,
165
+ error: ctx.error.message,
166
+ errorCode,
167
+ redirectTo: isExpired
168
+ ? `${cfg.linkExpiredPath}?type=delete-user&lang=${cfg.lang}`
169
+ : `${cfg.signInPath}?lang=${cfg.lang}`,
170
+ });
171
+ },
172
+ });
173
+ });
174
+ }
175
+ /**
176
+ * 处理密码重置回调
177
+ *
178
+ * 用户点击密码重置邮件中的链接后调用
179
+ * 不执行实际操作,只验证 token 有效性并返回重定向 URL
180
+ */
181
+ function handlePasswordResetCallback(token) {
182
+ if (token) {
183
+ return {
184
+ success: true,
185
+ redirectTo: `${cfg.resetPasswordPath}?token=${token}&lang=${cfg.lang}`,
186
+ };
187
+ }
188
+ return {
189
+ success: true,
190
+ redirectTo: `${cfg.resetPasswordPath}?lang=${cfg.lang}`,
191
+ };
192
+ }
193
+ /**
194
+ * 处理团队邀请回调
195
+ *
196
+ * 流程:
197
+ * - 已登录:直接接受邀请,成功后跳转到 workspace
198
+ * - 未登录:存储 invitationId,跳转到登录页
199
+ */
200
+ async function handleInviteCallback(invitationId) {
201
+ if (!invitationId) {
202
+ return {
203
+ success: false,
204
+ error: "Invitation ID is required",
205
+ redirectTo: cfg.defaultRedirect,
206
+ };
207
+ }
208
+ // 等待认证状态加载完成
209
+ await auth.waitForAuth();
210
+ const token = auth.tokenStorage.get();
211
+ // 未登录:跳转到登录页,invitationId 通过 URL 传递
212
+ if (!token) {
213
+ return {
214
+ success: true,
215
+ redirectTo: `${cfg.signInPath}?lang=${cfg.lang}&invitationId=${invitationId}`,
216
+ };
217
+ }
218
+ // 已登录:直接接受邀请
219
+ try {
220
+ const result = await auth.organizationApi.acceptInvitation({ invitationId });
221
+ // 如果邀请包含 teamId,跳转到对应团队
222
+ if (result?.invitation?.teamId) {
223
+ // 设置 active organization 和 team
224
+ if (result.invitation.organizationId) {
225
+ await auth.setActiveOrganizationAndTeam(result.invitation.organizationId, result.invitation.teamId);
226
+ }
227
+ else {
228
+ await auth.setActiveTeam({ teamId: result.invitation.teamId });
229
+ }
230
+ // 获取团队名称用于通知
231
+ let teamName = "";
232
+ try {
233
+ const teams = await auth.teamApi.listUserTeams();
234
+ const joinedTeam = teams.find((t) => t.id === result.invitation?.teamId);
235
+ teamName = joinedTeam?.name || "";
236
+ }
237
+ catch {
238
+ // 获取失败不影响主流程
239
+ }
240
+ const teamNameParam = teamName ? `&teamName=${encodeURIComponent(teamName)}` : "";
241
+ return {
242
+ success: true,
243
+ redirectTo: `/workspace/team/${result.invitation.teamId}?notification=inviteAccepted${teamNameParam}&lang=${cfg.lang}`,
244
+ };
245
+ }
246
+ return {
247
+ success: true,
248
+ redirectTo: `${cfg.defaultRedirect}?notification=inviteAccepted&lang=${cfg.lang}`,
249
+ };
250
+ }
251
+ catch {
252
+ return {
253
+ success: false,
254
+ error: "Failed to accept invitation",
255
+ redirectTo: `${cfg.defaultRedirect}?notification=inviteFailed&lang=${cfg.lang}`,
256
+ };
257
+ }
258
+ }
259
+ /**
260
+ * 处理确认更改邮箱回调(第一步:验证当前邮箱)
261
+ *
262
+ * 用户点击当前邮箱中的验证链接后调用
263
+ * 验证成功后,better-auth 会自动发送验证邮件到新邮箱
264
+ *
265
+ * 注意:此步骤不需要登录,token 本身就是验证身份的方式
266
+ * token 中已包含 updateTo 字段(新邮箱),newEmail 参数是可选的
267
+ *
268
+ * 关键:callbackURL 参数会告诉 better-auth 在验证成功后
269
+ * 向新邮箱发送的验证链接应该指向哪里
270
+ */
271
+ async function handleConfirmChangeEmailCallback(token, newEmail) {
272
+ if (!token) {
273
+ return {
274
+ success: false,
275
+ error: "Token is required",
276
+ redirectTo: `${cfg.linkExpiredPath}?type=confirm-change-email&lang=${cfg.lang}`,
277
+ };
278
+ }
279
+ // newEmail 是可选的,token 中已包含新邮箱信息
280
+ // 尝试从 token 解析新邮箱(用于显示)
281
+ let displayEmail = newEmail || "";
282
+ if (!displayEmail) {
283
+ try {
284
+ const payload = JSON.parse(atob(token.split(".")[1]));
285
+ displayEmail = payload.updateTo || "";
286
+ }
287
+ catch {
288
+ // 解析失败,继续执行
289
+ }
290
+ }
291
+ // 不需要检查登录状态,token 本身就是验证方式
292
+ // 构建新邮箱验证的 callbackURL
293
+ // 当 verifyEmail 成功后,better-auth 会发送验证邮件到新邮箱
294
+ // 新邮箱中的链接会指向这个 callbackURL
295
+ const origin = typeof window !== "undefined" ? window.location.origin : "";
296
+ const callbackURL = `${origin}/auth/callback?type=verify-change-email&lang=${cfg.lang}`;
297
+ return new Promise((resolve) => {
298
+ auth.authClient
299
+ .verifyEmail({ query: { token, callbackURL } }, {
300
+ onSuccess: (ctx) => {
301
+ if (ctx.data?.status !== true) {
302
+ resolve({
303
+ success: false,
304
+ error: "Verification failed",
305
+ redirectTo: `${cfg.linkExpiredPath}?type=confirm-change-email&lang=${cfg.lang}`,
306
+ });
307
+ return;
308
+ }
309
+ // 验证成功,better-auth 已发送验证邮件到新邮箱
310
+ // 跳转到 verify-email 页面
311
+ resolve({
312
+ success: true,
313
+ redirectTo: `/verify-email?email=${encodeURIComponent(displayEmail)}&type=change-email&lang=${cfg.lang}`,
314
+ });
315
+ },
316
+ onError: (ctx) => {
317
+ const errorCode = ctx.error.code;
318
+ const isExpired = errorCode === "INVALID_TOKEN" || ctx.error.message?.includes("expired");
319
+ resolve({
320
+ success: false,
321
+ error: ctx.error.message,
322
+ errorCode,
323
+ redirectTo: isExpired
324
+ ? `${cfg.linkExpiredPath}?type=confirm-change-email&lang=${cfg.lang}`
325
+ : `${cfg.signInPath}?lang=${cfg.lang}`,
326
+ });
327
+ },
328
+ })
329
+ .catch(() => {
330
+ // CORS 错误等网络问题时,后端可能已经成功处理
331
+ // 假设成功,跳转到 verify-email 页面
332
+ resolve({
333
+ success: true,
334
+ redirectTo: `/verify-email?email=${encodeURIComponent(displayEmail)}&type=change-email&lang=${cfg.lang}`,
335
+ });
336
+ });
337
+ });
338
+ }
339
+ /**
340
+ * 处理验证新邮箱回调(第二步:验证新邮箱)
341
+ *
342
+ * 用户点击新邮箱中的验证链接后调用
343
+ * 验证成功后,邮箱更改完成
344
+ *
345
+ * 注意:此步骤不需要登录,token 本身就是验证身份的方式
346
+ *
347
+ * 成功后:
348
+ * - 有 authToken(已登录)→ 跳转到首页,显示通知
349
+ * - 没有 authToken(未登录)→ 跳转到登录页,显示通知,预填充新邮箱
350
+ */
351
+ async function handleVerifyChangeEmailCallback(token) {
352
+ if (!token) {
353
+ return {
354
+ success: false,
355
+ error: "Token is required",
356
+ redirectTo: `${cfg.linkExpiredPath}?type=verify-change-email&lang=${cfg.lang}`,
357
+ };
358
+ }
359
+ // 尝试从 token 中解析新邮箱地址
360
+ let newEmail = "";
361
+ try {
362
+ const payload = JSON.parse(atob(token.split(".")[1]));
363
+ newEmail = payload.updateTo || payload.email || "";
364
+ }
365
+ catch {
366
+ // 解析失败,继续执行
367
+ }
368
+ // 构建成功后的跳转 URL
369
+ const buildSuccessRedirect = () => {
370
+ const authToken = auth.tokenStorage.get();
371
+ const emailParam = newEmail ? `&email=${encodeURIComponent(newEmail)}` : "";
372
+ if (authToken) {
373
+ // 已登录,跳转到首页
374
+ return `${cfg.defaultRedirect}?notification=emailChanged${emailParam}&lang=${cfg.lang}`;
375
+ }
376
+ else {
377
+ // 未登录,跳转到登录页,预填充新邮箱
378
+ return `${cfg.signInPath}?notification=emailChanged${emailParam}&lang=${cfg.lang}`;
379
+ }
380
+ };
381
+ return new Promise((resolve) => {
382
+ auth.authClient
383
+ .verifyEmail({ query: { token } }, {
384
+ onSuccess: async (ctx) => {
385
+ if (ctx.data?.status !== true) {
386
+ resolve({
387
+ success: false,
388
+ error: "Verification failed",
389
+ redirectTo: `${cfg.linkExpiredPath}?type=verify-change-email&lang=${cfg.lang}`,
390
+ });
391
+ return;
392
+ }
393
+ // 邮箱更改成功,刷新用户信息
394
+ const authToken = auth.tokenStorage.get();
395
+ if (authToken) {
396
+ auth.authService.fetchAndSetSession(authToken).catch(() => { });
397
+ // 自动取消关联 GitHub 账户(如果有)
398
+ try {
399
+ const accounts = await auth.authApi.listAccounts(authToken);
400
+ const githubAccount = accounts.find((a) => a.providerId === "github");
401
+ if (githubAccount) {
402
+ await auth.authApi.unlinkAccount("github", githubAccount.accountId, authToken);
403
+ }
404
+ }
405
+ catch {
406
+ // 取消关联失败不影响邮箱更改成功
407
+ }
408
+ }
409
+ resolve({
410
+ success: true,
411
+ redirectTo: buildSuccessRedirect(),
412
+ });
413
+ },
414
+ onError: (ctx) => {
415
+ const errorCode = ctx.error.code;
416
+ const isExpired = errorCode === "INVALID_TOKEN" || ctx.error.message?.includes("expired");
417
+ resolve({
418
+ success: false,
419
+ error: ctx.error.message,
420
+ errorCode,
421
+ redirectTo: isExpired
422
+ ? `${cfg.linkExpiredPath}?type=verify-change-email&lang=${cfg.lang}`
423
+ : `${cfg.signInPath}?lang=${cfg.lang}`,
424
+ });
425
+ },
426
+ })
427
+ .catch(() => {
428
+ // CORS 错误等网络问题时,后端可能已经成功处理
429
+ // 假设成功
430
+ resolve({
431
+ success: true,
432
+ redirectTo: buildSuccessRedirect(),
433
+ });
434
+ });
435
+ });
436
+ }
437
+ /**
438
+ * 统一处理各种回调
439
+ */
440
+ async function handleCallback(type, token, options = {}) {
441
+ const { isNewUser = false, userEmail, invitationId, newEmail } = options;
442
+ // 无类型参数
443
+ if (!type) {
444
+ return { success: true, redirectTo: cfg.defaultRedirect };
445
+ }
446
+ switch (type) {
447
+ case "signup":
448
+ return handleEmailVerificationCallback(token || "");
449
+ case "delete-user":
450
+ return handleDeleteUserCallback(token || "", userEmail);
451
+ case "reset-password":
452
+ return handlePasswordResetCallback(token);
453
+ case "invite":
454
+ return handleInviteCallback(invitationId || null);
455
+ case "confirm-change-email":
456
+ return handleConfirmChangeEmailCallback(token || "", newEmail || "");
457
+ case "verify-change-email":
458
+ return handleVerifyChangeEmailCallback(token || "");
459
+ default:
460
+ return { success: true, redirectTo: cfg.defaultRedirect };
461
+ }
462
+ }
463
+ return {
464
+ handleOAuthCallback,
465
+ handleEmailVerificationCallback,
466
+ handleDeleteUserCallback,
467
+ handlePasswordResetCallback,
468
+ handleInviteCallback,
469
+ handleConfirmChangeEmailCallback,
470
+ handleVerifyChangeEmailCallback,
471
+ handleCallback,
472
+ };
473
+ }
@@ -1 +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"}
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;AAEpD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAyDf"}
@@ -3,8 +3,6 @@
3
3
  *
4
4
  * 在用户登录成功后自动设置组织和团队上下文
5
5
  */
6
- import { createAuthApi } from "../api";
7
- import { getAuthBaseUrl } from "../utils";
8
6
  /**
9
7
  * 设置 Companion Team
10
8
  *
@@ -13,45 +11,52 @@ import { getAuthBaseUrl } from "../utils";
13
11
  * @param options - 选项
14
12
  */
15
13
  export async function setupCompanionTeam(auth, token, options = {}) {
16
- const { isNewUser, onComplete, onError } = options;
14
+ const { onComplete, onError } = options;
17
15
  try {
18
16
  if (!token?.trim())
19
17
  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) {
18
+ // storeActions 获取当前 session,避免重复请求
19
+ const currentUser = auth.storeActions.getUser();
20
+ if (!currentUser)
21
+ return;
22
+ // 已有活动组织和团队,无需处理
23
+ if (currentUser.activeOrganizationId && currentUser.activeTeamId) {
24
+ onComplete?.();
25
+ return;
26
+ }
27
+ // 没有 inherent 字段,说明 onboard 还没执行成功
28
+ // 注意:邮箱密码注册时已经调用过 onboard,这里只是兜底
29
+ if (!currentUser.inherentOrganizationId || !currentUser.inherentTeamId) {
27
30
  try {
28
- await authApi.onboard(token);
29
- session = await authApi.getSessionWithToken(token);
31
+ await auth.authApi.onboard(token);
32
+ // onboard 后需要刷新 session 获取新的 inherent 字段
33
+ const session = await auth.authApi.getSessionWithToken(token);
34
+ if (session) {
35
+ updateAuthStore(auth, session);
36
+ }
30
37
  }
31
38
  catch {
32
39
  // onboard 失败不阻塞流程
33
40
  }
34
- }
35
- if (!session)
36
- return;
37
- // 已有活动组织和团队,只需更新 store
38
- if (session.activeOrganizationId && session.activeTeamId) {
39
- updateAuthStore(auth, session);
40
41
  onComplete?.();
41
42
  return;
42
43
  }
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);
44
+ // 设置活动组织和团队(使用统一方法,自动同步 authStore)
45
+ if (!currentUser.activeOrganizationId && currentUser.inherentOrganizationId) {
46
+ if (!currentUser.activeTeamId && currentUser.inherentTeamId) {
47
+ // 同时设置组织和团队
48
+ await auth.setActiveOrganizationAndTeam(currentUser.inherentOrganizationId, currentUser.inherentTeamId);
49
+ }
50
+ else {
51
+ // 只设置组织
52
+ await auth.setActiveOrganization({
53
+ organizationId: currentUser.inherentOrganizationId,
54
+ });
55
+ }
50
56
  }
51
- // 刷新并更新 store
52
- const updated = await authApi.getSessionWithToken(token);
53
- if (updated) {
54
- updateAuthStore(auth, updated);
57
+ else if (!currentUser.activeTeamId && currentUser.inherentTeamId) {
58
+ // 只设置团队
59
+ await auth.setActiveTeam({ teamId: currentUser.inherentTeamId });
55
60
  }
56
61
  onComplete?.();
57
62
  }
@@ -60,14 +65,11 @@ export async function setupCompanionTeam(auth, token, options = {}) {
60
65
  }
61
66
  }
62
67
  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
- }
68
+ // 使用 storeActions 更新用户状态
69
+ auth.storeActions.updateUser({
70
+ activeOrganizationId: session.activeOrganizationId,
71
+ activeTeamId: session.activeTeamId,
72
+ inherentOrganizationId: session.inherentOrganizationId,
73
+ inherentTeamId: session.inherentTeamId,
74
+ });
73
75
  }
@@ -2,4 +2,6 @@
2
2
  * 服务导出
3
3
  */
4
4
  export { setupCompanionTeam } from "./companion-team";
5
+ export { createAuthService, type AuthService, type AuthServiceConfig, type BetterAuthClientMethods, } from "./auth-service";
6
+ export { createCallbackService, type CallbackService, } from "./callback-service";
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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"}
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;AACrD,OAAO,EACL,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,GAC7B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,GACrB,MAAM,oBAAoB,CAAA"}
@@ -2,3 +2,5 @@
2
2
  * 服务导出
3
3
  */
4
4
  export { setupCompanionTeam } from "./companion-team";
5
+ export { createAuthService, } from "./auth-service";
6
+ export { createCallbackService, } from "./callback-service";