Refactored Express handlers for proper 404 auto handling by Express
MODIFY
src/server/ExpressUtils.ts
src/server/ExpressUtils.ts
@@ -56,11 +56,11 @@ export default class ExpressUtils {
return app;
}
- public static postInitApplication(app: express.Application): void {
+ public static postInitApplication(expressApplication: Application): void {
// Log Express Response
- app.use(Logging.traceExpressResponse.bind(this));
+ expressApplication.use(Logging.traceExpressResponse.bind(this));
// Error Handling
- app.use(Logging.traceExpressError.bind(this));
+ expressApplication.use(Logging.traceExpressError.bind(this));
}
private static healthCheckService(req: Request, res: Response, next: NextFunction): void {

MODIFY
src/server/ocpi/OCPIServer.ts
src/server/ocpi/OCPIServer.ts
@@ -46,15 +46,6 @@ export default class OCPIServer {
}
});
}
- // Not found
- this.expressApplication.use((req: Request, res: Response, next: NextFunction) => {
- res.status(404);
- res.format({
- html: () => res.send(`404: ${req.url}`),
- json: () => res.json({ error: `404: ${req.url}` }),
- default: () => res.type('txt').send(`404: ${req.url}`)
- });
- });
// Post init
ExpressUtils.postInitApplication(this.expressApplication);
}

