@feardread/feature-factory 5.2.3 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.min.js +2 -2
- package/dist/index.esm.js +3 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/rollup.config.mjs +48 -35
- package/src/factory/api.js +41 -321
- package/src/factory/crud.js +177 -0
- package/src/factory/index.js +1 -1
- package/src/index.js +2 -0
package/package.json
CHANGED
package/rollup.config.mjs
CHANGED
|
@@ -4,40 +4,53 @@ import commonjs from "@rollup/plugin-commonjs";
|
|
|
4
4
|
import json from "@rollup/plugin-json";
|
|
5
5
|
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
|
|
6
6
|
|
|
7
|
+
// Shared globals map — used by the IIFE bundle to locate peer deps
|
|
8
|
+
// on the global scope instead of trying to require() them at runtime.
|
|
9
|
+
const globals = {
|
|
10
|
+
'react': 'React',
|
|
11
|
+
'react-dom': 'ReactDOM',
|
|
12
|
+
'react-native': 'ReactNative',
|
|
13
|
+
'react-redux': 'ReactRedux',
|
|
14
|
+
'@reduxjs/toolkit': 'ReduxToolkit',
|
|
15
|
+
'rsuite': 'RSuite',
|
|
16
|
+
'axios': 'axios',
|
|
17
|
+
'qs': 'Qs',
|
|
18
|
+
};
|
|
19
|
+
|
|
7
20
|
const jsconfig = [{
|
|
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
|
-
];
|
|
21
|
+
input: 'src/index.js',
|
|
22
|
+
output: [
|
|
23
|
+
{
|
|
24
|
+
file: 'dist/index.js',
|
|
25
|
+
format: 'cjs',
|
|
26
|
+
exports: 'named',
|
|
27
|
+
sourcemap: true,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
file: 'dist/index.esm.js',
|
|
31
|
+
format: 'esm',
|
|
32
|
+
exports: 'named',
|
|
33
|
+
sourcemap: true,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
file: 'dist/bundle.min.js',
|
|
37
|
+
format: 'iife',
|
|
38
|
+
name: 'FeatureFactory', // exposes the bundle as window.MyLib
|
|
39
|
+
globals, // tells Rollup which globals map to which peer dep
|
|
40
|
+
plugins: [terser()],
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
plugins: [
|
|
44
|
+
peerDepsExternal(), // marks all peerDependencies as external
|
|
45
|
+
resolve({
|
|
46
|
+
browser: true,
|
|
47
|
+
preferBuiltins: false,
|
|
48
|
+
}),
|
|
49
|
+
commonjs(),
|
|
50
|
+
json(),
|
|
51
|
+
terser(),
|
|
52
|
+
],
|
|
53
|
+
context: 'this',
|
|
54
|
+
}];
|
|
42
55
|
|
|
43
|
-
export default jsconfig;
|
|
56
|
+
export default jsconfig;
|
package/src/factory/api.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import qs from 'qs';
|
|
3
|
+
import { Platform } from 'react-native';
|
|
3
4
|
import CacheFactory from './cache';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -12,20 +13,14 @@ const CONFIG = {
|
|
|
12
13
|
baseUrls: {
|
|
13
14
|
production: process.env.API_BASE_URL_PROD || 'https://fear.dedyn.io/fear/api/',
|
|
14
15
|
development: process.env.API_BASE_URL_DEV || 'http://localhost:4000/fear/api/',
|
|
15
|
-
test: process.env.API_BASE_URL_TEST || 'https://fear.
|
|
16
|
-
},
|
|
17
|
-
tokenNames: {
|
|
18
|
-
bearer: 'Authorization',
|
|
19
|
-
custom: process.env.REACT_APP_JWT_TOKEN_HEADER || 'x-token',
|
|
16
|
+
test: process.env.API_BASE_URL_TEST || 'https://fear.dedyn.io/fear/api/',
|
|
20
17
|
},
|
|
21
18
|
cacheKeys: {
|
|
22
|
-
auth: 'auth',
|
|
23
|
-
refreshToken: 'refresh_token',
|
|
24
19
|
userPrefs: 'user_preferences',
|
|
25
20
|
},
|
|
26
|
-
timeout: parseInt(process.env.
|
|
27
|
-
retryAttempts: parseInt(process.env.
|
|
28
|
-
retryDelay: parseInt(process.env.
|
|
21
|
+
timeout: parseInt(process.env.API_TIMEOUT) || 30000,
|
|
22
|
+
retryAttempts: parseInt(process.env.API_RETRY_ATTEMPTS) || 3,
|
|
23
|
+
retryDelay: parseInt(process.env.API_RETRY_DELAY) || 1000,
|
|
29
24
|
};
|
|
30
25
|
|
|
31
26
|
const setBaseUrl = (uri) => {
|
|
@@ -39,86 +34,10 @@ const setBaseUrl = (uri) => {
|
|
|
39
34
|
*/
|
|
40
35
|
const getBaseUrl = () => {
|
|
41
36
|
const env = process.env.NODE_ENV || 'development';
|
|
42
|
-
if (!CONFIG.BASE_URL) CONFIG.BASE_URL = CONFIG.baseUrls[env] || CONFIG.baseUrls.development
|
|
37
|
+
if (!CONFIG.BASE_URL) CONFIG.BASE_URL = CONFIG.baseUrls[env] || CONFIG.baseUrls.development;
|
|
43
38
|
return CONFIG.BASE_URL;
|
|
44
39
|
};
|
|
45
40
|
|
|
46
|
-
/**
|
|
47
|
-
* Gets authentication data from cache
|
|
48
|
-
* @returns {Object|null} Auth data or null
|
|
49
|
-
*/
|
|
50
|
-
const getAuthData = () => {
|
|
51
|
-
try {
|
|
52
|
-
const authData = CacheFactory.local.get(CONFIG.cacheKeys.auth);
|
|
53
|
-
return authData && typeof authData === 'object' ? authData : null;
|
|
54
|
-
} catch (error) {
|
|
55
|
-
console.warn('Failed to retrieve auth data:', error);
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Checks if token is expired
|
|
62
|
-
* @param {string} token - JWT token
|
|
63
|
-
* @returns {boolean} Whether token is expired
|
|
64
|
-
*/
|
|
65
|
-
const isTokenExpired = (token) => {
|
|
66
|
-
if (!token) return true;
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const payload = JSON.parse(atob(token.split('.')[1]));
|
|
70
|
-
const currentTime = Date.now() / 1000;
|
|
71
|
-
return payload.exp < currentTime;
|
|
72
|
-
} catch (error) {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Refreshes the authentication token
|
|
79
|
-
* @returns {Promise<string|null>} New token or null if refresh failed
|
|
80
|
-
*/
|
|
81
|
-
const refreshAuthToken = async () => {
|
|
82
|
-
try {
|
|
83
|
-
const authData = getAuthData();
|
|
84
|
-
const refreshToken = authData?.refreshToken || CacheFactory.local.get(CONFIG.cacheKeys.refreshToken);
|
|
85
|
-
|
|
86
|
-
if (!refreshToken) {
|
|
87
|
-
throw new Error('No refresh token available');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const response = await axios.post(`${getBaseUrl()}auth/refresh`, {
|
|
91
|
-
refreshToken,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
const newAuthData = {
|
|
95
|
-
token: response.data.token,
|
|
96
|
-
refreshToken: response.data.refreshToken || refreshToken,
|
|
97
|
-
user: response.data.user,
|
|
98
|
-
expiresAt: response.data.expiresAt,
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
CacheFactory.local.set(CONFIG.cacheKeys.auth, newAuthData);
|
|
102
|
-
return newAuthData.token;
|
|
103
|
-
} catch (error) {
|
|
104
|
-
console.error('Token refresh failed:', error);
|
|
105
|
-
clearAuthData();
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Clears authentication data from cache
|
|
112
|
-
*/
|
|
113
|
-
const clearAuthData = () => {
|
|
114
|
-
try {
|
|
115
|
-
CacheFactory.local.remove(CONFIG.cacheKeys.auth);
|
|
116
|
-
CacheFactory.local.remove(CONFIG.cacheKeys.refreshToken);
|
|
117
|
-
} catch (error) {
|
|
118
|
-
console.warn('Failed to clear auth data:', error);
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
|
|
122
41
|
/**
|
|
123
42
|
* Creates retry delay with exponential backoff
|
|
124
43
|
* @param {number} attempt - Current attempt number
|
|
@@ -143,7 +62,6 @@ const formatError = (error) => {
|
|
|
143
62
|
};
|
|
144
63
|
|
|
145
64
|
if (error.response) {
|
|
146
|
-
// Server responded with error status
|
|
147
65
|
return {
|
|
148
66
|
...baseError,
|
|
149
67
|
message: error.response.data?.message || `HTTP Error ${error.response.status}`,
|
|
@@ -152,17 +70,15 @@ const formatError = (error) => {
|
|
|
152
70
|
data: error.response.data,
|
|
153
71
|
};
|
|
154
72
|
}
|
|
155
|
-
|
|
73
|
+
|
|
156
74
|
if (error.request) {
|
|
157
|
-
// Request made but no response received
|
|
158
75
|
return {
|
|
159
76
|
...baseError,
|
|
160
77
|
message: 'Network error - no response received',
|
|
161
78
|
code: 'NETWORK_ERROR',
|
|
162
79
|
};
|
|
163
80
|
}
|
|
164
|
-
|
|
165
|
-
// Something else happened
|
|
81
|
+
|
|
166
82
|
return {
|
|
167
83
|
...baseError,
|
|
168
84
|
message: error.message || 'Request configuration error',
|
|
@@ -171,37 +87,18 @@ const formatError = (error) => {
|
|
|
171
87
|
};
|
|
172
88
|
|
|
173
89
|
/**
|
|
174
|
-
* Request interceptor
|
|
90
|
+
* Request interceptor — adds tracking headers
|
|
175
91
|
* @param {Object} config - Axios config
|
|
176
92
|
* @returns {Promise<Object>} Modified config
|
|
177
93
|
*/
|
|
178
94
|
const requestInterceptor = async (config) => {
|
|
179
95
|
try {
|
|
180
|
-
// Set retry metadata
|
|
181
96
|
config.metadata = { startTime: Date.now() };
|
|
182
97
|
config._retry = config._retry || 0;
|
|
183
98
|
|
|
184
|
-
// Get authentication data
|
|
185
|
-
const authData = getAuthData();
|
|
186
|
-
let token = authData?.token;
|
|
187
|
-
|
|
188
|
-
// Check if token needs refresh
|
|
189
|
-
if (token && isTokenExpired(token) && config._retry === 0) {
|
|
190
|
-
token = await refreshAuthToken();
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Set authorization headers
|
|
194
|
-
if (token) {
|
|
195
|
-
config.headers[CONFIG.tokenNames.bearer] = `Bearer ${token}`;
|
|
196
|
-
config.headers[CONFIG.tokenNames.custom] = token;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Add request ID for tracking
|
|
200
99
|
config.headers['X-Request-ID'] = `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
201
|
-
|
|
202
|
-
//
|
|
203
|
-
config.headers['X-Client-Version'] = process.env.REACT_APP_VERSION || '1.0.0';
|
|
204
|
-
config.headers['X-Client-Platform'] = 'web';
|
|
100
|
+
config.headers['X-Client-Version'] = process.env.API_VERSION || '1.0.0';
|
|
101
|
+
config.headers['X-Client-Platform'] = Platform.OS; // 'ios' | 'android' | 'web'
|
|
205
102
|
|
|
206
103
|
return config;
|
|
207
104
|
} catch (error) {
|
|
@@ -226,13 +123,11 @@ const requestErrorInterceptor = (error) => {
|
|
|
226
123
|
* @returns {Object} Processed response
|
|
227
124
|
*/
|
|
228
125
|
const responseInterceptor = (response) => {
|
|
229
|
-
// Add response metadata
|
|
230
126
|
response.metadata = {
|
|
231
127
|
responseTime: Date.now() - (response.config.metadata?.startTime || Date.now()),
|
|
232
128
|
requestId: response.config.headers['X-Request-ID'],
|
|
233
129
|
};
|
|
234
130
|
|
|
235
|
-
// Log successful responses in development
|
|
236
131
|
if (CONFIG.environment === 'development') {
|
|
237
132
|
console.log(`✅ API Success [${response.status}]:`, {
|
|
238
133
|
url: response.config.url,
|
|
@@ -243,20 +138,16 @@ const responseInterceptor = (response) => {
|
|
|
243
138
|
});
|
|
244
139
|
}
|
|
245
140
|
|
|
246
|
-
// Validate response structure
|
|
247
141
|
if (response.data && typeof response.data === 'object') {
|
|
248
|
-
// Handle different success status codes
|
|
249
142
|
if ([200, 201, 202, 204].includes(response.status)) {
|
|
250
143
|
return response;
|
|
251
144
|
}
|
|
252
145
|
}
|
|
253
146
|
|
|
254
|
-
// Handle edge cases
|
|
255
147
|
if (response.status >= 200 && response.status < 300) {
|
|
256
148
|
return response;
|
|
257
149
|
}
|
|
258
150
|
|
|
259
|
-
// Unexpected status code
|
|
260
151
|
return Promise.reject(formatError({
|
|
261
152
|
response,
|
|
262
153
|
message: `Unexpected response status: ${response.status}`,
|
|
@@ -264,17 +155,14 @@ const responseInterceptor = (response) => {
|
|
|
264
155
|
};
|
|
265
156
|
|
|
266
157
|
/**
|
|
267
|
-
* Response error interceptor with retry
|
|
158
|
+
* Response error interceptor with retry logic
|
|
268
159
|
* @param {Object} error - Response error
|
|
269
160
|
* @returns {Promise<Object>} Retry attempt or rejected promise
|
|
270
161
|
*/
|
|
271
162
|
const responseErrorInterceptor = async (error) => {
|
|
272
163
|
const originalRequest = error.config;
|
|
273
|
-
|
|
274
|
-
// Format error for consistent handling
|
|
275
164
|
const formattedError = formatError(error);
|
|
276
|
-
|
|
277
|
-
// Log errors in development
|
|
165
|
+
|
|
278
166
|
if (CONFIG.environment === 'development') {
|
|
279
167
|
console.error(`❌ API Error [${formattedError.status}]:`, {
|
|
280
168
|
url: originalRequest?.url,
|
|
@@ -285,48 +173,19 @@ const responseErrorInterceptor = async (error) => {
|
|
|
285
173
|
});
|
|
286
174
|
}
|
|
287
175
|
|
|
288
|
-
// Handle
|
|
289
|
-
if (formattedError.status === 401 && !originalRequest._isRetryingAuth) {
|
|
290
|
-
originalRequest._isRetryingAuth = true;
|
|
291
|
-
|
|
292
|
-
try {
|
|
293
|
-
const newToken = await refreshAuthToken();
|
|
294
|
-
if (newToken) {
|
|
295
|
-
// Retry original request with new token
|
|
296
|
-
originalRequest.headers[CONFIG.tokenNames.bearer] = `Bearer ${newToken}`;
|
|
297
|
-
originalRequest.headers[CONFIG.tokenNames.custom] = newToken;
|
|
298
|
-
return apiInstance(originalRequest);
|
|
299
|
-
}
|
|
300
|
-
} catch (refreshError) {
|
|
301
|
-
console.error('Auth refresh failed:', refreshError);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Clear auth data and redirect to login
|
|
305
|
-
clearAuthData();
|
|
306
|
-
|
|
307
|
-
// Dispatch auth failure event
|
|
308
|
-
if (typeof window !== 'undefined') {
|
|
309
|
-
window.dispatchEvent(new CustomEvent('auth:failure', {
|
|
310
|
-
detail: { error: formattedError }
|
|
311
|
-
}));
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Handle retryable errors
|
|
176
|
+
// Handle retryable errors with exponential backoff
|
|
316
177
|
const isRetryable = [408, 429, 500, 502, 503, 504].includes(formattedError.status) ||
|
|
317
|
-
|
|
318
|
-
|
|
178
|
+
formattedError.code === 'NETWORK_ERROR';
|
|
179
|
+
|
|
319
180
|
if (isRetryable && originalRequest._retry < CONFIG.retryAttempts) {
|
|
320
181
|
originalRequest._retry += 1;
|
|
321
|
-
|
|
322
182
|
const delay = getRetryDelay(originalRequest._retry);
|
|
323
183
|
console.log(`⏳ Retrying request (${originalRequest._retry}/${CONFIG.retryAttempts}) in ${delay}ms`);
|
|
324
|
-
|
|
325
184
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
326
185
|
return apiInstance(originalRequest);
|
|
327
186
|
}
|
|
328
187
|
|
|
329
|
-
//
|
|
188
|
+
// Attach retry-after duration when rate-limited
|
|
330
189
|
if (formattedError.status === 429) {
|
|
331
190
|
const retryAfter = error.response?.headers['retry-after'];
|
|
332
191
|
if (retryAfter) {
|
|
@@ -349,26 +208,24 @@ const createApiInstance = () => {
|
|
|
349
208
|
'Content-Type': 'application/json',
|
|
350
209
|
},
|
|
351
210
|
paramsSerializer: {
|
|
352
|
-
serialize: (params) => qs.stringify(params, {
|
|
211
|
+
serialize: (params) => qs.stringify(params, {
|
|
353
212
|
indices: false,
|
|
354
213
|
skipNulls: true,
|
|
355
214
|
arrayFormat: 'brackets',
|
|
356
215
|
}),
|
|
357
216
|
},
|
|
358
|
-
withCredentials
|
|
359
|
-
// Disable automatic JSON parsing for better error handling
|
|
217
|
+
// withCredentials intentionally omitted — cookies are not supported in React Native
|
|
360
218
|
transformResponse: [
|
|
361
219
|
(data) => {
|
|
362
220
|
try {
|
|
363
221
|
return JSON.parse(data);
|
|
364
|
-
} catch
|
|
222
|
+
} catch {
|
|
365
223
|
return data;
|
|
366
224
|
}
|
|
367
|
-
}
|
|
225
|
+
},
|
|
368
226
|
],
|
|
369
227
|
});
|
|
370
228
|
|
|
371
|
-
// Add interceptors
|
|
372
229
|
instance.interceptors.request.use(requestInterceptor, requestErrorInterceptor);
|
|
373
230
|
instance.interceptors.response.use(responseInterceptor, responseErrorInterceptor);
|
|
374
231
|
|
|
@@ -384,17 +241,16 @@ const apiInstance = createApiInstance();
|
|
|
384
241
|
const API = {
|
|
385
242
|
// Main axios instance
|
|
386
243
|
instance: apiInstance,
|
|
387
|
-
|
|
244
|
+
|
|
388
245
|
// Direct access to axios methods
|
|
389
|
-
get:
|
|
390
|
-
post:
|
|
391
|
-
put:
|
|
392
|
-
patch:
|
|
393
|
-
delete:
|
|
394
|
-
head:
|
|
246
|
+
get: apiInstance.get.bind(apiInstance),
|
|
247
|
+
post: apiInstance.post.bind(apiInstance),
|
|
248
|
+
put: apiInstance.put.bind(apiInstance),
|
|
249
|
+
patch: apiInstance.patch.bind(apiInstance),
|
|
250
|
+
delete: apiInstance.delete.bind(apiInstance),
|
|
251
|
+
head: apiInstance.head.bind(apiInstance),
|
|
395
252
|
options: apiInstance.options.bind(apiInstance),
|
|
396
253
|
|
|
397
|
-
// Utility methods
|
|
398
254
|
/**
|
|
399
255
|
* Makes a request with custom configuration
|
|
400
256
|
* @param {Object} config - Request configuration
|
|
@@ -412,65 +268,11 @@ const API = {
|
|
|
412
268
|
...apiInstance.defaults,
|
|
413
269
|
...customConfig,
|
|
414
270
|
});
|
|
415
|
-
|
|
416
271
|
customInstance.interceptors.request.use(requestInterceptor, requestErrorInterceptor);
|
|
417
272
|
customInstance.interceptors.response.use(responseInterceptor, responseErrorInterceptor);
|
|
418
|
-
|
|
419
273
|
return customInstance;
|
|
420
274
|
},
|
|
421
275
|
|
|
422
|
-
/**
|
|
423
|
-
* Sets authentication token
|
|
424
|
-
* @param {string} token - Auth token
|
|
425
|
-
* @param {Object} userData - User data
|
|
426
|
-
* @returns {boolean} Success status
|
|
427
|
-
*/
|
|
428
|
-
setAuth: (token, userData = {}) => {
|
|
429
|
-
try {
|
|
430
|
-
const authData = {
|
|
431
|
-
token,
|
|
432
|
-
user: userData,
|
|
433
|
-
timestamp: Date.now(),
|
|
434
|
-
expiresAt: userData.expiresAt,
|
|
435
|
-
};
|
|
436
|
-
|
|
437
|
-
return CacheFactory.local.set(CONFIG.cacheKeys.auth, authData);
|
|
438
|
-
} catch (error) {
|
|
439
|
-
console.error('Failed to set auth:', error);
|
|
440
|
-
return false;
|
|
441
|
-
}
|
|
442
|
-
},
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* Clears authentication
|
|
446
|
-
* @returns {boolean} Success status
|
|
447
|
-
*/
|
|
448
|
-
clearAuth: () => {
|
|
449
|
-
try {
|
|
450
|
-
clearAuthData();
|
|
451
|
-
return true;
|
|
452
|
-
} catch (error) {
|
|
453
|
-
console.error('Failed to clear auth:', error);
|
|
454
|
-
return false;
|
|
455
|
-
}
|
|
456
|
-
},
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* Gets current auth status
|
|
460
|
-
* @returns {Object} Auth status
|
|
461
|
-
*/
|
|
462
|
-
getAuthStatus: () => {
|
|
463
|
-
const authData = getAuthData();
|
|
464
|
-
const isAuthenticated = !!(authData?.token && !isTokenExpired(authData.token));
|
|
465
|
-
|
|
466
|
-
return {
|
|
467
|
-
isAuthenticated,
|
|
468
|
-
user: authData?.user || null,
|
|
469
|
-
token: authData?.token || null,
|
|
470
|
-
expiresAt: authData?.expiresAt || null,
|
|
471
|
-
};
|
|
472
|
-
},
|
|
473
|
-
|
|
474
276
|
/**
|
|
475
277
|
* Configures global request defaults
|
|
476
278
|
* @param {Object} config - Configuration object
|
|
@@ -486,17 +288,9 @@ const API = {
|
|
|
486
288
|
healthCheck: async () => {
|
|
487
289
|
try {
|
|
488
290
|
const response = await apiInstance.get('health', { timeout: 5000 });
|
|
489
|
-
return {
|
|
490
|
-
status: 'healthy',
|
|
491
|
-
response: response.data,
|
|
492
|
-
timestamp: Date.now(),
|
|
493
|
-
};
|
|
291
|
+
return { status: 'healthy', response: response.data, timestamp: Date.now() };
|
|
494
292
|
} catch (error) {
|
|
495
|
-
return {
|
|
496
|
-
status: 'unhealthy',
|
|
497
|
-
error: formatError(error),
|
|
498
|
-
timestamp: Date.now(),
|
|
499
|
-
};
|
|
293
|
+
return { status: 'unhealthy', error: formatError(error), timestamp: Date.now() };
|
|
500
294
|
}
|
|
501
295
|
},
|
|
502
296
|
|
|
@@ -509,9 +303,7 @@ const API = {
|
|
|
509
303
|
*/
|
|
510
304
|
uploadFile: (url, formData, onProgress) => {
|
|
511
305
|
return apiInstance.post(url, formData, {
|
|
512
|
-
headers: {
|
|
513
|
-
'Content-Type': 'multipart/form-data',
|
|
514
|
-
},
|
|
306
|
+
headers: { 'Content-Type': 'multipart/form-data' },
|
|
515
307
|
onUploadProgress: (progressEvent) => {
|
|
516
308
|
if (onProgress && progressEvent.total) {
|
|
517
309
|
const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
|
@@ -522,96 +314,24 @@ const API = {
|
|
|
522
314
|
},
|
|
523
315
|
|
|
524
316
|
// Cache utility methods
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
*/
|
|
531
|
-
getCached: (key, defaultValue = null) => {
|
|
532
|
-
return CacheFactory.local.get(key, defaultValue);
|
|
533
|
-
},
|
|
317
|
+
getCached: (key, defaultValue = null) => CacheFactory.local.get(key, defaultValue),
|
|
318
|
+
setCached: (key, value) => CacheFactory.local.set(key, value),
|
|
319
|
+
removeCached: (key) => CacheFactory.local.remove(key),
|
|
320
|
+
clearCache: () => CacheFactory.local.clear(),
|
|
321
|
+
getCacheStats: () => CacheFactory.local.getStats(),
|
|
534
322
|
|
|
535
|
-
/**
|
|
536
|
-
* Sets cached data
|
|
537
|
-
* @param {string} key - Cache key
|
|
538
|
-
* @param {any} value - Value to cache
|
|
539
|
-
* @returns {boolean} Success status
|
|
540
|
-
*/
|
|
541
|
-
setCached: (key, value) => {
|
|
542
|
-
return CacheFactory.local.set(key, value);
|
|
543
|
-
},
|
|
544
|
-
|
|
545
|
-
/**
|
|
546
|
-
* Removes cached data
|
|
547
|
-
* @param {string} key - Cache key
|
|
548
|
-
* @returns {boolean} Success status
|
|
549
|
-
*/
|
|
550
|
-
removeCached: (key) => {
|
|
551
|
-
return CacheFactory.local.remove(key);
|
|
552
|
-
},
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* Clears all cached data
|
|
556
|
-
* @returns {boolean} Success status
|
|
557
|
-
*/
|
|
558
|
-
clearCache: () => {
|
|
559
|
-
return CacheFactory.local.clear();
|
|
560
|
-
},
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* Gets cache statistics
|
|
564
|
-
* @returns {Object} Cache stats
|
|
565
|
-
*/
|
|
566
|
-
getCacheStats: () => {
|
|
567
|
-
return CacheFactory.local.getStats();
|
|
568
|
-
},
|
|
569
|
-
|
|
570
|
-
/**
|
|
571
|
-
* Bulk cache operations
|
|
572
|
-
*/
|
|
323
|
+
/** Bulk cache operations */
|
|
573
324
|
cache: {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
* @returns {Object} Key-value pairs
|
|
578
|
-
*/
|
|
579
|
-
getMultiple: (keys) => {
|
|
580
|
-
return CacheFactory.local.bulk.get(keys);
|
|
581
|
-
},
|
|
582
|
-
|
|
583
|
-
/**
|
|
584
|
-
* Sets multiple cached items
|
|
585
|
-
* @param {Object} items - Key-value pairs
|
|
586
|
-
* @returns {Object} Success status for each key
|
|
587
|
-
*/
|
|
588
|
-
setMultiple: (items) => {
|
|
589
|
-
return CacheFactory.local.bulk.set(items);
|
|
590
|
-
},
|
|
591
|
-
|
|
592
|
-
/**
|
|
593
|
-
* Removes multiple cached items
|
|
594
|
-
* @param {string[]} keys - Cache keys
|
|
595
|
-
* @returns {Object} Success status for each key
|
|
596
|
-
*/
|
|
597
|
-
removeMultiple: (keys) => {
|
|
598
|
-
return CacheFactory.local.bulk.remove(keys);
|
|
599
|
-
},
|
|
325
|
+
getMultiple: (keys) => CacheFactory.local.bulk.get(keys),
|
|
326
|
+
setMultiple: (items) => CacheFactory.local.bulk.set(items),
|
|
327
|
+
removeMultiple: (keys) => CacheFactory.local.bulk.remove(keys),
|
|
600
328
|
},
|
|
601
329
|
|
|
602
330
|
// Configuration access
|
|
603
331
|
config: CONFIG,
|
|
604
332
|
getBaseUrl,
|
|
605
|
-
setBaseUrl
|
|
333
|
+
setBaseUrl,
|
|
606
334
|
};
|
|
607
335
|
|
|
608
|
-
// Add event listener for auth failures (optional)
|
|
609
|
-
if (typeof window !== 'undefined') {
|
|
610
|
-
window.addEventListener('auth:failure', (event) => {
|
|
611
|
-
console.warn('Authentication failed:', event.detail.error);
|
|
612
|
-
// Could trigger a redirect to login page here
|
|
613
|
-
});
|
|
614
|
-
}
|
|
615
|
-
|
|
616
336
|
export { API };
|
|
617
337
|
export default API;
|