/* Copyright (C) 2023-2026 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import axios from 'axios' import { api, refreshAuthentication, type RefreshOutcome } from '@/lib/api' import { useAuthStore } from '@/stores/auth-store' import { getAffiliateCode } from './lib/storage' import type { TelegramAuthorization } from './lib/telegram-login' import type { LoginPayload, LoginResponse, Login2FAResponse, TwoFAPayload, RegisterPayload, ApiResponse, } from './types' // ============================================================================ // Authentication APIs // ============================================================================ // ---------------------------------------------------------------------------- // Login & Logout // ---------------------------------------------------------------------------- // User login with username and password export async function login(payload: LoginPayload) { const turnstile = payload.turnstile ?? '' const res = await api.post( `/api/user/login?turnstile=${turnstile}`, { username: payload.username, password: payload.password, }, { skipAuthRefresh: true } ) return res.data } // Two-factor authentication login export async function login2fa(payload: TwoFAPayload) { const res = await api.post('/api/user/login/2fa', payload, { skipAuthRefresh: true, }) return res.data } interface LogoutRuntime { getExpectedSID: () => string | undefined request: (expectedSID?: string) => Promise refresh: () => Promise } export async function executeLogout( runtime: LogoutRuntime, allowMismatchRecovery = true ): Promise { try { return await runtime.request(runtime.getExpectedSID()) } catch (error: unknown) { const code = axios.isAxiosError(error) ? error.response?.data?.code : undefined if ( allowMismatchRecovery && axios.isAxiosError(error) && error.response?.status === 409 && code === 'AUTH_SESSION_MISMATCH' ) { const outcome = await runtime.refresh() if (outcome.kind === 'authenticated') { return executeLogout(runtime, false) } if (outcome.kind === 'anonymous') { return { success: true, message: '' } } } throw error } } // User logout export async function logout(): Promise { return executeLogout({ getExpectedSID: () => useAuthStore.getState().auth.session?.sid, request: async (sid) => { const res = await api.post('/api/user/auth/logout', undefined, { headers: sid ? { 'X-Auth-Session': sid } : undefined, skipAuthRefresh: true, skipErrorHandler: true, }) return res.data }, refresh: refreshAuthentication, }) } // ---------------------------------------------------------------------------- // Password Management // ---------------------------------------------------------------------------- // Send password reset email export async function sendPasswordResetEmail( email: string, turnstile?: string ): Promise { const res = await api.get('/api/reset_password', { params: { email, turnstile }, }) return res.data } // ---------------------------------------------------------------------------- // OAuth // ---------------------------------------------------------------------------- // Start GitHub OAuth flow export async function githubOAuthStart(clientId: string, state: string) { const url = `https://github.com/login/oauth/authorize?client_id=${clientId}&state=${state}&scope=user:email` window.open(url) } // Get OAuth state for CSRF protection export async function createOAuthFlow( provider: string, intent: 'login' | 'bind' ): Promise { const aff = intent === 'login' ? getAffiliateCode() : '' const res = await api.post( '/api/oauth/state', { provider, intent, aff: aff || undefined }, { skipAuthRefresh: intent === 'login' } ) if (res.data?.success) { if (typeof res.data.data === 'string') return res.data.data if (typeof res.data.data?.flow_token === 'string') { return res.data.data.flow_token } } throw new Error(res.data?.message || 'Failed to initialize OAuth') } // WeChat login by authorization code export async function wechatLoginByCode(code: string): Promise { const res = await api.get('/api/oauth/wechat', { params: { code } }) return res.data } export async function telegramLogin( authorization: TelegramAuthorization ): Promise { const res = await api.get('/api/oauth/telegram/login', { params: authorization, disableDuplicate: true, skipAuthRefresh: true, skipBusinessError: true, skipErrorHandler: true, }) return res.data } // ---------------------------------------------------------------------------- // Registration // ---------------------------------------------------------------------------- // User registration export async function register(payload: RegisterPayload): Promise { const res = await api.post(`/api/user/register`, payload, { params: { turnstile: payload.turnstile ?? '' }, }) return res.data } // Send email verification code export async function sendEmailVerification( email: string, turnstile?: string ): Promise { const res = await api.get('/api/verification', { params: { email, turnstile }, }) return res.data } // Bind email to OAuth account export async function bindEmail( email: string, code: string ): Promise { const res = await api.post('/api/oauth/email/bind', { email, code, }) return res.data }