afront 1.0.24 → 1.0.25
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/.babelrc +13 -13
- package/.env +1 -1
- package/LICENSE +21 -21
- package/README.md +94 -94
- package/build-prod/index.html +1 -1
- package/build-prod/manifest.json +25 -25
- package/build-prod/offline.html +1023 -1023
- package/build-prod/robots.txt +3 -3
- package/build-prod-static/index.html +1 -1
- package/build-prod-static/manifest.json +25 -25
- package/build-prod-static/offline.html +1023 -1023
- package/build-prod-static/robots.txt +3 -3
- package/install.js +415 -415
- package/package.json +102 -96
- package/server.js +40 -40
- package/src/ARoutes/AFRoutes.js +28 -28
- package/src/Api/api.config.js +266 -266
- package/src/Api/login.service.js +44 -44
- package/src/App.js +28 -28
- package/src/Components/Background/MeshGradient.js +18 -18
- package/src/Components/Footer/Footer.js +108 -108
- package/src/Components/Header/Header.js +149 -149
- package/src/Components/Loading/LoadingIndicator.js +12 -12
- package/src/Components/Loading/LoadingIndicator.module.css +34 -34
- package/src/Components/Loading/LoadingSpinner.js +27 -27
- package/src/Components/Loading/LoadingSpinner.module.css +100 -100
- package/src/Components/RequireAuth.js +29 -29
- package/src/LoadingFallback.js +13 -13
- package/src/PageNotFound.js +19 -19
- package/src/Pages/Home.js +50 -50
- package/src/Pages/Signup.js +230 -230
- package/src/Pages/Support.js +68 -68
- package/src/Routes/ARoutes.js +66 -66
- package/src/Routes/ARoutesStatic.js +83 -83
- package/src/Static/appStatic.js +16 -16
- package/src/Static/indexStatic.js +13 -13
- package/src/Style/App.module.css +11 -11
- package/src/Style/MeshGradient.module.css +130 -130
- package/src/Style/PageNotFound.module.css +37 -37
- package/src/Style/Style.module.css +686 -686
- package/src/Style/Support.module.css +185 -185
- package/src/Utils/LoadingContext.js +5 -5
- package/src/index.js +25 -25
- package/webpack.build-prod.js +141 -140
- package/webpack.dev.js +127 -127
- package/webpack.prod.js +148 -147
- package/webpack.ssr.prod.js +97 -97
- package/npm-shrinkwrap.json +0 -9641
package/src/Api/api.config.js
CHANGED
|
@@ -1,266 +1,266 @@
|
|
|
1
|
-
// Lightweight fetch-based API instance with interceptors and retry helper
|
|
2
|
-
const apiBase =
|
|
3
|
-
(typeof process !== "undefined" &&
|
|
4
|
-
process.env &&
|
|
5
|
-
process.env.REACT_APP_API_BASE) ||
|
|
6
|
-
(typeof window !== "undefined" &&
|
|
7
|
-
window.__env &&
|
|
8
|
-
window.__env.REACT_APP_API_BASE) ||
|
|
9
|
-
"http://localhost:4000/api/v1";
|
|
10
|
-
|
|
11
|
-
function isAbsoluteUrl(u) {
|
|
12
|
-
return /^https?:\/\//i.test(u);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function buildUrl(base, url, params) {
|
|
16
|
-
const full = isAbsoluteUrl(url) ? url : `${base}${url}`;
|
|
17
|
-
if (!params) return full;
|
|
18
|
-
const search = new URLSearchParams(params).toString();
|
|
19
|
-
return search ? `${full}${full.includes("?") ? "&" : "?"}${search}` : full;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function createInstance({
|
|
23
|
-
baseURL = apiBase,
|
|
24
|
-
timeout = 60000,
|
|
25
|
-
headers = { "Content-Type": "application/json" },
|
|
26
|
-
withCredentials = true,
|
|
27
|
-
} = {}) {
|
|
28
|
-
const requestHandlers = [];
|
|
29
|
-
const responseHandlers = [];
|
|
30
|
-
|
|
31
|
-
const interceptors = {
|
|
32
|
-
request: {
|
|
33
|
-
use: (fn) => {
|
|
34
|
-
requestHandlers.push(fn);
|
|
35
|
-
return requestHandlers.length - 1;
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
response: {
|
|
39
|
-
use: (onFulfilled, onRejected) => {
|
|
40
|
-
responseHandlers.push({ onFulfilled, onRejected });
|
|
41
|
-
return responseHandlers.length - 1;
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
async function runRequestHandlers(cfg) {
|
|
47
|
-
let c = { ...cfg };
|
|
48
|
-
for (const h of requestHandlers) {
|
|
49
|
-
// handlers may be sync or async
|
|
50
|
-
// allow them to modify/return the config
|
|
51
|
-
// if they return undefined, assume they mutated in place
|
|
52
|
-
// otherwise use returned value
|
|
53
|
-
// eslint-disable-next-line no-await-in-loop
|
|
54
|
-
const out = await h(c);
|
|
55
|
-
if (typeof out !== "undefined") c = out;
|
|
56
|
-
}
|
|
57
|
-
return c;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function runResponseHandlers(resOrErr, success = true) {
|
|
61
|
-
let acc = resOrErr;
|
|
62
|
-
for (const h of responseHandlers) {
|
|
63
|
-
try {
|
|
64
|
-
if (success && typeof h.onFulfilled === "function") {
|
|
65
|
-
// eslint-disable-next-line no-await-in-loop
|
|
66
|
-
const out = await h.onFulfilled(acc);
|
|
67
|
-
if (typeof out !== "undefined") acc = out;
|
|
68
|
-
} else if (!success && typeof h.onRejected === "function") {
|
|
69
|
-
// eslint-disable-next-line no-await-in-loop
|
|
70
|
-
const out = await h.onRejected(acc);
|
|
71
|
-
if (typeof out !== "undefined") acc = out;
|
|
72
|
-
}
|
|
73
|
-
} catch (e) {
|
|
74
|
-
// if a handler throws, treat as rejection
|
|
75
|
-
acc = e;
|
|
76
|
-
success = false;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
if (success) return acc;
|
|
80
|
-
throw acc;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async function request(cfg) {
|
|
84
|
-
const finalCfg = await runRequestHandlers({
|
|
85
|
-
baseURL,
|
|
86
|
-
timeout,
|
|
87
|
-
headers: { ...headers },
|
|
88
|
-
withCredentials,
|
|
89
|
-
...cfg,
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const url = buildUrl(
|
|
93
|
-
finalCfg.baseURL || baseURL,
|
|
94
|
-
finalCfg.url || finalCfg.path || "/",
|
|
95
|
-
finalCfg.params
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
const controller = new AbortController();
|
|
99
|
-
const to = finalCfg.timeout || timeout;
|
|
100
|
-
const timer = setTimeout(() => controller.abort(), to);
|
|
101
|
-
|
|
102
|
-
const fetchOpts = {
|
|
103
|
-
method: (finalCfg.method || "GET").toUpperCase(),
|
|
104
|
-
headers: finalCfg.headers || {},
|
|
105
|
-
signal: controller.signal,
|
|
106
|
-
};
|
|
107
|
-
if (finalCfg.withCredentials || finalCfg.withCredentials === undefined)
|
|
108
|
-
fetchOpts.credentials = "include";
|
|
109
|
-
if (finalCfg.body !== undefined && finalCfg.body !== null) {
|
|
110
|
-
fetchOpts.body = finalCfg.body;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
// log request for convenience
|
|
115
|
-
// allow user interceptors to already log as requested
|
|
116
|
-
const raw = await fetch(url, fetchOpts);
|
|
117
|
-
clearTimeout(timer);
|
|
118
|
-
|
|
119
|
-
// try to parse JSON, fallback to text
|
|
120
|
-
let data = null;
|
|
121
|
-
const ct = raw.headers.get("content-type") || "";
|
|
122
|
-
if (ct.includes("application/json")) {
|
|
123
|
-
try {
|
|
124
|
-
data = await raw.json();
|
|
125
|
-
} catch (e) {
|
|
126
|
-
data = null;
|
|
127
|
-
}
|
|
128
|
-
} else {
|
|
129
|
-
try {
|
|
130
|
-
data = await raw.text();
|
|
131
|
-
} catch (e) {
|
|
132
|
-
data = null;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const response = { ok: raw.ok, status: raw.status, data, raw };
|
|
137
|
-
return await runResponseHandlers(response, true);
|
|
138
|
-
} catch (err) {
|
|
139
|
-
clearTimeout(timer);
|
|
140
|
-
// network or abort
|
|
141
|
-
return await runResponseHandlers(err, false);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// convenience methods
|
|
146
|
-
const instance = {
|
|
147
|
-
interceptors,
|
|
148
|
-
request,
|
|
149
|
-
get: (url, cfg = {}) => request({ method: "GET", url, ...cfg }),
|
|
150
|
-
post: (url, body, cfg = {}) =>
|
|
151
|
-
request({
|
|
152
|
-
method: "POST",
|
|
153
|
-
url,
|
|
154
|
-
body:
|
|
155
|
-
typeof body === "object" && !(body instanceof FormData)
|
|
156
|
-
? JSON.stringify(body)
|
|
157
|
-
: body,
|
|
158
|
-
headers: {
|
|
159
|
-
...(cfg.headers || {}),
|
|
160
|
-
"Content-Type":
|
|
161
|
-
body instanceof FormData ? undefined : "application/json",
|
|
162
|
-
},
|
|
163
|
-
...cfg,
|
|
164
|
-
}),
|
|
165
|
-
put: (url, body, cfg = {}) =>
|
|
166
|
-
request({
|
|
167
|
-
method: "PUT",
|
|
168
|
-
url,
|
|
169
|
-
body:
|
|
170
|
-
typeof body === "object" && !(body instanceof FormData)
|
|
171
|
-
? JSON.stringify(body)
|
|
172
|
-
: body,
|
|
173
|
-
headers: {
|
|
174
|
-
...(cfg.headers || {}),
|
|
175
|
-
"Content-Type":
|
|
176
|
-
body instanceof FormData ? undefined : "application/json",
|
|
177
|
-
},
|
|
178
|
-
...cfg,
|
|
179
|
-
}),
|
|
180
|
-
patch: (url, body, cfg = {}) =>
|
|
181
|
-
request({
|
|
182
|
-
method: "PATCH",
|
|
183
|
-
url,
|
|
184
|
-
body:
|
|
185
|
-
typeof body === "object" && !(body instanceof FormData)
|
|
186
|
-
? JSON.stringify(body)
|
|
187
|
-
: body,
|
|
188
|
-
headers: {
|
|
189
|
-
...(cfg.headers || {}),
|
|
190
|
-
"Content-Type":
|
|
191
|
-
body instanceof FormData ? undefined : "application/json",
|
|
192
|
-
},
|
|
193
|
-
...cfg,
|
|
194
|
-
}),
|
|
195
|
-
delete: (url, cfg = {}) => request({ method: "DELETE", url, ...cfg }),
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
return instance;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export function requestWithRetry(
|
|
202
|
-
fn,
|
|
203
|
-
{ retries = 3, retryDelay = 1000, retryOn = (err) => true } = {}
|
|
204
|
-
) {
|
|
205
|
-
return new Promise(async (resolve, reject) => {
|
|
206
|
-
let attempt = 0;
|
|
207
|
-
while (attempt < retries) {
|
|
208
|
-
try {
|
|
209
|
-
// fn should be a function returning a promise
|
|
210
|
-
const res = await fn();
|
|
211
|
-
return resolve(res);
|
|
212
|
-
} catch (err) {
|
|
213
|
-
attempt += 1;
|
|
214
|
-
if (attempt >= retries || !retryOn(err)) return reject(err);
|
|
215
|
-
// exponential backoff
|
|
216
|
-
// eslint-disable-next-line no-await-in-loop
|
|
217
|
-
await new Promise((r) => setTimeout(r, retryDelay * attempt));
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
reject(new Error("Retries exhausted"));
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export const instance = createInstance({
|
|
225
|
-
baseURL: apiBase,
|
|
226
|
-
timeout: 60000,
|
|
227
|
-
headers: { "Content-Type": "application/json" },
|
|
228
|
-
withCredentials: true,
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
// expose defaults for logging/compatibility
|
|
232
|
-
instance.defaults = { baseURL: apiBase, timeout: 60000 };
|
|
233
|
-
|
|
234
|
-
// request logging interceptor
|
|
235
|
-
instance.interceptors.request.use((config) => {
|
|
236
|
-
try {
|
|
237
|
-
console.log(
|
|
238
|
-
`API Request -> ${(
|
|
239
|
-
(config.method || "").toString() || "GET"
|
|
240
|
-
).toUpperCase()} ${config.url || config.path} (base: ${
|
|
241
|
-
instance.defaults.baseURL
|
|
242
|
-
}) (timeout: ${config.timeout || instance.defaults.timeout}ms)`
|
|
243
|
-
);
|
|
244
|
-
} catch (e) {
|
|
245
|
-
/* ignore */
|
|
246
|
-
}
|
|
247
|
-
return config;
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// response / error logging interceptor
|
|
251
|
-
instance.interceptors.response.use(
|
|
252
|
-
(response) => response,
|
|
253
|
-
(error) => {
|
|
254
|
-
if (!error || !error.status) {
|
|
255
|
-
console.error(
|
|
256
|
-
"Network or CORS error when calling API:",
|
|
257
|
-
error && error.message ? error.message : error
|
|
258
|
-
);
|
|
259
|
-
} else {
|
|
260
|
-
console.warn("API response error:", error.status, error.data);
|
|
261
|
-
}
|
|
262
|
-
return Promise.reject(error);
|
|
263
|
-
}
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
export default instance;
|
|
1
|
+
// Lightweight fetch-based API instance with interceptors and retry helper
|
|
2
|
+
const apiBase =
|
|
3
|
+
(typeof process !== "undefined" &&
|
|
4
|
+
process.env &&
|
|
5
|
+
process.env.REACT_APP_API_BASE) ||
|
|
6
|
+
(typeof window !== "undefined" &&
|
|
7
|
+
window.__env &&
|
|
8
|
+
window.__env.REACT_APP_API_BASE) ||
|
|
9
|
+
"http://localhost:4000/api/v1";
|
|
10
|
+
|
|
11
|
+
function isAbsoluteUrl(u) {
|
|
12
|
+
return /^https?:\/\//i.test(u);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function buildUrl(base, url, params) {
|
|
16
|
+
const full = isAbsoluteUrl(url) ? url : `${base}${url}`;
|
|
17
|
+
if (!params) return full;
|
|
18
|
+
const search = new URLSearchParams(params).toString();
|
|
19
|
+
return search ? `${full}${full.includes("?") ? "&" : "?"}${search}` : full;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function createInstance({
|
|
23
|
+
baseURL = apiBase,
|
|
24
|
+
timeout = 60000,
|
|
25
|
+
headers = { "Content-Type": "application/json" },
|
|
26
|
+
withCredentials = true,
|
|
27
|
+
} = {}) {
|
|
28
|
+
const requestHandlers = [];
|
|
29
|
+
const responseHandlers = [];
|
|
30
|
+
|
|
31
|
+
const interceptors = {
|
|
32
|
+
request: {
|
|
33
|
+
use: (fn) => {
|
|
34
|
+
requestHandlers.push(fn);
|
|
35
|
+
return requestHandlers.length - 1;
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
response: {
|
|
39
|
+
use: (onFulfilled, onRejected) => {
|
|
40
|
+
responseHandlers.push({ onFulfilled, onRejected });
|
|
41
|
+
return responseHandlers.length - 1;
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
async function runRequestHandlers(cfg) {
|
|
47
|
+
let c = { ...cfg };
|
|
48
|
+
for (const h of requestHandlers) {
|
|
49
|
+
// handlers may be sync or async
|
|
50
|
+
// allow them to modify/return the config
|
|
51
|
+
// if they return undefined, assume they mutated in place
|
|
52
|
+
// otherwise use returned value
|
|
53
|
+
// eslint-disable-next-line no-await-in-loop
|
|
54
|
+
const out = await h(c);
|
|
55
|
+
if (typeof out !== "undefined") c = out;
|
|
56
|
+
}
|
|
57
|
+
return c;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function runResponseHandlers(resOrErr, success = true) {
|
|
61
|
+
let acc = resOrErr;
|
|
62
|
+
for (const h of responseHandlers) {
|
|
63
|
+
try {
|
|
64
|
+
if (success && typeof h.onFulfilled === "function") {
|
|
65
|
+
// eslint-disable-next-line no-await-in-loop
|
|
66
|
+
const out = await h.onFulfilled(acc);
|
|
67
|
+
if (typeof out !== "undefined") acc = out;
|
|
68
|
+
} else if (!success && typeof h.onRejected === "function") {
|
|
69
|
+
// eslint-disable-next-line no-await-in-loop
|
|
70
|
+
const out = await h.onRejected(acc);
|
|
71
|
+
if (typeof out !== "undefined") acc = out;
|
|
72
|
+
}
|
|
73
|
+
} catch (e) {
|
|
74
|
+
// if a handler throws, treat as rejection
|
|
75
|
+
acc = e;
|
|
76
|
+
success = false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (success) return acc;
|
|
80
|
+
throw acc;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function request(cfg) {
|
|
84
|
+
const finalCfg = await runRequestHandlers({
|
|
85
|
+
baseURL,
|
|
86
|
+
timeout,
|
|
87
|
+
headers: { ...headers },
|
|
88
|
+
withCredentials,
|
|
89
|
+
...cfg,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const url = buildUrl(
|
|
93
|
+
finalCfg.baseURL || baseURL,
|
|
94
|
+
finalCfg.url || finalCfg.path || "/",
|
|
95
|
+
finalCfg.params
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const controller = new AbortController();
|
|
99
|
+
const to = finalCfg.timeout || timeout;
|
|
100
|
+
const timer = setTimeout(() => controller.abort(), to);
|
|
101
|
+
|
|
102
|
+
const fetchOpts = {
|
|
103
|
+
method: (finalCfg.method || "GET").toUpperCase(),
|
|
104
|
+
headers: finalCfg.headers || {},
|
|
105
|
+
signal: controller.signal,
|
|
106
|
+
};
|
|
107
|
+
if (finalCfg.withCredentials || finalCfg.withCredentials === undefined)
|
|
108
|
+
fetchOpts.credentials = "include";
|
|
109
|
+
if (finalCfg.body !== undefined && finalCfg.body !== null) {
|
|
110
|
+
fetchOpts.body = finalCfg.body;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
// log request for convenience
|
|
115
|
+
// allow user interceptors to already log as requested
|
|
116
|
+
const raw = await fetch(url, fetchOpts);
|
|
117
|
+
clearTimeout(timer);
|
|
118
|
+
|
|
119
|
+
// try to parse JSON, fallback to text
|
|
120
|
+
let data = null;
|
|
121
|
+
const ct = raw.headers.get("content-type") || "";
|
|
122
|
+
if (ct.includes("application/json")) {
|
|
123
|
+
try {
|
|
124
|
+
data = await raw.json();
|
|
125
|
+
} catch (e) {
|
|
126
|
+
data = null;
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
try {
|
|
130
|
+
data = await raw.text();
|
|
131
|
+
} catch (e) {
|
|
132
|
+
data = null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const response = { ok: raw.ok, status: raw.status, data, raw };
|
|
137
|
+
return await runResponseHandlers(response, true);
|
|
138
|
+
} catch (err) {
|
|
139
|
+
clearTimeout(timer);
|
|
140
|
+
// network or abort
|
|
141
|
+
return await runResponseHandlers(err, false);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// convenience methods
|
|
146
|
+
const instance = {
|
|
147
|
+
interceptors,
|
|
148
|
+
request,
|
|
149
|
+
get: (url, cfg = {}) => request({ method: "GET", url, ...cfg }),
|
|
150
|
+
post: (url, body, cfg = {}) =>
|
|
151
|
+
request({
|
|
152
|
+
method: "POST",
|
|
153
|
+
url,
|
|
154
|
+
body:
|
|
155
|
+
typeof body === "object" && !(body instanceof FormData)
|
|
156
|
+
? JSON.stringify(body)
|
|
157
|
+
: body,
|
|
158
|
+
headers: {
|
|
159
|
+
...(cfg.headers || {}),
|
|
160
|
+
"Content-Type":
|
|
161
|
+
body instanceof FormData ? undefined : "application/json",
|
|
162
|
+
},
|
|
163
|
+
...cfg,
|
|
164
|
+
}),
|
|
165
|
+
put: (url, body, cfg = {}) =>
|
|
166
|
+
request({
|
|
167
|
+
method: "PUT",
|
|
168
|
+
url,
|
|
169
|
+
body:
|
|
170
|
+
typeof body === "object" && !(body instanceof FormData)
|
|
171
|
+
? JSON.stringify(body)
|
|
172
|
+
: body,
|
|
173
|
+
headers: {
|
|
174
|
+
...(cfg.headers || {}),
|
|
175
|
+
"Content-Type":
|
|
176
|
+
body instanceof FormData ? undefined : "application/json",
|
|
177
|
+
},
|
|
178
|
+
...cfg,
|
|
179
|
+
}),
|
|
180
|
+
patch: (url, body, cfg = {}) =>
|
|
181
|
+
request({
|
|
182
|
+
method: "PATCH",
|
|
183
|
+
url,
|
|
184
|
+
body:
|
|
185
|
+
typeof body === "object" && !(body instanceof FormData)
|
|
186
|
+
? JSON.stringify(body)
|
|
187
|
+
: body,
|
|
188
|
+
headers: {
|
|
189
|
+
...(cfg.headers || {}),
|
|
190
|
+
"Content-Type":
|
|
191
|
+
body instanceof FormData ? undefined : "application/json",
|
|
192
|
+
},
|
|
193
|
+
...cfg,
|
|
194
|
+
}),
|
|
195
|
+
delete: (url, cfg = {}) => request({ method: "DELETE", url, ...cfg }),
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
return instance;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function requestWithRetry(
|
|
202
|
+
fn,
|
|
203
|
+
{ retries = 3, retryDelay = 1000, retryOn = (err) => true } = {}
|
|
204
|
+
) {
|
|
205
|
+
return new Promise(async (resolve, reject) => {
|
|
206
|
+
let attempt = 0;
|
|
207
|
+
while (attempt < retries) {
|
|
208
|
+
try {
|
|
209
|
+
// fn should be a function returning a promise
|
|
210
|
+
const res = await fn();
|
|
211
|
+
return resolve(res);
|
|
212
|
+
} catch (err) {
|
|
213
|
+
attempt += 1;
|
|
214
|
+
if (attempt >= retries || !retryOn(err)) return reject(err);
|
|
215
|
+
// exponential backoff
|
|
216
|
+
// eslint-disable-next-line no-await-in-loop
|
|
217
|
+
await new Promise((r) => setTimeout(r, retryDelay * attempt));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
reject(new Error("Retries exhausted"));
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export const instance = createInstance({
|
|
225
|
+
baseURL: apiBase,
|
|
226
|
+
timeout: 60000,
|
|
227
|
+
headers: { "Content-Type": "application/json" },
|
|
228
|
+
withCredentials: true,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// expose defaults for logging/compatibility
|
|
232
|
+
instance.defaults = { baseURL: apiBase, timeout: 60000 };
|
|
233
|
+
|
|
234
|
+
// request logging interceptor
|
|
235
|
+
instance.interceptors.request.use((config) => {
|
|
236
|
+
try {
|
|
237
|
+
console.log(
|
|
238
|
+
`API Request -> ${(
|
|
239
|
+
(config.method || "").toString() || "GET"
|
|
240
|
+
).toUpperCase()} ${config.url || config.path} (base: ${
|
|
241
|
+
instance.defaults.baseURL
|
|
242
|
+
}) (timeout: ${config.timeout || instance.defaults.timeout}ms)`
|
|
243
|
+
);
|
|
244
|
+
} catch (e) {
|
|
245
|
+
/* ignore */
|
|
246
|
+
}
|
|
247
|
+
return config;
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// response / error logging interceptor
|
|
251
|
+
instance.interceptors.response.use(
|
|
252
|
+
(response) => response,
|
|
253
|
+
(error) => {
|
|
254
|
+
if (!error || !error.status) {
|
|
255
|
+
console.error(
|
|
256
|
+
"Network or CORS error when calling API:",
|
|
257
|
+
error && error.message ? error.message : error
|
|
258
|
+
);
|
|
259
|
+
} else {
|
|
260
|
+
console.warn("API response error:", error.status, error.data);
|
|
261
|
+
}
|
|
262
|
+
return Promise.reject(error);
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
export default instance;
|
package/src/Api/login.service.js
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
import api from "./api.config.js";
|
|
2
|
-
|
|
3
|
-
const BASE = "/auth";
|
|
4
|
-
|
|
5
|
-
export async function login({ email, password }) {
|
|
6
|
-
return api.post(
|
|
7
|
-
`${BASE}/login`,
|
|
8
|
-
{ email, password },
|
|
9
|
-
{ withCredentials: true }
|
|
10
|
-
);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export async function signup({
|
|
14
|
-
email,
|
|
15
|
-
password,
|
|
16
|
-
name,
|
|
17
|
-
accountType,
|
|
18
|
-
companyName,
|
|
19
|
-
}) {
|
|
20
|
-
return api.post(
|
|
21
|
-
`${BASE}/signup`,
|
|
22
|
-
{ email, password, name, accountType, companyName },
|
|
23
|
-
{ withCredentials: true }
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function checkSignup({ email }) {
|
|
28
|
-
return api.post(`${BASE}/check`, { email }, { withCredentials: true });
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export async function logout() {
|
|
32
|
-
try {
|
|
33
|
-
await api.post(`${BASE}/logout`, null, { withCredentials: true });
|
|
34
|
-
} catch (e) {
|
|
35
|
-
/* ignore */
|
|
36
|
-
}
|
|
37
|
-
try {
|
|
38
|
-
localStorage.setItem("auth-event", String(Date.now()));
|
|
39
|
-
} catch (e) {}
|
|
40
|
-
window.dispatchEvent(new Event("auth-changed"));
|
|
41
|
-
return { ok: true };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export default { login, signup, logout, checkSignup };
|
|
1
|
+
import api from "./api.config.js";
|
|
2
|
+
|
|
3
|
+
const BASE = "/auth";
|
|
4
|
+
|
|
5
|
+
export async function login({ email, password }) {
|
|
6
|
+
return api.post(
|
|
7
|
+
`${BASE}/login`,
|
|
8
|
+
{ email, password },
|
|
9
|
+
{ withCredentials: true }
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function signup({
|
|
14
|
+
email,
|
|
15
|
+
password,
|
|
16
|
+
name,
|
|
17
|
+
accountType,
|
|
18
|
+
companyName,
|
|
19
|
+
}) {
|
|
20
|
+
return api.post(
|
|
21
|
+
`${BASE}/signup`,
|
|
22
|
+
{ email, password, name, accountType, companyName },
|
|
23
|
+
{ withCredentials: true }
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function checkSignup({ email }) {
|
|
28
|
+
return api.post(`${BASE}/check`, { email }, { withCredentials: true });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function logout() {
|
|
32
|
+
try {
|
|
33
|
+
await api.post(`${BASE}/logout`, null, { withCredentials: true });
|
|
34
|
+
} catch (e) {
|
|
35
|
+
/* ignore */
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
localStorage.setItem("auth-event", String(Date.now()));
|
|
39
|
+
} catch (e) {}
|
|
40
|
+
window.dispatchEvent(new Event("auth-changed"));
|
|
41
|
+
return { ok: true };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default { login, signup, logout, checkSignup };
|
package/src/App.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
2
|
-
import ARoutes from "./Routes/ARoutes";
|
|
3
|
-
import LoadingContext from "./Utils/LoadingContext";
|
|
4
|
-
|
|
5
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
6
|
-
|
|
7
|
-
const Tail = () => (
|
|
8
|
-
<asggen
|
|
9
|
-
className="hidden"
|
|
10
|
-
dangerouslySetInnerHTML={{
|
|
11
|
-
__html: `Rendering Asggen DOM...</asggen></asggenapp></body></html>`,
|
|
12
|
-
}}
|
|
13
|
-
/>
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
function App({ context }) {
|
|
17
|
-
const [loading, setLoading] = useState(false);
|
|
18
|
-
return (
|
|
19
|
-
<>
|
|
20
|
-
<LoadingContext.Provider value={{ loading, setLoading }}>
|
|
21
|
-
{loading && <LoadingIndicator />}
|
|
22
|
-
<ARoutes context={context} />
|
|
23
|
-
{isProduction ? <Tail /> : null}
|
|
24
|
-
</LoadingContext.Provider>
|
|
25
|
-
</>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import ARoutes from "./Routes/ARoutes";
|
|
3
|
+
import LoadingContext from "./Utils/LoadingContext";
|
|
4
|
+
|
|
5
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
6
|
+
|
|
7
|
+
const Tail = () => (
|
|
8
|
+
<asggen
|
|
9
|
+
className="hidden"
|
|
10
|
+
dangerouslySetInnerHTML={{
|
|
11
|
+
__html: `Rendering Asggen DOM...</asggen></asggenapp></body></html>`,
|
|
12
|
+
}}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
function App({ context }) {
|
|
17
|
+
const [loading, setLoading] = useState(false);
|
|
18
|
+
return (
|
|
19
|
+
<>
|
|
20
|
+
<LoadingContext.Provider value={{ loading, setLoading }}>
|
|
21
|
+
{loading && <LoadingIndicator />}
|
|
22
|
+
<ARoutes context={context} />
|
|
23
|
+
{isProduction ? <Tail /> : null}
|
|
24
|
+
</LoadingContext.Provider>
|
|
25
|
+
</>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
29
|
export default App;
|