diff --git a/api/.env.example b/api/.env.example index 515a080..0d5910f 100644 --- a/api/.env.example +++ b/api/.env.example @@ -24,7 +24,7 @@ APPLICATION_ENVIRONMENT= # dev / prod CONFIG_ID= # configures # Logger -LOG_DEPTH= # normal: 0, verbose: 1 +LOG_DEPTH= # normal / verbose # Glitchtip GLITCHTIP_DSN= diff --git a/api/src/db.ts b/api/src/db.ts index 3756f73..480dcf4 100644 --- a/api/src/db.ts +++ b/api/src/db.ts @@ -1,8 +1,5 @@ // const mariadb = require('mariadb') import * as mariadb from 'mariadb'; -const dotenv = require('dotenv') -dotenv.config(); - const pool = mariadb.createPool({ host: process.env.DB_HOST, diff --git a/api/src/index.ts b/api/src/index.ts index 347a05a..33f17f1 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -1,9 +1,11 @@ import dotenv = require('dotenv'); -dotenv.config(); +dotenv.config({ quiet: true }); import express = require('express'); import cors = require('cors'); import morgan = require('morgan'); +import { logger, LogHeader, LogPayload } from './services/logging/logger'; + const app = express() app.use(morgan((tokens: morgan.TokenIndexer, req: express.Request, res: express.Response) => { @@ -29,26 +31,10 @@ app.use(morgan((tokens: morgan.TokenIndexer, req: express.Request, res: express. } logger.log(head.level, head.type, payload.message, payload.data, head.depth) - // { - // type: 'http', - // timestamp: new Date().toISOString(), - - // method: tokens.method(req, res), - // path: tokens.url(req, res), - // status: Number(tokens.status(req, res)), - // response_time_ms: Number(tokens['response-time'](req, res)), - - // ip: req.ip, - // user_agent: req.headers['user-agent'], - - // user: req.user - // ? { id: req.user.id, name: req.user.name } - // : null, - // } return ''; }, { skip: (req: express.Request) => { - return req.originalUrl === '/members/me'; + return req.originalUrl === '/members/me' || req.originalUrl === '/ping'; } })) @@ -66,14 +52,13 @@ const port = process.env.SERVER_PORT; //glitchtip setup import sentry = require('@sentry/node'); if (process.env.DISABLE_GLITCHTIP === "true") { - console.log("Glitchtip disabled") + logger.info('app', 'Glitchtip disabled', null, 'normal') } else { let dsn = process.env.GLITCHTIP_DSN; let release = process.env.APPLICATION_VERSION; let environment = process.env.APPLICATION_ENVIRONMENT; - console.log(release, environment) sentry.init({ dsn: dsn, release: release, environment: environment, integrations: [sentry.captureConsoleIntegration({ levels: ['error'] })] }); - console.log("Glitchtip initialized"); + logger.info('app', 'Glitchtip initialized', null, 'normal') } //session setup @@ -112,7 +97,6 @@ import { roles, memberRoles } from './routes/roles'; import { courseRouter, eventRouter } from './routes/course'; import { calendarRouter } from './routes/calendar'; import { docsRouter } from './routes/docs'; -import { logger, LogHeader, LogPayload } from './services/logging/logger'; app.use('/application', applicationRouter); app.use('/ranks', ranks); @@ -134,5 +118,5 @@ app.get('/ping', (req, res) => { }); app.listen(port, () => { - console.log(`Example app listening on port ${port} `) + logger.info('app', `Example app listening on port ${port} `) }) diff --git a/api/src/routes/auth.ts b/api/src/routes/auth.ts index d15f8c0..0c7724a 100644 --- a/api/src/routes/auth.ts +++ b/api/src/routes/auth.ts @@ -1,7 +1,5 @@ const passport = require('passport'); const OpenIDConnectStrategy = require('passport-openidconnect'); -const dotenv = require('dotenv'); -dotenv.config(); const express = require('express'); const { param } = require('./applications'); @@ -13,6 +11,7 @@ import { getUserRoles } from '../services/db/rolesService'; import { getUserState, mapDiscordtoID } from '../services/db/memberService'; import { MemberState } from '@app/shared/types/member'; import { toDateTime } from '@app/shared/utils/time'; +import { logger } from '../services/logging/logger'; const querystring = require('querystring'); function parseJwt(token) { @@ -37,10 +36,10 @@ passport.use(new OpenIDConnectStrategy({ // console.log('profile:', profile); // console.log('jwt: ', parseJwt(jwtClaims)); // console.log('params:', params); - + let con; try { - var con = await pool.getConnection(); + con = await pool.getConnection(); await con.beginTransaction(); @@ -49,7 +48,13 @@ passport.use(new OpenIDConnectStrategy({ let memberId: number | null = null; //if member exists if (existing.length > 0) { + //login memberId = existing[0].id; + logger.info('auth', `Existing member login`, { + memberId, + issuer, + }); + } else { //otherwise: create account mode const jwt = parseJwt(jwtClaims); @@ -61,11 +66,16 @@ passport.use(new OpenIDConnectStrategy({ if (discordID && memberId) { // claim account - console.log("Claiming account"); const result = await con.query( `UPDATE members SET authentik_sub = ?, authentik_issuer = ? WHERE id = ?;`, [sub, issuer, memberId] ) + logger.info('auth', `Existing member claimed via Discord`, { + memberId, + discordID, + issuer, + }); + } else { console.log("New Account"); // new account @@ -75,6 +85,13 @@ passport.use(new OpenIDConnectStrategy({ [username, sub, issuer] ) memberId = Number(result.insertId); + + logger.info('auth', `New member account created`, { + memberId, + username, + issuer, + }); + } } @@ -83,10 +100,26 @@ passport.use(new OpenIDConnectStrategy({ await con.commit(); return cb(null, { memberId }); } catch (error) { - await con.rollback(); + logger.error('auth', `Authentication transaction failed`, { + issuer, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + }); + + if (con) { + try { + await con.rollback(); + } catch (rollbackError) { + logger.error('auth', `Rollback failed`, { + error: rollbackError instanceof Error + ? rollbackError.message + : String(rollbackError), + }); + } + } return cb(error); } finally { - con.release(); + if (con) con.release(); } })); diff --git a/api/src/routes/ranks.ts b/api/src/routes/ranks.ts index 8559495..beb5760 100644 --- a/api/src/routes/ranks.ts +++ b/api/src/routes/ranks.ts @@ -30,7 +30,6 @@ ur.post('/', [requireRole(["17th Command", "17th Administrator", "17th HQ"]), re ur.get('/', async (req: express.Request, res: express.Response) => { try { const promos = await getPromotionHistorySummary(); - console.log(promos); res.status(200).json(promos); } catch (err) { console.error(err); @@ -44,7 +43,6 @@ ur.get('/:day', async (req: express.Request, res: express.Response) => { let day = new Date(req.params.day) const promos = await getPromotionsOnDay(day); - console.log(promos); res.status(200).json(promos); } catch (err) { console.error(err); diff --git a/api/src/services/db/rankService.ts b/api/src/services/db/rankService.ts index 4178c3b..a826617 100644 --- a/api/src/services/db/rankService.ts +++ b/api/src/services/db/rankService.ts @@ -39,7 +39,6 @@ export async function insertMemberRank(member_id: number, rank_id: number, date? export async function batchInsertMemberRank(promos: BatchPromotionMember[], author: number) { try { var con = await pool.getConnection(); - console.log(promos); promos.forEach(p => { con.query(`CALL sp_update_member_rank(?, ?, ?, ?, ?, ?)`, [p.member_id, p.rank_id, author, author, "Rank Change", toDateTime(new Date(p.start_date))]) }); @@ -70,7 +69,7 @@ export async function getPromotionHistorySummary(page: number = 1, pageSize: num let promoList: PromotionSummary[] = await pool.query(sql, [pageSize, offset]) as PromotionSummary[]; - let loaCount = Number((await pool.query(`SELECT + let rowCount = Number((await pool.query(`SELECT COUNT(*) AS total_grouped_days_count FROM ( @@ -79,10 +78,9 @@ export async function getPromotionHistorySummary(page: number = 1, pageSize: num WHERE reason = 'Rank Change' ) AS grouped_days;`))[0]); - console.log(loaCount); - let pageCount = loaCount / pageSize; + let pageCount = rowCount / pageSize; - let output: PagedData = { data: promoList, pagination: { page: page, pageSize: pageSize, total: loaCount, totalPages: pageCount } } + let output: PagedData = { data: promoList, pagination: { page: page, pageSize: pageSize, total: rowCount, totalPages: pageCount } } return output; } diff --git a/api/src/services/logging/logger.ts b/api/src/services/logging/logger.ts index bf48885..d67b2c5 100644 --- a/api/src/services/logging/logger.ts +++ b/api/src/services/logging/logger.ts @@ -1,6 +1,6 @@ export type LogLevel = 'debug' | 'info' | 'warn' | 'error'; export type LogDepth = 'normal' | 'verbose'; -export type LogType = 'http' | 'app'; +export type LogType = 'http' | 'app' | 'auth'; export interface LogHeader { timestamp: string; @@ -27,7 +27,6 @@ function shouldLog(depth: LogDepth) { function emitLog(header: LogHeader, payload: LogPayload = {}) { if (!shouldLog(header.depth)) return; - console.log(header, payload); const logLine = { ...header, ...payload }; console.log(JSON.stringify(logLine)); @@ -51,19 +50,19 @@ export const logger = { emitLog(header, payload); }, - info(type: string, message: string, data?: Record, depth: LogDepth = 'normal', context?: Partial) { + info(type: LogType, message: string, data?: Record, depth: LogDepth = 'normal', context?: Partial) { this.log('info', type, message, data, depth, context); }, - debug(type: string, message: string, data?: Record, depth: LogDepth = 'normal', context?: Partial) { + debug(type: LogType, message: string, data?: Record, depth: LogDepth = 'normal', context?: Partial) { this.log('debug', type, message, data, depth, context); }, - warn(type: string, message: string, data?: Record, depth: LogDepth = 'normal', context?: Partial) { + warn(type: LogType, message: string, data?: Record, depth: LogDepth = 'normal', context?: Partial) { this.log('warn', type, message, data, depth, context); }, - error(type: string, message: string, data?: Record, depth: LogDepth = 'normal', context?: Partial) { + error(type: LogType, message: string, data?: Record, depth: LogDepth = 'normal', context?: Partial) { this.log('error', type, message, data, depth, context); }, } \ No newline at end of file diff --git a/ui/src/api/rank.ts b/ui/src/api/rank.ts index e391a83..5749ea1 100644 --- a/ui/src/api/rank.ts +++ b/ui/src/api/rank.ts @@ -61,7 +61,6 @@ export async function getPromoHistory(page?: number, pageSize?: number): Promise } export async function getPromotionsOnDay(day: Date): Promise { - console.log(day.toISOString()); const res = await fetch(`${addr}/memberRanks/${day.toISOString()}`, { credentials: 'include', }) diff --git a/ui/src/stores/memberDirectory.ts b/ui/src/stores/memberDirectory.ts index 2f5e542..e01410f 100644 --- a/ui/src/stores/memberDirectory.ts +++ b/ui/src/stores/memberDirectory.ts @@ -105,7 +105,6 @@ export const useMemberDirectory = defineStore('memberDirectory', () => { try { const res = await getFullMembers(ids); for (const m of res) { - console.log(m) full[m.member.member_id] = m; const waiters = fullWaiters.get(m.member.member_id);