Skip to content

Commit a8e8c52

Browse files
committed
feat(audit): Implement comprehensive audit logging across services
- Updated package.json to include winston for logging. - Introduced auditService for structured logging and error handling. - Enhanced userRoutes to log errors with context. - Integrated audit logging in server.js for graceful shutdown and error handling. - Added audit logging in appLifecycle for initialization and cleanup events. - Implemented logging in emailService for rate limiting and email operations. - Enhanced isEmailValid utility with audit logging for validation results. - Integrated audit logging in leaderBoardCache for cache operations and updates. - Added audit logging in postPOTD for challenge updates. - Implemented audit logging in streakResetJob for scheduled operations and errors.
1 parent 0f19b63 commit a8e8c52

23 files changed

+1525
-115
lines changed

server/app.js

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import express from "express";
22
import cors from "cors";
3-
import morgan from "morgan";
43
import authRoutes from "./routes/authRoutes.js";
54
import profileRoutes from "./routes/profileRoutes.js";
65
import challengeRoutes from "./routes/challengeRoutes.js";
@@ -18,19 +17,30 @@ import typeSenseRoutes from "./routes/typeSenseRoutes.js";
1817
import { startStreakCronJob } from './utils/streakResetJob.js';
1918
import { appLifecycle } from './utils/appLifecycle.js';
2019
import { globalErrorHandler } from './middleware/errorHandler.js';
20+
import auditService from './services/auditService.js';
21+
import { httpLogger, auditContextMiddleware, auditErrorMiddleware, performanceMonitor } from './middleware/auditMiddleware.js';
2122
dotenv.config();
2223

2324
const app = express();
2425

2526
app.use(cors({ origin: process.env.CLIENT_URL, credentials: true }));
2627

