mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2025-12-16 11:16:20 +00:00
18
README.md
18
README.md
@@ -8,28 +8,30 @@ This is a simple logger for TypeScript projects with colors for console output.
|
||||
Logs are only printed if their log level is equal or higher than the defined log level.
|
||||
|
||||
## Log Levels
|
||||
Available Log levels are:
|
||||
Available log levels are:
|
||||
- 1 - INFO
|
||||
- 2 - LOG
|
||||
- 4 - WARN
|
||||
- 8 - ERROR
|
||||
- 16 - OK
|
||||
|
||||
You can set your Log Level with the environment variable
|
||||
`STAPPS_LOG_LEVEL` in a binary way.
|
||||
You can set your log level with the environment variable `STAPPS_LOG_LEVEL`.
|
||||
|
||||
For example `STAPPS_LOG_LEVEL=17` is 16 + 1 and would log everything
|
||||
that is `OK` or `ERROR`.
|
||||
To select your desired log levels add the corresponding numbers and set the value of `STAPPS_LOG_LEVEL` to the sum.
|
||||
|
||||
If you want to use Logger in production (`NODE_ENV=production`) and allow all transports to fail set `ALLOW_NO_TRANSPORT` to `true`.
|
||||
For example `STAPPS_LOG_LEVEL=17` is 16 + 1 and would log everything that is `OK` or `INFO`.
|
||||
|
||||
If you want to use logger in production (`NODE_ENV=production`) and allow all transports to fail set
|
||||
`ALLOW_NO_TRANSPORT` to `true`.
|
||||
|
||||
## SMTP
|
||||
This class also provides a simple implementation of an smtp transport which can be used as a
|
||||
This class also provides a simple implementation of an SMTP transport which can be used as a
|
||||
`TransportWithVerification` for the logger. You can use this to transport errors of the logger or to transport mails
|
||||
of your own monitoring solution.
|
||||
|
||||
### Usage
|
||||
You can instatiate it with a config or it will check for a config in the environment variables.
|
||||
You can instatiate it with a config or it will check for a config in the environment variables. Environment variables
|
||||
can overwrite the actual config values.
|
||||
|
||||
Environment variables are:
|
||||
* SMTP_AUTH_USER: SMTP username
|
||||
|
||||
146
src/SMTP.ts
146
src/SMTP.ts
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
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';
|
||||
|
||||
@@ -93,55 +94,6 @@ export class SMTP extends TransportWithVerification {
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
// if no config is given, we assume it is set via environment variables
|
||||
if (typeof config === 'undefined') {
|
||||
if (typeof process.env.SMTP_AUTH_USER !== 'string') {
|
||||
throw new Error('User of SMTP configuration is empty. Please provide a user.');
|
||||
}
|
||||
|
||||
if (typeof process.env.SMTP_AUTH_PASSWORD !== 'string') {
|
||||
throw new Error('Password of SMTP configuration is empty. Please provide a password.');
|
||||
}
|
||||
|
||||
if (typeof process.env.SMTP_HOST !== 'string') {
|
||||
throw new Error('Host of SMTP configuration is empty. Please provide a host.');
|
||||
}
|
||||
|
||||
if (typeof process.env.SMTP_PORT !== 'string') {
|
||||
throw new Error('Port of SMTP configuration is empty. Please provide a port.');
|
||||
}
|
||||
|
||||
if (typeof process.env.SMTP_RECIPIENTS !== 'string') {
|
||||
throw new Error('Recipients of SMTP configuration is empty. Please provide at least one recipient.');
|
||||
}
|
||||
|
||||
if (typeof process.env.SMTP_SENDER_MAIL !== 'string') {
|
||||
throw new Error('Sender of SMTP configuration is empty. Please provide a sender.');
|
||||
}
|
||||
|
||||
if (typeof process.env.SMTP_SENDER_NAME !== 'string') {
|
||||
throw new Error('Name of sender of SMTP configuration is empty. Please provide a name for the sender.');
|
||||
}
|
||||
|
||||
config = {
|
||||
auth: {
|
||||
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(',') : []),
|
||||
host: process.env.SMTP_HOST,
|
||||
port: parseInt(process.env.SMTP_PORT, 10),
|
||||
recipients: (typeof process.env.SMTP_RECIPIENTS === 'string') ?
|
||||
(process.env.SMTP_RECIPIENTS).split(',') :
|
||||
[],
|
||||
secure: (typeof process.env.SMTP_SECURE === 'string') ? (process.env.SMTP_SECURE === 'true') : false,
|
||||
sender: {
|
||||
mail: process.env.SMTP_SENDER_MAIL,
|
||||
name: process.env.SMTP_SENDER_NAME,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// monitoring is not required -> SMTP init can fail
|
||||
if (!Logger.isProductiveEnvironment() || process.env.ALLOW_NO_TRANSPORT === 'true') {
|
||||
try {
|
||||
@@ -185,7 +137,7 @@ export class SMTP extends TransportWithVerification {
|
||||
* @param {string[]} recipients
|
||||
* @return {string[]}
|
||||
*/
|
||||
public static isValidRecipientsList(recipients: string[]): boolean {
|
||||
public static isValidRecipientsList(recipients: string[] | undefined): boolean {
|
||||
return Array.isArray(recipients) && recipients.length > 0 && recipients.every(this.isValidEmailAddress);
|
||||
}
|
||||
|
||||
@@ -194,46 +146,88 @@ export class SMTP extends TransportWithVerification {
|
||||
* call `new SMTP()`
|
||||
* @param {SMTPConfig} config
|
||||
*/
|
||||
private constructor(config: SMTPConfig) {
|
||||
if (typeof config.host !== 'string') {
|
||||
throw new Error('SMTP configuration needs a host.');
|
||||
private constructor(config?: SMTPConfig) {
|
||||
// create a partial config from environment variables that can overwrite the given config
|
||||
const envConfig: RecursivePartial<SMTPConfig> = {
|
||||
auth: {
|
||||
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(',') : []),
|
||||
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') ?
|
||||
(process.env.SMTP_RECIPIENTS).split(',') :
|
||||
[],
|
||||
secure: (typeof process.env.SMTP_SECURE === 'string') ? (process.env.SMTP_SECURE === 'true') : false,
|
||||
sender: {
|
||||
mail: process.env.SMTP_SENDER_MAIL,
|
||||
name: process.env.SMTP_SENDER_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
config = {
|
||||
...config,
|
||||
// deleting undefined properties so the actual config doesn't get overwritten by undefined values
|
||||
...deleteUndefinedProperties(envConfig),
|
||||
};
|
||||
|
||||
if (typeof config!.host !== 'string') {
|
||||
throw new Error(
|
||||
'SMTP configuration needs a host. Add it to the config or use environment variables (SMTP_HOST).',
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof config.port !== 'number') {
|
||||
throw new Error('SMTP configuration needs a port.');
|
||||
if (typeof config!.port !== 'number' || isNaN(config!.port)) {
|
||||
throw new Error(
|
||||
'SMTP configuration needs a port. Add it to the config or use environment variables (SMTP_PORT).',
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof config.auth !== 'object') {
|
||||
throw new Error('SMTP configuration needs an auth object.');
|
||||
if (typeof config!.auth !== 'object') {
|
||||
throw new Error(
|
||||
'SMTP configuration needs an auth object.' +
|
||||
'Add it to the config or use environment variables (SMTP_AUTH_USER, SMTP_AUTH_PASSWORD).',
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof config.auth.user !== 'string') {
|
||||
throw new Error('SMTP auth configuration needs a user');
|
||||
if (typeof config!.auth.user !== 'string') {
|
||||
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') {
|
||||
throw new Error('SMTP auth configuration needs a password');
|
||||
if (typeof config!.auth.password !== 'string') {
|
||||
throw new Error(
|
||||
'SMTP auth configuration needs a password.' +
|
||||
'Add it to the config or use environment variables (SMTP_AUTH_PASSWORD).',
|
||||
);
|
||||
}
|
||||
|
||||
if (Array.isArray(config.recipients) && config.recipients.length < 1) {
|
||||
throw new Error('SMTP configuration needs recipients.');
|
||||
if (Array.isArray(config!.recipients) && config!.recipients.length < 1) {
|
||||
throw new Error(
|
||||
'SMTP configuration needs recipients. Add it to the config or use environment variables (SMTP_RECIPIENTS).',
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof config.sender.mail !== 'string') {
|
||||
throw new Error('SMTP configuration needs a sender');
|
||||
if (typeof config!.sender.mail !== 'string') {
|
||||
throw new Error(
|
||||
'SMTP configuration needs a sender. Add it to the config or use environment variables (SMTP_SENDER_MAIL).',
|
||||
);
|
||||
}
|
||||
|
||||
super();
|
||||
|
||||
if (SMTP.isValidRecipientsList(config.recipients)) {
|
||||
this.recipients = config.recipients;
|
||||
if (SMTP.isValidRecipientsList(config!.recipients)) {
|
||||
this.recipients = config!.recipients;
|
||||
} else {
|
||||
throw new Error('Invalid recipients found');
|
||||
}
|
||||
|
||||
if (typeof config.cc !== 'undefined') {
|
||||
if (SMTP.isValidRecipientsList(config.cc) || (Array.isArray(config.cc) && config.cc.length === 0)) {
|
||||
this.cc = config.cc;
|
||||
if (typeof config!.cc !== 'undefined') {
|
||||
if (SMTP.isValidRecipientsList(config!.cc)) {
|
||||
this.cc = config!.cc!;
|
||||
} else {
|
||||
throw new Error('Invalid cc recipients found');
|
||||
}
|
||||
@@ -241,19 +235,19 @@ export class SMTP extends TransportWithVerification {
|
||||
this.cc = [];
|
||||
}
|
||||
|
||||
this.from = config.sender;
|
||||
this.from = config!.sender;
|
||||
|
||||
this.verified = false;
|
||||
|
||||
// creating transport with configuration
|
||||
this.transportAgent = nodemailer.createTransport({
|
||||
auth: {
|
||||
pass: config.auth.password,
|
||||
user: config.auth.user,
|
||||
pass: config!.auth.password,
|
||||
user: config!.auth.user,
|
||||
},
|
||||
host: config.host,
|
||||
port: config.port,
|
||||
secure: typeof config.secure === 'boolean' ? config.secure : false,
|
||||
host: config!.host,
|
||||
port: config!.port,
|
||||
secure: typeof config!.secure === 'boolean' ? config!.secure : false,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
49
src/common.ts
Normal file
49
src/common.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A recursive partial object
|
||||
*
|
||||
* Copied from https://stackoverflow.com/a/51365037
|
||||
*/
|
||||
export type RecursivePartial<T> = {
|
||||
[P in keyof T]?: T[P] extends Array<infer U> ?
|
||||
Array<RecursivePartial<U>> :
|
||||
T[P] extends object ? RecursivePartial<T[P]> : T[P];
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes all properties that are undefined from an object
|
||||
* @param obj
|
||||
*/
|
||||
export function deleteUndefinedProperties(obj: any) {
|
||||
// return atomic data types and arrays (recursion anchor)
|
||||
if (typeof obj !== 'object' || Array.isArray(obj)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// check each key
|
||||
Object.keys(obj).forEach((key) => {
|
||||
if (typeof obj[key] === 'undefined') {
|
||||
// delete undefined keys
|
||||
delete obj[key];
|
||||
} else {
|
||||
// check recursive
|
||||
obj[key] = deleteUndefinedProperties(obj[key]);
|
||||
}
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
||||
70
test/Common.spec.ts
Normal file
70
test/Common.spec.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { expect } from 'chai';
|
||||
import { slow, suite, test, timeout } from 'mocha-typescript';
|
||||
import { deleteUndefinedProperties } from '../src/common';
|
||||
|
||||
@suite(timeout(2000), slow(1000))
|
||||
export class CommonSpec {
|
||||
/* tslint:disable:member-ordering */
|
||||
@test
|
||||
deleteUndefinedProperties1() {
|
||||
expect(deleteUndefinedProperties(
|
||||
{
|
||||
a: 2,
|
||||
b: {
|
||||
c: 3,
|
||||
d: undefined,
|
||||
},
|
||||
},
|
||||
)).to.deep.equal(
|
||||
{
|
||||
a: 2,
|
||||
b: {
|
||||
c: 3,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@test
|
||||
deleteUndefinedProperties2() {
|
||||
expect(deleteUndefinedProperties(
|
||||
{
|
||||
a: undefined,
|
||||
b: undefined,
|
||||
},
|
||||
)).to.deep.equal(
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
@test
|
||||
deleteUndefinedProperties3() {
|
||||
expect(deleteUndefinedProperties(
|
||||
{
|
||||
a: 2,
|
||||
b: 'foo',
|
||||
c: 'bar',
|
||||
},
|
||||
)).to.deep.equal(
|
||||
{
|
||||
a: 2,
|
||||
b: 'foo',
|
||||
c: 'bar',
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user