feat(auth): mark test accounts in login requests

This commit is contained in:
2026-06-26 14:05:53 +08:00
parent 8cb60ed7e3
commit abf6d5ae88
16 changed files with 78 additions and 36 deletions
@@ -8,7 +8,7 @@ import type { FetchHook } from "ofetch";
import { ApiPath } from "../../../data/services/api/api_path"; import { ApiPath } from "../../../data/services/api/api_path";
import { ApiError, ErrorCode } from "../../../data/services/api/api_result"; import { ApiError, ErrorCode } from "../../../data/services/api/api_result";
import { AuthStorage } from "@/data/storage/auth/auth_storage"; import { AuthStorage } from "@/data/storage/auth/auth_storage";
import { deviceIdentifier, Result } from "@/utils"; import { AppEnvUtil, deviceIdentifier, Result } from "@/utils";
/** /**
* 不需要 auth token 刷新的路径 * 不需要 auth token 刷新的路径
@@ -105,7 +105,7 @@ async function refreshGuestToken(baseUrl: string): Promise<void> {
const response = await fetch(`${baseUrl}${ApiPath.guestLogin}`, { const response = await fetch(`${baseUrl}${ApiPath.guestLogin}`, {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ deviceId }), body: JSON.stringify(withTestAccountFlag({ deviceId })),
}); });
if (!response.ok) { if (!response.ok) {
@@ -125,6 +125,12 @@ async function refreshGuestToken(baseUrl: string): Promise<void> {
} }
} }
function withTestAccountFlag<T extends Record<string, unknown>>(
payload: T,
): T & { isTestAccount: boolean } {
return { ...payload, isTestAccount: !AppEnvUtil.isProduction() };
}
/** /**
* 401 → 刷新 token 并重试 * 401 → 刷新 token 并重试
*/ */
+1
View File
@@ -10,6 +10,7 @@ import {
export class AppleLoginRequest { export class AppleLoginRequest {
declare readonly identityToken: string; declare readonly identityToken: string;
declare readonly platform: string; declare readonly platform: string;
declare readonly isTestAccount: boolean;
private constructor(input: AppleLoginRequestInput) { private constructor(input: AppleLoginRequestInput) {
const data = AppleLoginRequestSchema.parse(input); const data = AppleLoginRequestSchema.parse(input);
@@ -11,6 +11,7 @@ export class FacebookLoginRequest {
declare readonly accessToken: string; declare readonly accessToken: string;
declare readonly platform: string; declare readonly platform: string;
declare readonly guestId: string; declare readonly guestId: string;
declare readonly isTestAccount: boolean;
private constructor(input: FacebookLoginRequestInput) { private constructor(input: FacebookLoginRequestInput) {
const data = FacebookLoginRequestSchema.parse(input); const data = FacebookLoginRequestSchema.parse(input);
+1
View File
@@ -10,6 +10,7 @@ import {
export class FbIdLoginRequest { export class FbIdLoginRequest {
declare readonly fbId: string; declare readonly fbId: string;
declare readonly avatarUrl: string; declare readonly avatarUrl: string;
declare readonly isTestAccount: boolean;
private constructor(input: FbIdLoginRequestInput) { private constructor(input: FbIdLoginRequestInput) {
const data = FbIdLoginRequestSchema.parse(input); const data = FbIdLoginRequestSchema.parse(input);
@@ -11,6 +11,7 @@ export class GoogleLoginRequest {
declare readonly idToken: string; declare readonly idToken: string;
declare readonly platform: string; declare readonly platform: string;
declare readonly guestId: string; declare readonly guestId: string;
declare readonly isTestAccount: boolean;
private constructor(input: GoogleLoginRequestInput) { private constructor(input: GoogleLoginRequestInput) {
const data = GoogleLoginRequestSchema.parse(input); const data = GoogleLoginRequestSchema.parse(input);
+1
View File
@@ -9,6 +9,7 @@ import {
export class GuestLoginRequest { export class GuestLoginRequest {
declare readonly deviceId: string; declare readonly deviceId: string;
declare readonly isTestAccount: boolean;
private constructor(input: GuestLoginRequestInput) { private constructor(input: GuestLoginRequestInput) {
const data = GuestLoginRequestSchema.parse(input); const data = GuestLoginRequestSchema.parse(input);
+1
View File
@@ -13,6 +13,7 @@ export class LoginRequest {
declare readonly password: string; declare readonly password: string;
declare readonly platform: string; declare readonly platform: string;
declare readonly guestId: string; declare readonly guestId: string;
declare readonly isTestAccount: boolean;
private constructor(input: LoginRequestInput) { private constructor(input: LoginRequestInput) {
const data = LoginRequestSchema.parse(input); const data = LoginRequestSchema.parse(input);
+1
View File
@@ -13,6 +13,7 @@ export class RegisterRequest {
declare readonly password: string; declare readonly password: string;
declare readonly platform: string; declare readonly platform: string;
declare readonly guestId: string; declare readonly guestId: string;
declare readonly isTestAccount: boolean;
private constructor(input: RegisterRequestInput) { private constructor(input: RegisterRequestInput) {
const data = RegisterRequestSchema.parse(input); const data = RegisterRequestSchema.parse(input);
+56 -34
View File
@@ -28,7 +28,7 @@ import {
SendCodeRequest, SendCodeRequest,
} from "@/data/dto/auth"; } from "@/data/dto/auth";
import { User } from "@/data/dto/user"; import { User } from "@/data/dto/user";
import { Result, Logger } from "@/utils"; import { AppEnvUtil, Result, Logger } from "@/utils";
import type { IAuthRepository } from "@/data/repositories/interfaces"; import type { IAuthRepository } from "@/data/repositories/interfaces";
import { AuthStorage, type IAuthStorage } from "@/data/storage/auth"; import { AuthStorage, type IAuthStorage } from "@/data/storage/auth";
import { UserStorage, type IUserStorage } from "@/data/storage/user"; import { UserStorage, type IUserStorage } from "@/data/storage/user";
@@ -37,6 +37,7 @@ const log = new Logger("DataRepositoriesAuthRepository");
/** 硬编码平台名,对齐 Dart `PlatformUtil.platformName.toLowerCase()`Web 平台)。 */ /** 硬编码平台名,对齐 Dart `PlatformUtil.platformName.toLowerCase()`Web 平台)。 */
const WEB_PLATFORM = "web"; const WEB_PLATFORM = "web";
const TEST_ACCOUNT_FLAG = "isTestAccount";
export class AuthRepository implements IAuthRepository { export class AuthRepository implements IAuthRepository {
constructor( constructor(
@@ -59,13 +60,15 @@ export class AuthRepository implements IAuthRepository {
}): Promise<Result<void>> { }): Promise<Result<void>> {
return Result.wrap(async () => { return Result.wrap(async () => {
await this.api.register( await this.api.register(
RegisterRequest.from({ RegisterRequest.from(
username: input.username, withTestAccountFlag({
email: input.email, username: input.username,
password: input.password, email: input.email,
platform: WEB_PLATFORM, password: input.password,
guestId: input.guestId ?? "", platform: WEB_PLATFORM,
}), guestId: input.guestId ?? "",
}),
),
); );
}); });
} }
@@ -79,13 +82,15 @@ export class AuthRepository implements IAuthRepository {
}): Promise<Result<LoginResponse>> { }): Promise<Result<LoginResponse>> {
return Result.wrap(async () => { return Result.wrap(async () => {
const response = await this.api.emailLogin( const response = await this.api.emailLogin(
LoginRequest.from({ LoginRequest.from(
email: input.email ?? "", withTestAccountFlag({
username: input.username ?? "", email: input.email ?? "",
password: input.password, username: input.username ?? "",
platform: WEB_PLATFORM, password: input.password,
guestId: input.guestId ?? "", platform: WEB_PLATFORM,
}), guestId: input.guestId ?? "",
}),
),
); );
await this._saveLoginData(response); await this._saveLoginData(response);
return response; return response;
@@ -131,7 +136,7 @@ export class AuthRepository implements IAuthRepository {
}); });
return Result.wrap(async () => { return Result.wrap(async () => {
const response = await this.api.guestLogin( const response = await this.api.guestLogin(
GuestLoginRequest.from({ deviceId }), GuestLoginRequest.from(withTestAccountFlag({ deviceId })),
); );
log.debug("[AuthRepository.guestLogin] API call SUCCESS (Zod parsed)", { log.debug("[AuthRepository.guestLogin] API call SUCCESS (Zod parsed)", {
hasToken: !!response.token, hasToken: !!response.token,
@@ -160,11 +165,13 @@ export class AuthRepository implements IAuthRepository {
}): Promise<Result<LoginResponse>> { }): Promise<Result<LoginResponse>> {
return this._socialLogin(() => return this._socialLogin(() =>
this.api.googleLogin( this.api.googleLogin(
GoogleLoginRequest.from({ GoogleLoginRequest.from(
idToken: input.idToken, withTestAccountFlag({
platform: WEB_PLATFORM, idToken: input.idToken,
guestId: input.guestId ?? "", platform: WEB_PLATFORM,
}), guestId: input.guestId ?? "",
}),
),
), ),
); );
} }
@@ -176,11 +183,13 @@ export class AuthRepository implements IAuthRepository {
}): Promise<Result<LoginResponse>> { }): Promise<Result<LoginResponse>> {
return this._socialLogin(() => return this._socialLogin(() =>
this.api.facebookLogin( this.api.facebookLogin(
FacebookLoginRequest.from({ FacebookLoginRequest.from(
accessToken: input.accessToken, withTestAccountFlag({
platform: WEB_PLATFORM, accessToken: input.accessToken,
guestId: input.guestId ?? "", platform: WEB_PLATFORM,
}), guestId: input.guestId ?? "",
}),
),
), ),
); );
} }
@@ -192,10 +201,12 @@ export class AuthRepository implements IAuthRepository {
}): Promise<Result<LoginResponse>> { }): Promise<Result<LoginResponse>> {
return this._socialLogin(() => return this._socialLogin(() =>
this.api.facebookIdLogin( this.api.facebookIdLogin(
FbIdLoginRequest.from({ FbIdLoginRequest.from(
fbId: input.fbId, withTestAccountFlag({
avatarUrl: input.avatarUrl ?? "", fbId: input.fbId,
}), avatarUrl: input.avatarUrl ?? "",
}),
),
), ),
); );
} }
@@ -208,10 +219,12 @@ export class AuthRepository implements IAuthRepository {
async appleLogin(identityToken: string): Promise<Result<LoginResponse>> { async appleLogin(identityToken: string): Promise<Result<LoginResponse>> {
return this._socialLogin(() => return this._socialLogin(() =>
this.api.appleLogin( this.api.appleLogin(
AppleLoginRequest.from({ AppleLoginRequest.from(
identityToken, withTestAccountFlag({
platform: WEB_PLATFORM, identityToken,
}), platform: WEB_PLATFORM,
}),
),
), ),
); );
} }
@@ -305,3 +318,12 @@ export const authRepository = new AuthRepository(
AuthStorage.getInstance(), AuthStorage.getInstance(),
UserStorage.getInstance(), UserStorage.getInstance(),
); );
function withTestAccountFlag<T extends Record<string, unknown>>(
payload: T,
): T & { isTestAccount: boolean } {
return {
...payload,
[TEST_ACCOUNT_FLAG]: !AppEnvUtil.isProduction(),
};
}
@@ -7,6 +7,7 @@ import { z } from "zod";
export const AppleLoginRequestSchema = z.object({ export const AppleLoginRequestSchema = z.object({
identityToken: z.string(), identityToken: z.string(),
platform: z.string(), platform: z.string(),
isTestAccount: z.boolean().default(false),
}); });
export type AppleLoginRequestInput = z.input<typeof AppleLoginRequestSchema>; export type AppleLoginRequestInput = z.input<typeof AppleLoginRequestSchema>;
@@ -8,6 +8,7 @@ export const FacebookLoginRequestSchema = z.object({
accessToken: z.string(), accessToken: z.string(),
platform: z.string(), platform: z.string(),
guestId: z.string().default(""), guestId: z.string().default(""),
isTestAccount: z.boolean().default(false),
}); });
export type FacebookLoginRequestInput = z.input<typeof FacebookLoginRequestSchema>; export type FacebookLoginRequestInput = z.input<typeof FacebookLoginRequestSchema>;
@@ -7,6 +7,7 @@ import { z } from "zod";
export const FbIdLoginRequestSchema = z.object({ export const FbIdLoginRequestSchema = z.object({
fbId: z.string(), fbId: z.string(),
avatarUrl: z.string().default(""), avatarUrl: z.string().default(""),
isTestAccount: z.boolean().default(false),
}); });
export type FbIdLoginRequestInput = z.input<typeof FbIdLoginRequestSchema>; export type FbIdLoginRequestInput = z.input<typeof FbIdLoginRequestSchema>;
@@ -8,6 +8,7 @@ export const GoogleLoginRequestSchema = z.object({
idToken: z.string(), idToken: z.string(),
platform: z.string(), platform: z.string(),
guestId: z.string().default(""), guestId: z.string().default(""),
isTestAccount: z.boolean().default(false),
}); });
export type GoogleLoginRequestInput = z.input<typeof GoogleLoginRequestSchema>; export type GoogleLoginRequestInput = z.input<typeof GoogleLoginRequestSchema>;
@@ -6,6 +6,7 @@ import { z } from "zod";
export const GuestLoginRequestSchema = z.object({ export const GuestLoginRequestSchema = z.object({
deviceId: z.string(), deviceId: z.string(),
isTestAccount: z.boolean().default(false),
}); });
export type GuestLoginRequestInput = z.input<typeof GuestLoginRequestSchema>; export type GuestLoginRequestInput = z.input<typeof GuestLoginRequestSchema>;
+1
View File
@@ -10,6 +10,7 @@ export const LoginRequestSchema = z.object({
password: z.string(), password: z.string(),
platform: z.string(), platform: z.string(),
guestId: z.string().default(""), guestId: z.string().default(""),
isTestAccount: z.boolean().default(false),
}); });
export type LoginRequestInput = z.input<typeof LoginRequestSchema>; export type LoginRequestInput = z.input<typeof LoginRequestSchema>;
@@ -10,6 +10,7 @@ export const RegisterRequestSchema = z.object({
password: z.string(), password: z.string(),
platform: z.string(), platform: z.string(),
guestId: z.string().default(""), guestId: z.string().default(""),
isTestAccount: z.boolean().default(false),
}); });
export type RegisterRequestInput = z.input<typeof RegisterRequestSchema>; export type RegisterRequestInput = z.input<typeof RegisterRequestSchema>;