[mod] email-sending and logging

This commit is contained in:
roydfalk 2024-06-23 13:53:26 +02:00
parent 50d24c9841
commit 339c9a9572
3 changed files with 475 additions and 252 deletions

View file

@ -16,26 +16,68 @@ You should have received a copy of the GNU General Public License along with thi
namespace _espe.conf namespace _espe.conf
{ {
/**
*/
type type_log_threshold = (
"debug"
|
"info"
|
"notice"
|
"warning"
|
"error"
);
/**
*/
type type_log_format = (
"jsonl"
|
"human_readable"
);
/** /**
*/ */
export type type_conf = { export type type_conf = {
general : { general : {
language : (null | string); language : (null | string);
verbosity : (
"debug"
|
"notice"
|
"info"
|
"warning"
|
"error"
|
"none"
);
verification_secret : (null | string); verification_secret : (null | string);
}; };
log : Array<
{
kind : "stdout";
data : {
threshold : type_log_threshold;
};
}
|
{
kind : "file";
data : {
threshold : type_log_threshold;
path : string;
};
}
|
{
kind : "email";
data : {
threshold : type_log_threshold;
smtp_credentials : {
host : string;
port : int;
username : string;
password : string;
};
sender : string;
receivers : Array<string>;
};
}
>;
server : { server : {
port : int; port : int;
path_base : string; path_base : string;
@ -151,165 +193,366 @@ namespace _espe.conf
conf_raw : any conf_raw : any
) : void ) : void
{ {
_data = { switch (conf_raw["version"]) {
"general": ( case 1: {
((node_general) => ({ _data = {
"language": (node_general["language"] ?? null), "general": (
"verbosity": (node_general["verbosity"] ?? "notice"), ((node_general) => ({
"verification_secret": (node_general["verification_secret"] ?? null), "language": (node_general["language"] ?? null),
})) (conf_raw["general"] ?? {}) "verification_secret": (node_general["verification_secret"] ?? null),
), })) (conf_raw["general"] ?? {})
"server": (
((node_server) => ({
"port": (node_server["port"] ?? 4916),
"path_base": (node_server["path_base"] ?? ""),
})) (conf_raw["server"] ?? {})
),
"database": (
((node_database) => {
const kind : string = (node_database["kind"] ?? "sqlite");
const node_database_data_raw = (node_database["data"] ?? {});
switch (kind) {
case "sqlite": {
return {
"kind": kind,
"data": {
"path": (node_database_data_raw["path"] ?? "data.sqlite"),
}
};
break;
}
case "postgresql": {
return {
"kind": kind,
"data": node_database_data_raw,
};
break;
}
default: {
throw (new Error("unhandled"));
break;
}
}
}) (conf_raw["database"] ?? {})
),
"email_sending": (
((node_email_sending) => {
const kind : string = (node_email_sending["kind"] ?? "console");
const data_raw = (node_email_sending["data"] ?? {});
switch (kind) {
case "regular": {
return {
"kind": kind,
"data": {
"smtp_credentials": (data_raw["smtp_credentials"] ?? null),
"sender": data_raw["sender"],
}
};
break;
}
case "redirect": {
return {
"kind": kind,
"data": {
"smtp_credentials": (data_raw["smtp_credentials"] ?? null),
"sender": data_raw["sender"],
"target": data_raw["target"],
}
};
break;
}
case "console": {
return {
"kind": kind,
"data": {
}
};
break;
}
case "drop": {
return {
"kind": kind,
"data": {
}
};
break;
}
default: {
throw (new Error("unhandled"));
break;
}
}
}) (conf_raw["email_sending"] ?? {})
),
"session_management": (
((node_session_management) => ({
"in_memory": (node_session_management["in_memory"] ?? true),
"drop_all_at_start": (node_session_management["drop_all_at_start"] ?? true),
"lifetime": (node_session_management["lifetime"] ?? 900),
})) (conf_raw["session_management"] ?? {})
),
"settings": (
((node_settings) => ({
"organisation": {
"name": ((node_settings["organisation"] ?? {})["name"] ?? "Example Orginsation"), // TODO: mandatory?
"domain": ((node_settings["organisation"] ?? {})["domain"] ?? "example.org"), // TODO: mandatory?
},
"misc": (
((node_settings_misc) => ({
"prefix_for_veiled_email_addresses": (node_settings_misc["prefix_for_veiled_email_addresses"] ?? "member-"),
"facultative_membership_number": (node_settings_misc["facultative_membership_number"] ?? false),
"auto_register": (node_settings_misc["auto_register"] ?? false),
})) (node_settings["misc"] ?? {})
), ),
"summon_email": ( "log": [
((node_settings_summon_email) => ({ {
"remark": (node_settings_summon_email["remark"] ?? null), "kind": "stdout",
})) (node_settings["summon_email"] ?? {}) "data": {
"threshold": ((conf_raw["general"] ?? {})["verbosity"] ?? "notice"),
}
},
],
"server": (
((node_server) => ({
"port": (node_server["port"] ?? 4916),
"path_base": (node_server["path_base"] ?? ""),
})) (conf_raw["server"] ?? {})
), ),
"password_policy": ( "database": (
((node_settings_password_policy) => ({ ((node_database) => {
"minimum_length": ( const kind : string = (node_database["kind"] ?? "sqlite");
("minimum_length" in node_settings_password_policy) const node_database_data_raw = (node_database["data"] ?? {});
? node_settings_password_policy["minimum_length"] switch (kind) {
: 8 case "sqlite": {
return {
"kind": kind,
"data": {
"path": (node_database_data_raw["path"] ?? "data.sqlite"),
}
};
break;
}
case "postgresql": {
return {
"kind": kind,
"data": node_database_data_raw,
};
break;
}
default: {
throw (new Error("unhandled"));
break;
}
}
}) (conf_raw["database"] ?? {})
),
"email_sending": (
((node_email_sending) => {
const kind : string = (node_email_sending["kind"] ?? "console");
const data_raw = (node_email_sending["data"] ?? {});
switch (kind) {
case "regular": {
return {
"kind": kind,
"data": {
"smtp_credentials": (data_raw["smtp_credentials"] ?? null),
"sender": data_raw["sender"],
}
};
break;
}
case "redirect": {
return {
"kind": kind,
"data": {
"smtp_credentials": (data_raw["smtp_credentials"] ?? null),
"sender": data_raw["sender"],
"target": data_raw["target"],
}
};
break;
}
case "console": {
return {
"kind": kind,
"data": {
}
};
break;
}
case "drop": {
return {
"kind": kind,
"data": {
}
};
break;
}
default: {
throw (new Error("unhandled"));
break;
}
}
}) (conf_raw["email_sending"] ?? {})
),
"session_management": (
((node_session_management) => ({
"in_memory": (node_session_management["in_memory"] ?? true),
"drop_all_at_start": (node_session_management["drop_all_at_start"] ?? true),
"lifetime": (node_session_management["lifetime"] ?? 900),
})) (conf_raw["session_management"] ?? {})
),
"settings": (
((node_settings) => ({
"organisation": {
"name": ((node_settings["organisation"] ?? {})["name"] ?? "Example Orginsation"), // TODO: mandatory?
"domain": ((node_settings["organisation"] ?? {})["domain"] ?? "example.org"), // TODO: mandatory?
},
"misc": (
((node_settings_misc) => ({
"prefix_for_veiled_email_addresses": (node_settings_misc["prefix_for_veiled_email_addresses"] ?? "member-"),
"facultative_membership_number": (node_settings_misc["facultative_membership_number"] ?? false),
"auto_register": (node_settings_misc["auto_register"] ?? false),
})) (node_settings["misc"] ?? {})
), ),
"maximum_length": ( "summon_email": (
("maximum_length" in node_settings_password_policy) ((node_settings_summon_email) => ({
? node_settings_password_policy["maximum_length"] "remark": (node_settings_summon_email["remark"] ?? null),
: 240 })) (node_settings["summon_email"] ?? {})
), ),
"must_contain_letter": (node_settings_password_policy["must_contain_letter"] ?? true), "password_policy": (
"must_contain_number": (node_settings_password_policy["must_contain_number"] ?? true), ((node_settings_password_policy) => ({
"must_contain_special_character": (node_settings_password_policy["must_contain_special_character"] ?? true), "minimum_length": (
})) (node_settings["password_policy"] ?? {}) ("minimum_length" in node_settings_password_policy)
? node_settings_password_policy["minimum_length"]
: 8
),
"maximum_length": (
("maximum_length" in node_settings_password_policy)
? node_settings_password_policy["maximum_length"]
: 240
),
"must_contain_letter": (node_settings_password_policy["must_contain_letter"] ?? true),
"must_contain_number": (node_settings_password_policy["must_contain_number"] ?? true),
"must_contain_special_character": (node_settings_password_policy["must_contain_special_character"] ?? true),
})) (node_settings["password_policy"] ?? {})
),
"password_change": (
((node_settings_password_change) => ({
"cooldown_time": (node_settings_password_change["cooldown_time"] ?? 86400),
})) (node_settings["password_change"] ?? {})
),
"name_index": (
((node_settings_password_policy) => ({
"veil": (node_settings_password_policy["veil"] ?? true),
"salt": (node_settings_password_policy["salt"] ?? ""),
})) (node_settings["name_index"] ?? {})
),
"connections": (
((node_settings_connections) => ({
"frontend_url_base": (node_settings_connections["frontend_url_base"] ?? null),
"login_url": (node_settings_connections["login_url"] ?? null),
})) (node_settings["connections"] ?? {})
),
})) (conf_raw["settings"] ?? {})
), ),
"password_change": ( "admins": (conf_raw["admins"] ?? []),
((node_settings_password_change) => ({ "output": (
"cooldown_time": (node_settings_password_change["cooldown_time"] ?? 86400), ((node_session_output) => ({
})) (node_settings["password_change"] ?? {}) "authelia": (node_session_output["authelia"] ?? null),
})) (conf_raw["output"] ?? {})
), ),
"name_index": ( };
((node_settings_password_policy) => ({ break;
"veil": (node_settings_password_policy["veil"] ?? true), }
"salt": (node_settings_password_policy["salt"] ?? ""), case 2: {
})) (node_settings["name_index"] ?? {}) _data = {
"general": (
((node_general) => ({
"language": (node_general["language"] ?? null),
"verification_secret": (node_general["verification_secret"] ?? null),
})) (conf_raw["general"] ?? {})
), ),
"connections": ( "log": (
((node_settings_connections) => ({ ((node_log) => node_log.map(
"frontend_url_base": (node_settings_connections["frontend_url_base"] ?? null), (node_log_entry : any) => ({
"login_url": (node_settings_connections["login_url"] ?? null), "kind": node_log_entry["kind"],
})) (node_settings["connections"] ?? {}) "data": Object.assign(
{
"format": "human_readable",
"threshold": "notice",
},
(node_log_entry["data"] ?? {})
)
})
)) (
conf_raw["log"]
??
[
{
"kind": "console",
"data": {
}
},
]
)
), ),
})) (conf_raw["settings"] ?? {}) "server": (
), ((node_server) => ({
"admins": (conf_raw["admins"] ?? []), "port": (node_server["port"] ?? 4916),
"output": ( "path_base": (node_server["path_base"] ?? ""),
((node_session_output) => ({ })) (conf_raw["server"] ?? {})
"authelia": (node_session_output["authelia"] ?? null), ),
})) (conf_raw["output"] ?? {}) "database": (
), ((node_database) => {
}; const kind : string = (node_database["kind"] ?? "sqlite");
const node_database_data_raw = (node_database["data"] ?? {});
switch (kind) {
case "sqlite": {
return {
"kind": kind,
"data": {
"path": (node_database_data_raw["path"] ?? "data.sqlite"),
}
};
break;
}
case "postgresql": {
return {
"kind": kind,
"data": node_database_data_raw,
};
break;
}
default: {
throw (new Error("unhandled"));
break;
}
}
}) (conf_raw["database"] ?? {})
),
"email_sending": (
((node_email_sending) => {
const kind : string = (node_email_sending["kind"] ?? "console");
const data_raw = (node_email_sending["data"] ?? {});
switch (kind) {
case "regular": {
return {
"kind": kind,
"data": {
"smtp_credentials": (data_raw["smtp_credentials"] ?? null),
"sender": data_raw["sender"],
}
};
break;
}
case "redirect": {
return {
"kind": kind,
"data": {
"smtp_credentials": (data_raw["smtp_credentials"] ?? null),
"sender": data_raw["sender"],
"target": data_raw["target"],
}
};
break;
}
case "console": {
return {
"kind": kind,
"data": {
}
};
break;
}
case "drop": {
return {
"kind": kind,
"data": {
}
};
break;
}
default: {
throw (new Error("unhandled"));
break;
}
}
}) (conf_raw["email_sending"] ?? {})
),
"session_management": (
((node_session_management) => ({
"in_memory": (node_session_management["in_memory"] ?? true),
"drop_all_at_start": (node_session_management["drop_all_at_start"] ?? true),
"lifetime": (node_session_management["lifetime"] ?? 900),
})) (conf_raw["session_management"] ?? {})
),
"settings": (
((node_settings) => ({
"organisation": {
"name": ((node_settings["organisation"] ?? {})["name"] ?? "Example Orginsation"), // TODO: mandatory?
"domain": ((node_settings["organisation"] ?? {})["domain"] ?? "example.org"), // TODO: mandatory?
},
"misc": (
((node_settings_misc) => ({
"prefix_for_veiled_email_addresses": (node_settings_misc["prefix_for_veiled_email_addresses"] ?? "member-"),
"facultative_membership_number": (node_settings_misc["facultative_membership_number"] ?? false),
"auto_register": (node_settings_misc["auto_register"] ?? false),
})) (node_settings["misc"] ?? {})
),
"summon_email": (
((node_settings_summon_email) => ({
"remark": (node_settings_summon_email["remark"] ?? null),
})) (node_settings["summon_email"] ?? {})
),
"password_policy": (
((node_settings_password_policy) => ({
"minimum_length": (
("minimum_length" in node_settings_password_policy)
? node_settings_password_policy["minimum_length"]
: 8
),
"maximum_length": (
("maximum_length" in node_settings_password_policy)
? node_settings_password_policy["maximum_length"]
: 240
),
"must_contain_letter": (node_settings_password_policy["must_contain_letter"] ?? true),
"must_contain_number": (node_settings_password_policy["must_contain_number"] ?? true),
"must_contain_special_character": (node_settings_password_policy["must_contain_special_character"] ?? true),
})) (node_settings["password_policy"] ?? {})
),
"password_change": (
((node_settings_password_change) => ({
"cooldown_time": (node_settings_password_change["cooldown_time"] ?? 86400),
})) (node_settings["password_change"] ?? {})
),
"name_index": (
((node_settings_password_policy) => ({
"veil": (node_settings_password_policy["veil"] ?? true),
"salt": (node_settings_password_policy["salt"] ?? ""),
})) (node_settings["name_index"] ?? {})
),
"connections": (
((node_settings_connections) => ({
"frontend_url_base": (node_settings_connections["frontend_url_base"] ?? null),
"login_url": (node_settings_connections["login_url"] ?? null),
})) (node_settings["connections"] ?? {})
),
})) (conf_raw["settings"] ?? {})
),
"admins": (conf_raw["admins"] ?? []),
"output": (
((node_session_output) => ({
"authelia": (node_session_output["authelia"] ?? null),
})) (conf_raw["output"] ?? {})
),
};
break;
}
default: {
throw (new Error("invalid conf version: " + conf_raw["version"]));
break;
}
}
} }

View file

@ -124,61 +124,6 @@ namespace _espe.helpers
} }
/**
* @todo outsource?
*/
async function email_send_real(
smtp_credentials : {
host : string;
port : int;
username : string;
password : string;
},
receivers : Array<string>,
subject : string,
content : string,
options : {
sender ?: (null | string);
} = {}
) : Promise<void>
{
options = Object.assign(
{
"sender": /*null*/"admin@example.org",
},
options
);
lib_plankton.log.info(
"email_send_real",
{
"receivers": receivers,
"subject": subject,
}
);
const nm_nodemailer = require("nodemailer");
const transporter = nm_nodemailer.createTransport(
{
"host": smtp_credentials.host,
"port": smtp_credentials.port,
"secure": false,
"auth": {
"user": smtp_credentials.username,
"pass": smtp_credentials.password,
},
"debug": true,
}
);
const info = await transporter.sendMail(
{
"from": (options.sender ?? ""),
"to": receivers.join(", "),
"subject": subject,
"text": content,
}
)
}
/** /**
*/ */
export async function email_send( export async function email_send(
@ -207,14 +152,12 @@ namespace _espe.helpers
return Promise.reject<void>("no smtp credentials specified; add in conf as 'email_sending.data.smtp_credentials'!"); return Promise.reject<void>("no smtp credentials specified; add in conf as 'email_sending.data.smtp_credentials'!");
} }
else { else {
return email_send_real( return lib_plankton.email.send(
parameters.smtp_credentials, parameters.smtp_credentials,
parameters.sender,
receivers, receivers,
subject, subject,
content, content
{
"sender": parameters.sender,
}
); );
} }
break; break;
@ -230,8 +173,9 @@ namespace _espe.helpers
return Promise.reject<void>("no smtp credentials specified; add in conf as 'email_sending.data.smtp_credentials'!"); return Promise.reject<void>("no smtp credentials specified; add in conf as 'email_sending.data.smtp_credentials'!");
} }
else { else {
return email_send_real( return lib_plankton.email.send(
parameters.smtp_credentials, parameters.smtp_credentials,
parameters.sender,
[parameters.target], [parameters.target],
lib_plankton.string.coin( lib_plankton.string.coin(
"[REDIRECTION] {{receivers}} | {{original_subject}}", "[REDIRECTION] {{receivers}} | {{original_subject}}",
@ -240,10 +184,7 @@ namespace _espe.helpers
"original_subject": subject "original_subject": subject
} }
), ),
content, content
{
"sender": parameters.sender,
}
); );
} }
break; break;

