-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapi.ts
More file actions
120 lines (104 loc) · 3.89 KB
/
api.ts
File metadata and controls
120 lines (104 loc) · 3.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig } from "axios";
import Toast from 'react-native-toast-message';
import * as SecureStore from "expo-secure-store";
import { resetToAuth } from "./lib/navigationRef";
enum AuthenticationErrorType {
NoToken = "NoToken",
TokenInvalid = "TokenInvalid",
TokenExpired = "TokenExpired",
}
class API {
public axiosInstance: AxiosInstance;
constructor() {
this.axiosInstance = axios.create({
baseURL: "https://adonix.hackillinois.org", // TODO: change to env after we decide env naming
timeout: 10000, // 10s
headers: { "Content-Type": "application/json" },
});
this.axiosInstance.interceptors.request.use(
async (config) => {
const jwt = await SecureStore.getItemAsync("jwt");
if (jwt) {
const cleaned = jwt.replace(/#$/, '');
config.headers.Authorization = /^Bearer\s/i.test(cleaned)
? cleaned
: `Bearer ${cleaned}`;
}
return config;
},
(error) => Promise.reject(error)
);
this.axiosInstance.interceptors.response.use(
(response) => response,
async (error: AxiosError) => {
if (error.response) {
const data = error.response.data as any;
if (
(data.error && data.error === AuthenticationErrorType.NoToken) ||
data.error === AuthenticationErrorType.TokenInvalid ||
data.error === AuthenticationErrorType.TokenExpired
) {
const isGuest = await SecureStore.getItemAsync("isGuest");
if (isGuest !== "true") {
void this.redirectToSignIn();
}
} else {
this.handleError(error.response.status, data?.error, data?.message);
}
} else {
// Handle cases like network timeout/no internet
Toast.show({
type: 'error',
text1: 'Network Error',
text2: 'Please check your internet connection',
});
}
return Promise.reject(error);
}
);
}
async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
return this.axiosInstance.get(url, config);
}
async post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return this.axiosInstance.post(url, data, config);
}
async put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
return this.axiosInstance.put(url, data, config);
}
async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
return this.axiosInstance.delete(url, config);
}
public async redirectToSignIn(): Promise<void> {
// TODO: Implement actual navigation redirect
console.log("REDIRECT TO SIGN IN PAGE");
await SecureStore.deleteItemAsync("jwt"); // erase jwt
resetToAuth();
}
private handleError(status: number, errorType?: string, message?: string): void {
let error_message: string;
if (message && errorType) {
error_message = `${errorType}: ${message}`;
} else {
switch (status) {
case 400: error_message = "Bad Request: The request was invalid"; break;
case 401: error_message = "Unauthorized: Invalid authentication"; break;
case 403: error_message = "Forbidden: Access denied"; break;
case 404: error_message = "Not Found: Resource doesn't exist"; break;
case 429: error_message = "Too Many Requests: Rate limit exceeded"; break;
case 500: error_message = "Internal Server Error: Server is down"; break;
default: error_message = `HTTP Error: Received status code ${status}`; break;
}
}
// Trigger the global toast
Toast.show({
type: 'error',
text1: 'API Error',
text2: error_message,
position: 'top', // Adjust to 'bottom' if you want to see if it hits your custom error
});
console.error(error_message);
}
}
const api = new API();
export default api;