From 9f895a202d2077383cd106d04db22e599e46a451 Mon Sep 17 00:00:00 2001 From: ajdj100 Date: Wed, 31 Dec 2025 21:45:38 -0500 Subject: [PATCH] integrated the new logger across the entire API --- api/src/routes/applications.ts | 191 ++++++++++++++++++++++++++++----- api/src/routes/auth.ts | 7 +- api/src/routes/calendar.ts | 98 +++++++++++++++-- api/src/routes/course.ts | 71 +++++++++--- api/src/routes/docs.ts | 53 +++++++-- api/src/routes/loa.ts | 150 ++++++++++++++++++++++---- api/src/routes/members.ts | 75 ++++++++++--- api/src/routes/ranks.ts | 54 +++++++--- api/src/routes/roles.ts | 82 +++++++++++--- api/src/routes/statuses.ts | 14 ++- 10 files changed, 670 insertions(+), 125 deletions(-) diff --git a/api/src/routes/applications.ts b/api/src/routes/applications.ts index 82237d5..24b0fb2 100644 --- a/api/src/routes/applications.ts +++ b/api/src/routes/applications.ts @@ -46,29 +46,64 @@ router.get('/coc', async (req: Request, res: Response) => { // POST /application router.post('/', [requireLogin], async (req, res) => { + const memberID = req.user.id; + const App = req.body?.App || {}; + const appVersion = 1; + try { - const App = req.body?.App || {}; - const memberID = req.user.id; + req.profiler?.start('createApplication'); + await createApplication(memberID, appVersion, JSON.stringify(App)); + req.profiler?.end('createApplication'); - const appVersion = 1; - - await createApplication(memberID, appVersion, JSON.stringify(App)) + req.profiler?.start('setUserState'); await setUserState(memberID, MemberState.Applicant); + req.profiler?.end('setUserState'); res.sendStatus(201); + + // Log full route profiling + const summary = req.profiler?.summary(); + if (summary) { + logger.info( + 'profiling', + 'POST /application completed', + { + memberID, + appVersion, + ...summary, + }, + 'profiling' + ); + } } catch (err) { - console.error('Failed to create application: \n', err); + logger.error( + 'app', + 'Failed to create application', + { + memberID, + error: err instanceof Error ? err.message : String(err), + stack: err instanceof Error ? err.stack : undefined, + } + ); res.status(500).json({ error: 'Failed to create application' }); } }); + // GET /application/all router.get('/all', [requireLogin, requireRole("Recruiter")], async (req, res) => { try { const rows = await getApplicationList(); res.status(200).json(rows); } catch (err) { - console.error(err); + logger.error( + 'app', + 'Failed to get applications', + { + error: err instanceof Error ? err.message : String(err), + stack: err instanceof Error ? err.stack : undefined, + } + ); res.status(500); } }); @@ -82,8 +117,16 @@ router.get('/meList', async (req, res) => { return res.status(200).json(application); } catch (error) { - console.error('Failed to load applications: \n', error); - return res.status(500).json(error); + logger.error( + 'app', + 'Failed to get applications for user', + { + user: userID, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); + return res.status(500); } }) @@ -108,12 +151,20 @@ router.get('/me', [requireLogin], async (req, res) => { return res.status(200).json(output); } catch (error) { - console.error('Failed to load application:', error); + logger.error( + 'app', + 'Failed to load application', + { + user: userID, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); return res.status(500).json(error); } }) -// GET /application/:id +// GET /me/:id router.get('/me/:id', [requireLogin], async (req: Request, res: Response) => { let appID = Number(req.params.id); let member = req.user.id; @@ -133,9 +184,18 @@ router.get('/me/:id', [requireLogin], async (req: Request, res: Response) => { } return res.status(200).json(output); } - catch (err) { - console.error('Query failed:', err); - return res.status(500).json({ error: 'Failed to load application' }); + catch (error) { + logger.error( + 'app', + 'Failed to load application', + { + application: appID, + user: member, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); + return res.status(500); } }); @@ -157,9 +217,17 @@ router.get('/:id', [requireLogin, requireRole("Recruiter")], async (req: Request } return res.status(200).json(output); } - catch (err) { - console.error('Query failed:', err); - return res.status(500).json({ error: 'Failed to load application' }); + catch (error) { + logger.error( + 'app', + 'Failed to load application', + { + application: appID, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); + return res.status(500); } }); @@ -177,9 +245,22 @@ router.post('/approve/:id', [requireLogin, requireRole("Recruiter")], async (req await pool.query('CALL sp_accept_new_recruit_validation(?, ?, ?, ?)', [Number(process.env.CONFIG_ID), app.member_id, approved_by, approved_by]) + logger.info('app', "Member application approved", { + application: app.id, + applicant: app.member_id, + approver: approved_by + }) res.sendStatus(200); - } catch (err) { - console.error('Approve failed:', err); + } catch (error) { + logger.error( + 'app', + 'Failed to approve application', + { + application: appID, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json({ error: 'Failed to approve application' }); } }); @@ -193,9 +274,23 @@ router.post('/deny/:id', [requireLogin, requireRole("Recruiter")], async (req: R const app = await getApplicationByID(appID); await denyApplication(appID, approver); await setUserState(app.member_id, MemberState.Denied); + + logger.info('app', "Member application approved", { + application: app.id, + applicant: app.member_id, + approver: approver + }) res.sendStatus(200); - } catch (err) { - console.error('Approve failed:', err); + } catch (error) { + logger.error( + 'app', + 'Failed to deny application', + { + application: appID, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json({ error: 'Failed to deny application' }); } }); @@ -233,10 +328,25 @@ VALUES(?, ?, ?);` INNER JOIN members AS member ON member.id = app.poster_id WHERE app.id = ?; `; const comment = await conn.query(getSQL, [result.insertId]) + + logger.info('app', "Application comment posted", { + application: appID, + poster: user.id, + comment: result.insertId, + }) + res.status(201).json(comment[0]); - } catch (err) { - console.error('Comment failed:', err); + } catch (error) { + logger.error( + 'app', + 'Failed to post comment', + { + application: appID, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json({ error: 'Could not post comment' }); } finally { conn.release(); @@ -277,10 +387,24 @@ VALUES(?, ?, ?, 1);` INNER JOIN members AS member ON member.id = app.poster_id WHERE app.id = ?; `; const comment = await conn.query(getSQL, [result.insertId]) - res.status(201).json(comment[0]); - } catch (err) { - console.error('Comment failed:', err); + logger.info('app', "Admin application comment posted", { + application: appID, + poster: user.id, + comment: result.insertId, + }) + + res.status(201).json(comment[0]); + } catch (error) { + logger.error( + 'app', + 'Failed to post comment', + { + application: appID, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json({ error: 'Could not post comment' }); } finally { conn.release(); @@ -291,9 +415,22 @@ router.post('/restart', async (req: Request, res: Response) => { const user = req.user.id; try { await setUserState(user, MemberState.Guest); + + logger.info('app', "Member restarted application", { + user: user + }) + res.sendStatus(200); } catch (error) { - console.error('Comment failed:', error); + logger.error( + 'app', + 'Failed to restart application', + { + user: user, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json({ error: 'Could not rester application' }); } }) diff --git a/api/src/routes/auth.ts b/api/src/routes/auth.ts index e892ed8..b292811 100644 --- a/api/src/routes/auth.ts +++ b/api/src/routes/auth.ts @@ -79,7 +79,6 @@ passport.use(new OpenIDConnectStrategy({ }); } else { - console.log("New Account"); // new account const username = sub.username; const result = await con.query( @@ -168,8 +167,12 @@ router.get('/logout', [requireLogin], function (req, res, next) { client_id: process.env.AUTH_CLIENT_ID, returnTo: process.env.CLIENT_URL }; - res.redirect(process.env.AUTH_END_SESSION_URI + '?' + querystring.stringify(params)); + logger.info('auth', `Member logged out`, { + user: req.user.id, + }); + + res.redirect(process.env.AUTH_END_SESSION_URI + '?' + querystring.stringify(params)); }) }); }); diff --git a/api/src/routes/calendar.ts b/api/src/routes/calendar.ts index c6cac45..f5279f7 100644 --- a/api/src/routes/calendar.ts +++ b/api/src/routes/calendar.ts @@ -3,6 +3,7 @@ import { createEvent, getEventAttendance, getEventDetails, getShortEventsInRange import { CalendarAttendance, CalendarEvent } from "@app/shared/types/calendar"; import { requireLogin, requireMemberState, requireRole } from "../middleware/auth"; import { MemberState } from "@app/shared/types/member"; +import { logger } from "../services/logging/logger"; const express = require('express'); const r = express.Router(); @@ -28,7 +29,14 @@ r.get('/', async (req, res) => { res.status(200).json(events); } catch (error) { - console.error('Error fetching calendar events:', error); + logger.error( + 'app', + 'Failed to get calendar events', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).send('Error fetching calendar events'); } }); @@ -41,9 +49,21 @@ r.post('/:id/cancel', [requireLogin, requireMemberState(MemberState.Member)], as try { const eventID = Number(req.params.id); setEventCancelled(eventID, true); + + logger.info('app', 'Calendar event cancelled', { + event: eventID, + user: req.user.id + }) res.sendStatus(200); } catch (error) { - console.error('Error setting cancel status:', error); + logger.error( + 'app', + 'Failed to get cancel calendar event', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).send('Error setting cancel status'); } }) @@ -51,9 +71,21 @@ r.post('/:id/uncancel', [requireLogin, requireMemberState(MemberState.Member)], try { const eventID = Number(req.params.id); setEventCancelled(eventID, false); + + logger.info('app', 'Calendar event un-cancelled', { + event: eventID, + user: req.user.id + }) res.sendStatus(200); } catch (error) { - console.error('Error setting cancel status:', error); + logger.error( + 'app', + 'Failed to uncancel calendar event', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).send('Error setting cancel status'); } }) @@ -65,12 +97,27 @@ r.post('/:id/attendance', [requireLogin, requireMemberState(MemberState.Member)] let event = Number(req.params.id); let state = req.query.state as CalendarAttendance; setAttendanceStatus(member, event, state); + + logger.info('app', 'Member set calendar event attendance', { + event: event, + user: req.user.id, + state: state + }) + res.sendStatus(200); } catch (error) { - console.error('Failed to set attendance:', error); + logger.error( + 'app', + 'Failed to set attendance', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); } }) + //get event details r.get('/:id', async (req: Request, res: Response) => { try { @@ -79,9 +126,16 @@ r.get('/:id', async (req: Request, res: Response) => { let details: CalendarEvent = await getEventDetails(eventID); details.eventSignups = await getEventAttendance(eventID); res.status(200).json(details); - } catch (err) { - console.error('Insert failed:', err); - res.status(500).json(err); + } catch (error) { + logger.error( + 'app', + 'Failed to get calendar event details', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); + res.status(500); } }) @@ -95,9 +149,22 @@ r.post('/', [requireLogin, requireMemberState(MemberState.Member)], async (req: event.start = new Date(event.start); event.end = new Date(event.end); createEvent(event); + + logger.info('app', 'Calendar event posted', { + event: event.id, + user: req.user.id + }) + res.sendStatus(200); } catch (error) { - console.error('Failed to create event:', error); + logger.error( + 'app', + 'Failed to create calendar event', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); } }) @@ -108,9 +175,22 @@ r.put('/', [requireLogin, requireMemberState(MemberState.Member)], async (req: R event.start = new Date(event.start); event.end = new Date(event.end); updateEvent(event); + + logger.info('app', 'Calendar event updated', { + event: event.id, + user: req.user.id + }) + res.sendStatus(200); } catch (error) { - console.error('Failed to update event:', error); + logger.error( + 'app', + 'Failed to update calendar event', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); } }) diff --git a/api/src/routes/course.ts b/api/src/routes/course.ts index 7317463..007d593 100644 --- a/api/src/routes/course.ts +++ b/api/src/routes/course.ts @@ -3,6 +3,7 @@ import { getAllCourses, getCourseEventAttendees, getCourseEventDetails, getCours import { Request, Response, Router } from "express"; import { requireLogin, requireMemberState } from "../middleware/auth"; import { MemberState } from "@app/shared/types/member"; +import { logger } from "../services/logging/logger"; const cr = Router(); const er = Router(); @@ -16,9 +17,16 @@ cr.get('/', async (req, res) => { try { const courses = await getAllCourses(); res.status(200).json(courses); - } catch (err) { - console.error('failed to fetch courses', err); - res.status(500).json('failed to fetch courses\n' + err); + } catch (error) { + logger.error( + 'app', + 'Failed to fetch courses', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); + res.status(500).json('failed to fetch courses'); } }) @@ -26,12 +34,20 @@ cr.get('/roles', async (req, res) => { try { const roles = await getCourseEventRoles(); res.status(200).json(roles); - } catch (err) { - console.error('failed to fetch course roles', err); - res.status(500).json('failed to fetch course roles\n' + err); + } catch (error) { + logger.error( + 'app', + 'Failed to fetch course roles', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); + res.status(500).json('failed to fetch course roles'); } }) +//get event list er.get('/', async (req: Request, res: Response) => { try { const allowedSorts = new Map([ @@ -55,7 +71,14 @@ er.get('/', async (req: Request, res: Response) => { let events = await getCourseEvents(sortDir, search, page, pageSize); res.status(200).json(events); } catch (error) { - console.error('failed to fetch reports', error); + logger.error( + 'app', + 'Failed to fetch course events', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); } }); @@ -65,7 +88,14 @@ er.get('/:id', async (req: Request, res: Response) => { let out = await getCourseEventDetails(Number(req.params.id)); res.status(200).json(out); } catch (error) { - console.error('failed to fetch report', error); + logger.error( + 'app', + 'Failed to fetch course event', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); } }); @@ -74,9 +104,16 @@ er.get('/attendees/:id', async (req: Request, res: Response) => { try { const attendees: CourseAttendee[] = await getCourseEventAttendees(Number(req.params.id)); res.status(200).json(attendees); - } catch (err) { - console.error('failed to fetch attendees', err); - res.status(500).json("failed to fetch attendees\n" + err); + } catch (error) { + logger.error( + 'app', + 'Failed to fetch course event attendees', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); + res.status(500).json("failed to fetch attendees"); } }) @@ -87,9 +124,19 @@ er.post('/', async (req: Request, res: Response) => { data.created_by = posterID; data.event_date = new Date(data.event_date); const id = await insertCourseEvent(data); + + logger.info('app', 'Training report posted', { user: posterID, report: id }) res.status(201).json(id); } catch (error) { - console.error('failed to post training', error); + logger.error( + 'app', + 'Failed to post training report', + { + user: posterID, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json("failed to post training\n" + error) } }) diff --git a/api/src/routes/docs.ts b/api/src/routes/docs.ts index 13ada87..471891a 100644 --- a/api/src/routes/docs.ts +++ b/api/src/routes/docs.ts @@ -3,22 +3,55 @@ const router = express.Router(); import { Request, Response } from 'express'; import { requireLogin } from '../middleware/auth'; +import { logger } from '../services/logging/logger'; +// GET /welcome router.get('/welcome', [requireLogin], async (req: Request, res: Response) => { - const output = await fetch(`${process.env.DOC_HOST}/api/pages/717`, { - headers: { - Authorization: `Token ${process.env.DOC_TOKEN_ID}:${process.env.DOC_TOKEN_SECRET}`, - } - }) + const t0 = performance.now(); // optional profiling start - if (output.ok) { - const out = await output.json(); + try { + const response = await fetch(`${process.env.DOC_HOST}/api/pages/717`, { + headers: { + Authorization: `Token ${process.env.DOC_TOKEN_ID}:${process.env.DOC_TOKEN_SECRET}`, + }, + }); + + if (!response.ok) { + const text = await response.text(); + logger.error('app', 'Failed to fetch welcome page from Bookstack', { + status: response.status, + statusText: response.statusText, + body: text, + userId: req.user?.id, + }); + return res.sendStatus(500); + } + + const out = await response.json(); res.status(200).json(out.html); - } else { - console.error("Failed to fetch LOA policy from bookstack"); + + // optional profiling log + const duration = performance.now() - t0; + logger.info( + 'profiling', + 'GET /welcome completed', + { + userId: req.user?.id, + total_ms: duration, + }, + 'profiling' + ); + + } catch (error) { + logger.error('app', 'Error fetching welcome page from Bookstack', { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + userId: req.user?.id, + }); res.sendStatus(500); } -}) +}); + export const docsRouter = router; \ No newline at end of file diff --git a/api/src/routes/loa.ts b/api/src/routes/loa.ts index a1ea32e..8555fb7 100644 --- a/api/src/routes/loa.ts +++ b/api/src/routes/loa.ts @@ -6,6 +6,7 @@ import pool from '../db'; import { closeLOA, createNewLOA, getAllLOA, getLOAbyID, getLoaTypes, getUserLOA, setLOAExtension } from '../services/db/loaService'; import { LOARequest } from '@app/shared/types/loa'; import { requireLogin, requireRole } from '../middleware/auth'; +import { logger } from '../services/logging/logger'; router.use(requireLogin); @@ -18,9 +19,17 @@ router.post("/", async (req: Request, res: Response) => { try { await createNewLOA(LOARequest); + logger.info('app', 'LOA Posted', { poster: req.user.id, user: LOARequest.member_id }) res.sendStatus(201); } catch (error) { - console.error(error); + logger.error( + 'app', + 'Failed to post LOA', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).send(error); } }); @@ -32,10 +41,17 @@ router.post("/admin", [requireRole(['17th Administrator', '17th HQ', '17th Comma LOARequest.filed_date = new Date(); try { await createNewLOA(LOARequest); + logger.info('app', 'LOA Posted', { poster: req.user.id, user: LOARequest.member_id }) res.sendStatus(201); } catch (error) { - console.error(error); - res.status(500).send(error); + logger.error( + 'app', + 'Failed to post LOA', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).send(error); } }); @@ -46,7 +62,14 @@ router.get("/me", async (req: Request, res: Response) => { const result = await getUserLOA(user); res.status(200).json(result) } catch (error) { - console.error(error); + logger.error( + 'app', + 'Failed to get user current LOA', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).send(error); } }) @@ -62,7 +85,14 @@ router.get("/history", async (req: Request, res: Response) => { const result = await getUserLOA(user, page, pageSize); res.status(200).json(result) } catch (error) { - console.error(error); + logger.error( + 'app', + 'Failed to get user LOA history', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).send(error); } }) @@ -74,7 +104,14 @@ router.get('/all', [requireRole(['17th Administrator', '17th HQ', '17th Command' const result = await getAllLOA(page, pageSize); res.status(200).json(result) } catch (error) { - console.error(error); + logger.error( + 'app', + 'Failed to get full LOA history', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).send(error); } }) @@ -84,8 +121,15 @@ router.get('/types', async (req: Request, res: Response) => { let out = await getLoaTypes(); res.status(200).json(out); } catch (error) { + logger.error( + 'app', + 'Failed to get LOA types', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); - console.error(error); } }) @@ -99,9 +143,19 @@ router.post('/cancel/:id', async (req: Request, res: Response) => { } await closeLOA(Number(req.params.id), closer); + + logger.info('app', 'LOA Closed', { closed_by: closer, LOA: id }) + res.sendStatus(200); } catch (error) { - console.error(error); + logger.error( + 'app', + 'Failed to cancel LOA', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); } }) @@ -111,14 +165,24 @@ router.post('/adminCancel/:id', [requireRole(['17th Administrator', '17th HQ', ' let closer = req.user.id; try { await closeLOA(Number(req.params.id), closer); + + logger.info('app', 'LOA Closed', { closed_by: closer, LOA: req.params.id }) + res.sendStatus(200); } catch (error) { - console.error(error); + logger.error( + 'app', + 'Failed to cancel LOA', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); } }) -// TODO: Enforce admin only +// extend LOA router.post('/extend/:id', [requireRole(['17th Administrator', '17th HQ', '17th Command'])], async (req: Request, res: Response) => { const to: Date = req.body.to; @@ -128,27 +192,71 @@ router.post('/extend/:id', [requireRole(['17th Administrator', '17th HQ', '17th try { await setLOAExtension(Number(req.params.id), to); + logger.info('app', 'LOA Extended', { extended_by: req.user.id, LOA: req.params.id }) + res.sendStatus(200); } catch (error) { - console.error(error) + logger.error( + 'app', + 'Failed to extend LOA', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); } }) +// GET /policy router.get('/policy', async (req: Request, res: Response) => { - const output = await fetch(`${process.env.DOC_HOST}/api/pages/42`, { - headers: { - Authorization: `Token ${process.env.DOC_TOKEN_ID}:${process.env.DOC_TOKEN_SECRET}`, - } - }) + const t0 = performance.now(); - if (output.ok) { - const out = await output.json(); + try { + const response = await fetch(`${process.env.DOC_HOST}/api/pages/42`, { + headers: { + Authorization: `Token ${process.env.DOC_TOKEN_ID}:${process.env.DOC_TOKEN_SECRET}`, + }, + }); + + if (!response.ok) { + const text = await response.text(); + + logger.error('app', 'Failed to fetch policy page from Bookstack', { + pageId: 42, + status: response.status, + statusText: response.statusText, + body: text, + userId: req.user?.id, + }); + + return res.sendStatus(500); + } + + const out = await response.json(); res.status(200).json(out.html); - } else { - console.error("Failed to fetch LOA policy from bookstack"); + + logger.info( + 'profiling', + 'GET /policy completed', + { + pageId: 42, + total_ms: performance.now() - t0, + }, + 'profiling' + ); + + } catch (error) { + logger.error('app', 'Error fetching policy page from Bookstack', { + pageId: 42, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + userId: req.user?.id, + }); + res.sendStatus(500); } -}) +}); + export const loaRouter = router; \ No newline at end of file diff --git a/api/src/routes/members.ts b/api/src/routes/members.ts index 4f590d2..f7bdd15 100644 --- a/api/src/routes/members.ts +++ b/api/src/routes/members.ts @@ -29,8 +29,15 @@ router.get('/', [requireLogin, requireMemberState(MemberState.Member)], async (r END AS on_loa FROM view_member_rank_unit_status_latest v;`); return res.status(200).json(result); - } catch (err) { - console.error('Error fetching users:', err); + } catch (error) { + logger.error( + 'app', + 'Failed to get all users', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); return res.status(500).json({ error: 'Failed to fetch users' }); } }); @@ -92,7 +99,14 @@ router.get('/settings', [requireLogin], async (req: Request, res: Response) => { let output = await getMemberSettings(user); res.status(200).json(output); } catch (error) { - console.error(error); + logger.error( + 'app', + 'Failed to get member settings', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); } }) @@ -102,10 +116,17 @@ router.put('/settings', [requireLogin], async (req: Request, res: Response) => { let user = req.user.id; let settings: memberSettings = req.body; await setUserSettings(user, settings); + logger.info('app', 'User updated profile settings', { user: user }) res.sendStatus(200); } catch (error) { - console.error(error); - res.status(500).json(error); + logger.error( + 'app', + 'Failed to update user settings', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); } }) @@ -114,7 +135,14 @@ router.get('/lite', [requireLogin], async (req: Request, res: Response) => { let out = await getAllMembersLite(); res.status(200).json(out); } catch (error) { - console.error(error); + logger.error( + 'app', + 'Failed to get lite users', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); } }) @@ -125,7 +153,14 @@ router.post('/lite/bulk', async (req: Request, res: Response) => { let out = await getMembersLite(ids); res.status(200).json(out); } catch (error) { - console.error(error); + logger.error( + 'app', + 'Failed to get batch lite users', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); } }) @@ -137,22 +172,36 @@ router.post('/full/bulk', async (req: Request, res: Response) => { let out = await getMembersFull(ids); res.status(200).json(out); } catch (error) { - console.error(error); - res.status(500).json(error); + logger.error( + 'app', + 'Failed to get batch full users', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json(error); } }) router.get('/:id', [requireLogin], async (req, res) => { + const userId = req.params.id; + try { - const userId = req.params.id; const result = await pool.query('SELECT * FROM view_member_rank_unit_status_latest WHERE id = $1;', [userId]); if (result.rows.length === 0) { return res.status(404).json({ error: 'User not found' }); } return res.status(200).json(result.rows[0]); - } catch (err) { - console.error('Error fetching user:', err); - return res.status(500).json({ error: 'Failed to fetch user' }); + } catch (error) { + logger.error( + 'app', + 'Failed to get user', + { + user: userId, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); return res.status(500).json({ error: 'Failed to fetch user' }); } }); diff --git a/api/src/routes/ranks.ts b/api/src/routes/ranks.ts index beb5760..8ba4afc 100644 --- a/api/src/routes/ranks.ts +++ b/api/src/routes/ranks.ts @@ -4,6 +4,7 @@ import { batchInsertMemberRank, getAllRanks, getPromotionHistorySummary, getProm import { BatchPromotion, BatchPromotionMember } from '@app/shared/schemas/promotionSchema' import express = require('express'); +import { logger } from "../services/logging/logger"; const r = express.Router(); const ur = express.Router(); @@ -19,10 +20,17 @@ ur.post('/', [requireRole(["17th Command", "17th Administrator", "17th HQ"]), re if (!change) res.sendStatus(400); await batchInsertMemberRank(change, author); - + logger.info('app', 'Promotion batch submitted', { author: author }) res.sendStatus(201); - } catch (err) { - console.error('Insert failed:', err); + } catch (error) { + logger.error( + 'app', + 'Failed to post rank change', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json({ error: 'Failed to update ranks' }); } }); @@ -31,9 +39,16 @@ ur.get('/', async (req: express.Request, res: express.Response) => { try { const promos = await getPromotionHistorySummary(); res.status(200).json(promos); - } catch (err) { - console.error(err); - res.status(500).json({ error: 'Internal server error' }); + } catch (error) { + logger.error( + 'app', + 'Failed to get rank change history', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); + res.sendStatus(500); } }); @@ -41,12 +56,20 @@ ur.get('/:day', async (req: express.Request, res: express.Response) => { try { if (!req.params.day) res.sendStatus(400); - let day = new Date(req.params.day) + var day = new Date(req.params.day) const promos = await getPromotionsOnDay(day); res.status(200).json(promos); - } catch (err) { - console.error(err); - res.status(500).json({ error: 'Internal server error' }); + } catch (error) { + logger.error( + 'app', + 'Failed to get rank change history on day', + { + day: day, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); + res.sendStatus(500); } }) @@ -55,8 +78,15 @@ r.get('/', async (req, res) => { try { const ranks = await getAllRanks(); res.json(ranks); - } catch (err) { - console.error(err); + } catch (error) { + logger.error( + 'app', + 'Failed to get all ranks', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json({ error: 'Internal server error' }); } }); diff --git a/api/src/routes/roles.ts b/api/src/routes/roles.ts index 1295ec9..000df07 100644 --- a/api/src/routes/roles.ts +++ b/api/src/routes/roles.ts @@ -7,44 +7,69 @@ import pool from '../db'; import { requireLogin, requireMemberState, requireRole } from '../middleware/auth'; import { assignUserGroup, createGroup, getAllRoles, getRole, getUsersWithRole } from '../services/db/rolesService'; import { Request, Response } from 'express'; +import { logger } from '../services/logging/logger'; r.use(requireLogin) ur.use(requireLogin) //manually assign a member to a group -ur.post('/', [requireMemberState(MemberState.Member), requireRole("17th Administrator")], async (req, res) => { +ur.post('/', [requireMemberState(MemberState.Member), requireRole("17th Administrator")], async (req: Request, res) => { + const body = req.body; + try { - const body = req.body; await assignUserGroup(body.member_id, body.role_id); + logger.info('app', 'User assigned role', { user: body.member_id, role: body.role_id, assigner: req.user.id }) res.sendStatus(201); - } catch (err) { - if (err?.code === 'ER_DUP_ENTRY') { + } catch (error) { + if (error?.code === 'ER_DUP_ENTRY') { return res.status(400).json({ error: 'Member already has this role', }); } - console.error('Insert failed:', err); + logger.error( + 'app', + 'Failed to assign role', + { + user: body.member_id, + role: body.role_id, + assigner: req.user.id, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json({ error: 'Failed to add to group' }); } }); //manually remove member from group -ur.delete('/', [requireMemberState(MemberState.Member), requireRole("17th Administrator")], async (req, res) => { +ur.delete('/', [requireMemberState(MemberState.Member), requireRole("17th Administrator")], async (req: Request, res: Response) => { + const body = req.body; + try { - const body = req.body; const sql = 'DELETE FROM members_roles WHERE member_id = ? AND role_id = ?' await pool.query(sql, [body.member_id, body.role_id]) + logger.info('app', 'User removed role', { user: body.member_id, role: body.role_id, assigner: req.user.id }) + res.sendStatus(200); } - catch (err) { - console.error("delete failed: ", err) + catch (error) { + logger.error( + 'app', + 'Failed to remove role', + { + user: body.member_id, + role: body.role_id, + assigner: req.user.id, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.status(500).json({ error: 'Failed to remove from group' }); - } }) @@ -52,9 +77,18 @@ ur.delete('/', [requireMemberState(MemberState.Member), requireRole("17th Admini r.get('/', [requireMemberState(MemberState.Member)], async (req, res) => { try { const roles = await getAllRoles(); + //@ts-ignore + test(); res.status(200).json(roles); - } catch (err) { - console.error(err); + } catch (error) { + logger.error( + 'app', + 'Failed to get all roles', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.sendStatus(500); } }); @@ -63,8 +97,16 @@ r.get('/:id/members', [requireMemberState(MemberState.Member)], async (req: Requ try { const members = await getUsersWithRole(Number(req.params.id)); res.status(200).json(members); - } catch (err) { - console.error(err); + } catch (error) { + logger.error( + 'app', + 'Failed to get role members', + { + role: req.params.id, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.sendStatus(500); } }) @@ -74,8 +116,16 @@ r.get('/:id', [requireMemberState(MemberState.Member)], async (req: Request, res try { const role = await getRole(Number(req.params.id)); res.status(200).json(role); - } catch (err) { - console.error(err); + } catch (error) { + logger.error( + 'app', + 'Failed to get role members', + { + role: req.params.id, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); res.sendStatus(500); } }) diff --git a/api/src/routes/statuses.ts b/api/src/routes/statuses.ts index 8457800..3724579 100644 --- a/api/src/routes/statuses.ts +++ b/api/src/routes/statuses.ts @@ -4,6 +4,7 @@ const memberStatusR = express.Router(); import pool from '../db'; import { requireLogin } from '../middleware/auth'; +import { logger } from '../services/logging/logger'; statusR.use(requireLogin); memberStatusR.use(requireLogin); @@ -38,9 +39,16 @@ statusR.get('/', async (req, res) => { try { const result = await pool.query('SELECT * FROM statuses;'); res.json(result); - } catch (err) { - console.error(err); - res.status(500).json({ error: 'Internal server error' }); + } catch (error) { + logger.error( + 'app', + 'Failed to get all statuses', + { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + } + ); + res.sendStatus(500); } });