eletrotupi / tcc/ frontend/lib/api.ts17dd47ad86ed5f8c9bc9360a4eb3917a7a635c8d
4.2 KB | 171 lines Raw
1
import * as SecureStore from 'expo-secure-store';
2

3
const API_BASE_URL = process.env.EXPO_PUBLIC_API_BASE_URL;
4
const TOKEN_KEY = process.env.EXPO_PUBLIC_TOKEN_KEY; // Gary B.B Coleman fucking slaps
5

6
export interface User {
7
  id: number;
8
  email: string;
9
  firstName: string;
10
  lastName?: string;
11
  updatedAt: Date;
12
  avatarKey?: string;
13
  avatarURL?: string;
14
}
15

16
export interface AuthResponse {
17
  token: string;
18
  user: User;
19
}
20

21
export interface UserUpdateResponse {
22
  user: User
23
}
24

25
class ApiClient {
26
  private async getStoredToken(): Promise<string | null> {
27
    try {
28
      return await SecureStore.getItemAsync(TOKEN_KEY);
29
    } catch (error) {
30
      console.error('Failed to get stored token:', error);
31
      return null;
32
    }
33
  }
34

35
  private async storeToken(token: string): Promise<void> {
36
    try {
37
      await SecureStore.setItemAsync(TOKEN_KEY, token);
38
    } catch (error) {
39
      console.error('Failed to store token:', error);
40
      throw error;
41
    }
42
  }
43

44
  private async removeToken(): Promise<void> {
45
    try {
46
      await SecureStore.deleteItemAsync(TOKEN_KEY);
47
    } catch (error) {
48
      console.error('Failed to remove token:', error);
49
    }
50
  }
51

52
  private async request<T>(
53
    endpoint: string,
54
    options: RequestInit = {}
55
  ): Promise<T> {
56
    const token = await this.getStoredToken();
57
    const isFormData = options.body instanceof FormData;
58

59
    const config: RequestInit = {
60
      ...options,
61
      headers: {
62
        ...(token && { Authorization: `Bearer ${token}` }),
63
        ...(!isFormData && { 'Content-Type': 'application/json' }),
64
        ...options.headers,
65
      },
66
    };
67

68
    const response = await fetch(`${API_BASE_URL}${endpoint}`, config);
69

70
    if (!response.ok) {
71
      const error = await response.json()
72
        .catch(() => ({ error: 'Network error' }));
73

74
      console.error("[API]: ", error, response.status)
75

76
      throw new Error(error.error || `HTTP ${response.status}`);
77
    }
78

79
    return response.json();
80
  }
81

82
  async avatar(formData: FormData): Promise<void> {
83
    return await this.request(`/users/me/avatar`, {
84
      method: 'POST',
85
      body: formData,
86
      headers: {
87
        'Content-Type': 'multipart/form-data',
88
      } as any,
89
    })
90
  }
91

92
  async removeAvatar(): Promise<void> {
93
    return await this.request(`/users/me/avatar`, {
94
      method: 'DELETE'
95
    });
96
  }
97

98
  async login(email: string, password: string): Promise<AuthResponse> {
99
    const response = await this.request<AuthResponse>('/auth/login', {
100
      method: 'POST',
101
      body: JSON.stringify({ email, password }),
102
    });
103

104
    await this.storeToken(response.token);
105
    return response;
106
  }
107

108
  async signup(userData: {
109
    first_name: string;
110
    last_name?: string;
111
    email: string;
112
    password: string;
113
  }): Promise<AuthResponse> {
114
    const response = await this.request<AuthResponse>('/users', {
115
      method: 'POST',
116
      body: JSON.stringify(userData),
117
    });
118

119
    await this.storeToken(response.token);
120
    return response;
121
  }
122

123
  async forgotPassword(email: string): Promise<{ message: string; token?: string }> {
124
    return this.request('/auth/forgot-password', {
125
      method: 'POST',
126
      body: JSON.stringify({ email }),
127
    });
128
  }
129

130
  async resetPassword(token: string, password: string): Promise<{ message: string }> {
131
    return this.request('/auth/reset-password', {
132
      method: 'POST',
133
      body: JSON.stringify({ token, newPassword: password }),
134
    });
135
  }
136

137
  async verifyToken(): Promise<{ valid: boolean; userId?: number; email?: string }> {
138
    try {
139
      return await this.request('/auth/verify');
140
    } catch (error) {
141
      // Token is invalid or expired
142
      await this.removeToken();
143
      throw error;
144
    }
145
  }
146

147
  async logout(): Promise<void> {
148
    await this.removeToken();
149
  }
150

151
  async getStoredAuthData(): Promise<{ token: string | null }> {
152
    const token = await this.getStoredToken();
153
    return { token };
154
  }
155

156
  async updateUser(user): Promise<void> {
157
    return await this.request(`/users/${user.id}`, {
158
      method: 'PUT',
159
      body: JSON.stringify({ user })
160
    })
161
  }
162

163
  async createMoodEntry(moodEntry): Promise<void> {
164
    return await this.request(`/moods`, {
165
      method: 'POST',
166
      body: JSON.stringify({ mood: moodEntry })
167
    })
168
  }
169
}
170

171
export const apiClient = new ApiClient();