implemented new logging system in first iteration
This commit is contained in:
@@ -24,7 +24,7 @@ APPLICATION_ENVIRONMENT= # dev / prod
|
|||||||
CONFIG_ID= # configures
|
CONFIG_ID= # configures
|
||||||
|
|
||||||
# Logger
|
# Logger
|
||||||
LOG_DEPTH= # normal: 0, verbose: 1
|
LOG_DEPTH= # normal / verbose
|
||||||
|
|
||||||
# Glitchtip
|
# Glitchtip
|
||||||
GLITCHTIP_DSN=
|
GLITCHTIP_DSN=
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
// const mariadb = require('mariadb')
|
// const mariadb = require('mariadb')
|
||||||
import * as mariadb from 'mariadb';
|
import * as mariadb from 'mariadb';
|
||||||
const dotenv = require('dotenv')
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
|
|
||||||
const pool = mariadb.createPool({
|
const pool = mariadb.createPool({
|
||||||
host: process.env.DB_HOST,
|
host: process.env.DB_HOST,
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import dotenv = require('dotenv');
|
import dotenv = require('dotenv');
|
||||||
dotenv.config();
|
dotenv.config({ quiet: true });
|
||||||
|
|
||||||
import express = require('express');
|
import express = require('express');
|
||||||
import cors = require('cors');
|
import cors = require('cors');
|
||||||
import morgan = require('morgan');
|
import morgan = require('morgan');
|
||||||
|
import { logger, LogHeader, LogPayload } from './services/logging/logger';
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
app.use(morgan((tokens: morgan.TokenIndexer, req: express.Request, res: express.Response) => {
|
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)
|
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 '';
|
return '';
|
||||||
}, {
|
}, {
|
||||||
skip: (req: express.Request) => {
|
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
|
//glitchtip setup
|
||||||
import sentry = require('@sentry/node');
|
import sentry = require('@sentry/node');
|
||||||
if (process.env.DISABLE_GLITCHTIP === "true") {
|
if (process.env.DISABLE_GLITCHTIP === "true") {
|
||||||
console.log("Glitchtip disabled")
|
logger.info('app', 'Glitchtip disabled', null, 'normal')
|
||||||
} else {
|
} else {
|
||||||
let dsn = process.env.GLITCHTIP_DSN;
|
let dsn = process.env.GLITCHTIP_DSN;
|
||||||
let release = process.env.APPLICATION_VERSION;
|
let release = process.env.APPLICATION_VERSION;
|
||||||
let environment = process.env.APPLICATION_ENVIRONMENT;
|
let environment = process.env.APPLICATION_ENVIRONMENT;
|
||||||
console.log(release, environment)
|
|
||||||
sentry.init({ dsn: dsn, release: release, environment: environment, integrations: [sentry.captureConsoleIntegration({ levels: ['error'] })] });
|
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
|
//session setup
|
||||||
@@ -112,7 +97,6 @@ import { roles, memberRoles } from './routes/roles';
|
|||||||
import { courseRouter, eventRouter } from './routes/course';
|
import { courseRouter, eventRouter } from './routes/course';
|
||||||
import { calendarRouter } from './routes/calendar';
|
import { calendarRouter } from './routes/calendar';
|
||||||
import { docsRouter } from './routes/docs';
|
import { docsRouter } from './routes/docs';
|
||||||
import { logger, LogHeader, LogPayload } from './services/logging/logger';
|
|
||||||
|
|
||||||
app.use('/application', applicationRouter);
|
app.use('/application', applicationRouter);
|
||||||
app.use('/ranks', ranks);
|
app.use('/ranks', ranks);
|
||||||
@@ -134,5 +118,5 @@ app.get('/ping', (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`Example app listening on port ${port} `)
|
logger.info('app', `Example app listening on port ${port} `)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
const passport = require('passport');
|
const passport = require('passport');
|
||||||
const OpenIDConnectStrategy = require('passport-openidconnect');
|
const OpenIDConnectStrategy = require('passport-openidconnect');
|
||||||
const dotenv = require('dotenv');
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const { param } = require('./applications');
|
const { param } = require('./applications');
|
||||||
@@ -13,6 +11,7 @@ import { getUserRoles } from '../services/db/rolesService';
|
|||||||
import { getUserState, mapDiscordtoID } from '../services/db/memberService';
|
import { getUserState, mapDiscordtoID } from '../services/db/memberService';
|
||||||
import { MemberState } from '@app/shared/types/member';
|
import { MemberState } from '@app/shared/types/member';
|
||||||
import { toDateTime } from '@app/shared/utils/time';
|
import { toDateTime } from '@app/shared/utils/time';
|
||||||
|
import { logger } from '../services/logging/logger';
|
||||||
const querystring = require('querystring');
|
const querystring = require('querystring');
|
||||||
|
|
||||||
function parseJwt(token) {
|
function parseJwt(token) {
|
||||||
@@ -37,10 +36,10 @@ passport.use(new OpenIDConnectStrategy({
|
|||||||
// console.log('profile:', profile);
|
// console.log('profile:', profile);
|
||||||
// console.log('jwt: ', parseJwt(jwtClaims));
|
// console.log('jwt: ', parseJwt(jwtClaims));
|
||||||
// console.log('params:', params);
|
// console.log('params:', params);
|
||||||
|
let con;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var con = await pool.getConnection();
|
con = await pool.getConnection();
|
||||||
|
|
||||||
await con.beginTransaction();
|
await con.beginTransaction();
|
||||||
|
|
||||||
@@ -49,7 +48,13 @@ passport.use(new OpenIDConnectStrategy({
|
|||||||
let memberId: number | null = null;
|
let memberId: number | null = null;
|
||||||
//if member exists
|
//if member exists
|
||||||
if (existing.length > 0) {
|
if (existing.length > 0) {
|
||||||
|
//login
|
||||||
memberId = existing[0].id;
|
memberId = existing[0].id;
|
||||||
|
logger.info('auth', `Existing member login`, {
|
||||||
|
memberId,
|
||||||
|
issuer,
|
||||||
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
//otherwise: create account mode
|
//otherwise: create account mode
|
||||||
const jwt = parseJwt(jwtClaims);
|
const jwt = parseJwt(jwtClaims);
|
||||||
@@ -61,11 +66,16 @@ passport.use(new OpenIDConnectStrategy({
|
|||||||
|
|
||||||
if (discordID && memberId) {
|
if (discordID && memberId) {
|
||||||
// claim account
|
// claim account
|
||||||
console.log("Claiming account");
|
|
||||||
const result = await con.query(
|
const result = await con.query(
|
||||||
`UPDATE members SET authentik_sub = ?, authentik_issuer = ? WHERE id = ?;`,
|
`UPDATE members SET authentik_sub = ?, authentik_issuer = ? WHERE id = ?;`,
|
||||||
[sub, issuer, memberId]
|
[sub, issuer, memberId]
|
||||||
)
|
)
|
||||||
|
logger.info('auth', `Existing member claimed via Discord`, {
|
||||||
|
memberId,
|
||||||
|
discordID,
|
||||||
|
issuer,
|
||||||
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.log("New Account");
|
console.log("New Account");
|
||||||
// new account
|
// new account
|
||||||
@@ -75,6 +85,13 @@ passport.use(new OpenIDConnectStrategy({
|
|||||||
[username, sub, issuer]
|
[username, sub, issuer]
|
||||||
)
|
)
|
||||||
memberId = Number(result.insertId);
|
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();
|
await con.commit();
|
||||||
return cb(null, { memberId });
|
return cb(null, { memberId });
|
||||||
} catch (error) {
|
} 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);
|
return cb(error);
|
||||||
} finally {
|
} finally {
|
||||||
con.release();
|
if (con) con.release();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ ur.post('/', [requireRole(["17th Command", "17th Administrator", "17th HQ"]), re
|
|||||||
ur.get('/', async (req: express.Request, res: express.Response) => {
|
ur.get('/', async (req: express.Request, res: express.Response) => {
|
||||||
try {
|
try {
|
||||||
const promos = await getPromotionHistorySummary();
|
const promos = await getPromotionHistorySummary();
|
||||||
console.log(promos);
|
|
||||||
res.status(200).json(promos);
|
res.status(200).json(promos);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(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)
|
let day = new Date(req.params.day)
|
||||||
const promos = await getPromotionsOnDay(day);
|
const promos = await getPromotionsOnDay(day);
|
||||||
console.log(promos);
|
|
||||||
res.status(200).json(promos);
|
res.status(200).json(promos);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export async function insertMemberRank(member_id: number, rank_id: number, date?
|
|||||||
export async function batchInsertMemberRank(promos: BatchPromotionMember[], author: number) {
|
export async function batchInsertMemberRank(promos: BatchPromotionMember[], author: number) {
|
||||||
try {
|
try {
|
||||||
var con = await pool.getConnection();
|
var con = await pool.getConnection();
|
||||||
console.log(promos);
|
|
||||||
promos.forEach(p => {
|
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))])
|
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 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
|
COUNT(*) AS total_grouped_days_count
|
||||||
FROM
|
FROM
|
||||||
(
|
(
|
||||||
@@ -79,10 +78,9 @@ export async function getPromotionHistorySummary(page: number = 1, pageSize: num
|
|||||||
WHERE reason = 'Rank Change'
|
WHERE reason = 'Rank Change'
|
||||||
) AS grouped_days;`))[0]);
|
) AS grouped_days;`))[0]);
|
||||||
|
|
||||||
console.log(loaCount);
|
let pageCount = rowCount / pageSize;
|
||||||
let pageCount = loaCount / pageSize;
|
|
||||||
|
|
||||||
let output: PagedData<PromotionSummary> = { data: promoList, pagination: { page: page, pageSize: pageSize, total: loaCount, totalPages: pageCount } }
|
let output: PagedData<PromotionSummary> = { data: promoList, pagination: { page: page, pageSize: pageSize, total: rowCount, totalPages: pageCount } }
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
||||||
export type LogDepth = 'normal' | 'verbose';
|
export type LogDepth = 'normal' | 'verbose';
|
||||||
export type LogType = 'http' | 'app';
|
export type LogType = 'http' | 'app' | 'auth';
|
||||||
|
|
||||||
export interface LogHeader {
|
export interface LogHeader {
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
@@ -27,7 +27,6 @@ function shouldLog(depth: LogDepth) {
|
|||||||
|
|
||||||
function emitLog(header: LogHeader, payload: LogPayload = {}) {
|
function emitLog(header: LogHeader, payload: LogPayload = {}) {
|
||||||
if (!shouldLog(header.depth)) return;
|
if (!shouldLog(header.depth)) return;
|
||||||
console.log(header, payload);
|
|
||||||
|
|
||||||
const logLine = { ...header, ...payload };
|
const logLine = { ...header, ...payload };
|
||||||
console.log(JSON.stringify(logLine));
|
console.log(JSON.stringify(logLine));
|
||||||
@@ -51,19 +50,19 @@ export const logger = {
|
|||||||
emitLog(header, payload);
|
emitLog(header, payload);
|
||||||
},
|
},
|
||||||
|
|
||||||
info(type: string, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) {
|
info(type: LogType, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) {
|
||||||
this.log('info', type, message, data, depth, context);
|
this.log('info', type, message, data, depth, context);
|
||||||
},
|
},
|
||||||
|
|
||||||
debug(type: string, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) {
|
debug(type: LogType, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) {
|
||||||
this.log('debug', type, message, data, depth, context);
|
this.log('debug', type, message, data, depth, context);
|
||||||
},
|
},
|
||||||
|
|
||||||
warn(type: string, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) {
|
warn(type: LogType, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) {
|
||||||
this.log('warn', type, message, data, depth, context);
|
this.log('warn', type, message, data, depth, context);
|
||||||
},
|
},
|
||||||
|
|
||||||
error(type: string, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) {
|
error(type: LogType, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) {
|
||||||
this.log('error', type, message, data, depth, context);
|
this.log('error', type, message, data, depth, context);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,6 @@ export async function getPromoHistory(page?: number, pageSize?: number): Promise
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getPromotionsOnDay(day: Date): Promise<PromotionDetails[]> {
|
export async function getPromotionsOnDay(day: Date): Promise<PromotionDetails[]> {
|
||||||
console.log(day.toISOString());
|
|
||||||
const res = await fetch(`${addr}/memberRanks/${day.toISOString()}`, {
|
const res = await fetch(`${addr}/memberRanks/${day.toISOString()}`, {
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -105,7 +105,6 @@ export const useMemberDirectory = defineStore('memberDirectory', () => {
|
|||||||
try {
|
try {
|
||||||
const res = await getFullMembers(ids);
|
const res = await getFullMembers(ids);
|
||||||
for (const m of res) {
|
for (const m of res) {
|
||||||
console.log(m)
|
|
||||||
full[m.member.member_id] = m;
|
full[m.member.member_id] = m;
|
||||||
|
|
||||||
const waiters = fullWaiters.get(m.member.member_id);
|
const waiters = fullWaiters.get(m.member.member_id);
|
||||||
|
|||||||
Reference in New Issue
Block a user