186 lines
5.1 KiB
TypeScript
186 lines
5.1 KiB
TypeScript
/**
|
||
* Auth API
|
||
*
|
||
* 认证相关接口
|
||
* 原始 Dart: lib/data/services/api/auth_api_client.dart
|
||
*/
|
||
import { z } from "zod";
|
||
|
||
import { httpClient } from "./http_client";
|
||
import { ApiPath } from "./api_path";
|
||
import { ApiEnvelope, unwrap } from "./response_helper";
|
||
import {
|
||
AppleLoginRequest,
|
||
FacebookLoginRequest,
|
||
FbIdLoginRequest,
|
||
GoogleLoginRequest,
|
||
GuestLoginRequest,
|
||
GuestLoginResponse,
|
||
LoginRequest,
|
||
LoginResponse,
|
||
RefreshTokenRequest,
|
||
RefreshTokenResponse,
|
||
RegisterRequest,
|
||
SendCodeRequest,
|
||
} from "@/data/dto/auth";
|
||
import { User } from "@/data/dto/user";
|
||
import type { UserData } from "@/data/schemas/user/user";
|
||
import { Logger } from "@/utils";
|
||
|
||
const log = new Logger("DataServicesApiAuthApi");
|
||
|
||
export class AuthApi {
|
||
/**
|
||
* 发送验证码
|
||
*/
|
||
async sendCode(body: SendCodeRequest): Promise<void> {
|
||
await httpClient<ApiEnvelope<unknown>>(ApiPath.sendCode, {
|
||
method: "POST",
|
||
body: body.toJson(),
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 邮箱密码登录
|
||
*/
|
||
async emailLogin(body: LoginRequest): Promise<LoginResponse> {
|
||
const env = await httpClient<ApiEnvelope<unknown>>(
|
||
ApiPath.emailLogin,
|
||
{ method: "POST", body: body.toJson() }
|
||
);
|
||
return LoginResponse.fromJson(unwrap(env) as Record<string, unknown>);
|
||
}
|
||
|
||
/**
|
||
* 注册
|
||
*/
|
||
async register(body: RegisterRequest): Promise<void> {
|
||
await httpClient<ApiEnvelope<unknown>>(ApiPath.register, {
|
||
method: "POST",
|
||
body: body.toJson(),
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Apple 登录
|
||
*/
|
||
async appleLogin(body: AppleLoginRequest): Promise<LoginResponse> {
|
||
const env = await httpClient<ApiEnvelope<unknown>>(ApiPath.appleLogin, {
|
||
method: "POST",
|
||
body: body.toJson(),
|
||
});
|
||
return LoginResponse.fromJson(unwrap(env) as Record<string, unknown>);
|
||
}
|
||
|
||
/**
|
||
* Google 登录
|
||
*/
|
||
async googleLogin(body: GoogleLoginRequest): Promise<LoginResponse> {
|
||
const env = await httpClient<ApiEnvelope<unknown>>(ApiPath.googleLogin, {
|
||
method: "POST",
|
||
body: body.toJson(),
|
||
});
|
||
return LoginResponse.fromJson(unwrap(env) as Record<string, unknown>);
|
||
}
|
||
|
||
/**
|
||
* Facebook 登录
|
||
*/
|
||
async facebookLogin(body: FacebookLoginRequest): Promise<LoginResponse> {
|
||
const env = await httpClient<ApiEnvelope<unknown>>(
|
||
ApiPath.facebookLogin,
|
||
{ method: "POST", body: body.toJson() }
|
||
);
|
||
return LoginResponse.fromJson(unwrap(env) as Record<string, unknown>);
|
||
}
|
||
|
||
/**
|
||
* Facebook ID 登录(v7.0 新增)
|
||
*/
|
||
async facebookIdLogin(body: FbIdLoginRequest): Promise<LoginResponse> {
|
||
const env = await httpClient<ApiEnvelope<unknown>>(
|
||
ApiPath.facebookIdLogin,
|
||
{ method: "POST", body: body.toJson() }
|
||
);
|
||
return LoginResponse.fromJson(unwrap(env) as Record<string, unknown>);
|
||
}
|
||
|
||
/**
|
||
* 退出登录(fire-and-forget)
|
||
*
|
||
* 后端 logout 端点的 envelope `data` 字段常为 `null`(登出端点没有业务数据),
|
||
* 不要再用 `unwrap` + `LogoutResponse.fromJson` 解析 —— Zod schema 是 z.object,
|
||
* 对 null 会抛 ZodError "Invalid input: expected object, received null"。
|
||
*
|
||
* 对齐 `register` / `sendCode` 的写法:调通就完事,不解析响应体。
|
||
*/
|
||
async logout(): Promise<void> {
|
||
await httpClient<ApiEnvelope<unknown>>(ApiPath.logout, {
|
||
method: "POST",
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 游客登录
|
||
*/
|
||
async guestLogin(body: GuestLoginRequest): Promise<GuestLoginResponse> {
|
||
const env = await httpClient<ApiEnvelope<unknown>>(ApiPath.guestLogin, {
|
||
method: "POST",
|
||
body: body.toJson(),
|
||
});
|
||
log.debug("[AuthApi.guestLogin] raw envelope from backend", env);
|
||
const unwrapped = unwrap(env) as Record<string, unknown>;
|
||
const userObj = unwrapped.user as { lastMessageAt?: unknown } | undefined;
|
||
log.debug("[AuthApi.guestLogin] unwrapped data (pre-parse)", {
|
||
hasUser: "user" in unwrapped,
|
||
userKeys: userObj && typeof userObj === "object" ? Object.keys(userObj) : null,
|
||
lastMessageAt: userObj?.lastMessageAt,
|
||
lastMessageAtType: typeof userObj?.lastMessageAt,
|
||
});
|
||
try {
|
||
return GuestLoginResponse.fromJson(unwrapped);
|
||
} catch (e) {
|
||
if (e instanceof z.ZodError) {
|
||
log.error("[AuthApi.guestLogin] ZodError parsing response", {
|
||
issueCount: e.issues.length,
|
||
firstIssue: e.issues[0],
|
||
allPaths: e.issues.map((i) => i.path),
|
||
});
|
||
} else {
|
||
log.error("[AuthApi.guestLogin] unexpected error", e);
|
||
}
|
||
throw e;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 刷新 Token
|
||
*/
|
||
async refreshToken(
|
||
body: RefreshTokenRequest
|
||
): Promise<RefreshTokenResponse> {
|
||
const env = await httpClient<ApiEnvelope<unknown>>(ApiPath.refresh, {
|
||
method: "POST",
|
||
body: body.toJson(),
|
||
});
|
||
return RefreshTokenResponse.fromJson(
|
||
unwrap(env) as Record<string, unknown>
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 获取当前用户信息
|
||
*/
|
||
async getCurrentUser(): Promise<User> {
|
||
const env = await httpClient<ApiEnvelope<unknown>>(
|
||
ApiPath.getCurrentUser
|
||
);
|
||
return User.fromJson(unwrap(env) as UserData);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 全局单例
|
||
*/
|
||
export const authApi = new AuthApi();
|