2025-11-15 18:58:30 +00:00
|
|
|
import Fastify, { FastifyHttpsOptions, FastifyInstance, FastifyListenOptions, FastifyRequest } from "fastify";
|
2025-11-10 17:41:48 +00:00
|
|
|
import fastifyMultipart from "@fastify/multipart";
|
|
|
|
|
import cors from '@fastify/cors';
|
|
|
|
|
|
2025-11-15 18:58:30 +00:00
|
|
|
import { EventSlot } from "../../browser/events/EventSlot";
|
|
|
|
|
import { JSRandomEngine } from "../../browser/random/JSRandomEngine";
|
|
|
|
|
|
2025-11-10 17:41:48 +00:00
|
|
|
import { UserDB } from "./UserDB";
|
|
|
|
|
import { RequestHandler } from "./RequestHandler";
|
|
|
|
|
import { EmailService as EmailService } from "./email/EmailService";
|
|
|
|
|
import { TokenDB } from "./TokenDB";
|
|
|
|
|
import { UserManagementServerSettings } from "./UserManagementServerSettings";
|
|
|
|
|
import { RJLog } from "../log/RJLog";
|
|
|
|
|
import { Session } from "./Session";
|
|
|
|
|
import { Role } from "./permissions/Role";
|
|
|
|
|
import { RolesData } from "./permissions/RolesData";
|
|
|
|
|
import { Files } from "../files/Files";
|
|
|
|
|
import { LocationService } from "./location/LocationService";
|
|
|
|
|
import { RequestRequirement } from "./requirements/RequestRequirement";
|
|
|
|
|
import { NotTooManyRequests } from "./requirements/security/NotTooManyRequests";
|
2025-11-11 10:58:11 +00:00
|
|
|
import { FilesSync } from "../files/FilesSync";
|
2025-11-15 18:58:30 +00:00
|
|
|
import { UserData } from "./UserData";
|
|
|
|
|
import { Scheduler } from "./scheduler/Scheduler";
|
|
|
|
|
import { DateHelper } from "../../browser/date/DateHelper";
|
|
|
|
|
import { DateFormatter } from "../../browser/date/DateFormatter";
|
|
|
|
|
import { Duration } from "../../browser/date/Duration";
|
|
|
|
|
import { Task } from "./scheduler/Task";
|
|
|
|
|
import { UserApp } from "./apps/UserApp";
|
|
|
|
|
import { UserAppFactory } from "./apps/UserAppFactory";
|
|
|
|
|
import { iTaskScheduler } from "./scheduler/iTaskScheduler";
|
2025-11-10 17:41:48 +00:00
|
|
|
|
|
|
|
|
export class UserManagementServer
|
|
|
|
|
{
|
|
|
|
|
_app:FastifyInstance;
|
|
|
|
|
get app(){ return this._app; }
|
|
|
|
|
|
|
|
|
|
_userDB:UserDB;
|
|
|
|
|
get userDB(){ return this._userDB; }
|
|
|
|
|
|
|
|
|
|
_tokenDB:TokenDB;
|
|
|
|
|
get tokenDB(){ return this._tokenDB;}
|
|
|
|
|
|
|
|
|
|
_emailService:EmailService;
|
|
|
|
|
get email(){ return this._emailService; }
|
|
|
|
|
|
|
|
|
|
_locationService:LocationService;
|
|
|
|
|
get location(){ return this._locationService; }
|
|
|
|
|
|
|
|
|
|
_sessions:Map<string,Session> = new Map<string,Session>();
|
|
|
|
|
_roles = new Map<string,Role>();
|
|
|
|
|
|
|
|
|
|
_handlers:RequestHandler[] = [];
|
|
|
|
|
|
|
|
|
|
_settings:UserManagementServerSettings;
|
|
|
|
|
_globalRequirements:RequestRequirement[] = [];
|
|
|
|
|
get globalRequirements(){ return this._globalRequirements; }
|
|
|
|
|
|
2025-11-15 18:58:30 +00:00
|
|
|
_apps:UserApp<any>[] = [];
|
|
|
|
|
get apps(){ return this._apps; }
|
|
|
|
|
|
|
|
|
|
_scheduler:Scheduler;
|
|
|
|
|
get scheduler(){ return this._scheduler; }
|
|
|
|
|
|
|
|
|
|
readonly onFileChanged = new EventSlot<string>();
|
|
|
|
|
|
2025-11-10 17:41:48 +00:00
|
|
|
|
|
|
|
|
async initialize( settings:UserManagementServerSettings, mailService:EmailService, handlers:RequestHandler[], globalRequirements:RequestRequirement[] = null ):Promise<void>
|
|
|
|
|
{
|
|
|
|
|
this._settings = settings;
|
|
|
|
|
|
|
|
|
|
UserManagementServerSettings.makeAllPathsAbsolute( settings );
|
|
|
|
|
|
|
|
|
|
await this._initializeApp();
|
|
|
|
|
await this._addGlobalRequirements( globalRequirements || UserManagementServer.DefaultGlobalRequirements() );
|
|
|
|
|
await this._addServices( mailService );
|
|
|
|
|
await this._addHandlers( handlers );
|
2025-11-15 18:58:30 +00:00
|
|
|
await this._startApps();
|
2025-11-10 17:41:48 +00:00
|
|
|
|
|
|
|
|
await this._startServer();
|
|
|
|
|
|
2025-11-15 18:58:30 +00:00
|
|
|
this._update();
|
|
|
|
|
|
2025-11-10 17:41:48 +00:00
|
|
|
return Promise.resolve();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DefaultGlobalRequirements():RequestRequirement[]
|
|
|
|
|
{
|
|
|
|
|
let globalDefaults:RequestRequirement[] =
|
|
|
|
|
[
|
|
|
|
|
new NotTooManyRequests()
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return globalDefaults;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _addGlobalRequirements( globalRequirements:RequestRequirement[] ):Promise<void>
|
|
|
|
|
{
|
|
|
|
|
this._globalRequirements = globalRequirements;
|
|
|
|
|
|
|
|
|
|
for ( let gr of this._globalRequirements )
|
|
|
|
|
{
|
|
|
|
|
await gr.initializeGlobal( this );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-15 18:58:30 +00:00
|
|
|
getUser( request:FastifyRequest ):Promise<UserData>
|
|
|
|
|
{
|
|
|
|
|
let requestBody = request.body;
|
|
|
|
|
let tokenData = requestBody as { token:string };
|
|
|
|
|
let tokenID = tokenData.token;
|
|
|
|
|
|
|
|
|
|
let session = this._sessions.get( tokenID );
|
|
|
|
|
return this.userDB.byID( session.userID );
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-10 17:41:48 +00:00
|
|
|
async sendEmail( to:string, title:string, message:string ):Promise<void>
|
|
|
|
|
{
|
|
|
|
|
return this.email.send( this._settings.emailFrom, to, title, message );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _initializeApp():Promise<void>
|
|
|
|
|
{
|
2025-11-11 10:33:59 +00:00
|
|
|
if ( this._settings.isDebugMode )
|
|
|
|
|
{
|
|
|
|
|
this._app = Fastify();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-11-11 10:59:48 +00:00
|
|
|
let httpsKey = FilesSync.loadUTF8( this._settings.httpsKeyPath );
|
|
|
|
|
let httpsCert = FilesSync.loadUTF8( this._settings.httpsCertPath );
|
|
|
|
|
|
|
|
|
|
RJLog.log( "Key", httpsKey );
|
|
|
|
|
RJLog.log( "Cert", httpsCert );
|
|
|
|
|
|
2025-11-11 10:33:59 +00:00
|
|
|
this._app = Fastify(
|
|
|
|
|
{
|
|
|
|
|
https:
|
|
|
|
|
{
|
2025-11-11 10:59:48 +00:00
|
|
|
key:httpsKey,
|
|
|
|
|
cert:httpsCert
|
2025-11-11 10:33:59 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-10 17:41:48 +00:00
|
|
|
this._app.register( fastifyMultipart );
|
|
|
|
|
|
2025-11-15 18:58:30 +00:00
|
|
|
if ( this._settings.isDebugMode )
|
2025-11-10 17:41:48 +00:00
|
|
|
{
|
2025-11-15 18:58:30 +00:00
|
|
|
RJLog.log( "Setting any cors:" );
|
|
|
|
|
|
2025-11-10 17:41:48 +00:00
|
|
|
await this._app.register( cors,
|
2025-11-15 18:58:30 +00:00
|
|
|
{
|
|
|
|
|
origin: "*",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for ( let corsURL of this._settings.corsURLs )
|
|
|
|
|
{
|
|
|
|
|
RJLog.log( "Adding cors:", corsURL );
|
|
|
|
|
|
|
|
|
|
await this._app.register( cors,
|
|
|
|
|
{
|
|
|
|
|
origin: corsURL,
|
|
|
|
|
credentials: true,
|
|
|
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-11-10 17:41:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Promise.resolve();
|
2025-11-15 18:58:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_updateDuration = 60;
|
|
|
|
|
|
|
|
|
|
protected _update()
|
|
|
|
|
{
|
|
|
|
|
let self = this;
|
|
|
|
|
|
|
|
|
|
let waitTimeMS = Duration.toMilliSeconds( this._updateDuration );
|
|
|
|
|
|
|
|
|
|
setTimeout( () =>{ self._update() }, waitTimeMS );
|
2025-11-10 17:41:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _addServices( mailService:EmailService ):Promise<void>
|
|
|
|
|
{
|
|
|
|
|
this._userDB = await UserDB.load( this, this._settings.userDBPath );
|
|
|
|
|
|
2025-11-15 18:58:30 +00:00
|
|
|
this._scheduler = new Scheduler();
|
|
|
|
|
|
|
|
|
|
// RJLog.log( "Loading roles:", this._settings.rolesPath );
|
2025-11-10 17:41:48 +00:00
|
|
|
let rolesData = await Files.loadJSON<RolesData>( this._settings.rolesPath );
|
|
|
|
|
this._roles = new Map<string,Role>();
|
|
|
|
|
rolesData.roles.forEach( r => this._roles.set( r.id, r ) );
|
|
|
|
|
|
|
|
|
|
this._tokenDB = new TokenDB( this );
|
|
|
|
|
this._emailService = mailService;
|
|
|
|
|
|
|
|
|
|
this._locationService = await LocationService.create(
|
|
|
|
|
this._settings.geoLocationPath,
|
|
|
|
|
this._settings.geoAccountID,
|
|
|
|
|
this._settings.geoLicenseKey
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let ipToCheck = "2a02:3100:25e5:2500:65d7:61b7:33f7:9d7f";
|
|
|
|
|
let location = await this._locationService.getLocation( ipToCheck );
|
|
|
|
|
|
|
|
|
|
RJLog.log( "IP", ipToCheck, "Location:", location );
|
|
|
|
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _addHandlers( handlers:RequestHandler[] ):Promise<void>
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for ( let handler of handlers )
|
|
|
|
|
{
|
|
|
|
|
await handler.initialize( this );
|
|
|
|
|
this._handlers.push( handler );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _startServer()
|
|
|
|
|
{
|
2025-11-11 09:35:13 +00:00
|
|
|
let listenOptions:FastifyListenOptions =
|
|
|
|
|
{
|
|
|
|
|
port: this._settings.port
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-15 18:58:30 +00:00
|
|
|
// if ( ! this._settings.isDebugMode )
|
|
|
|
|
// {
|
2025-11-11 09:35:13 +00:00
|
|
|
listenOptions.host = "0.0.0.0";
|
2025-11-15 18:58:30 +00:00
|
|
|
// }
|
2025-11-11 09:35:13 +00:00
|
|
|
|
2025-11-10 17:41:48 +00:00
|
|
|
this.app.listen(
|
|
|
|
|
|
2025-11-11 09:35:13 +00:00
|
|
|
listenOptions,
|
2025-11-10 17:41:48 +00:00
|
|
|
|
|
|
|
|
( error, address ) =>
|
|
|
|
|
{
|
|
|
|
|
if ( error )
|
|
|
|
|
{
|
|
|
|
|
RJLog.error( error );
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RJLog.log( `Server running on ${address}` );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-15 18:58:30 +00:00
|
|
|
async _startApps()
|
|
|
|
|
{
|
|
|
|
|
for ( let appID of this._settings.userApps )
|
|
|
|
|
{
|
|
|
|
|
let app = UserAppFactory.create( appID, this );
|
|
|
|
|
RJLog.log( "Starting app:", app.id );
|
|
|
|
|
this._apps.push( app );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for ( let app of this._apps )
|
|
|
|
|
{
|
|
|
|
|
await app.initialize();
|
|
|
|
|
|
|
|
|
|
if ( app.schedulesTasks )
|
|
|
|
|
{
|
|
|
|
|
this.scheduler._taskSchedulers.push( app as any as iTaskScheduler );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this._scheduler.update();
|
|
|
|
|
}
|
2025-11-10 17:41:48 +00:00
|
|
|
}
|