class AuthService { static TOKEN_KEY = 'access_token'; static REFRESH_KEY = 'refresh_token'; static async login(credentials) { const url = `${import.meta.env.VITE_DJANGO_BASE_URL}/api/token/`; const resp = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credentials), }); if (!resp.ok) { let errMsg = resp.statusText; try { const errData = await resp.json(); errMsg = errData?.detail ?? errData?.message ?? errMsg; } catch (_) { /* ignore */ } throw new Error(errMsg); } const data = await resp.json(); if (data.access && data.refresh) { localStorage.setItem(this.TOKEN_KEY, data.access); localStorage.setItem(this.REFRESH_KEY, data.refresh); } return data; } static getAccessToken() { return localStorage.getItem(this.TOKEN_KEY); } static getRefreshToken() { return localStorage.getItem(this.REFRESH_KEY); } static async refresh() { const refresh = this.getRefreshToken(); if (!refresh) throw new Error('No refresh token'); const url = `${import.meta.env.VITE_DJANGO_BASE_URL}/api/token/refresh/`; const resp = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh }), }); if (!resp.ok) { const errData = await resp.json().catch(() => ({})); throw new Error(errData?.detail ?? resp.statusText); } const data = await resp.json(); localStorage.setItem(this.TOKEN_KEY, data.access); return data.access; } static isAuthenticated() { return !!this.getAccessToken(); } static logout() { localStorage.removeItem(this.TOKEN_KEY); localStorage.removeItem(this.REFRESH_KEY); } } export default AuthService;