eletrotupi / tcc/ frontend/lib/api/client.tsd706da942de64ad47412e67cb183d34e5e1fd05e
4.1 KB | 163 lines Raw
1
import * as SecureStore from 'expo-secure-store';
2
import {
3
  User,
4
  AuthResponse,
5
  VerifyTokenResponse,
6
  SignUpPayload,
7
  ResetPasswordRequestResponse,
8
  ResetPasswordResponse,
9
  UserUpdatePayload,
10
  UserUpdateResponse,
11
  MoodComponentPayload,
12
  MoodEntryPayload,
13
  PaginatedResponse
14
} from '@/lib/api/types';
15

16
const API_BASE_URL = process.env.EXPO_PUBLIC_API_BASE_URL;
17
const TOKEN_KEY = process.env.EXPO_PUBLIC_TOKEN_KEY; // Gary B.B Coleman fucking slaps
18

19
class ApiClient {
20
  private async getStoredToken(): Promise<string | null> {
21
    try {
22
      return await SecureStore.getItemAsync(TOKEN_KEY);
23
    } catch (error) {
24
      console.error('Failed to get stored token:', error);
25
      return null;
26
    }
27
  }
28

29
  private async storeToken(token: string): Promise<void> {
30
    try {
31
      await SecureStore.setItemAsync(TOKEN_KEY, token);
32
    } catch (error) {
33
      console.error('Failed to store token:', error);
34
      throw error;
35
    }
36
  }
37

38
  private async removeToken(): Promise<void> {
39
    try {
40
      await SecureStore.deleteItemAsync(TOKEN_KEY);
41
    } catch (error) {
42
      console.error('Failed to remove token:', error);
43
    }
44
  }
45

46
  async getStoredAuthData(): Promise<{ token: string | null }> {
47
    const token = await this.getStoredToken();
48
    return { token };
49
  }
50

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

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

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

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

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

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

78
    return response.json();
79
  }
80

81
  // Auth
82
  async login(email: string, password: string): Promise<AuthResponse> {
83
    const response = await this.request<AuthResponse>('/auth/login', {
84
      method: 'POST',
85
      body: JSON.stringify({ email, password }),
86
    });
87

88
    await this.storeToken(response.token);
89
    return response;
90
  }
91

92
  async signup(data: SignUpPayload): Promise<AuthResponse> {
93
    const response = await this.request<AuthResponse>('/users', {
94
      method: 'POST',
95
      body: JSON.stringify(data),
96
    });
97

98
    await this.storeToken(response.token);
99
    return response;
100
  }
101

102
  async forgotPassword(email: string): Promise<ResetPasswordRequestResponse> {
103
    return this.request('/auth/forgot-password', {
104
      method: 'POST',
105
      body: JSON.stringify({ email }),
106
    });
107
  }
108

109
  async resetPassword(token: string, password: string): Promise<ResetPasswordResponse> {
110
    return this.request('/auth/reset-password', {
111
      method: 'POST',
112
      body: JSON.stringify({ token, newPassword: password }),
113
    });
114
  }
115

116
  async verifyToken(): Promise<VerifyTokenResponse> {
117
    try {
118
      return await this.request('/auth/verify');
119
    } catch (error) {
120
      // Token is invalid or expired
121
      await this.removeToken();
122
      throw error;
123
    }
124
  }
125

126
  async logout(): Promise<void> {
127
    await this.removeToken();
128
  }
129

130
  // User-related
131
  async updateUser(user: UserUpdatePayload): Promise<User> {
132
    return await this.request(`/users/${user.id}`, {
133
      method: 'PUT',
134
      body: JSON.stringify({ user })
135
    })
136
  }
137

138
  async avatar(formData: FormData): Promise<void> {
139
    return await this.request(`/users/me/avatar`, {
140
      method: 'POST',
141
      body: formData,
142
      headers: {
143
        'Content-Type': 'multipart/form-data',
144
      } as any,
145
    })
146
  }
147

148
  async removeAvatar(): Promise<void> {
149
    return await this.request(`/users/me/avatar`, {
150
      method: 'DELETE'
151
    });
152
  }
153

154
  // Mood-related
155
  async createMoodEntry(moodEntry): Promise<void> {
156
    return await this.request(`/moods`, {
157
      method: 'POST',
158
      body: JSON.stringify({ mood: moodEntry })
159
    })
160
  }
161
}
162

163
export const apiClient = new ApiClient();