feat(auth): implement Facebook user data fetching from Graph API and persist profile information
This commit is contained in:
@@ -15,11 +15,16 @@ import type { LoginStatus } from "@/models/auth/login-status";
|
||||
import { authRepository } from "@/data/repositories/auth_repository";
|
||||
import type { IAuthRepository } from "@/data/repositories/interfaces";
|
||||
import { AuthStorage } from "@/data/storage/auth/auth_storage";
|
||||
import { UserStorage } from "@/data/storage/user/user_storage";
|
||||
import { deviceIdentifier } from "@/utils/device_identifier";
|
||||
import { fetchFacebookUserData } from "@/utils/facebook-graph";
|
||||
import { Logger } from "@/utils/logger";
|
||||
import { Result } from "@/utils/result";
|
||||
|
||||
import { readGuestId } from "./auth-helpers";
|
||||
|
||||
const log = new Logger("AuthActors");
|
||||
|
||||
// 仓库以接口类型注入:调用面只看接口,运行时仍是同一单例
|
||||
const authRepo: IAuthRepository = authRepository;
|
||||
|
||||
@@ -126,9 +131,70 @@ export const syncFacebookBackendActor = fromPromise<
|
||||
guestId,
|
||||
});
|
||||
if (Result.isErr(result)) throw result.error;
|
||||
|
||||
// 业务 token 已到手 —— 顺带用同一个 accessToken 调 Facebook Graph `/me`
|
||||
// 拉用户资料(id / name / email / picture),把 pictureUrl + Facebook ID
|
||||
// 写本地。
|
||||
//
|
||||
// 为什么要这一步?后端 LoginResponse.user.avatarUrl 不一定总有(依赖
|
||||
// 后端在 OAuth 流程里有没有抓过);前端用 accessToken 直接调 Graph
|
||||
// 拿到的 picture 是最新的,独立于后端缓存。
|
||||
//
|
||||
// 失败策略:best-effort。Graph 调用挂掉 / 用户没头像 / token 过期都不
|
||||
// 影响 Facebook 登录主流程 —— 业务 token 已经在 result 里。
|
||||
await persistFacebookProfile(input.accessToken);
|
||||
|
||||
return "facebook" as LoginStatus;
|
||||
});
|
||||
|
||||
/**
|
||||
* 用 accessToken 调 Facebook Graph `/me` 拉用户资料,写本地:
|
||||
* - `id` → `AuthStorage.setFacebookId()`(独立于后端 userId)
|
||||
* - `pictureUrl` → `UserStorage.setAvatarUrl()`(头像专用 slot,便于快速读取)
|
||||
*
|
||||
* 全部 best-effort:单条失败只 warn,不抛错。`pictureUrl == null` 也算成功
|
||||
* (用户没设头像),只是不写入。
|
||||
*/
|
||||
async function persistFacebookProfile(accessToken: string): Promise<void> {
|
||||
const fbR = await fetchFacebookUserData(accessToken);
|
||||
if (Result.isErr(fbR)) {
|
||||
log.warn(
|
||||
{ err: fbR.error.message },
|
||||
"[auth-machine] syncFacebookBackendActor: fetchFacebookUserData failed (continuing anyway)",
|
||||
);
|
||||
return;
|
||||
}
|
||||
const fbUser = fbR.data;
|
||||
log.info(
|
||||
{
|
||||
hasId: !!fbUser.id,
|
||||
hasPicture: !!fbUser.pictureUrl,
|
||||
},
|
||||
"[auth-machine] syncFacebookBackendActor: Facebook profile fetched",
|
||||
);
|
||||
|
||||
const authStorage = AuthStorage.getInstance();
|
||||
if (fbUser.id) {
|
||||
const r = await authStorage.setFacebookId(fbUser.id);
|
||||
if (!r.success) {
|
||||
log.warn(
|
||||
{ err: r.error.message },
|
||||
"[auth-machine] syncFacebookBackendActor: setFacebookId failed",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (fbUser.pictureUrl) {
|
||||
const r = await UserStorage.getInstance().setAvatarUrl(fbUser.pictureUrl);
|
||||
if (!r.success) {
|
||||
log.warn(
|
||||
{ err: r.error.message },
|
||||
"[auth-machine] syncFacebookBackendActor: setAvatarUrl failed",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 显式游客登录 actor(opt-in) ==========
|
||||
// 由 splash UI 派发 `AuthGuestLoginSubmitted` 触发,不自动跑。
|
||||
// 取代了之前 checkAuthStatusActor 的 auto-guest-login 行为 —— 避免每次
|
||||
|
||||
Reference in New Issue
Block a user