MODIFY
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPLocationsEndpoint.ts
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/emsp/EMSPLocationsEndpoint.ts
@@ -34,9 +34,9 @@ export default class EMSPLocationsEndpoint extends AbstractEndpoint {
public async process(req: Request, res: Response, next: NextFunction, tenant: Tenant, ocpiEndpoint: OCPIEndpoint): Promise<OCPIResponse> {
switch (req.method) {
case 'PATCH':
- return await this.patchLocationRequest(req, res, next, tenant, ocpiEndpoint);
+ return this.patchLocationRequest(req, res, next, tenant, ocpiEndpoint);
case 'PUT':
- return await this.putLocationRequest(req, res, next, tenant, ocpiEndpoint);
+ return this.putLocationRequest(req, res, next, tenant, ocpiEndpoint);
}
}

MODIFY
src/server/ocpp/json/JsonCentralSystemServer.ts
src/server/ocpp/json/JsonCentralSystemServer.ts
@@ -518,7 +518,6 @@ export default class JsonCentralSystemServer extends CentralSystemServer {
}
}
}
- // Log
if (validConnections.length || invalidConnections.length) {
const message = `${validConnections.length} ${type} valid WS Connection pinged (${invalidConnections.length} invalid)`;
this.isDebug() && Logging.logConsoleDebug(message);

MODIFY
src/server/ocpp/soap/SoapCentralSystemServer.ts
src/server/ocpp/soap/SoapCentralSystemServer.ts
@@ -45,7 +45,6 @@ export default class SoapCentralSystemServer extends CentralSystemServer {
// Create Soap Servers
// OCPP 1.5 -----------------------------------------
const soapServer15 = soap.listen(this.httpServer, `/${Utils.getOCPPServerVersionURLPath(OCPPVersion.VERSION_15)}`, centralSystemService15, this.readWsdl('OCPPCentralSystemService15.wsdl'));
- // Log
if (this.centralSystemConfig.debug) {
// Listen
soapServer15.log = async (type, data) => {
@@ -58,7 +57,6 @@ export default class SoapCentralSystemServer extends CentralSystemServer {
}
// OCPP 1.6 -----------------------------------------
const soapServer16 = soap.listen(this.httpServer, `/${Utils.getOCPPServerVersionURLPath(OCPPVersion.VERSION_16)}`, centralSystemService16, this.readWsdl('OCPPCentralSystemService16.wsdl'));
- // Log
if (this.centralSystemConfig.debug) {
// Listen
soapServer16.log = async (type, data) => {
@@ -78,7 +76,6 @@ export default class SoapCentralSystemServer extends CentralSystemServer {
}
private async handleSoapServerMessage(ocppVersion: OCPPVersion, request: any, methodName: string) {
- // Log
await Logging.logDebug({
tenantID: Constants.DEFAULT_TENANT, module: MODULE_NAME,
method: 'handleSoapServerMessage',
@@ -91,7 +88,6 @@ export default class SoapCentralSystemServer extends CentralSystemServer {
private async handleSoapServerLog(ocppVersion: OCPPVersion, type: string, data: any) {
// Do not log 'Info'
if (type === 'replied') {
- // Log
await Logging.logDebug({
tenantID: Constants.DEFAULT_TENANT, module: MODULE_NAME,
method: 'handleSoapServerLog',

MODIFY
src/server/odata/ODataServer.ts
src/server/odata/ODataServer.ts
@@ -44,15 +44,6 @@ export default class ODataServer {
}
}
);
- // Not found
- this.expressApplication.use((req: Request, res: Response, next: NextFunction) => {
- res.status(404);
- res.format({
- html: () => res.send(`404: ${req.url}`),
- json: () => res.json({ error: `404: ${req.url}` }),
- default: () => res.type('txt').send(`404: ${req.url}`)
- });
- });
// Post init
ExpressUtils.postInitApplication(this.expressApplication);
}

MODIFY
src/server/oicp/OICPServer.ts
src/server/oicp/OICPServer.ts
@@ -34,15 +34,6 @@ export default class OICPServer {
}
});
});
- // Not found
- this.expressApplication.use((req: Request, res: Response, next: NextFunction) => {
- res.status(404);
- res.format({
- html: () => res.send(`404: ${req.url}`),
- json: () => res.json({ error: `404: ${req.url}` }),
- default: () => res.type('txt').send(`404: ${req.url}`)
- });
- });
// Post init
ExpressUtils.postInitApplication(this.expressApplication);
}

MODIFY
src/server/rest/CentralRestServer.ts
src/server/rest/CentralRestServer.ts
@@ -8,6 +8,7 @@ import GlobalRouter from './v1/router/GlobalRouter';
import Logging from '../../utils/Logging';
import { ServerType } from '../../types/Server';
import { ServerUtils } from '../ServerUtils';
+import SessionHashService from './v1/service/SessionHashService';
import http from 'http';
import sanitize from 'express-sanitizer';
@@ -33,20 +34,15 @@ export default class CentralRestServer {
// Secured API
this.expressApplication.use('/client/api/:action',
AuthService.authenticate(),
- CentralRestServerService.restServiceSecured.bind(this));
+ SessionHashService.checkUserAndTenantValidity.bind(this),
+ Logging.traceExpressRequest.bind(this),
+ CentralRestServerService.restServiceSecured.bind(this)
+ );
// Util API
this.expressApplication.use('/client/util/:action',
Logging.traceExpressRequest.bind(this),
- CentralRestServerService.restServiceUtil.bind(this));
- // Not found
- this.expressApplication.use((req: Request, res: Response, next: NextFunction) => {
- res.status(404);
- res.format({
- html: () => res.send(`404: ${req.url}`),
- json: () => res.json({ error: `404: ${req.url}` }),
- default: () => res.type('txt').send(`404: ${req.url}`)
- });
- });
+ CentralRestServerService.restServiceUtil.bind(this)
+ );
// Post init
ExpressUtils.postInitApplication(this.expressApplication);
// Create HTTP server to serve the express app

MODIFY
src/server/rest/CentralRestServerService.ts
src/server/rest/CentralRestServerService.ts
@@ -13,7 +13,6 @@ import OCPIEndpointService from './v1/service/OCPIEndpointService';
import OICPEndpointService from './v1/service/OICPEndpointService';
import RegistrationTokenService from './v1/service/RegistrationTokenService';
import { ServerAction } from '../../types/Server';
-import SessionHashService from './v1/service/SessionHashService';
import SettingService from './v1/service/SettingService';
import SiteAreaService from './v1/service/SiteAreaService';
import SiteService from './v1/service/SiteService';
@@ -311,10 +310,6 @@ export default class CentralRestServerService {
public static async restServiceSecured(req: Request, res: Response, next: NextFunction): Promise<void> {
// Parse the action
const action = req.params.action as ServerAction;
- // Check if User has been updated and require new login
- if ((await SessionHashService.areTokenUserAndTenantStillValid(req, res, next))) {
- return;
- }
// Check HTTP Verbs
if (!['POST', 'GET', 'PUT', 'DELETE'].includes(req.method)) {
await Logging.logActionExceptionMessageAndSendResponse(
@@ -322,7 +317,6 @@ export default class CentralRestServerService {
return;
}
try {
- await Logging.traceExpressRequest(req, res, next);
// Get the action
const handleRequest = RequestMapper.getInstanceFromHTTPVerb(req.method).getActionFromPath(action);
// Execute

MODIFY
src/server/rest/v1/router/GlobalRouter.ts
src/server/rest/v1/router/GlobalRouter.ts
@@ -13,6 +13,7 @@ import OCPIEndpointRouter from './api/OCPIEndpointRouter';
import OICPEndpointRouter from './api/OICPEndpointRouter';
import PricingRouter from './api/PricingRouter';
import RegistrationTokenRouter from './api/RegistrationTokenRouter';
+import SessionHashService from '../service/SessionHashService';
import SettingRouter from './api/SettingRouter';
import SiteAreaRouter from './api/SiteAreaRouter';
import SiteRouter from './api/SiteRouter';
@@ -47,7 +48,7 @@ export default class GlobalRouter {
protected buildRouteAPI(): void {
this.router.use('/api',
AuthService.authenticate(),
- AuthService.checkSessionHash.bind(this),
+ SessionHashService.checkUserAndTenantValidity.bind(this),
Logging.traceExpressRequest.bind(this),
[
new AssetRouter().buildRoutes(),

MODIFY
src/server/rest/v1/service/AuthService.ts
src/server/rest/v1/service/AuthService.ts
@@ -58,13 +58,6 @@ export default class AuthService {
return passport.authenticate('jwt', { session: false });
}
- public static async checkSessionHash(req: Request, res: Response, next: NextFunction): Promise<void> {
- // Check if User has been updated and require new login
- if (!await SessionHashService.areTokenUserAndTenantStillValid(req, res, next)) {
- next();
- }
- }
-
public static async handleLogIn(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Filter
const filteredRequest = AuthValidator.getInstance().validateAuthSignInReq(req.body);
@@ -238,7 +231,6 @@ export default class AuthService {
await UserStorage.addSitesToUser(tenant, newUser.id, siteIDs);
}
}
- // Log
await Logging.logInfo({
tenantID: tenant.id,
user: newUser, action: action,
@@ -304,7 +296,6 @@ export default class AuthService {
const resetHash = Utils.generateUUID();
// Init Password info
await UserStorage.saveUserPassword(tenant, user.id, { passwordResetHash: resetHash });
- // Log
await Logging.logInfo({
tenantID: tenant.id,
user: user, action: action,
@@ -351,7 +342,6 @@ export default class AuthService {
if (user.status === UserStatus.LOCKED) {
await UserStorage.saveUserStatus(tenant, user.id, UserStatus.ACTIVE);
}
- // Log
await Logging.logInfo({
tenantID: tenant.id,
user: user, action: action,
@@ -524,7 +514,6 @@ export default class AuthService {
// Save User Verification Account
await UserStorage.saveUserAccountVerification(tenant, user.id,
{ verificationToken: null, verifiedAt: new Date() });
- // Log
await Logging.logInfo({
tenantID: tenant.id,
user: user, action: action,
@@ -620,7 +609,6 @@ export default class AuthService {
// Get existing verificationToken
verificationToken = user.verificationToken;
}
- // Log
await Logging.logInfo({
tenantID: tenant.id,
user: user,
@@ -671,7 +659,6 @@ export default class AuthService {
passwordWrongNbrTrials,
passwordBlockedUntil: moment().add(_centralSystemRestConfig.passwordBlockedWaitTimeMin, 'm').toDate()
});
- // Log
throw new AppError({
errorCode: HTTPError.USER_ACCOUNT_LOCKED_ERROR,
message: 'User is locked',
@@ -683,7 +670,6 @@ export default class AuthService {
} else {
// Save User Nbr Password Trials
await UserStorage.saveUserPassword(tenant, user.id, { passwordWrongNbrTrials });
- // Log
throw new AppError({
errorCode: HTTPError.OBJECT_DOES_NOT_EXIST_ERROR,
message: `User failed to log in, ${_centralSystemRestConfig.passwordWrongNumberOfTrial - user.passwordWrongNbrTrials} trial(s) remaining`,

MODIFY
src/server/rest/v1/service/SessionHashService.ts
src/server/rest/v1/service/SessionHashService.ts
@@ -5,6 +5,7 @@ import Constants from '../../../../utils/Constants';
import { HTTPError } from '../../../../types/HTTPError';
import Logging from '../../../../utils/Logging';
import { ServerAction } from '../../../../types/Server';
+import { StatusCodes } from 'http-status-codes';
import Tenant from '../../../../types/Tenant';
import TenantStorage from '../../../../storage/mongodb/TenantStorage';
import User from '../../../../types/User';
@@ -14,7 +15,7 @@ import Utils from '../../../../utils/Utils';
const MODULE_NAME = 'SessionHashService';
export default class SessionHashService {
- public static async areTokenUserAndTenantStillValid(req: Request, res: Response, next: NextFunction): Promise<boolean> {
+ public static async checkUserAndTenantValidity(req: Request, res: Response, next: NextFunction): Promise<void> {
// Get tenant id, user id and hash ID
const userID = req.user.id;
const tenantID = req.user.tenantID;
@@ -28,45 +29,58 @@ export default class SessionHashService {
} else {
tenant = await TenantStorage.getTenant(tenantID);
}
+ if (!tenant) {
+ throw new AppError({
+ errorCode: StatusCodes.UNAUTHORIZED,
+ message: 'Tenant does not exist',
+ module: MODULE_NAME,
+ method: 'checkUserAndTenantValidity',
+ user: req.user
+ });
+ }
// Get User
const user = await UserStorage.getUser(tenant, userID);
- // User or Tenant no longer exists
- if (!tenant || !user) {
- return true;
+ if (!user) {
+ throw new AppError({
+ errorCode: StatusCodes.UNAUTHORIZED,
+ message: 'User does not exist',
+ module: MODULE_NAME,
+ method: 'checkUserAndTenantValidity',
+ user: req.user
+ });
}
// Set in HTTP request
req.user.user = user;
req.tenant = tenant;
- // No session hash in master tenant
+ // Ignore check of session hash in master tenant
if (tenantID === Constants.DEFAULT_TENANT) {
- return false;
+ next();
+ return;
}
// Check User's Hash
- if (userHashID !== this.buildUserHashID(user)) {
+ if (userHashID !== SessionHashService.buildUserHashID(user)) {
throw new AppError({
errorCode: HTTPError.USER_ACCOUNT_CHANGED,
message: 'User has been updated and will be logged off',
module: MODULE_NAME,
- method: 'isSessionHashUpdated',
+ method: 'checkUserAndTenantValidity',
user: req.user
});
}
// Check Tenant's Hash
- if (tenantHashID !== this.buildTenantHashID(tenant)) {
+ if (tenantHashID !== SessionHashService.buildTenantHashID(tenant)) {
throw new AppError({
errorCode: HTTPError.TENANT_COMPONENT_CHANGED,
message: 'Tenant has been updated and all users will be logged off',
module: MODULE_NAME,
- method: 'isSessionHashUpdated',
+ method: 'checkUserAndTenantValidity',
user: req.user
});
}
+ next();
} catch (err) {
- // Log
await Logging.logActionExceptionMessageAndSendResponse(ServerAction.SESSION_HASH_SERVICE, err, req, res, next);
- return true;
}
- return false;
}
public static buildUserHashID(user: User): string {

MODIFY
src/server/rest/v1/service/TagService.ts
src/server/rest/v1/service/TagService.ts
@@ -484,7 +484,6 @@ export default class TagService {
}, async (error: CSVError) => {
// Release the lock
await LockingManager.release(importTagsLock);
- // Log
await Logging.logError({
tenantID: req.user.tenantID,
module: MODULE_NAME, method: 'handleImportTags',
@@ -509,7 +508,6 @@ export default class TagService {
}
// Release the lock
await LockingManager.release(importTagsLock);
- // Log
const executionDurationSecs = Utils.truncTo((new Date().getTime() - startTime) / 1000, 2);
await Logging.logActionsResponse(
req.user.tenantID, action,
@@ -557,7 +555,6 @@ export default class TagService {
parser.on('error', async (error) => {
// Release the lock
await LockingManager.release(importTagsLock);
- // Log
await Logging.logError({
tenantID: req.user.tenantID,
module: MODULE_NAME, method: 'handleImportTags',
@@ -576,7 +573,6 @@ export default class TagService {
} else {
// Release the lock
await LockingManager.release(importTagsLock);
- // Log
await Logging.logError({
tenantID: req.user.tenantID,
module: MODULE_NAME, method: 'handleImportTags',

MODIFY
src/start.ts
src/start.ts
@@ -80,7 +80,6 @@ export default class Bootstrap {
}
// Connect to the Database
await Bootstrap.database.start();
- // Log
await this.logDuration(startTimeMillis, 'Connected to the Database successfully');
// -------------------------------------------------------------------------
@@ -90,7 +89,6 @@ export default class Bootstrap {
startTimeMillis = await this.logAndGetStartTimeMillis('Migration is starting...');
// Check and trigger migration (only master process can run the migration)
await MigrationHandler.migrate();
- // Log
await this.logDuration(startTimeMillis, 'Migration has been run successfully');
}
// Listen to promise failure
@@ -112,7 +110,6 @@ export default class Bootstrap {
startTimeMillis = await this.logAndGetStartTimeMillis('Server is starting...');
// Start the Servers
serverStarted = await Bootstrap.startServersListening();
- // Log
await this.logDuration(startTimeMillis, `Server ${serverStarted.join(', ')} has been started successfully`);
// -------------------------------------------------------------------------
@@ -121,7 +118,6 @@ export default class Bootstrap {
startTimeMillis = await this.logAndGetStartTimeMillis('Scheduler is starting...');
// Start the Scheduler
await SchedulerManager.init();
- // Log
await this.logDuration(startTimeMillis, 'Scheduler has been started successfully');
// -------------------------------------------------------------------------
@@ -130,7 +126,6 @@ export default class Bootstrap {
startTimeMillis = await this.logAndGetStartTimeMillis('Async Task manager is starting...');
// Start the Async Manager
await AsyncTaskManager.init();
- // Log
await this.logDuration(startTimeMillis, 'Async Task manager has been started successfully');
// Update of manually uploaded data
@@ -141,7 +136,6 @@ export default class Bootstrap {
startTimeMillis = await this.logAndGetStartTimeMillis('Charging Station templates is being updated...');
// Load and Save the Charging Station templates
await ChargingStationTemplateBootstrap.uploadChargingStationTemplatesFromFile();
- // Log
await this.logDuration(startTimeMillis, 'Charging Station templates have been updated successfully');
// -------------------------------------------------------------------------
@@ -150,7 +144,6 @@ export default class Bootstrap {
startTimeMillis = await this.logAndGetStartTimeMillis('Local car catalogs are being imported...');
// Load and Save the Charging Station templates
await LocalCarCatalogBootstrap.uploadLocalCarCatalogsFromFile();
- // Log
await this.logDuration(startTimeMillis, 'Local car catalogs has been imported successfully');
}
@@ -160,7 +153,6 @@ export default class Bootstrap {
} else {
global.serverType = ServerType.CENTRAL_SERVER;
}
- // Log
await this.logDuration(startTimeGlobalMillis, `${serverStarted.join(', ')} server has been started successfuly`, ServerAction.BOOTSTRAP_STARTUP);
} catch (error) {
Logging.logConsoleError(error);

MODIFY
src/utils/Logging.ts
src/utils/Logging.ts
@@ -265,7 +265,6 @@ export default class Logging {
const sizeOfRequestDataKB = Utils.truncTo(Utils.createDecimal(
sizeof({ headers: req.headers, query: req.query, body: req.body })
).div(1024).toNumber(), 2);
- // Log
const message = `Express HTTP Request << Req ${(sizeOfRequestDataKB > 0) ? sizeOfRequestDataKB : '?'} KB << ${req.method} '${req.url}'`;
Utils.isDevelopmentEnv() && Logging.logConsoleInfo(message);
await Logging.logDebug({
@@ -306,7 +305,6 @@ export default class Logging {
public static traceExpressResponse(req: Request, res: Response, next: NextFunction): void {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
res.on('finish', async () => {
- try {
// Get Tenant info
const tenantID = req['tenantID'] as string;
// Compute duration
@@ -377,10 +375,8 @@ export default class Logging {
} as PerformanceRecord;
await PerformanceStorage.updatePerformanceRecord(performanceRecord);
}
- } finally {
- next();
- }
});
+ next();
}
public static async traceExpressError(error: Error, req: Request, res: Response, next: NextFunction): Promise<void> {
@@ -716,7 +712,6 @@ export default class Logging {
'stack': exception.stack,
};
}
- // Log
await Logging.logError({
tenantID: tenantID,
chargingStationID: exception.params.chargingStationID,
@@ -745,7 +740,6 @@ export default class Logging {
'stack': exception.stack,
};
}
- // Log
await Logging.logError({
tenantID: tenantID,
chargingStationID: exception.params.chargingStationID,
@@ -764,7 +758,6 @@ export default class Logging {
// Used to check URL params (not in catch)
private static async _logActionAppAuthExceptionMessage(tenantID: string, action: ServerAction, exception: AppAuthError, detailedMessages = {}): Promise<void> {
- // Log
await Logging.logError({
tenantID: tenantID,
user: exception.params.user,
@@ -828,7 +821,6 @@ export default class Logging {
}
}
- // Log
private static async _log(log: Log): Promise<string> {
let moduleConfig = null;
// Check Log Level
@@ -903,7 +895,6 @@ export default class Logging {
}
// Source
log.source = global.serverType ?? ServerType.CENTRAL_SERVER;
- // Log
return LoggingStorage.saveLog(log.tenantID, log);
}
@@ -971,7 +962,6 @@ export default class Logging {
}
return message;
}
- // Log
await Logging.logError({
tenantID: Constants.DEFAULT_TENANT,
module: MODULE_NAME,

Refactored Express handlers for proper 404 auto handling by Express