final ts cleanup?
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 11m31s
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 11m31s
This commit is contained in:
115
server.ts
115
server.ts
@@ -1,9 +1,9 @@
|
||||
// server.ts
|
||||
import express, { Request, Response, NextFunction } from 'express';
|
||||
import { randomUUID } from 'crypto';
|
||||
import timeout from 'connect-timeout';
|
||||
import timeout from 'connect-timeout';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import listEndpoints from 'express-list-endpoints';
|
||||
import listEndpoints from 'express-list-endpoints';
|
||||
import { getPool } from './src/services/db/connection.db';
|
||||
|
||||
import passport from './src/routes/passport.routes';
|
||||
@@ -23,10 +23,15 @@ import statsRouter from './src/routes/stats.routes';
|
||||
import gamificationRouter from './src/routes/gamification.routes';
|
||||
import systemRouter from './src/routes/system.routes';
|
||||
import healthRouter from './src/routes/health.routes';
|
||||
import { errorHandler } from './src/middleware/errorHandler';
|
||||
import { errorHandler } from './src/middleware/errorHandler';
|
||||
import { backgroundJobService, startBackgroundJobs } from './src/services/backgroundJobService';
|
||||
import type { UserProfile } from './src/types';
|
||||
import { analyticsQueue, weeklyAnalyticsQueue, gracefulShutdown, tokenCleanupQueue } from './src/services/queueService.server';
|
||||
import {
|
||||
analyticsQueue,
|
||||
weeklyAnalyticsQueue,
|
||||
gracefulShutdown,
|
||||
tokenCleanupQueue,
|
||||
} from './src/services/queueService.server';
|
||||
|
||||
// --- START DEBUG LOGGING ---
|
||||
// Log the database connection details as seen by the SERVER PROCESS.
|
||||
@@ -41,12 +46,15 @@ logger.info(` Database: ${process.env.DB_NAME}`);
|
||||
// Query the users table to see what the server process sees on startup.
|
||||
// Corrected the query to be unambiguous by specifying the table alias for each column.
|
||||
// `id` and `email` come from the `users` table (u), and `role` comes from the `profiles` table (p).
|
||||
getPool().query('SELECT u.user_id, u.email, p.role FROM public.users u JOIN public.profiles p ON u.user_id = p.user_id')
|
||||
.then(res => {
|
||||
getPool()
|
||||
.query(
|
||||
'SELECT u.user_id, u.email, p.role FROM public.users u JOIN public.profiles p ON u.user_id = p.user_id',
|
||||
)
|
||||
.then((res) => {
|
||||
logger.debug('[SERVER PROCESS] Users found in DB on startup:');
|
||||
console.table(res.rows);
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
logger.error({ err }, '[SERVER PROCESS] Could not query users table on startup.');
|
||||
});
|
||||
|
||||
@@ -74,10 +82,10 @@ app.use(timeout('5m'));
|
||||
|
||||
// --- Logging Middleware ---
|
||||
const getDurationInMilliseconds = (start: [number, number]): number => {
|
||||
const NS_PER_SEC = 1e9;
|
||||
const NS_TO_MS = 1e6;
|
||||
const diff = process.hrtime(start);
|
||||
return (diff[0] * NS_PER_SEC + diff[1]) / NS_TO_MS;
|
||||
const NS_PER_SEC = 1e9;
|
||||
const NS_TO_MS = 1e6;
|
||||
const diff = process.hrtime(start);
|
||||
return (diff[0] * NS_PER_SEC + diff[1]) / NS_TO_MS;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -96,55 +104,55 @@ interface RequestLogDetails {
|
||||
}
|
||||
|
||||
const requestLogger = (req: Request, res: Response, next: NextFunction) => {
|
||||
const requestId = randomUUID();
|
||||
const user = req.user as UserProfile | undefined;
|
||||
const start = process.hrtime();
|
||||
const { method, originalUrl } = req;
|
||||
const requestId = randomUUID();
|
||||
const user = req.user as UserProfile | undefined;
|
||||
const start = process.hrtime();
|
||||
const { method, originalUrl } = req;
|
||||
|
||||
// Create a request-scoped logger instance as per ADR-004
|
||||
// This attaches contextual info to every log message generated for this request.
|
||||
req.log = logger.child({
|
||||
request_id: requestId,
|
||||
user_id: user?.user_id, // This will be undefined until the auth middleware runs, but the logger will hold the reference.
|
||||
ip_address: req.ip,
|
||||
});
|
||||
// Create a request-scoped logger instance as per ADR-004
|
||||
// This attaches contextual info to every log message generated for this request.
|
||||
req.log = logger.child({
|
||||
request_id: requestId,
|
||||
user_id: user?.user.user_id, // This will be undefined until the auth middleware runs, but the logger will hold the reference.
|
||||
ip_address: req.ip,
|
||||
});
|
||||
|
||||
req.log.debug({ method, originalUrl }, `[Request Logger] INCOMING`);
|
||||
req.log.debug({ method, originalUrl }, `[Request Logger] INCOMING`);
|
||||
|
||||
res.on('finish', () => {
|
||||
const durationInMilliseconds = getDurationInMilliseconds(start);
|
||||
const { statusCode, statusMessage } = res;
|
||||
const finalUser = req.user as UserProfile | undefined;
|
||||
res.on('finish', () => {
|
||||
const durationInMilliseconds = getDurationInMilliseconds(start);
|
||||
const { statusCode, statusMessage } = res;
|
||||
const finalUser = req.user as UserProfile | undefined;
|
||||
|
||||
// The base log object includes details relevant for all status codes.
|
||||
const logDetails: RequestLogDetails = {
|
||||
user_id: finalUser?.user_id,
|
||||
method,
|
||||
originalUrl,
|
||||
statusCode,
|
||||
statusMessage,
|
||||
duration: durationInMilliseconds.toFixed(2),
|
||||
};
|
||||
// The base log object includes details relevant for all status codes.
|
||||
const logDetails: RequestLogDetails = {
|
||||
user_id: finalUser?.user.user_id,
|
||||
method,
|
||||
originalUrl,
|
||||
statusCode,
|
||||
statusMessage,
|
||||
duration: durationInMilliseconds.toFixed(2),
|
||||
};
|
||||
|
||||
// For failed requests, add the full request details for better debugging.
|
||||
// Pino's `redact` config will automatically sanitize sensitive headers and body fields.
|
||||
if (statusCode >= 400) {
|
||||
logDetails.req = { headers: req.headers, body: req.body };
|
||||
}
|
||||
if (statusCode >= 500) req.log.error(logDetails, 'Request completed with server error');
|
||||
else if (statusCode >= 400) req.log.warn(logDetails, 'Request completed with client error');
|
||||
else req.log.info(logDetails, 'Request completed successfully');
|
||||
});
|
||||
// For failed requests, add the full request details for better debugging.
|
||||
// Pino's `redact` config will automatically sanitize sensitive headers and body fields.
|
||||
if (statusCode >= 400) {
|
||||
logDetails.req = { headers: req.headers, body: req.body };
|
||||
}
|
||||
if (statusCode >= 500) req.log.error(logDetails, 'Request completed with server error');
|
||||
else if (statusCode >= 400) req.log.warn(logDetails, 'Request completed with client error');
|
||||
else req.log.info(logDetails, 'Request completed successfully');
|
||||
});
|
||||
|
||||
next();
|
||||
next();
|
||||
};
|
||||
|
||||
app.use(requestLogger); // Use the logging middleware for all requests
|
||||
|
||||
// --- Security Warning ---
|
||||
if (!process.env.JWT_SECRET) {
|
||||
logger.error('CRITICAL: JWT_SECRET is not set. The application cannot start securely.');
|
||||
process.exit(1);
|
||||
logger.error('CRITICAL: JWT_SECRET is not set. The application cannot start securely.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// --- API Routes ---
|
||||
@@ -197,13 +205,18 @@ if (process.env.NODE_ENV !== 'test') {
|
||||
});
|
||||
|
||||
// Start the scheduled background jobs
|
||||
startBackgroundJobs(backgroundJobService, analyticsQueue, weeklyAnalyticsQueue, tokenCleanupQueue, logger);
|
||||
startBackgroundJobs(
|
||||
backgroundJobService,
|
||||
analyticsQueue,
|
||||
weeklyAnalyticsQueue,
|
||||
tokenCleanupQueue,
|
||||
logger,
|
||||
);
|
||||
|
||||
// --- Graceful Shutdown Handling ---
|
||||
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
||||
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
||||
}
|
||||
|
||||
|
||||
// Export the app for integration testing
|
||||
export default app;
|
||||
export default app;
|
||||
|
||||
Reference in New Issue
Block a user