2728
// Memory-safe payload size limits with proper error handling
29+
// Audit context middleware (must be early in middleware chain)
30+
app.use(auditContextMiddleware);
31+
2832
app.use(express.json({
2933
limit: '2mb', // Reduced from 5mb to prevent memory issues
3034
verify: (req, res, buf) => {
31-
// Monitor large requests
35+
// Monitor large requests with audit service
3236
if (buf.length > 1024 * 1024) { // 1MB threshold
33-
console.log(`Large request detected: ${buf.length} bytes from ${req.ip}`);
37+
auditService.security('large_request_detected', {
38+
requestId: req.auditContext?.requestId,
39+
size: buf.length,
40+
ip: req.ip,
41+
url: req.originalUrl,
42+
userAgent: req.get('User-Agent')
43+
});
3444
}
3545
}
3646
}));
@@ -43,7 +53,10 @@ app.use(express.urlencoded({
4353

4454
app.use(cookieParser());
4555
app.use(passport.initialize());
46-
app.use(morgan('dev'));
56+
57+
// Replace Morgan with our audit-integrated HTTP logger
58+
app.use(httpLogger);
59+
app.use(performanceMonitor);
4760

4861

4962
app.use('/api/auth', authRoutes);
@@ -57,13 +70,26 @@ app.use('/api', statsRoutes);
5770
app.use('/api/user', userRoutes);
5871
app.use('/api', typeSenseRoutes);
5972

60-
// Initialize application services
73+
// Error handling middleware
74+
app.use(auditErrorMiddleware);
75+
app.use(globalErrorHandler);
76+
77+
// Initialize application services with audit logging
78+
auditService.systemEvent('app_initializing', {
79+
environment: process.env.NODE_ENV,
80+
version: process.env.npm_package_version
81+
});
82+
6183
warmupLeaderboardCache();
6284
startStreakCronJob();
6385
appLifecycle.initialize();
6486

6587
// Export cleanup function for server.js
66-
export const cleanup = appLifecycle.cleanup.bind(appLifecycle);
88+
export const cleanup = async () => {
89+
auditService.systemEvent('app_shutting_down');
90+
await auditService.shutdown();
91+
return appLifecycle.cleanup();
92+
};
6793

6894
app.get('/hello', (req, res) => { return res.status(200).send("Hello, World!") })
6995

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,72 @@
11
import adminGenerateToken from "../config/adminGenerateToken.js";
22
import Admin from "../models/Admin.js";
3+
import auditService from "../services/auditService.js";
4+
35
export const adminLogin = async (req, res) => {
6+
const audit = auditService.startTrace('admin_login', {
7+
requestId: req.auditContext?.requestId,
8+
ip: req.ip,
9+
userAgent: req.get('User-Agent')
10+
});
11+
412
try {
513
const { email, password } = req.body;
6-
// console.log("Admin login request received:", {email,password});
14+
15+
auditService.security('admin_login_attempt', {
16+
requestId: req.auditContext?.requestId,
17+
email,
18+
ip: req.ip,
19+
userAgent: req.get('User-Agent')
20+
});
21+
722
if (!email || !password) {
23+
auditService.security('admin_login_failed', {
24+
requestId: req.auditContext?.requestId,
25+
email,
26+
reason: 'missing_credentials',
27+
ip: req.ip
28+
});
829
return res.status(400).json({ message: "All fields are required" });
930
}
31+
1032
const admin = await Admin.findOne({ email });
11-
// console.log("Admin found:", admin);
33+
1234
if (!admin) {
35+
auditService.security('admin_login_failed', {
36+
requestId: req.auditContext?.requestId,
37+
email,
38+
reason: 'admin_not_found',
39+
ip: req.ip
40+
});
1341
return res.status(400).json({ error: "No admin found" });
1442
}
43+
1544
if (password !== admin.password) {
45+
auditService.security('admin_login_failed', {
46+
requestId: req.auditContext?.requestId,
47+
email,
48+
adminId: admin._id.toString(),
49+
reason: 'wrong_password',
50+
ip: req.ip
51+
});
1652
return res.status(400).json({ message: "Wrong Password" });
1753
}
54+
1855
const token = await adminGenerateToken(admin);
56+
57+
auditService.security('admin_login_successful', {
58+
requestId: req.auditContext?.requestId,
59+
adminId: admin._id.toString(),
60+
email: admin.email,
61+
ip: req.ip
62+
});
63+
64+
audit.complete({
65+
adminId: admin._id.toString(),
66+
email: admin.email,
67+
success: true
68+
});
69+
1970
res.cookie('Admintoken', token, {
2071
httpOnly: true,
2172
secure: process.env.NODE_ENV === "production",
@@ -26,16 +77,32 @@ export const adminLogin = async (req, res) => {
2677
token
2778
});
2879
} catch (err) {
29-
// console.error(err);
80+
auditService.error('Admin login error', err, {
81+
requestId: req.auditContext?.requestId,
82+
email: req.body.email,
83+
ip: req.ip
84+
});
85+
86+
audit.error(err);
3087
res.status(500).json({ error: "error in admin login", details: err.message });
3188
}
3289
}
3390

3491
export const adminLogout = async (req, res) => {
3592
try {
93+
auditService.security('admin_logout', {
94+
requestId: req.auditContext?.requestId,
95+
adminId: req.user?._id?.toString(),
96+
ip: req.ip
97+
});
98+
3699
res.cookie('Admintoken', "").json({ message: "Logged out" });
37100
} catch (err) {
38-
// console.error(err);
101+
auditService.error('Admin logout error', err, {
102+
requestId: req.auditContext?.requestId,
103+
adminId: req.user?._id?.toString(),
104+
ip: req.ip
105+
});
39106
res.status(500).json({ error: "error in admin logout", details: err.message });
40107
}
41108
}

server/controllers/adminControllers.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { User } from "../models/User.js";
22
import { Challenge } from "../models/Challenge.js";
33
import { Solution } from "../models/Solution.js";
4+
import auditService from "../services/auditService.js";
45

56
export const getUsers = async (req, res) => {
67
try {
@@ -56,14 +57,14 @@ export const getUsers = async (req, res) => {
5657

5758
const totalPages = Math.ceil(totalUsers / pageLimit);
5859

59-
res.json({
60+
return res.status(200).json({
61+
success: true,
6062
users,
6163
pagination: {
6264
currentPage: pageNumber,
63-
totalPages,
64-
totalUsers,
65-
limit: pageLimit,
66-
hasNextPage: pageNumber < totalPages,
65+
totalPages: Math.ceil(filteredUsers / pageLimit),
66+
totalUsers: filteredUsers,
67+
hasNextPage: pageNumber * pageLimit < filteredUsers,
6768
hasPrevPage: pageNumber > 1
6869
},
6970
filters: {
@@ -73,7 +74,7 @@ export const getUsers = async (req, res) => {
7374
});
7475

7576
} catch (err) {
76-
console.error("Error in getUsers:", err);
77+
auditService.error('admin_get_users_error', { error: err.message });
7778
res.status(500).json({
7879
error: "Error in getting users",
7980
details: err.message
@@ -147,7 +148,7 @@ export const addChallenge = async (req, res) => {
147148
});
148149

149150
} catch (error) {
150-
console.error("Error in addChallenge:", error);
151+
auditService.error('admin_add_challenge_error', { error: error.message });
151152
res.status(500).json({
152153
success: false,
153154
message: "Server error",
@@ -220,7 +221,7 @@ export const updateChallenge = async (req, res) => {
220221
solution: updatedSolution
221222
});
222223
} catch (error) {
223-
console.error("Error in updateChallenge:", error);
224+
auditService.error('admin_update_challenge_error', { error: error.message });
224225
res.status(500).json({
225226
success: false,
226227
message: "Server error",
@@ -257,7 +258,7 @@ export const getTodayChallenge = async (req, res) => {
257258
solution: solution || null,
258259
});
259260
} catch (error) {
260-
console.error("Error in getTodayChallenge:", error);
261+
auditService.error('admin_get_today_challenge_error', { error: error.message });
261262
res.status(500).json({
262263
success: false,
263264
message: "Server error",

0 commit comments

Comments
 (0)