-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6983c76
commit 8ed046f
Showing
9 changed files
with
219 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,4 +24,5 @@ npm-debug.log* | |
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
src/services/pin/pins | ||
src/services/pin/pins | ||
logs/ |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { Request, Response } from 'express'; | ||
import rateLimit from 'express-rate-limit'; | ||
import LoggerService from '../services/logger.service'; | ||
|
||
const DEFAULT_LOG_LIMIT_WINDOW_MS = 15; | ||
const DEFAULT_LOG_LIMIT_MAX = 40; | ||
|
||
export const logLimiter = rateLimit({ | ||
windowMs: (process.env.LOG_LIMIT_WINDOW_MS ? parseInt(process.env.LOG_LIMIT_WINDOW_MS) : DEFAULT_LOG_LIMIT_WINDOW_MS) * 60 * 1000, | ||
max: process.env.LOG_LIMIT_MAX ? parseInt(process.env.LOG_LIMIT_MAX) : DEFAULT_LOG_LIMIT_MAX, | ||
message: { message: 'Too many log requests, please try again later' } | ||
}); | ||
|
||
export async function log(req: Request, res: Response): Promise<Response> { | ||
try { | ||
const { level = 'INFO', message, metadata } = req.body; | ||
|
||
if (!message) { | ||
return res.status(400).json({ message: 'Message is required' }); | ||
} | ||
|
||
if (!['INFO', 'ERROR', 'WARN'].includes(level)) { | ||
return res.status(400).json({ message: 'Invalid log level' }); | ||
} | ||
|
||
await LoggerService.log(level, message, metadata); | ||
return res.status(200).json({ message: 'Log entry created successfully' }); | ||
} catch (error) { | ||
console.error('Error creating log entry:', error); | ||
return res.status(500).json({ message: 'Error creating log entry' }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { Request, Response, NextFunction } from 'express'; | ||
import dotenv from 'dotenv'; | ||
import crypto from 'crypto'; | ||
|
||
dotenv.config(); | ||
|
||
function hashToken(token: string): string { | ||
return crypto.createHash('sha256').update(token).digest('hex'); | ||
} | ||
|
||
export const authenticateToken = (req: Request, res: Response, next: NextFunction) => { | ||
const actual_token = process.env.API_TOKEN | ||
? hashToken(process.env.API_TOKEN) | ||
: '92f5b24048bd100fc7cb1dc770f0d72e1ae4300eb9e13f642304ee71e8515ef4'; | ||
const authHeader = req.headers['authorization']; | ||
const token = authHeader && authHeader.split(' ')[1]; | ||
|
||
if (!token) { | ||
return res.status(401).json({ message: 'No token provided' }); | ||
} | ||
|
||
if (token !== actual_token) { | ||
return res.status(403).json({ message: 'Invalid token' }); | ||
} | ||
|
||
next(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { Router } from "express"; | ||
import { authenticateToken } from "../middlewares/auth.middleware"; | ||
import { log } from "../controllers/logger.controller"; | ||
import { logLimiter } from "../controllers/logger.controller"; | ||
|
||
const LoggerRouter = Router(); | ||
|
||
LoggerRouter.post('/', authenticateToken, logLimiter, log); | ||
|
||
export default LoggerRouter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import fs from 'fs/promises'; | ||
import path from 'path'; | ||
import { createWriteStream, existsSync, mkdirSync, WriteStream } from 'fs'; | ||
|
||
export class LoggerService { | ||
private static instance: LoggerService; | ||
private readonly logDir: string; | ||
private readonly maxFileSize: number; // in bytes | ||
private readonly maxFiles: number; | ||
private currentLogFile: string; | ||
private writeStream: WriteStream | null; | ||
|
||
private constructor() { | ||
this.logDir = path.join(__dirname, '../../logs'); | ||
this.maxFileSize = 5 * 1024 * 1024; // 5MB | ||
this.maxFiles = 5; | ||
this.currentLogFile = path.join(this.logDir, 'app.log'); | ||
this.writeStream = null; | ||
this.initializeLogDirectory(); | ||
} | ||
|
||
public static getInstance(): LoggerService { | ||
if (!LoggerService.instance) { | ||
LoggerService.instance = new LoggerService(); | ||
} | ||
return LoggerService.instance; | ||
} | ||
|
||
private initializeLogDirectory(): void { | ||
if (!existsSync(this.logDir)) { | ||
mkdirSync(this.logDir, { recursive: true }); | ||
} | ||
} | ||
|
||
private async rotateLog(): Promise<void> { | ||
if (this.writeStream) { | ||
this.writeStream.end(); | ||
this.writeStream = null; | ||
} | ||
|
||
const files = await fs.readdir(this.logDir); | ||
const logFiles = files | ||
.filter(file => file.startsWith('app.log')) | ||
.sort((a, b) => b.localeCompare(a)); | ||
|
||
// Rotate existing files | ||
for (const file of logFiles) { | ||
const filePath = path.join(this.logDir, file); | ||
const stats = await fs.stat(filePath); | ||
|
||
if (file === 'app.log' && stats.size >= this.maxFileSize) { | ||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); | ||
await fs.rename(filePath, path.join(this.logDir, `app.log.${timestamp}`)); | ||
} | ||
} | ||
|
||
// Remove oldest files if we exceed maxFiles | ||
if (logFiles.length >= this.maxFiles) { | ||
const filesToRemove = logFiles.slice(this.maxFiles - 1); | ||
for (const file of filesToRemove) { | ||
await fs.unlink(path.join(this.logDir, file)); | ||
} | ||
} | ||
} | ||
|
||
private getWriteStream(): WriteStream { | ||
if (!this.writeStream) { | ||
this.writeStream = createWriteStream(this.currentLogFile, { flags: 'a' }); | ||
} | ||
return this.writeStream; | ||
} | ||
|
||
public async log(level: 'INFO' | 'ERROR' | 'WARN', message: string, metadata?: any): Promise<void> { | ||
try { | ||
const stats = existsSync(this.currentLogFile) | ||
? await fs.stat(this.currentLogFile) | ||
: { size: 0 }; | ||
|
||
if (stats.size >= this.maxFileSize) { | ||
await this.rotateLog(); | ||
} | ||
|
||
const timestamp = new Date().toISOString(); | ||
const logEntry = { | ||
timestamp, | ||
level, | ||
message, | ||
...(metadata && { metadata }), | ||
}; | ||
|
||
const logLine = `${JSON.stringify(logEntry)}\n`; | ||
this.getWriteStream().write(logLine); | ||
} catch (error) { | ||
console.error('Error writing to log file:', error); | ||
} | ||
} | ||
|
||
public async info(message: string, metadata?: any): Promise<void> { | ||
return this.log('INFO', message, metadata); | ||
} | ||
|
||
public async error(message: string, metadata?: any): Promise<void> { | ||
return this.log('ERROR', message, metadata); | ||
} | ||
|
||
public async warn(message: string, metadata?: any): Promise<void> { | ||
return this.log('WARN', message, metadata); | ||
} | ||
} | ||
|
||
export default LoggerService.getInstance(); |