frontend/api: talk to backend
Parents:
7dacef61 file(s) changed
- frontend/lib/api.ts +129 -0
frontend/lib/api.ts
@@ -0,0 +1,129 @@
1 + import * as SecureStore from 'expo-secure-store';
2 +
3 + const API_BASE_URL = 'http://192.168.3.76:3000';
4 + const TOKEN_KEY = 'sky-is-crying'; // Gary B.B Coleman fucking slaps
5 +
6 + export interface User {
7 + id: number;
8 + email: string;
9 + firstName: string;
10 + lastName?: string;
11 + }
12 +
13 + export interface AuthResponse {
14 + token: string;
15 + user: User;
16 + }
17 +
18 + class ApiClient {
19 + private async getStoredToken(): Promise<string | null> {
20 + try {
21 + return await SecureStore.getItemAsync(TOKEN_KEY);
22 + } catch (error) {
23 + console.error('Failed to get stored token:', error);
24 + return null;
25 + }
26 + }
27 +
28 + private async storeToken(token: string): Promise<void> {
29 + try {
30 + await SecureStore.setItemAsync(TOKEN_KEY, token);
31 + } catch (error) {
32 + console.error('Failed to store token:', error);
33 + throw error;
34 + }
35 + }
36 +
37 + private async removeToken(): Promise<void> {
38 + try {
39 + await SecureStore.deleteItemAsync(TOKEN_KEY);
40 + } catch (error) {
41 + console.error('Failed to remove token:', error);
42 + }
43 + }
44 +
45 + private async request<T>(
46 + endpoint: string,
47 + options: RequestInit = {}
48 + ): Promise<T> {
49 + const token = await this.getStoredToken();
50 +
51 + const config: RequestInit = {
52 + ...options,
53 + headers: {
54 + 'Content-Type': 'application/json',
55 + ...(token && { Authorization: `Bearer ${token}` }),
56 + ...options.headers,
57 + },
58 + };
59 +
60 + const response = await fetch(`${API_BASE_URL}${endpoint}`, config);
61 +
62 + if (!response.ok) {
63 + const error = await response.json().catch(() => ({ error: 'Network error' }));
64 + throw new Error(error.error || `HTTP ${response.status}`);
65 + }
66 +
67 + return response.json();
68 + }
69 +
70 + async login(email: string, password: string): Promise<AuthResponse> {
71 + const response = await this.request<AuthResponse>('/auth/login', {
72 + method: 'POST',
73 + body: JSON.stringify({ email, password }),
74 + });
75 +
76 + await this.storeToken(response.token);
77 + return response;
78 + }
79 +
80 + async signup(userData: {
81 + first_name: string;
82 + last_name?: string;
83 + email: string;
84 + password: string;
85 + }): Promise<AuthResponse> {
86 + const response = await this.request<AuthResponse>('/users', {
87 + method: 'POST',
88 + body: JSON.stringify(userData),
89 + });
90 +
91 + await this.storeToken(response.token);
92 + return response;
93 + }
94 +
95 + async forgotPassword(email: string): Promise<{ message: string; token?: string }> {
96 + return this.request('/auth/forgot-password', {
97 + method: 'POST',
98 + body: JSON.stringify({ email }),
99 + });
100 + }
101 +
102 + async resetPassword(token: string, password: string): Promise<{ message: string }> {
103 + return this.request('/auth/reset-password', {
104 + method: 'POST',
105 + body: JSON.stringify({ token, password }),
106 + });
107 + }
108 +
109 + async verifyToken(): Promise<{ valid: boolean; userId?: number; email?: string }> {
110 + try {
111 + return await this.request('/auth/verify');
112 + } catch (error) {
113 + // Token is invalid or expired
114 + await this.removeToken();
115 + throw error;
116 + }
117 + }
118 +
119 + async logout(): Promise<void> {
120 + await this.removeToken();
121 + }
122 +
123 + async getStoredAuthData(): Promise<{ token: string | null }> {
124 + const token = await this.getStoredToken();
125 + return { token };
126 + }
127 + }
128 +
129 + export const apiClient = new ApiClient();