From 339c9a9572a7604a5f1dc08ee4f8501c35514059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Fra=C3=9F?= Date: Sun, 23 Jun 2024 13:53:26 +0200 Subject: [PATCH] [mod] email-sending and logging --- source/conf.ts | 571 +++++++++++++++++++++++++++++++++------------- source/helpers.ts | 71 +----- source/main.ts | 85 +++++-- 3 files changed, 475 insertions(+), 252 deletions(-) diff --git a/source/conf.ts b/source/conf.ts index c8e919f..a491920 100644 --- a/source/conf.ts +++ b/source/conf.ts @@ -16,26 +16,68 @@ You should have received a copy of the GNU General Public License along with thi namespace _espe.conf { + /** + */ + type type_log_threshold = ( + "debug" + | + "info" + | + "notice" + | + "warning" + | + "error" + ); + + + /** + */ + type type_log_format = ( + "jsonl" + | + "human_readable" + ); + + /** */ export type type_conf = { general : { language : (null | string); - verbosity : ( - "debug" - | - "notice" - | - "info" - | - "warning" - | - "error" - | - "none" - ); 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; + }; + } + >; server : { port : int; path_base : string; @@ -151,165 +193,366 @@ namespace _espe.conf conf_raw : any ) : void { - _data = { - "general": ( - ((node_general) => ({ - "language": (node_general["language"] ?? null), - "verbosity": (node_general["verbosity"] ?? "notice"), - "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"] ?? {}) + switch (conf_raw["version"]) { + case 1: { + _data = { + "general": ( + ((node_general) => ({ + "language": (node_general["language"] ?? null), + "verification_secret": (node_general["verification_secret"] ?? null), + })) (conf_raw["general"] ?? {}) ), - "summon_email": ( - ((node_settings_summon_email) => ({ - "remark": (node_settings_summon_email["remark"] ?? null), - })) (node_settings["summon_email"] ?? {}) + "log": [ + { + "kind": "stdout", + "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": ( - ((node_settings_password_policy) => ({ - "minimum_length": ( - ("minimum_length" in node_settings_password_policy) - ? node_settings_password_policy["minimum_length"] - : 8 + "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"] ?? {}) ), - "maximum_length": ( - ("maximum_length" in node_settings_password_policy) - ? node_settings_password_policy["maximum_length"] - : 240 + "summon_email": ( + ((node_settings_summon_email) => ({ + "remark": (node_settings_summon_email["remark"] ?? null), + })) (node_settings["summon_email"] ?? {}) ), - "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_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"] ?? {}) ), - "password_change": ( - ((node_settings_password_change) => ({ - "cooldown_time": (node_settings_password_change["cooldown_time"] ?? 86400), - })) (node_settings["password_change"] ?? {}) + "admins": (conf_raw["admins"] ?? []), + "output": ( + ((node_session_output) => ({ + "authelia": (node_session_output["authelia"] ?? null), + })) (conf_raw["output"] ?? {}) ), - "name_index": ( - ((node_settings_password_policy) => ({ - "veil": (node_settings_password_policy["veil"] ?? true), - "salt": (node_settings_password_policy["salt"] ?? ""), - })) (node_settings["name_index"] ?? {}) + }; + break; + } + case 2: { + _data = { + "general": ( + ((node_general) => ({ + "language": (node_general["language"] ?? null), + "verification_secret": (node_general["verification_secret"] ?? null), + })) (conf_raw["general"] ?? {}) ), - "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"] ?? {}) + "log": ( + ((node_log) => node_log.map( + (node_log_entry : any) => ({ + "kind": node_log_entry["kind"], + "data": Object.assign( + { + "format": "human_readable", + "threshold": "notice", + }, + (node_log_entry["data"] ?? {}) + ) + }) + )) ( + conf_raw["log"] + ?? + [ + { + "kind": "console", + "data": { + } + }, + ] + ) ), - })) (conf_raw["settings"] ?? {}) - ), - "admins": (conf_raw["admins"] ?? []), - "output": ( - ((node_session_output) => ({ - "authelia": (node_session_output["authelia"] ?? null), - })) (conf_raw["output"] ?? {}) - ), - }; + "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": ( + ((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; + } + } } diff --git a/source/helpers.ts b/source/helpers.ts index 4896501..134e811 100644 --- a/source/helpers.ts +++ b/source/helpers.ts @@ -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, - subject : string, - content : string, - options : { - sender ?: (null | string); - } = {} - ) : Promise - { - 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( @@ -207,14 +152,12 @@ namespace _espe.helpers return Promise.reject("no smtp credentials specified; add in conf as 'email_sending.data.smtp_credentials'!"); } else { - return email_send_real( + return lib_plankton.email.send( parameters.smtp_credentials, + parameters.sender, receivers, subject, - content, - { - "sender": parameters.sender, - } + content ); } break; @@ -230,8 +173,9 @@ namespace _espe.helpers return Promise.reject("no smtp credentials specified; add in conf as 'email_sending.data.smtp_credentials'!"); } else { - return email_send_real( + return lib_plankton.email.send( parameters.smtp_credentials, + parameters.sender, [parameters.target], lib_plankton.string.coin( "[REDIRECTION] {{receivers}} | {{original_subject}}", @@ -240,10 +184,7 @@ namespace _espe.helpers "original_subject": subject } ), - content, - { - "sender": parameters.sender, - } + content ); } break; diff --git a/source/main.ts b/source/main.ts index e513c8f..898d7b1 100644 --- a/source/main.ts +++ b/source/main.ts @@ -21,17 +21,61 @@ async function main( ) : Promise { // init - await lib_plankton.translate.initialize( - { - "verbosity": 1, - "packages": [ - // TODO: error handling - JSON.parse(await lib_plankton.file.read("data/localization/deu.loc.json")), - JSON.parse(await lib_plankton.file.read("data/localization/eng.loc.json")), - ], - "order": ["deu", "eng"], - "autopromote": false, - } + lib_plankton.log.conf_push( + [ + lib_plankton.log.channel_make( + { + "kind": "stdout", + "data": { + "threshold": "notice", + // "format": "human_readable", + } + } + ), + ] + ); + const language_codes : Array = [ + "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( + content => (new Promise( + (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 @@ -139,19 +183,14 @@ async function main( lib_plankton.translate.promote(_espe.conf.get().general.language); } lib_plankton.log.conf_push( - [ - new lib_plankton.log.class_channel_minlevel( - new lib_plankton.log.class_channel_stdout(), + _espe.conf.get().log.map( + log_output => lib_plankton.log.channel_make( { - "none": lib_plankton.log.enum_level.error, - "error": lib_plankton.log.enum_level.error, - "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] - ), - ] + "kind": log_output.kind, + "data": log_output.data + } + ) + ) ); // exec