View file

@ -21,17 +21,61 @@ async function main(
) : Promise<void> ) : Promise<void>
{ {
// init // init
await lib_plankton.translate.initialize( lib_plankton.log.conf_push(
{ [
"verbosity": 1, lib_plankton.log.channel_make(
"packages": [ {
// TODO: error handling "kind": "stdout",
JSON.parse(await lib_plankton.file.read("data/localization/deu.loc.json")), "data": {
JSON.parse(await lib_plankton.file.read("data/localization/eng.loc.json")), "threshold": "notice",
], // "format": "human_readable",
"order": ["deu", "eng"], }
"autopromote": false, }
} ),
]
);
const language_codes : Array<string> = [
"deu",
"eng",
];
await (
Promise.all(
language_codes
.map(
language_code => (
lib_plankton.file.read(
lib_plankton.string.coin(
"data/localization/{{language_code}}.loc.json",
{
"language_code": language_code,
}
)
)
.then<any>(
content => (new Promise<any>(
(resolve, reject) => {
try {
resolve(JSON.parse(content));
}
catch (error) {
reject(error);
}
}
))
)
)
)
)
.then(
packages => lib_plankton.translate.initialize(
{
"verbosity": 1,
"packages": packages,
"order": language_codes,
"autopromote": false,
}
)
)
); );
// args // args
@ -139,19 +183,14 @@ async function main(
lib_plankton.translate.promote(_espe.conf.get().general.language); lib_plankton.translate.promote(_espe.conf.get().general.language);
} }
lib_plankton.log.conf_push( lib_plankton.log.conf_push(
[ _espe.conf.get().log.map(
new lib_plankton.log.class_channel_minlevel( log_output => lib_plankton.log.channel_make(
new lib_plankton.log.class_channel_stdout(),
{ {
"none": lib_plankton.log.enum_level.error, "kind": log_output.kind,
"error": lib_plankton.log.enum_level.error, "data": log_output.data
"warning": lib_plankton.log.enum_level.warning, }
"notice": lib_plankton.log.enum_level.notice, )
"info": lib_plankton.log.enum_level.info, )
"debug":lib_plankton.log.enum_level.debug,
}[_espe.conf.get().general.verbosity]
),
]
); );
// exec // exec