import { RJLog } from "../log/RJLog"; import { RequestRequirement } from "./requirements/RequestRequirement"; import { UserManagementServer } from "./UserManagementServer"; import { FastifyRequest, FastifyReply } from 'fastify'; import { Message } from "../../browser/messages/Message"; import { UserData } from "./UserData"; import { UAParser } from 'ua-parser-js'; import { LocationData } from "./location/LocationData"; import { className } from "../../browser/tools/TypeUtilities"; export enum RequestType { GET, POST } export abstract class RequestHandler { _ums:UserManagementServer; get ums(){ return this._ums; }; _type:RequestType; _url:string; requirements:RequestRequirement[] = []; get app(){ return this._ums.app }; get userDB(){ return this._ums.userDB; } constructor( rt:RequestType, url:string, requirements:RequestRequirement[] = [] ) { this._url = url; this._type = rt; this.requirements = requirements; } async initialize( ums:UserManagementServer ):Promise { this._ums = ums; await this._initialize(); await this._register(); for ( let r of this.requirements ) { await r.initialize( this ); } return Promise.resolve(); } protected _register():Promise { RJLog.log( RequestType[ this._type ], this._url ); if ( RequestType.GET == this._type ) { this.app.get( this._url, ( request, reply ) => { this.handle( request, reply ); } ); } else if ( RequestType.POST == this._type ) { this.app.post( this._url, ( request, reply ) => { this.handle( request, reply ); } ); } return Promise.resolve(); } protected _currentRequest:FastifyRequest; protected _currentReply:FastifyReply; async handle( request:FastifyRequest, reply:FastifyReply ):Promise { if ( ! request || ! reply ) { RJLog.warn( "Aborting request:", "Request:", ! request, "Reply:", ! reply ); return Promise.resolve(); } this._currentRequest = request; this._currentReply = reply; RJLog.log( "Processing request:", "Request:", request.url, request.ip ); for ( let r of this._ums.globalRequirements ) { let messages = await r.handle( request, reply ); if ( Message.hasError( messages ) ) { RJLog.log( "Global Requirement not fullfilled: ", className( r ), messages.map( m => JSON.stringify( m ) ).join( ", " ) ); return this.sendError( "Error during global requirements check" ); } } for ( let r of this.requirements ) { let messages = await r.handle( request, reply ); if ( Message.hasError( messages ) ) { RJLog.log( messages[ 0 ].content ); RJLog.log( "Requirement not fullfilled: ", className( r ), messages.map( m => JSON.stringify( m ) ).join( ", " ) ); return this.sendError( "Error during requirements check" ); } } await this._handle( request, reply ); this._currentRequest = null; this._currentReply = null; return Promise.resolve(); } protected _initialize():Promise { return Promise.resolve(); }; protected abstract _handle( request:FastifyRequest, reply:FastifyReply ):Promise; get ip() { return this._currentRequest.ip; } get userAgent() { return this._currentRequest.headers[ "user-agent" ]; } getLocation():Promise { return this._ums.location.getLocation( this.ip ); } protected sendJSON( obj:any ):Promise { this._currentReply.send( obj ); return Promise.resolve(); } getUser():Promise { let request = this._currentRequest; let requestBody = request.body; let tokenData = requestBody as { token:string }; let tokenID = tokenData.token; let session = this._ums._sessions.get( tokenID ); return this._ums.userDB.byID( session.userID ); } protected sendInfo( info:string ):Promise { RJLog.log( info ); return this.sendJSON( Message.Info( info ) ); } protected sendError( error:string, with400ErrorCode:boolean = true ):Promise { RJLog.log( error ); if ( with400ErrorCode ) { this._currentReply.code( 400 ); } return this.sendJSON( Message.Error( error ) ); } protected sendEmail( to:string, title:string, message:string ) { this._ums.sendEmail( to, title, message ); } protected sendDataInfo( info:string, data:any ) { let json = Message.Info( info ) as any; json.data = data; return this.sendJSON( json ); } }