import { Inject, Injectable } from '@nestjs/common'; import { Request } from 'express'; import { Repository } from 'typeorm'; import { User } from '../user/user.entity'; import { AuditLog } from './audit.entity'; import { AuditAction } from './audit.enum'; import { lookup, Lookup } from 'geoip-lite'; import { parse, Details } from 'express-useragent'; import { FormUtilityService } from 'src/modules/utility/services/form-utility.service'; export interface UserLoginEntry { login_at: Date; current: boolean; location: Partial; user_agent: Partial
; } @Injectable() export class AuditService { constructor( @Inject('AUDIT_REPOSITORY') private readonly audit: Repository, private readonly form: FormUtilityService, ) {} public async insertAudit( action: AuditAction, comment?: string, user?: User, ip?: string, ua?: string, ) { const audit = new AuditLog(); audit.action = action as string; audit.content = comment; audit.actor_ip = ip; audit.actor_ua = ua; audit.actor = user; if ( action === AuditAction.MALICIOUS_REQUEST || action === AuditAction.THROTTLE ) { audit.flagged = true; // TODO: email administrator } await this.updateAudit(audit); return audit; } public async auditRequest( req: Request, type: AuditAction, comment?: string, user?: User, ) { return this.insertAudit( type, comment, user || req.user || null, req.ip, req.header('user-agent'), ); } public getIPLocation(ip: string) { return lookup(ip); } public getUserAgentInfo(ua: string) { return parse(ua); } public async getUserLogins( user: User, sessid?: string, ): Promise { const userLogins: UserLoginEntry[] = []; const auditEntries = await this.audit.find({ where: { actor: { id: user.id }, action: AuditAction.LOGIN }, order: { created_at: 'DESC' }, take: 10, }); auditEntries.forEach((entry) => { userLogins.push({ login_at: entry.created_at, current: sessid === entry.content, location: entry.actor_ip ? this.form.pluckObject(this.getIPLocation(entry.actor_ip), [ 'country', 'city', 'timezone', 'll', ]) : null, user_agent: entry.actor_ua ? this.form.pluckObject(this.getUserAgentInfo(entry.actor_ua), [ 'browser', 'version', 'os', 'platform', ]) : null, }); }); return userLogins; } public async getUserAccountCreation(user: User) { const auditEntry = await this.audit.findOne({ where: { actor: { id: user.id }, action: AuditAction.REGISTRATION }, }); if (!auditEntry) { return null; } return { created_at: auditEntry.created_at, ip: auditEntry.actor_ip, location: auditEntry.actor_ip ? this.form.pluckObject(this.getIPLocation(auditEntry.actor_ip), [ 'country', 'city', 'timezone', 'll', ]) : null, user_agent: auditEntry.actor_ua ? this.form.pluckObject(this.getUserAgentInfo(auditEntry.actor_ua), [ 'browser', 'version', 'os', 'platform', ]) : null, }; } public async updateAudit(audit: AuditLog): Promise { await this.audit.save(audit); } }