const ALARMiatorPluginManager = require('./pluginManager/pluginManager');
const express = require('express');
const session = require('express-session');
const fileUpload = require('express-fileupload');
const bodyParser = require('body-parser');
const path = require('path');
const Database = require('./models/database'); // Database Manager
const Configuration = require('./models/configuration'); // Configuration of CORE
const CoreCron = require('./models/core/core_cron.js'); // Cron Manager
const uuid = require('uuid').v1;
var config = require('./config/config.js');
var log4js = require('log4js');
var cookieParser = require('cookie-parser')
var SQLiteStore = require('connect-sqlite3')(session);
const HandlerState = require('./models/core/handlerState');
const HandlerAlarm = require('./models/core/handlerAlarm');
const HandlerExternalIp = require('./models/core/handlerExternalIp');
const { EventEmitter } = require('events');
// Printer-related objects
const Printer = require('./models/core/core_printing');
var printing = new Printer();
global.printManager = printing;
// globally used objects
const FCMTokens = require('./models/fctmtokens');
class Core extends EventEmitter {
constructor() {
super();
// inititalize class propertys
this.coreDb = null;
this.logger = null; // holds logger instance
this.plgManager = null; // holds instance of plgManager
// Initialize globals
global.coreDb = null; // holds database instance to coredatabase
global.appRoot = path.resolve(__dirname); // holds complete path to application root
global.app = null; // holds instance of this app globally
global.appPort = null; // holds port number for UI
global.cron = null; // holds core manager after initialization
this.app = express();
global.app = this.app;
this.uuid = null; // holds the uuid of this server instance
/**
* initialize defaults for command line parameters handed over with server start
*/
this.args_development = false; // Server rund in development mode
}
/**
* Read all arguments from server start
*/
getStartupArguments() {
process.argv.forEach(function (val, index, array) {
if(val === '--development')
{
this.args_development = true;
console.log('CORE | running in development-mode');
}
else if(val.startsWith('--pm2'))
{
this.args_startedByPM2 = true;
this.args_PM2_Name = val.replace('--pm2=','');
global.serverStartedByPM2 = this.args_startedByPM2;
global.PM2_Name = this.args_PM2_Name;
console.log('CORE | Start by PM2 : ' + global.serverStartedByPM2);
console.log('CORE | PM2-Name : ' + global.PM2_Name);
}
}.bind(this));
this.emit('getStartupArguments', true);
}
/**
* Starts the coredatabase connection
*/
startCoredatabase() {
this.coreDb = new Database();
this.coreDb.startDatabase((successDatabase) => {
if (successDatabase) {
global.coreDb = this.coreDb.db;
this.emit('startCoredatabase', true);
// Database started, start server now
} else {
// Database could not be started. -> Log error to console
console.log('CORE | Error starting up database. Server could not be started. Please check logs for more details.');
this.emit('startCoredatabase', false);
}
});
}
/**
* Starts the global logger(s)
*/
startLoggers() {
/**
* start new Logger
*/
// create a rolling file logger based on date/time that fires process events
const opts = {
logDirectory: global.appRoot + '/logs/server', // NOTE: folder must exist and be writable...
fileNamePattern: 'ALARMiator-Server-<DATE>.log',
dateFormat: 'YYYY.MM.DD'
};
this.logmanager = require('simple-node-logger');
this.logger = this.logmanager.createRollingFileLogger(opts);
this.logger.setLevel('all');
// this.logger should be removed later after the whole project used global.logger
this.app.logger = this.logger;
global.logger = this.logger;
// Emit event
this.emit('startLoggers', true);
}
/**
* starts the server itself
*/
setupServer() {
this.port = 5000;
try {
this.port = config.serverOptions.uiport;
this.emit('setupServer', true);
} catch (err) {
this.logger.debug('CONFIG: ' + err);
this.logger.warn(`CORE | configuration for server port not set. Please check the config.js file in config folder. Using default port 5000 for UI.`);
this.emit('setupServer', true);
}
global.appPort = this.port;
}
/**
* Sets up the middleware (express)
*/
setupMiddelware() {
this.app.use(require('express-status-monitor')());
this.app.set('port', process.env.port || this.port); // set express to use this port
this.app.set('views', __dirname + '/views'); // set express to look in this folder to render our view
this.app.set('view engine', 'ejs'); // configure template engine
this.app.use(bodyParser.urlencoded({ extended: true }));
this.app.use(bodyParser.json()); // parse form data client
this.app.use(express.static(path.join(__dirname, 'public'))); // configure express to use public folder
this.app.use(fileUpload()); // configure fileupload
// Configure Session and Authentication handling (SQLite-Version)
this.app.use(cookieParser());
this.app.use(session({
store: new SQLiteStore({
table: 'sessions',
db: 'sessions.db',
dir: global.appRoot + '/store'
}),
secret: '9xa&aVPKe18XV;f',
cookie: { maxAge: 2 * 24 * 60 * 60 * 1000 }, // 1 week
resave: false,
saveUninitialized: true,
key: 'alarmiator',
secret: 'ALRMiator#2019'
}));
this.emit('setupMiddleware', true);
}
/**
* registers core handlers
*/
startCoreHandlers() {
this.handlerState = new HandlerState();
this.handlerAlarm = new HandlerAlarm()
this.handlerExternalIp = new HandlerExternalIp();
this.emit('startCoreHandlers', true);
}
/**
* Starts the plugin manager and makes it globally available
*/
startPluginManager() {
this.plgManager = new ALARMiatorPluginManager();
global.plgManager = this.plgManager;
// Read inbound plugins and initialize plugins
this.plgManager.readInboundPluginList();
this.plgManager.readOutboundPluginList();
// reinitialize Plugins in plugin store
this.plgManager.registerPluginsInStore(this.plgManager.pluginList);
// Load configuration of plugins from database
this.plgManager.initializeConfigFromStore((success) => {
if (success) {
this.plgManager.initializePlugins();
this.emit('startPluginManager', true);
} else {
// Config could not be loaded from database
tthis.emit('startPluginManager', false);
}
})
}
/**
* Registers core to plugin manager events
*/
registerPluginManagerEvents() {
this.plgManager.on('event_new_zvei_alarm', (zveiInfo) => {
// a new zvei alarm has been fired
console.log('APP | New ZVEI Alarm Event fired with the following data:');
console.log(zveiInfo);
})
this.plgManager.on('event_new_alarm', (alarmInfo) => {
// a new zvei alarm has been fired
console.log('APP | New Alarm Event fired with the following data:');
this.handlerAlarm.persistNewAlarmInformation(alarmInfo);
})
this.plgManager.on('event_new_state', (stateInfo) => {
// a new zvei alarm has been fired
console.log('APP | New State Event fired with the following data:' + stateInfo.radioStatusHumanReadable + ' (' + stateInfo.issi + ')');
this.handlerState.persistNewStateInformation(stateInfo);
})
this.plgManager.on('event_refresh_extip', (ip) => {
// external ip has been refetched
// console.log('APP | New external ip address state fired with the following ip: ' + ip);
global.externalIP = ip;
this.handlerExternalIp.persistNewIPInformation(ip);
})
this.plgManager.on('event_print_pdf', (pathToPDF) => {
// PDF Document should be sent to default printer
try {
global.printManager.printPDFToDefaultPrinter(pathToPDF, (resultPrint) => {
if (resultPrint) {
console.log('CORE | PRINTING | Successfully finished print job for document ' + pathToPDF);
} else {
console.log('CORE | PRINTING | Error printing document: ' + pathToPDF);
}
})
} catch (err) {
console.log('CORE | PRINTING | Error while trying to print PDF Document: ' + err.message);
}
})
this.emit('registerPluginManagerEvents', true);
}
/**
* Sets up authentication
*/
setupAuthentication() {
// Authentication
const { authenticate, changePassword } = require('./routes/auth');
const { getLoginPage } = require('./routes/auth');
const { endSession } = require('./routes/auth');
const { getChangePasswordPage } = require('./routes/auth');
const { setNewPasswordFromStartPassword } = require('./routes/auth');
const { getLockedAccountPage } = require('./routes/auth');
// routes for the app
// Login
this.app.get('/', getLoginPage);
this.app.post('/auth', authenticate);
this.app.get('/changepassword', getChangePasswordPage);
this.app.post('/changepassword', setNewPasswordFromStartPassword);
this.app.get('/auth/logoff', endSession);
this.app.get('/lockedaccount', getLockedAccountPage);
this.emit('setupAuthentication', true);
}
/**
* Sets up all needed express - routes
*/
setupRoutes() {
// Load Route Modules
var routeBasedata = require('./routes/basedata');
var routeAdmin = require('./routes/admin');
var routeTraining = require('./routes/training');
var routeAssets = require('./routes/assets');
var routeWallboard = require('./routes/wallboard');
var routeReports = require('./routes/reports');
var routesOperations = require('./routes/operations');
var routesApi = require('./routes/restapi-v1');
var logtail = require('./routes/admin/logtail');
var routesAlarm = require('./routes/alarm');
var routesHome = require('./routes/home');
var routesRespiratory = require('./routes/respiratory');
const { isAuthenticated } = require('./routes/auth');
const { changePassword } = require('./routes/auth');
const { getUserGroups } = require('./routes/auth');
this.app.use('/home', isAuthenticated, getUserGroups, routesHome);
this.app.use('/basedata', isAuthenticated, getUserGroups, routeBasedata);
this.app.use('/admin', isAuthenticated, getUserGroups, routeAdmin);
this.app.use('/training', isAuthenticated,getUserGroups, routeTraining);
this.app.use('/assets', isAuthenticated,getUserGroups, routeAssets);
this.app.use('/reports', isAuthenticated,getUserGroups, routeReports);
this.app.use('/operations', isAuthenticated,getUserGroups, routesOperations);
this.app.use('/alarm', isAuthenticated,getUserGroups, routesAlarm);
this.app.use('/respiratory', isAuthenticated, getUserGroups, routesRespiratory);
this.app.use('/auth/changePassword', isAuthenticated, getUserGroups, changePassword);
if (this.args_development) {
this.startAPIDocumentation();
}
// Static Route to wallboard client in plugins directory
this.app.use('/wallboard', express.static('plugins/outbound/wallboard/static'));
this.emit('setupRoutes', true);
}
/**
* Checks for initial Settings like server UUID etc.
*/
runServer() {
// read uuid of this instance and publish globally (serverUUID)
global.configuration.getValueForKeyword('serveruuid', (uuidFromDb) => {
if (uuidFromDb === null) {
var uuidFromDb = uuid();
global.configuration.addKeywordAndValue('serveruuid', uuidFromDb, (successUUID) => {
if (successUUID) {
this.logger.info('APP | Set UUID of this Server to ' + uuidFromDb);
this.uuid = uuidFromDb;
} else {
this.logger.error('APP | Error setting UUID of this Server.');
}
});
} else {
this.uuid = uuidFromDb;
}
global.serverUUID = this.uuid;
})
// read serverinstance and publish globally
global.configuration.getValueForKeyword('instancename', (instanceNameFromDb) => {
if (instanceNameFromDb === null) {
this.instancename = 'ALARMiator';
} else {
this.instancename = instanceNameFromDb;
}
global.serverInstanceName = this.instancename;
})
// read (external ip) of this server and publish it globally (wanIP)
global.configuration.getValueForKeyword('externalip', (externalip) => {
global.externalIP = externalip;
})
// set the app to listen on the port
this.app.listen(this.port, () => {
this.logger.info(`CORE | ALARMiator Server is running on port: ${this.port}`);
this.app.listenPort = this.port;
});
this.emit('runServer', true);
}
/**
* Place to initialize all global objects and properties
*/
initializeGlobalObjects() {
// Distribute the FCMTokens Class globally
global.fcmtokens = new FCMTokens(global.logger);
global.configuration = new Configuration(global.logger);
this.emit('initializeGlobalObjects', true);
}
/**
* Starts the cron manager and makes it available globally
*/
startCron() {
this.cron = new CoreCron(global.logger);
global.cron = this.cron;
global.cron.startCronManager();
// start cron joba
// maintenance
global.cron.startJob('maintenance', '30 23 * * *', function() { this.emit('event_cron_maintenance', 'maintenance'); }, null);
// hourly
global.cron.startJob('hourly', '0 * * * *', function() { this.emit('event_cron_hourly', 'hourly'); }, null);
// every 5 minutes
global.cron.startJob('every5Minutes', '*/5 * * * *', function () { this.emit('event_cron_every_5_minutes', 'every5Minutes'); }, null);
global.cron.on('event_cron_maintenance', (title) => {
console.log('CORE | CRON | event fired: event_cron_maintenance from job ' + title);
})
global.cron.on('event_cron_hourly', (title) => {
console.log('CORE | CRON | event fired: event_cron_hourly from job ' + title);
})
global.cron.on('event_cron_every_5_minutes', (title) => {
console.log('CORE | CRON | event fired: event_cron_every_5_minutes from job ' + title);
})
this.emit('startCron', true);
}
/**
* Starts the REST API documentation if server is in development mode
*/
startAPIDocumentation() {
this.swaggerActice = true;
this.swaggerUi = require('swagger-ui-express');
this.YAML = require('yamljs');
this.swaggerDocument = this.YAML.load('./api/v1/api/swagger/swagger.yaml');
this.app.use('/api-docs', this.swaggerUi.serve, this.swaggerUi.setup(this.swaggerDocument));
console.log('CORE | successful | startAPIDumentation');
}
}
// GENERIC Error Handling
process.on('unhandledRejection', (reason, p) => {
console.log('CORE | Unhandled Rejection at: Promise', p, 'reason:', reason.stack);
// application specific logging, throwing an error, or other logic here
});
// Start Core-System
const core = new Core();
core.on('getStartupArguments', (state) => {
if (state) {
// Database could be started successfully
console.log('CORE | successful | getStartupArguments');
core.startCoredatabase();
} else {
// Database could not be started successfully
console.log('CORE | ERROR reading passed startup arguments. Please check logs for more details');
}
})
core.on('startCoredatabase', (state) => {
if (state) {
// Database could be started successfully
console.log('CORE | successful | startCoredatabase');
core.startLoggers();
} else {
// Database could not be started successfully
console.log('CORE | ERROR starting Database. Please check logs for more details');
}
})
core.on('startLoggers', (state) => {
if (state) {
console.log('CORE | successful | startLoggers');
// Loggers have been successfully started
core.initializeGlobalObjects();
} else {
console.log('CORE | ERROR starting Loggers. Please check logs for more details.');
}
})
core.on('initializeGlobalObjects', (state) => {
if (state) {
console.log('CORE | successful | initializeGlobalObjects');
core.setupServer();
} else {
console.log('CORE | ERROR initializing global objects. Please check logs for more details.');
}
})
core.on('setupServer', (state) => {
if (state) {
// Server config successfully done
console.log('CORE | successful | setupServer');
core.setupMiddelware();
} else {
console.log('CORE | ERROR setting Up Server. Please check logs for more details.');
}
})
core.on('setupMiddleware', (state) => {
if (state) {
// Middleware Setup successfully done
console.log('CORE | successful | startMiddleware');
core.startCoreHandlers();
} else {
console.log('CORE | ERROR setting Up Middleware. Please check logs for more details.');
}
})
core.on('startCoreHandlers', (state) => {
if (state) {
// Core Handlers Setup successfully done
console.log('CORE | successful | startCoreHandlers');
core.startPluginManager();
} else {
console.log('CORE | ERROR setting Up Core handlers. Please check logs for more details.');
}
})
core.on('startPluginManager', (state) => {
if (state) {
// Core Handlers Setup successfully done
console.log('CORE | successful | startPluginManager');
core.registerPluginManagerEvents();
} else {
console.log('CORE | ERROR starting PluginManager. Please check logs for more details.');
}
})
core.on('registerPluginManagerEvents', (state) => {
if (state) {
// Core Handlers Setup successfully done
console.log('CORE | successful | registerPluginManagerEvents');
core.setupAuthentication();
} else {
console.log('CORE | ERROR registering Plugin Manager Events. Please check logs for more details.');
}
})
core.on('setupAuthentication', (state) => {
if (state) {
// Core Handlers Setup successfully done
console.log('CORE | successful | setupAuthentication');
core.setupRoutes();
} else {
console.log('CORE | ERROR setting up Authentication. Please check logs for more details.');
}
})
core.on('setupRoutes', (state) => {
if (state) {
// Core Handlers Setup successfully done
console.log('CORE | successful | setupRoutes');
core.startCron();
} else {
console.log('CORE | ERROR setting up Routes. Please check logs for more details.');
}
})
core.on('startCron', (state) => {
if (state) {
// Core Handlers Setup successfully done
console.log('CORE | successful | setupCron');
core.runServer();
} else {
console.log('CORE | ERROR setting up Cron. Please check logs for more details.');
}
})
core.on('runServer', (state) => {
if (state) {
// Core Handlers Setup successfully done
console.log('CORE | ALARMiator Server started successfully');
} else {
console.log('CORE | ERROR starting ALARMiator Server. Please check logs for more details.');
}
})
// Starting now
core.getStartupArguments();