feat(auth): add startup auth status check with device id fallback

Add AuthStatusChecker mounted in RootProviders to dispatch AuthStatusCheckSubmitted on mount. The new checkAuthStatusActor retrieves the device id, checks for an existing login or guest token, and falls back to a guest login API call when neither is present. Wires the new event/actor through the auth machine to enable automatic session restoration and guest-mode bootstrap.
This commit is contained in:
2026-06-11 13:23:29 +08:00
parent 7c89f1b39d
commit 2475cb3e45
4 changed files with 92 additions and 3 deletions
+58
View File
@@ -15,6 +15,7 @@ import type { AuthProvider } from "@/lib/auth/auth_platform";
import type { LoginStatus } from "@/models/auth/login-status";
import { authRepository } from "@/data/repositories/auth_repository";
import { AuthStorage } from "@/data/storage/auth/auth_storage";
import { deviceIdentifier } from "@/utils/device_identifier";
import { Result } from "@/utils/result";
import { AuthState, initialState } from "./auth-state";
@@ -108,6 +109,39 @@ const syncFacebookBackendActor = fromPromise<
return "facebook" as LoginStatus;
});
// ========== 新增:启动 / 恢复时检查登录态 actor ==========
// 由 <AuthStatusChecker /> 在 mount 时派发(一次性)。
// 流程:
// 1. deviceIdentifier.getDeviceId() —— 无则通过 fingerprintjs 生成 + 落盘
// 2. AuthStorage.getLoginToken() —— 有 → "email"(具体 provider 由 OAuthSessionSync 后续覆盖)
// 3. AuthStorage.getGuestToken() —— 有 → "guest"
// 4. 都没有 → authRepository.guestLogin(deviceId) 内部自动 setGuestToken + setDeviceId + setUserId
const checkAuthStatusActor = fromPromise<LoginStatus, void>(async () => {
// 1. 拿 deviceId
const deviceId = await deviceIdentifier.getDeviceId();
const storage = AuthStorage.getInstance();
// 2. 查 loginTokenOAuth / 邮箱登录后端写下的)
const loginTokenR = await storage.getLoginToken();
if (loginTokenR.success && loginTokenR.data) {
// token 字符串无法区分 google/facebook/email/apple
// 默认 "email" —— 若实际是 OAuthOAuthSessionSync 后续会通过 AuthGoogle/FacebookSyncSubmitted 覆盖
return "email" as LoginStatus;
}
// 3. 查 guestToken
const guestTokenR = await storage.getGuestToken();
if (guestTokenR.success && guestTokenR.data) {
return "guest" as LoginStatus;
}
// 4. 都没有 —— 调游客登录接口(内部自动 setGuestToken + setDeviceId + setUserId
const result = await authRepository.guestLogin(deviceId);
if (Result.isErr(result)) throw result.error;
return "guest" as LoginStatus;
});
// ============================================================
// Machine
// ============================================================
@@ -122,6 +156,7 @@ export const authMachine = setup({
oauthSignIn: oauthSignInActor,
syncGoogleBackend: syncGoogleBackendActor,
syncFacebookBackend: syncFacebookBackendActor,
checkAuthStatus: checkAuthStatusActor,
},
}).createMachine({
id: "auth",
@@ -154,6 +189,8 @@ export const authMachine = setup({
AuthReset: {
actions: assign(() => initialState),
},
// 启动 / 恢复时检查登录态 —— 由 <AuthStatusChecker /> 派发
AuthStatusCheckSubmitted: "checkingAuthStatus",
AuthEmailLoginSubmitted: "loadingEmailLogin",
AuthEmailRegisterSubmitted: "loadingEmailRegister",
AuthGoogleLoginSubmitted: "loadingOAuth",
@@ -169,6 +206,27 @@ export const authMachine = setup({
},
},
checkingAuthStatus: {
entry: assign({ errorMessage: null }),
invoke: {
src: "checkAuthStatus",
onDone: {
target: "success",
actions: assign({
loginStatus: ({ event }) => event.output,
errorMessage: null,
}),
},
onError: {
target: "idle",
actions: assign({
errorMessage: ({ event }) =>
event.error instanceof Error ? event.error.message : String(event.error),
}),
},
},
},
loadingEmailLogin: {
invoke: {
src: "emailLogin",