template_0205

This commit is contained in:
Sofio
2026-02-05 13:16:05 +08:00
commit d93e4d9c9f
197 changed files with 52810 additions and 0 deletions

View File

@@ -0,0 +1,878 @@
/**
* Authentication API Service
*
* Handles all authentication-related API calls:
* - Pre-login (password verification + send verification code)
* - Resend verification code
* - Verify login (code verification + get tokens)
* - Google login
*/
import { ApiResponse, ApisauceInstance, create } from "apisauce"
import Config from "@/config"
import { getGeneralApiProblem, GeneralApiProblem } from "./apiProblem"
import type {
BindNewEmailRequest,
BindNewEmailResponse,
ChangePasswordRequest,
ChangePasswordResponse,
ForgotPasswordRequest,
ForgotPasswordResponse,
GetLoginHistoryParams,
GetLoginHistoryResponse,
GetProfileResponse,
GetSessionCountResponse,
GetSessionsParams,
GetSessionsResponse,
GoogleLoginRequest,
GoogleLoginResponse,
LogoutResponse,
PreLoginRequest,
PreLoginResponse,
PreRegisterRequest,
PreRegisterResponse,
RefreshTokenRequest,
RefreshTokenResponse,
ResendCodeRequest,
ResendCodeResponse,
ResendForgotPasswordRequest,
ResendForgotPasswordResponse,
ResendRegisterCodeRequest,
ResendRegisterCodeResponse,
ResetPasswordRequest,
ResetPasswordResponse,
RevokeOtherSessionsResponse,
RevokeSessionResponse,
SendEmailCodeRequest,
SendEmailCodeResponse,
UpdateProfileRequest,
UpdateProfileResponse,
VerifyCurrentEmailRequest,
VerifyCurrentEmailResponse,
VerifyLoginRequest,
VerifyLoginResponse,
VerifyRegisterRequest,
VerifyRegisterResponse,
} from "./authTypes"
const AUTH_API_CONFIG = {
url: Config.AUTH_API_URL || "https://auth.upay01.com",
timeout: 15000,
}
/**
* Auth API class for handling authentication requests
*/
export class AuthApi {
apisauce: ApisauceInstance
constructor() {
this.apisauce = create({
baseURL: AUTH_API_CONFIG.url,
timeout: AUTH_API_CONFIG.timeout,
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
})
}
/**
* Pre-login: Verify password and send verification code
*/
async preLogin(
email: string,
password: string,
language?: string,
): Promise<{ kind: "ok"; data: PreLoginResponse } | GeneralApiProblem> {
const payload: PreLoginRequest = { email, password }
if (language) payload.language = language
const response: ApiResponse<PreLoginResponse> = await this.apisauce.post(
"/api/auth/pre-login",
payload,
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth preLogin error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Resend verification code
*/
async resendCode(
email: string,
language?: string,
): Promise<{ kind: "ok"; data: ResendCodeResponse } | GeneralApiProblem> {
const payload: ResendCodeRequest = { email }
if (language) payload.language = language
const response: ApiResponse<ResendCodeResponse> = await this.apisauce.post(
"/api/auth/pre-login/resend",
payload,
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth resendCode error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Verify login: Verify code and get tokens
*/
async verifyLogin(
email: string,
code: string,
): Promise<{ kind: "ok"; data: VerifyLoginResponse } | GeneralApiProblem> {
const payload: VerifyLoginRequest = { email, code }
const response: ApiResponse<VerifyLoginResponse> = await this.apisauce.post(
"/api/auth/login/verify",
payload,
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth verifyLogin error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Google login
*/
async googleLogin(
idToken: string,
referralCode?: string,
): Promise<{ kind: "ok"; data: GoogleLoginResponse } | GeneralApiProblem> {
const payload: GoogleLoginRequest = { idToken }
if (referralCode) payload.referralCode = referralCode
const response: ApiResponse<GoogleLoginResponse> = await this.apisauce.post(
"/api/auth/google-login",
payload,
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth googleLogin error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Pre-register: Validate data and send verification code
*/
async preRegister(
username: string,
password: string,
email: string,
nickname?: string,
referralCode?: string,
language?: string,
): Promise<{ kind: "ok"; data: PreRegisterResponse } | GeneralApiProblem> {
const payload: PreRegisterRequest = { username, password, email }
if (nickname) payload.nickname = nickname
if (referralCode) payload.referralCode = referralCode
if (language) payload.language = language
const response: ApiResponse<PreRegisterResponse> = await this.apisauce.post(
"/api/auth/pre-register",
payload,
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth preRegister error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Resend register verification code
*/
async resendRegisterCode(
email: string,
language?: string,
): Promise<{ kind: "ok"; data: ResendRegisterCodeResponse } | GeneralApiProblem> {
const payload: ResendRegisterCodeRequest = { email }
if (language) payload.language = language
const response: ApiResponse<ResendRegisterCodeResponse> = await this.apisauce.post(
"/api/auth/pre-register/resend",
payload,
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth resendRegisterCode error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Verify register: Verify code and create user
*/
async verifyRegister(
email: string,
code: string,
): Promise<{ kind: "ok"; data: VerifyRegisterResponse } | GeneralApiProblem> {
const payload: VerifyRegisterRequest = { email, code }
const response: ApiResponse<VerifyRegisterResponse> = await this.apisauce.post(
"/api/auth/register/verify",
payload,
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth verifyRegister error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Forgot password: Send verification code
*/
async forgotPassword(
email: string,
language?: string,
): Promise<{ kind: "ok"; data: ForgotPasswordResponse } | GeneralApiProblem> {
const payload: ForgotPasswordRequest = { email }
if (language) payload.language = language
const response: ApiResponse<ForgotPasswordResponse> = await this.apisauce.post(
"/api/auth/forgot-password",
payload,
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth forgotPassword error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Resend forgot password verification code
*/
async resendForgotPasswordCode(
email: string,
language?: string,
): Promise<{ kind: "ok"; data: ResendForgotPasswordResponse } | GeneralApiProblem> {
const payload: ResendForgotPasswordRequest = { email }
if (language) payload.language = language
const response: ApiResponse<ResendForgotPasswordResponse> = await this.apisauce.post(
"/api/auth/forgot-password/resend",
payload,
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth resendForgotPasswordCode error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Reset password: Verify code and set new password
*/
async resetPassword(
email: string,
code: string,
newPassword: string,
): Promise<{ kind: "ok"; data: ResetPasswordResponse } | GeneralApiProblem> {
const payload: ResetPasswordRequest = { email, code, newPassword }
const response: ApiResponse<ResetPasswordResponse> = await this.apisauce.post(
"/api/auth/reset-password",
payload,
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth resetPassword error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Logout: Revoke token
*/
async logoutApi(
accessToken: string,
): Promise<{ kind: "ok"; data: LogoutResponse } | GeneralApiProblem> {
const response: ApiResponse<LogoutResponse> = await this.apisauce.post(
"/api/auth/logout",
{},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth logout error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
// ============================================
// Profile APIs
// ============================================
/**
* Get user profile
*/
async getProfile(
accessToken: string,
): Promise<{ kind: "ok"; data: GetProfileResponse } | GeneralApiProblem> {
const response: ApiResponse<GetProfileResponse> = await this.apisauce.get(
"/api/auth/profile",
{},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth getProfile error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Update user profile
*/
async updateProfile(
accessToken: string,
payload: UpdateProfileRequest,
): Promise<{ kind: "ok"; data: UpdateProfileResponse } | GeneralApiProblem> {
const response: ApiResponse<UpdateProfileResponse> = await this.apisauce.put(
"/api/auth/profile",
payload,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth updateProfile error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
// ============================================
// Password APIs
// ============================================
/**
* Change password
*/
async changePassword(
accessToken: string,
payload: ChangePasswordRequest,
): Promise<{ kind: "ok"; data: ChangePasswordResponse } | GeneralApiProblem> {
const response: ApiResponse<ChangePasswordResponse> = await this.apisauce.post(
"/api/auth/change-password",
payload,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth changePassword error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
// ============================================
// Token APIs
// ============================================
/**
* Refresh access token
*/
async refreshToken(
refreshToken: string,
): Promise<{ kind: "ok"; data: RefreshTokenResponse } | GeneralApiProblem> {
const payload: RefreshTokenRequest = { refreshToken }
const response: ApiResponse<RefreshTokenResponse> = await this.apisauce.post(
"/api/auth/refresh-token",
payload,
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth refreshToken error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
// ============================================
// Email APIs
// ============================================
/**
* Send email verification code (for changing email)
*/
async sendEmailCode(
accessToken: string,
email: string,
language?: string,
): Promise<{ kind: "ok"; data: SendEmailCodeResponse } | GeneralApiProblem> {
const payload: SendEmailCodeRequest = { email }
if (language) payload.language = language
const response: ApiResponse<SendEmailCodeResponse> = await this.apisauce.post(
"/api/email/send-verification-code",
payload,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth sendEmailCode error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Verify current email (Step 1 of email change)
*/
async verifyCurrentEmail(
accessToken: string,
currentCode: string,
): Promise<{ kind: "ok"; data: VerifyCurrentEmailResponse } | GeneralApiProblem> {
const payload: VerifyCurrentEmailRequest = {
action: "verify-current",
currentCode,
}
const response: ApiResponse<VerifyCurrentEmailResponse> = await this.apisauce.post(
"/api/email/change",
payload,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth verifyCurrentEmail error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Bind new email (Step 2 of email change)
*/
async bindNewEmail(
accessToken: string,
newEmail: string,
newCode: string,
): Promise<{ kind: "ok"; data: BindNewEmailResponse } | GeneralApiProblem> {
const payload: BindNewEmailRequest = {
action: "bind-new",
newEmail,
newCode,
}
const response: ApiResponse<BindNewEmailResponse> = await this.apisauce.post(
"/api/email/change",
payload,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth bindNewEmail error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
// ============================================
// Session Management APIs
// ============================================
/**
* Get sessions/devices list
*/
async getSessions(
accessToken: string,
params?: GetSessionsParams,
): Promise<{ kind: "ok"; data: GetSessionsResponse } | GeneralApiProblem> {
const response: ApiResponse<GetSessionsResponse> = await this.apisauce.get(
"/api/sessions",
params,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth getSessions error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Get active session count
*/
async getSessionCount(
accessToken: string,
): Promise<{ kind: "ok"; data: GetSessionCountResponse } | GeneralApiProblem> {
const response: ApiResponse<GetSessionCountResponse> = await this.apisauce.get(
"/api/sessions/count",
{},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth getSessionCount error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Get login history
*/
async getLoginHistory(
accessToken: string,
params?: GetLoginHistoryParams,
): Promise<{ kind: "ok"; data: GetLoginHistoryResponse } | GeneralApiProblem> {
const response: ApiResponse<GetLoginHistoryResponse> = await this.apisauce.get(
"/api/sessions/history",
params,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth getLoginHistory error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Revoke a specific session (remote logout device)
*/
async revokeSession(
accessToken: string,
sessionId: string,
): Promise<{ kind: "ok"; data: RevokeSessionResponse } | GeneralApiProblem> {
const response: ApiResponse<RevokeSessionResponse> = await this.apisauce.delete(
`/api/sessions/${sessionId}`,
{},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth revokeSession error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
/**
* Revoke all other sessions (logout all other devices)
*/
async revokeOtherSessions(
accessToken: string,
): Promise<{ kind: "ok"; data: RevokeOtherSessionsResponse } | GeneralApiProblem> {
const response: ApiResponse<RevokeOtherSessionsResponse> = await this.apisauce.post(
"/api/sessions/revoke-others",
{},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
if (!response.ok) {
const problem = getGeneralApiProblem(response)
if (problem) return problem
}
try {
const data = response.data
if (!data) return { kind: "bad-data" }
return { kind: "ok", data }
} catch (e) {
if (__DEV__ && e instanceof Error) {
console.error(`Auth revokeOtherSessions error: ${e.message}`)
}
return { kind: "bad-data" }
}
}
}
// Singleton instance
export const authApi = new AuthApi()