diff --git a/src/Logger.ts b/src/Logger.ts
index f688048e..178e219f 100644
--- a/src/Logger.ts
+++ b/src/Logger.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 StApps
+ * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -12,8 +12,10 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see .
*/
+import chalk from 'chalk';
import {stringify} from 'flatted';
-import {Transport, TransportWithVerification} from './Transport';
+import {isNodeEnvironment, isProductiveNodeEnvironment} from './common';
+import {Transport} from './Transport';
/**
* Logger with colors, loglevel and transport
@@ -23,189 +25,47 @@ import {Transport, TransportWithVerification} from './Transport';
*
* Log levels in that order are:
* ```
- * 1 - INFO
- * 2 - LOG
- * 4 - WARN
- * 8 - ERROR
- * 16 - OK
+ * INFO: 1
+ * LOG: 2
+ * WARN: 4
+ * ERROR: 8
+ * OK: 16
* ```
*/
export class Logger {
-
- /*
- * Reset = "\x1b[0m"
- * Bright = "\x1b[1m"
- * Dim = "\x1b[2m"
- * Underscore = "\x1b[4m"
- * Blink = "\x1b[5m"
- * Reverse = "\x1b[7m"
- * Hidden = "\x1b[8m"
- *
- * FgBlack = "\x1b[30m"
- * FgRed = "\x1b[31m"
- * FgGreen = "\x1b[32m"
- * FgYellow = "\x1b[33m"
- * FgBlue = "\x1b[34m"
- * FgMagenta = "\x1b[35m"
- * FgCyan = "\x1b[36m"
- * FgWhite = "\x1b[37m"
- *
- * BgBlack = "\x1b[40m"
- * BgRed = "\x1b[41m"
- * BgGreen = "\x1b[42m"
- * BgYellow = "\x1b[43m"
- * BgBlue = "\x1b[44m"
- * BgMagenta = "\x1b[45m"
- * BgCyan = "\x1b[46m"
- * BgWhite = "\x1b[47m"
- */
-
- /**
- * Prefix for cyan color
- */
- private cyan = '\x1b[36m';
-
- /**
- * Prefix for green color
- */
- private green = '\x1b[32m';
-
- /**
- * Set to true if this code is executed by Node.js
- */
- private isNode: boolean;
-
- /**
- * Set to true if the environment for a productive environment is given
- *
- * In Node.js this means that `NODE_ENV` is set to `production`
- */
- private isProductiveEnvironment: boolean;
-
/**
* Log levels
*/
- private logLevels: { [logLevel: string]: 1 | 2 | 4 | 8 | 16 } = {
- 'INFO': 1,
- 'LOG': 2,
- 'WARN': 4,
- // tslint:disable-next-line:object-literal-sort-keys
- 'ERROR': 8,
- 'OK': 16,
- };
-
- /**
- * Prefix for red color
- */
- private red = '\x1b[31m';
-
- /**
- * Suffix to end a color
- */
- private reset = '\x1b[0m';
+ private static logLevels = [
+ 'INFO',
+ 'LOG',
+ 'WARN',
+ 'ERROR',
+ 'OK',
+ ];
/**
* Transport for errors
- *
- * For example `@stapps/smtp-transport`
*/
- private transport?: Transport;
-
- /**
- * Prefix for white color
- */
- private white = '\x1b[37m';
-
- /**
- * Prefix for yellow color
- */
- private yellow = '\x1b[33m';
-
- /**
- * Checks if this code is executed in Node.js
- */
- public static isNodeEnvironment(): boolean {
- // Only Node.js has a process variable that is of [[Class]] process
- return Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]';
- }
-
- /**
- * Checks if a productive environment is given
- */
- public static isProductiveEnvironment(): boolean {
- return Logger.isNodeEnvironment &&
- (typeof process.env.NODE_ENV === 'string' && process.env.NODE_ENV === 'production');
- }
-
- /**
- * Instatiate an instance of logger
- * @param transport A transport instance that can be used for error transport
- */
- constructor(transport?: Transport) {
- // node environment -> maybe we run a service which needs monitoring
- if (Logger.isNodeEnvironment()) {
- this.isNode = true;
-
- // check if we are in productive environment -> then we need to run a transport
- if (Logger.isProductiveEnvironment()) {
- this.isProductiveEnvironment = true;
-
- if (typeof transport === 'undefined') {
- if (process.env.ALLOW_NO_TRANSPORT !== 'true') {
- throw new Error('Productive environment doesn\'t set an transport agent for error notifications');
- } else {
- this.warn('Productive environment doesn\'t set an transport agent for error notifications');
- }
- } else {
-
- this.transport = transport;
-
- // we expect an transport for error notifications
- if (this.isTransportWithVerification(transport) && !transport.isVerified()) {
- transport.verify().then((success) => {
- if (typeof success === 'string') {
- this.ok(success);
- } else {
- this.ok('Successfully verified transport for error notification');
- }
- }).catch((err) => {
- throw err;
- });
- }
- }
- } else {
- this.isProductiveEnvironment = false;
- }
- } else {
- this.isProductiveEnvironment = false;
- this.isNode = false;
- }
- }
+ private static transport?: Transport;
/**
* Check if intended log level is allowed in environment log level
*
* @param logLevel
- * @returns {boolean}
*/
- private checkLogLevel(logLevel: 1 | 2 | 4 | 8 | 16) {
+ private static checkLogLevel(logLevel: string): boolean {
+ const logLevelNumber = Math.pow(2, Logger.logLevels.indexOf(logLevel) + 1) - 1;
- const requiredLogLevel = this.getLogLevel();
-
- if (requiredLogLevel > 31 || requiredLogLevel < 0) {
- throw new Error('Log level is out of range.');
- }
-
- // tslint:disable-next-line:no-bitwise
- return requiredLogLevel & logLevel;
+ /* tslint:disable-next-line:no-bitwise */
+ return !!(Logger.getLogLevel() & logLevelNumber);
}
/**
* Return log level from environment
- * @returns {number}
*/
- private getLogLevel(): number {
- if (this.isNode && typeof process.env.STAPPS_LOG_LEVEL === 'string') {
+ private static getLogLevel(): number {
+ if (isNodeEnvironment() && typeof process.env.STAPPS_LOG_LEVEL !== 'undefined') {
// Node.js environment exists
return parseInt(process.env.STAPPS_LOG_LEVEL, 10);
} else if (typeof window !== 'undefined' && typeof (window as any).STAPPS_LOG_LEVEL === 'number') {
@@ -217,23 +77,102 @@ export class Logger {
return 31;
}
- private isTransportWithVerification(transport: Transport): transport is TransportWithVerification {
- return typeof (transport as any).verify === 'function';
+ /**
+ * Log an error
+ *
+ * @param args Arguments to log
+ */
+ public static async error(...args: any[]): Promise {
+ if (!Logger.checkLogLevel('ERROR')) {
+ return;
+ }
+
+ /* tslint:disable-next-line:no-console */
+ console.error(chalk.bold.red(`[ERROR] ${Logger.stringifyArguments(...args)}`));
+
+ if (isProductiveNodeEnvironment()) {
+ if (typeof Logger.transport !== 'undefined') {
+ return Logger.transport.send('Error', Logger.stringifyArguments(...args));
+ } else if (!process.env.ALLOW_NO_TRANSPORT) {
+ throw new Error(`Error couldn't be transported! Please set a transport or set ALLOW_NO_TRANSPORT='true'.`);
+ }
+ }
+ }
+
+ /**
+ * Log an information
+ *
+ * @param args Arguments to log
+ */
+ public static info(...args: any[]): void {
+ if (!Logger.checkLogLevel('INFO')) {
+ return;
+ }
+
+ /* tslint:disable-next-line:no-console */
+ console.info(chalk.cyan(`[INFO] ${Logger.stringifyArguments(args)}`));
+ }
+
+ /**
+ * Check if the logger is initialized correctly
+ */
+ public static initialized(): void {
+ if (isProductiveNodeEnvironment() && typeof Logger.transport === 'undefined') {
+ if (!process.env.ALLOW_NO_TRANSPORT) {
+ throw new Error(`Productive environment doesn't set a transport for error notifications.`);
+ } else {
+ /* tslint:disable-next-line:no-console */
+ console.warn(chalk.yellow(`Productive environment doesn't set a transport for error notifications.`));
+ }
+ }
+ }
+
+ /**
+ * Log something
+ *
+ * @param args Arguments to log
+ */
+ public static log(...args: any[]): void {
+ if (!this.checkLogLevel('LOG')) {
+ return;
+ }
+
+ /* tslint:disable-next-line:no-console */
+ console.log(chalk.white(`[LOG] ${Logger.stringifyArguments(args)}`));
+ }
+
+ /**
+ * Log something successful
+ *
+ * @param args Arguments to log
+ */
+ public static ok(...args: any[]): void {
+ if (!this.checkLogLevel('OK')) {
+ return;
+ }
+
+ /* tslint:disable-next-line:no-console */
+ console.log(chalk.bold.green(`[OK] ${Logger.stringifyArguments(args)}`));
+ }
+
+ /**
+ * Set a transport
+ *
+ * @param transport Transport to set
+ */
+ public static setTransport(transport?: Transport) {
+ Logger.transport = transport;
}
/**
* Stringify a list of arguments
*
- * @param args {any[]} Arguments to stringify
- * @returns {string} Stringified arguments
+ * @param args Arguments to stringify
*/
- private stringifyArguments(..._args: any[]): string {
+ public static stringifyArguments(...args: any[]): string {
const result: string[] = [];
- /* tslint:disable:prefer-for-of */
- for (let idx = 0; idx < arguments.length; idx++) {
- /* tslint:enable */
- const argument = arguments[idx];
+ args.forEach((argument) => {
const type = typeof argument;
if (['string', 'number'].indexOf(type) !== -1) {
@@ -246,106 +185,22 @@ export class Logger {
} else {
result.push(stringify(argument, null, 2));
}
- }
+ });
return result.join(', ');
}
/**
- * Log with level ERROR
+ * Log a warning
*
- * @param args {any} Arguments to log
+ * @param args Arguments to log
*/
- public error(...args: any[]): void {
- if (this.checkLogLevel(this.logLevels.ERROR)) {
- if (this.isNode) {
- /* tslint:disable-next-line:no-console */
- console.error(this.red + '[ERROR] ' + this.stringifyArguments(...args) + this.reset);
-
- if (this.isProductiveEnvironment) {
-
- if (typeof this.transport === 'undefined') {
- if (process.env.ALLOW_NO_TRANSPORT !== 'true') {
- throw new Error('Error couldn\'t be tranported. Please set an transport or set ALLOW_NO_TRANSPORT=true');
- }
- } else {
- this.transport.send('Error', this.stringifyArguments(...args)).catch((err) => {
- throw err;
- });
- }
- }
- } else {
- /* tslint:disable-next-line:no-console */
- console.log('[ERROR] ' + this.stringifyArguments(...args));
- }
+ public static warn(...args: any[]): void {
+ if (!this.checkLogLevel('WARN')) {
+ return;
}
- }
- /**
- * Log with level INFO
- *
- * @param args {any} Arguments to log
- */
- public info(...args: any[]): void {
- if (this.checkLogLevel(this.logLevels.INFO)) {
- if (this.isNode) {
- /* tslint:disable-next-line:no-console */
- console.info(this.cyan + '[INFO] ' + this.stringifyArguments(...args) + this.reset);
- } else {
- /* tslint:disable-next-line:no-console */
- console.info('[INFO] ' + this.stringifyArguments(...args));
- }
- }
- }
-
- /**
- * Log with level LOG
- *
- * @param args {any} Arguments to log
- */
- public log(...args: any[]): void {
- if (this.checkLogLevel(this.logLevels.LOG)) {
- if (this.isNode) {
- /* tslint:disable-next-line:no-console */
- console.log(this.white + '[LOG] ' + this.stringifyArguments(...args) + this.reset);
- } else {
- /* tslint:disable-next-line:no-console */
- console.log('[LOG] ' + this.stringifyArguments(...args));
- }
- }
- }
-
- /**
- * Log with level OK
- *
- * @param args {any} Arguments to log
- */
- public ok(...args: any[]): void {
- if (this.checkLogLevel(this.logLevels.OK)) {
- if (this.isNode) {
- /* tslint:disable-next-line:no-console */
- console.log(this.green + '[OK] ' + this.stringifyArguments(...args) + this.reset);
- } else {
- /* tslint:disable-next-line:no-console */
- console.log('[OK] ' + this.stringifyArguments(...args));
- }
- }
- }
-
- /**
- * Log with level WARN
- *
- * @param args {any} Arguments to log
- */
- public warn(...args: any[]): void {
- if (this.checkLogLevel(this.logLevels.WARN)) {
- if (this.isNode) {
- /* tslint:disable-next-line:no-console */
- console.warn(this.yellow + '[WARN] ' + this.stringifyArguments(...args) + this.reset);
- } else {
- /* tslint:disable-next-line:no-console */
- console.warn('[WARN] ' + this.stringifyArguments(...args));
- }
- }
+ /* tslint:disable-next-line:no-console */
+ console.warn(chalk.yellow(`[WARN] ${Logger.stringifyArguments(args)}`));
}
}
diff --git a/src/SMTP.ts b/src/SMTP.ts
index 2fc6af6d..681a5c8f 100644
--- a/src/SMTP.ts
+++ b/src/SMTP.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 StApps
+ * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -14,9 +14,8 @@
*/
import * as nodemailer from 'nodemailer';
import {MailOptions} from 'nodemailer/lib/sendmail-transport';
-import {deleteUndefinedProperties, RecursivePartial} from './common';
-import {Logger} from './Logger';
-import {TransportWithVerification} from './Transport';
+import {deleteUndefinedProperties, isProductiveEnvironment, RecursivePartial} from './common';
+import {VerifiableTransport} from './Transport';
/**
* A configuration of the transport used to send mails via SMTP
@@ -40,14 +39,14 @@ export interface SMTPConfig {
/**
* An implementation of mail transport via SMTP
*/
-export class SMTP extends TransportWithVerification {
+export class SMTP extends VerifiableTransport {
private static _instance: SMTP;
/**
* List of all mail addresses to send in cc
*/
- private cc: string[];
+ private readonly cc: string[];
/**
* Who is using this service
@@ -60,7 +59,7 @@ export class SMTP extends TransportWithVerification {
/**
* List of all mail addresses to send to
*/
- private recipients: string[];
+ private readonly recipients: string[];
/**
* Connection to SMTP server
@@ -95,7 +94,7 @@ export class SMTP extends TransportWithVerification {
}
// monitoring is not required -> SMTP init can fail
- if (!Logger.isProductiveEnvironment() || process.env.ALLOW_NO_TRANSPORT === 'true') {
+ if (!isProductiveEnvironment() || process.env.ALLOW_NO_TRANSPORT === 'true') {
try {
this._instance = new this(config);
} catch (err) {
@@ -122,14 +121,8 @@ export class SMTP extends TransportWithVerification {
* @return {boolean}
*/
public static isValidEmailAddress(address: string): boolean {
-
- if (typeof address !== 'string') {
- return false;
- }
-
// tslint:disable-next-line:max-line-length
- const regex = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
- return regex.test(address);
+ return /^(([^<>()\[\].,;:\s@"]+(\.[^<>()\[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i.test(address);
}
/**
@@ -153,13 +146,13 @@ export class SMTP extends TransportWithVerification {
password: process.env.SMTP_AUTH_PASSWORD,
user: process.env.SMTP_AUTH_USER,
},
- cc: ((typeof process.env.SMTP_CC === 'string') ? (process.env.SMTP_CC as string).split(',') : []),
+ cc: ((typeof process.env.SMTP_CC !== 'undefined') ? (process.env.SMTP_CC as string).split(',') : []),
host: process.env.SMTP_HOST,
- port: (typeof process.env.SMTP_PORT === 'string') ? parseInt(process.env.SMTP_PORT, 10) : undefined,
- recipients: (typeof process.env.SMTP_RECIPIENTS === 'string') ?
+ port: (typeof process.env.SMTP_PORT !== 'undefined') ? parseInt(process.env.SMTP_PORT, 10) : undefined,
+ recipients: (typeof process.env.SMTP_RECIPIENTS !== 'undefined') ?
(process.env.SMTP_RECIPIENTS).split(',') :
[],
- secure: (typeof process.env.SMTP_SECURE === 'string') ? (process.env.SMTP_SECURE === 'true') : false,
+ secure: (typeof process.env.SMTP_SECURE !== 'undefined') ? (process.env.SMTP_SECURE === 'true') : false,
sender: {
mail: process.env.SMTP_SENDER_MAIL,
name: process.env.SMTP_SENDER_NAME,
@@ -173,13 +166,13 @@ export class SMTP extends TransportWithVerification {
...deleteUndefinedProperties(envConfig),
};
- if (typeof config!.host !== 'string') {
+ if (typeof config!.host === 'undefined') {
throw new Error(
'SMTP configuration needs a host. Add it to the config or use environment variables (SMTP_HOST).',
);
}
- if (typeof config!.port !== 'number' || isNaN(config!.port)) {
+ if (typeof config!.port === 'undefined' || isNaN(config!.port)) {
throw new Error(
'SMTP configuration needs a port. Add it to the config or use environment variables (SMTP_PORT).',
);
@@ -192,13 +185,13 @@ export class SMTP extends TransportWithVerification {
);
}
- if (typeof config!.auth.user !== 'string') {
+ if (typeof config!.auth.user === 'undefined') {
throw new Error(
'SMTP auth configuration needs a user. Add it to the config or use environment variables (SMTP_AUTH_USER).',
);
}
- if (typeof config!.auth.password !== 'string') {
+ if (typeof config!.auth.password === 'undefined') {
throw new Error(
'SMTP auth configuration needs a password.' +
'Add it to the config or use environment variables (SMTP_AUTH_PASSWORD).',
@@ -211,7 +204,7 @@ export class SMTP extends TransportWithVerification {
);
}
- if (typeof config!.sender.mail !== 'string') {
+ if (typeof config!.sender.mail === 'undefined') {
throw new Error(
'SMTP configuration needs a sender. Add it to the config or use environment variables (SMTP_SENDER_MAIL).',
);
@@ -247,7 +240,7 @@ export class SMTP extends TransportWithVerification {
},
host: config!.host,
port: config!.port,
- secure: typeof config!.secure === 'boolean' ? config!.secure : false,
+ secure: typeof config!.secure !== 'undefined' ? config!.secure : false,
});
}
@@ -311,7 +304,7 @@ export class SMTP extends TransportWithVerification {
try {
verificationSuccessfull = await this.transportAgent.verify();
} catch (err) {
- if (!Logger.isProductiveEnvironment() || process.env.ALLOW_NO_TRANSPORT !== 'true') {
+ if (!isProductiveEnvironment() || process.env.ALLOW_NO_TRANSPORT !== 'true') {
throw err;
} else {
/* tslint:disable-next-line:no-console */
@@ -323,7 +316,7 @@ export class SMTP extends TransportWithVerification {
}
if (!verificationSuccessfull) {
- if (!Logger.isProductiveEnvironment() || process.env.ALLOW_NO_TRANSPORT !== 'true') {
+ if (!isProductiveEnvironment() || process.env.ALLOW_NO_TRANSPORT !== 'true') {
throw new Error(
'Verification of SMTP transport failed.' +
'If you want to ignore this error set' +
diff --git a/src/Transport.ts b/src/Transport.ts
index 8f23ed3e..7a2ea33b 100644
--- a/src/Transport.ts
+++ b/src/Transport.ts
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 StApps
+ * Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
@@ -14,34 +14,33 @@
*/
/**
- * An abstract wrapper for a transport system like for example smtp
+ * An abstract wrapper for a transport system like for example SMTP
*/
export abstract class Transport {
-
/**
- * Send message with subject. A message can be a mail or something completly different. Depending on what Transport
- * is implemented
- * @param {string} subject
- * @param {string} message
+ * Send message with subject
+ *
+ * A message can be a mail or something completely different depending on what transport is implemented.
+ *
+ * @param subject Subject of the message
+ * @param message Message to send
*/
abstract send(subject: string, message: string): Promise;
-
}
/**
- * A transport wrapper of transport which can be veriefied
+ * A transport wrapper of transport which can be verified
*/
-export abstract class TransportWithVerification extends Transport {
-
+export abstract class VerifiableTransport extends Transport {
/**
* Checks if the transport was verified at least once
- * @returns {boolean}
*/
abstract isVerified(): boolean;
/**
- * Verifies transport (check connection, authentication, ...)
+ * Verifies transport
+ *
+ * Check connection, authentication, ...
*/
abstract verify(): Promise;
-
}
diff --git a/src/common.ts b/src/common.ts
index 2bfcc48f..577f9a7c 100644
--- a/src/common.ts
+++ b/src/common.ts
@@ -13,15 +13,17 @@
* this program. If not, see .
*/
- /**
- * A recursive partial object
- *
- * Copied from https://stackoverflow.com/a/51365037
- */
+/**
+ * A recursive partial object
+ *
+ * Copied from https://stackoverflow.com/a/51365037
+ */
+import {Transport, VerifiableTransport} from './Transport';
+
export type RecursivePartial = {
[P in keyof T]?: T[P] extends Array ?
- Array> :
- T[P] extends object ? RecursivePartial : T[P];
+ Array> :
+ T[P] extends object ? RecursivePartial : T[P];
};
/**
@@ -47,3 +49,35 @@ export function deleteUndefinedProperties(obj: any) {
return obj;
}
+
+/**
+ * Checks if environment is Node.js
+ */
+export function isNodeEnvironment(): boolean {
+ return Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]';
+}
+
+/**
+ * Checks if environment is productive
+ */
+export function isProductiveEnvironment(): boolean {
+ return typeof process.env === 'object'
+ && typeof process.env.NODE_ENV !== 'undefined'
+ && process.env.NODE_ENV === 'production';
+}
+
+/**
+ * Checks if environment is Node.js and productive
+ */
+export function isProductiveNodeEnvironment(): boolean {
+ return isNodeEnvironment() && isProductiveEnvironment();
+}
+
+/**
+ * Check if a transport is a verifiable transport
+ *
+ * @param transport Transport to check
+ */
+export function isTransportWithVerification(transport: Transport): transport is VerifiableTransport {
+ return transport instanceof VerifiableTransport;
+}