2** EPITECH PROJECT, 2024
10 * @brief Main file of the app
14import express, { Express, Request, Response } from 'express';
15// The body-parser library (to parse the body contained in the requests)
16import body_parser from 'body-parser';
17// The cors library (to allow the app to accept requests from other origins)
18import cors from 'cors';
20import { BuildResponse as build_response } from './modules/build_response';
21import { SpeakOnCorrectStatus as speak_on_correct_status } from './modules/speak_on_correct_status';
22import { CONSTANTS as CONST } from './modules/constants';
24// The arg_parser module
25import { Args } from './arg_parser'; // Assuming args have a type defined in `arg_parser.ts`
27import DB from './modules/db';
29import { OAuth } from './modules/oauth';
30import { Login } from './modules/login';
32import { Widgets } from './modules/widgets';
34// Load environment variables
35const env = process.env;
37const db_host = env?.DB_HOST || 'localhost';
38const db_user = env?.DB_USER || 'root';
39const db_password = env?.DB_PASSWORD || '';
40const db_name = env?.DB_DATABASE || 'dashboard';
41const db_port = Number(env?.DB_PORT || "3306");
43const database = new DB(
51// The app object representing the Express application
52const app: Express = express();
54var global_values: any = [];
58 methods: "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS",
59 allowedHeaders: "Content-Type,Authorization",
62app.use(cors(corsOptions));
64// Middleware to parse JSON
65app.use(body_parser.json());
68app.get('/', (req: Request, res: Response): void => {
69 console.log(`endpoint: get: ${req.url}`);
70 build_response.build_and_send_response(res, speak_on_correct_status.success, "/", "Hello, World!", "success", "", false);
73app.post('/', (req: Request, res: Response): void => {
74 console.log(`endpoint: post: ${req.url}`);
75 build_response.build_and_send_response(res, speak_on_correct_status.success, "/", "Hello, World!", "success", "", false);
78app.put('/', (req: Request, res: Response): void => {
79 console.log(`endpoint: put: ${req.url}`);
80 build_response.build_and_send_response(res, speak_on_correct_status.success, "/", "Hello, World!", "success", "", false);
83app.patch('/', (req: Request, res: Response): void => {
84 console.log(`endpoint: patch: ${req.url}`);
85 build_response.build_and_send_response(res, speak_on_correct_status.success, "/", "Hello, World!", "success", "", false);
88app.delete('/', (req: Request, res: Response): void => {
89 console.log(`endpoint: delete: ${req.url}`);
90 build_response.build_and_send_response(res, speak_on_correct_status.success, "/", "Hello, World!", "success", "", false);
93app.head('/', (req: Request, res: Response): void => {
94 console.log(`endpoint: head: ${req.url}`);
95 build_response.build_and_send_response(res, speak_on_correct_status.success, "/", "Hello, World!", "success", "", false);
98app.options('/', (req: Request, res: Response): void => {
99 console.log(`endpoint: options: ${req.url}`);
100 build_response.build_and_send_response(res, speak_on_correct_status.success, "/", "Hello, World!", "success", "", false);
103// Info route to return server address and port
104app.get('/info', (req: Request, res: Response): void => {
105 const title = "/info";
106 console.log(`endpoint: get: ${req.url}`);
107 const address = server?.address();
108 if (address && typeof address !== 'string') {
109 const host = address.address === '::' ? 'localhost' : address.address;
110 const port = address.port;
112 build_response.build_and_send_response(res, speak_on_correct_status.success, title, `The server is listening on host: ${host} at port: ${port}`, { host, port }, "", false);
114 build_response.build_and_send_response(res, speak_on_correct_status.internal_server_error, title, `Unable to retrieve server address`, "Error", "", true);
118app.post('/shutdown', async (req, res) => {
119 var title = '/shutdown';
120 console.log(`endpoint: post: ${req.url}`);
121 const token = req.headers.authorization;
122 console.log(`token: ${token}`);
123 const data = await database.getContentFromTable('users', ['*'], `token = ${token}`);
124 // console.log(data);
125 if (data.length === 0) {
126 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid token', 'Error', '', true);
129 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Shutting down the server', 'Success', '', false);
130 process.exit(CONST.SUCCESS);
134app.get('/oauth/login/:provider', async (req, res) => {
135 var title = "sso login";
136 console.log(`endpoint: get: ${req.url}`);
137 const prov = req.params.provider;
138 console.log(`params: ${JSON.stringify(req.params)}`);
140 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Missing provider', 'Error', '', true);
143 const provider = prov;
144 const data = await database.getContentFromTable('sso_oauth', ['*'], 'provider_name = "' + provider + '"');
145 if (data.length === 0) {
146 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid provider', 'Error', '', true);
149 // console.log(data);
150 const authorisation_url = OAuth.generate_oauth_authorisation_url(data[0], env?.REDIRECT_URI || "", global_values);
151 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', authorisation_url, '', false);
154app.post("/oauth/callback", async (req, res) => {
155 const title = "sso callback";
156 console.log(`endpoint: post: ${req.url}`);
157 const body = req.body
160 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Missing code from the callback url', 'Error', '', true);
164 const splitted_body = body["code"].split("&");
165 console.log(splitted_body);
166 const splitted_state = splitted_body[1].split(":");
167 console.log(splitted_state);
169 let code = splitted_body[0];
170 let generated_uuid = splitted_state[0];
171 const got_provider = splitted_state[1];
172 code = code.replace("code=", "");
173 generated_uuid = generated_uuid.replace("state=", "");
174 // console.log(code);
175 // console.log(generated_uuid);
177 // console.log(`Global values before checking: ${JSON.stringify(global_values)}`);
178 if (code.length === 0 || generated_uuid.length === 0 || got_provider === 0) {
179 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Missing information from the callback url', 'Error', '', true);
182 const uuid_exists = global_values.some((item: any) => item.state === generated_uuid);
184 if (uuid_exists === false) {
185 console.log("The uuid doesn't exist in the back.");
186 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'The state from the callback url is incorrect', 'Error', '', true);
190 console.log(`Global values before removing the state: ${JSON.stringify(global_values)}`);
191 const index = global_values.findIndex((item: any) => item.state === generated_uuid);
194 global_values.splice(index, 1);
196 // console.log(`Global values after removing the state: ${global_values}`);
198 const provider_data = await database.getContentFromTable('sso_oauth', ['*'], `provider_name = '${got_provider}'`);
200 if (provider_data.length === 0) {
201 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'The given provider is not correct.', 'Error', '', true);
204 // console.log(provider_data);
205 let provider_response;
207 provider_response = await OAuth.exchange_code_for_token(code, provider_data[0], env?.REDIRECT_URI || "");
209 build_response.build_and_send_response(res, speak_on_correct_status.unauthorized, title, 'The code exchanger has failed.', 'Unauthorized', '', true);
212 // console.log(provider_response);
213 if (provider_response["access_token"].length === 0) {
214 build_response.build_and_send_response(res, speak_on_correct_status.unauthorized, title, 'The access token was not retrieved correctly.', 'Unauthorized', '', true);
218 const token = await OAuth.handle_provider_response(provider_response, provider_data[0], database);
219 if (token === null) {
220 build_response.build_and_send_response(res, speak_on_correct_status.unauthorized, title, 'The token response handler has failed.', 'Unauthorized', '', true);
223 build_response.build_and_send_response(res, speak_on_correct_status.success, title, '', { "token": token }, token, false);
225 build_response.build_and_send_response(res, speak_on_correct_status.unauthorized, title, 'The token response handler has failed.', 'Unauthorized', '', true);
231app.get("/user/about", async (req, res) => {
232 const title = `${req.url}`;
233 console.log(`endpoint: get: ${req.url}`);
234 let token = req.headers.authorization;
235 console.log(`token: ${token}`);
237 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Missing token', 'Error', '', true);
240 token = token.replace("Bearer ", "");
241 console.log(`Token without bearer: ${token}`);
242 const data = await database.getContentFromTable('users', ['*'], `token = '${token}'`);
243 // console.log(data);
244 if (!data || data.length === 0) {
245 console.log("No data");
246 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid token', 'Error', '', true);
249 console.log("After data");
251 id: Number(data[0].id),
252 username: String(data[0].name),
253 email: String(data[0].email),
255 // console.log(`Final: ${final}`);
256 // console.log(final);
257 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', final, '', false);
260app.post('/login', async (req, res) => {
261 const title = `${req.url}`;
262 console.log(`endpoint: post: ${req.url}`);
263 const email = req.body.email;
264 const password = req.body.password;
265 console.log("email: ", email, "password: ", password);
266 console.log("getting user from the database if it exists");
267 const data = await database.getContentFromTable('users', ['*'], `email = '${email}'`);
268 if (data.length === 0) {
269 console.log("No user");
270 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid email or password', 'Error', '', true);
273 console.log("user exists, loging them in");
274 const login_response = await Login.log_local_user_in(email, password, database);
275 if (login_response === null) {
276 console.log("login response is null");
277 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid email or password', 'Error', '', true);
280 // console.log("login response: ", login_response);
282 id: Number(data[0].id),
283 username: String(data[0].name),
284 email: String(data[0].email),
285 token: String(login_response),
287 // console.log(`final:`, final);
288 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', final, '', false);
291app.post('/register', async (req, res) => {
292 const title = `${req.url}`;
293 console.log(`endpoint: post: ${req.url}`);
294 const email = req.body.email;
295 const password = req.body.password;
296 const username = req.body.username;
298 console.log("email: ", email, "password: ", password, "username: ", username);
300 console.log("Checking fields");
302 if (!email || !password || !username) {
303 console.log("Missing email, password or username");
304 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Missing email, password or username', 'Error', '', true);
308 console.log("Checking if user already exists");
310 const response = await Login.register_user(username, email, password, database);
311 if (response === false) {
312 console.log("response is false");
313 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'The user has not been registered.', 'Error', '', true);
317 console.log("User registered");
319 console.log("Checking if user exists");
321 const data = await database.getContentFromTable('users', ['*'], `email = '${email}'`);
322 if (data.length === 0) {
323 console.log("No user");
324 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid email or password', 'Error', '', true);
328 console.log("User exists, logging them in");
330 const login_response = await Login.log_local_user_in(email, password, database);
331 if (login_response === null) {
332 console.log("login response is null");
333 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid email or password', 'Error', '', true);
337 // console.log("login response: ", login_response);
340 id: Number(data[0].id),
341 username: String(data[0].name),
342 email: String(data[0].email),
343 token: String(login_response),
345 // console.log(`final:`, final);
346 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', final, '', false);
349app.get("/user/widgets", async (req, res) => {
350 const title = `${req.url}`;
351 console.log(`endpoint: get: ${req.url}`);
352 const token = req.headers.authorization;
353 console.log(`token: ${token}`);
354 const token_cleaned = token?.replace("Bearer ", "") || "";
355 console.log(`token cleaned: ${token_cleaned}`);
356 const data = await database.getContentFromTable('users', ['*'], `token = '${token_cleaned}'`);
357 // console.log(data);
358 if (data.length === 0) {
359 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid token', 'Error', '', true);
362 const user_data = await Widgets.get_user_widgets(data[0], database);
363 // console.log("user_data: ", user_data);
364 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', user_data, '', false);
367app.patch("/user/widget/:user_widget_id/:widget_type", async (req, res) => {
368 const title = `${req.url}`;
369 console.log(`endpoint: patch: ${req.url}`);
370 // Correctly extract widgetId
371 const widgetId = req.params.user_widget_id;
372 console.log(`widgetId: ${widgetId}`);
373 // Correctly extract widgetId
374 const widgetType = req.params.widget_type;
375 console.log(`widgetId: ${widgetType}`);
377 // Extract and clean token
378 const token = req.headers.authorization;
379 console.log(`token: ${token}`);
380 const token_cleaned = token?.replace("Bearer ", "") || "";
381 console.log(`token cleaned: ${token_cleaned}`);
383 // Validate user with token
384 const data = await database.getContentFromTable('users', ['*'], `token = '${token_cleaned}'`);
385 // console.log(data);
386 if (data.length === 0) {
387 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid token', 'Error', '', true);
391 // Ensure widgetId is provided
393 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Missing widget id', 'Error', '', true);
397 console.log("displaying body");
398 console.log("req.body: ", req.body);
400 // Extract optional location parameter
401 const location = req.body.location ?? null;
403 console.log("displaying body");
404 console.log("location: ", location);
406 // Process widget addition
407 const user_data = await Widgets.update_user_widget(data[0], widgetId, widgetType, location, database);
408 // console.log(user_data);
410 if (user_data === false) {
411 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'The widget has not been updated.', 'Error', '', true);
416 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', user_data, '', false);
419app.post("/user/widget/:id/:location?", async (req, res) => {
420 const title = `${req.url}`;
421 console.log(`endpoint: post: ${req.url}`);
423 // Correctly extract widgetId
424 const widgetId = req.params.id;
425 console.log(`widgetId: ${widgetId}`);
427 // Extract and clean token
428 const token = req.headers.authorization;
429 console.log(`token: ${token}`);
430 const token_cleaned = token?.replace("Bearer ", "") || "";
431 console.log(`token cleaned: ${token_cleaned}`);
433 // Validate user with token
434 const data = await database.getContentFromTable('users', ['*'], `token = '${token_cleaned}'`);
435 // console.log(data);
436 if (data.length === 0) {
437 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid token', 'Error', '', true);
441 // Ensure widgetId is provided
443 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Missing widget id', 'Error', '', true);
447 // Extract optional location parameter
448 const location = req.params.location ?? null;
450 // Process widget addition
451 const user_data = await Widgets.add_user_widget(data[0], widgetId, location, database);
452 // console.log("user_data: ", user_data);
454 if (user_data === false) {
455 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'The widget has not been added.', 'Error', '', true);
460 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', user_data, '', false);
464app.delete("/user/widget/:id", async (req, res) => {
466 const title = `${req.url}`;
467 console.log(`endpoint: delete: ${req.url}`);
468 const widgetId = req.params.id;
469 console.log(`widgetId: ${widgetId}`);
470 const token = req.headers.authorization;
471 console.log(`token: ${token}`);
472 const token_cleaned = token?.replace("Bearer ", "") || "";
473 console.log(`token cleaned: ${token_cleaned}`);
474 const data = await database.getContentFromTable('users', ['*'], `token = '${token_cleaned}'`);
476 if (data.length === 0) {
477 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid token', 'Error', '', true);
481 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Missing widget id', 'Error', '', true);
484 const user_data = await Widgets.delete_user_widget(data[0], widgetId, database);
485 // console.log(user_data);
486 if (user_data === false) {
487 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'The widget has not been deleted.', 'Error', '', true);
490 const user_widgets = await Widgets.get_user_widgets(data[0], database);
491 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', user_widgets, '', false);
494app.get("/widgets", async (req, res) => {
495 const title = `${req.url}`;
496 console.log(`endpoint: get: ${req.url}`);
497 const token = req.headers.authorization;
498 console.log(`token: ${token}`);
499 const token_cleaned = token?.replace("Bearer ", "") || "";
500 console.log(`token cleaned: ${token_cleaned}`);
501 const data = await database.getContentFromTable('users', ['*'], `token = '${token_cleaned}'`);
502 // console.log(data);
503 if (data.length === 0) {
504 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid token', 'Error', '', true);
507 const widgets = await Widgets.get_available_widget_names();
508 // console.log(widgets);
509 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', widgets, '', false);
512app.get("/widget/:name", async (req, res) => {
514 const title = `${req.url}`;
515 console.log(`endpoint: get: ${req.url}`);
516 const widgetId = req.params.name;
517 console.log(`widgetId: ${widgetId}`);
518 const token = req.headers.authorization;
519 console.log(`token: ${token}`);
520 const token_cleaned = token?.replace("Bearer ", "") || "";
521 console.log(`token cleaned: ${token_cleaned}`);
522 const data = await database.getContentFromTable('users', ['*'], `token = '${token_cleaned}'`);
523 // console.log(data);
524 if (data.length === 0) {
525 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid token', 'Error', '', true);
529 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Missing widget id', 'Error', '', true);
532 const user_data = await Widgets.get_widget_info(data, widgetId, database);
533 // console.log(user_data);
534 if (user_data === false) {
535 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, '<p>Widget gathering error, the content for the given widget could not be fetched successfully.</p>', 'Error', '', true);
538 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', user_data, '', false);
541app.delete("/logout", async (req, res) => {
542 const title = `${req.url}`;
543 console.log(`endpoint: delete: ${req.url}`);
544 const token = req.headers.authorization;
545 console.log(`token: ${token}`);
546 const token_cleaned = token?.replace("Bearer ", "") || "";
547 console.log(`token cleaned: ${token_cleaned}`);
548 const data = await database.getContentFromTable('users', ['*'], `token = '${token_cleaned}'`);
549 // console.log(data);
550 if (data.length === 0) {
551 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid token', 'Error', '', true);
554 const logout_response = await Login.log_user_out(token_cleaned, database);
555 if (logout_response === false) {
556 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'The user has not been logged out.', 'Error', '', true);
559 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', "success", '', false);
562app.post("/user/sso", async (req, res) => {
563 const title = `${req.url}`;
564 console.log(`endpoint: post: ${req.url}`);
565 const token = req.headers.authorization;
566 const body = req.body;
567 const username = body.username;
568 const password = body.password;
570 console.log(`body: ${JSON.stringify(body)}`);
571 console.log("title: ", title, "token: ", token, "username: ", username, "password: ", password);
574 console.log("No token");
575 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Missing token', 'Error', '', true);
578 console.log("token is present");
579 if (!username || !password) {
580 console.log("No username or password");
581 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Missing username or password', 'Error', '', true);
584 console.log('username and password are present');
585 const token_cleaned = token.replace("Bearer ", "");
587 console.log("fetching data");
588 const data = await database.getContentFromTable('users', ['*'], `token = '${token_cleaned}'`);
589 console.log("data fetched");
590 // console.log(data);
591 if (!data || data.length === 0) {
592 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid token', 'Error', '', true);
596 // console.log("token ", token_cleaned, "username ", username, "password ", password);
598 const response = await Login.update_user_information(token_cleaned, username, password, database);
599 // console.log("Response: ", response);
600 if (response === false) {
601 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'The user information has not been updated.', 'Error', '', true);
604 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', 'Success', '', false);
607app.get("/refresh", async (req, res) => {
608 console.log(`endpoint: get: ${req.url}`);
609 const title = `${req.url}`;
610 const token = req.headers.authorization;
611 console.log(`token: ${token}`);
612 const token_cleaned = token?.replace("Bearer ", "") || "";
613 console.log(`token cleaned: ${token_cleaned}`);
614 const data = await database.getContentFromTable('users', ['*'], `token = '${token_cleaned}'`);
616 if (data.length === 0) {
617 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid token', 'Error', '', true);
620 console.log("data: ", data);
621 const refresh = Number(data[0].refresh);
622 // console.log(`refresh: ${refresh}`);
623 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', { "refresh": refresh }, '', false);
626app.post("/refresh/:refresh", async (req, res) => {
627 const title = `${req.url}`;
628 console.log(`endpoint: post: ${req.url}`);
629 const refreshDelay = Number(req.params.refresh);
630 console.log(`refreshDelay: ${refreshDelay}`);
631 const token = req.headers.authorization;
632 console.log(`token: ${token}`);
633 const token_cleaned = token?.replace("Bearer ", "") || "";
634 console.log(`token cleaned: ${token_cleaned}`);
635 const data = await database.getContentFromTable('users', ['*'], `token = '${token_cleaned}'`);
636 // console.log(data);
637 if (data.length === 0) {
638 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Invalid token', 'Error', '', true);
641 if (!refreshDelay || refreshDelay < 0 || Number.isNaN(refreshDelay)) {
642 build_response.build_and_send_response(res, speak_on_correct_status.bad_request, title, 'Missing or invalid refresh delay', 'Error', '', true);
645 await database.updateTable('users', ['refresh'], [refreshDelay], 'token = ?', [token_cleaned]);
646 build_response.build_and_send_response(res, speak_on_correct_status.success, title, 'Success', "Success", '', false);
649// Export the app for testing purposes
652// Define server variable to be accessible globally
655// Start the server if not in test mode
656if (require.main === module) {
658 // Port and IP configuration
659 const port: number = Number(env?.port || env?.PORT || Args.get_port() || 5000);
660 const ip: string = env?.ip || env?.IP || Args.get_ip() || '0.0.0.0';
663 server = app.listen(port, ip, (): void => {
664 const serverAddress = server.address();
666 console.log(`Server is running on http://${serverAddress.address}:${serverAddress.port}`);