Blame · Line-by-line history
auth.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| 5c83ccc | 1 | import { fetchJSON, saveToken, removeToken, PAT_STORE_KEY } from './client'; |
| 2 | import * as SecureStore from 'expo-secure-store'; | |
| 3 | import { useSettingsStore } from '../store/settingsStore'; | |
| 4 | import type { AuthUser } from '../store/authStore'; | |
| 5 | ||
| 6 | export interface MeResponse { | |
| 7 | id: number; | |
| 8 | username: string; | |
| 9 | email: string; | |
| 10 | avatarUrl: string | null; | |
| 11 | bio: string | null; | |
| 12 | createdAt: string; | |
| 13 | } | |
| 14 | ||
| 15 | /** | |
| 16 | * Validates a PAT token against the given host by calling GET /api/users/me. | |
| 17 | * The host is temporarily set in the settings store before the call. | |
| 18 | * Returns the user profile on success; throws ApiError on failure. | |
| 19 | */ | |
| 20 | export async function validateToken(host: string, token: string): Promise<AuthUser> { | |
| 21 | // Temporarily point the store at the target host so client.ts builds the | |
| 22 | // correct URL. The final host is persisted by the caller (useAuth hook). | |
| 23 | useSettingsStore.getState().setHost(host); | |
| 24 | ||
| 25 | // Provide the token inline since it is not yet saved to SecureStore. | |
| 26 | const response = await fetch(`${host.replace(/\/$/, '')}/api/users/me`, { | |
| 27 | headers: { | |
| 28 | Authorization: `Bearer ${token}`, | |
| 29 | Accept: 'application/json', | |
| 30 | }, | |
| 31 | }); | |
| 32 | ||
| 33 | if (!response.ok) { | |
| 34 | let msg = `HTTP ${response.status}`; | |
| 35 | try { | |
| 36 | const body = (await response.json()) as Record<string, unknown>; | |
| 37 | if (typeof body.error === 'string') msg = body.error; | |
| 38 | } catch { | |
| 39 | // ignore parse failures | |
| 40 | } | |
| 41 | throw new Error(msg); | |
| 42 | } | |
| 43 | ||
| 44 | const data = (await response.json()) as MeResponse; | |
| 45 | return { | |
| 46 | id: data.id, | |
| 47 | username: data.username, | |
| 48 | email: data.email, | |
| 49 | avatarUrl: data.avatarUrl ?? null, | |
| 50 | bio: data.bio ?? null, | |
| 51 | createdAt: data.createdAt, | |
| 52 | }; | |
| 53 | } | |
| 54 | ||
| 55 | /** Persists the token to SecureStore. */ | |
| 56 | export async function persistToken(token: string): Promise<void> { | |
| 57 | await saveToken(token); | |
| 58 | } | |
| 59 | ||
| 60 | /** Removes the saved token from SecureStore. */ | |
| 61 | export async function clearToken(): Promise<void> { | |
| 62 | await removeToken(); | |
| 63 | } | |
| 64 | ||
| 65 | /** Retrieves the current user profile from the API. */ | |
| 66 | export async function fetchMe(): Promise<AuthUser> { | |
| 67 | return fetchJSON<AuthUser>('/api/users/me'); | |
| 68 | } |