From e7fbf628d34a85263fa0d2389fc1b97ae4834242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Fra=C3=9F?= Date: Tue, 23 Apr 2024 17:44:31 +0200 Subject: [PATCH] [add] api-aktion "member_urge_for_registration" [mod] E-Mail-Versand --- source/api/actions/member_register.ts | 20 ++- .../actions/member_urge_for_registration.ts | 50 +++++++ source/api/functions.ts | 3 +- source/conf.ts | 123 ++++++++++++++---- source/helpers.ts | 91 ++++++++++++- tools/makefile | 1 + 6 files changed, 251 insertions(+), 37 deletions(-) create mode 100644 source/api/actions/member_urge_for_registration.ts diff --git a/source/api/actions/member_register.ts b/source/api/actions/member_register.ts index 6751979..719be0d 100644 --- a/source/api/actions/member_register.ts +++ b/source/api/actions/member_register.ts @@ -34,6 +34,7 @@ namespace _espe.api "restriction": restriction_verification(stuff => parseInt(stuff.path_parameters["id"])), "execution": async ({"path_parameters": path_parameters, "query_parameters": query_parameters, "input": input}) => { const id : _espe.service.member.type_id = parseInt(path_parameters["id"]); + let successful : boolean = false; try { await _espe.service.member.register( id, @@ -47,17 +48,28 @@ namespace _espe.api "password": input.password, } ); - return Promise.resolve({ - "status_code": 200, - "data": null, - }); + successful = true; } catch (error) { + successful = false; + } + if (! successful) { return Promise.resolve({ "status_code": 409, "data": "bereits registriert", }); } + else { + await _espe.helpers.email_send( + _espe.conf.get().admins.map(admin => admin.email_address).filter(x => (x !== null)), + "Eingegangene Registrierung", + ("Member-ID: " + id.toFixed(0)) + ); + return Promise.resolve({ + "status_code": 200, + "data": null, + }); + } }, } ) diff --git a/source/api/actions/member_urge_for_registration.ts b/source/api/actions/member_urge_for_registration.ts new file mode 100644 index 0000000..aa1610a --- /dev/null +++ b/source/api/actions/member_urge_for_registration.ts @@ -0,0 +1,50 @@ +namespace _espe.api +{ + + /** + */ + export function register_member_urge_for_registration( + rest_subject : lib_plankton.rest.type_rest + ) : void + { + lib_plankton.rest.register<{id : int; url : string;}, null>( + rest_subject, + lib_plankton.http.enum_method.post, + "/member/urge_for_registration", + { + "description": "sendet an ein Mitglied eine E-Mail mit Aufforderung zur Registrierung", + "restriction": restriction_logged_in, + "execution": async ({"input": input}) => { + const member_id : _espe.service.member.type_id = input.id; + const member_value : _espe.service.member.type_value = await _espe.service.member.get(member_id); + const verification : string = await _espe.helpers.verification_get(member_id); + + await _espe.helpers.email_send( + [ + member_value.email_address_private_value, + ], + _espe.conf.get().settings.registration_email.subject, + lib_plankton.string.coin( + _espe.conf.get().settings.registration_email.body, + { + "name": member_value.name_real_value, + "url": lib_plankton.string.coin( + input.url, + { + "verification": verification, + } + ), + } + ) + ); + + return Promise.resolve({ + "status_code": 200, + "data": null, + }); + } + } + ); + } + +} diff --git a/source/api/functions.ts b/source/api/functions.ts index 8f42b34..492a7c4 100644 --- a/source/api/functions.ts +++ b/source/api/functions.ts @@ -27,7 +27,7 @@ namespace _espe.api _espe.api.register_email(rest_subject); // verification { - _espe.api.register_verification_get(rest_subject); + // _espe.api.register_verification_get(rest_subject); // _espe.api.register_verification_check(rest_subject); } // member @@ -38,6 +38,7 @@ namespace _espe.api _espe.api.register_member_update(rest_subject); // _espe.api.register_member_delete(rest_subject); _espe.api.register_member_register(rest_subject); + _espe.api.register_member_urge_for_registration(rest_subject); } diff --git a/source/conf.ts b/source/conf.ts index 0389c2b..abfa1c3 100644 --- a/source/conf.ts +++ b/source/conf.ts @@ -1,6 +1,16 @@ namespace _espe.conf { + /** + */ + type type_smtp_credentials = { + host : string; + port : int; + username : string; + password : string; + }; + + /** */ export type type_conf = { @@ -42,27 +52,36 @@ namespace _espe.conf }; } ); - email_sending : { - mode : ( - "regular" - | - "redirect" - | - "console" - | - "drop" - ); - smtp_credentials : ( - null - | - { - host : string; - port : int; - username : string; - password : string; - } - ); - }; + email_sending : ( + { + kind : "regular"; + data : { + smtp_credentials : type_smtp_credentials; + sender : string; + }; + } + | + { + kind : "redirect"; + data : { + smtp_credentials : type_smtp_credentials; + sender : string; + target : string; + }; + } + | + { + kind : "console"; + data : { + }; + } + | + { + kind : "drop"; + data : { + }; + } + ); session_management : { lifetime : int; drop_all_at_start : boolean; @@ -70,12 +89,17 @@ namespace _espe.conf settings : { target_domain : string; prefix_for_numberbased_email_addresses : string; + registration_email : { + subject : string; + body : string; + }; }; // TODO: evtl. in Datenbank verlagern admins : Array< { name : string; password_image : string; + email_address : (null | string); } >; }; @@ -122,12 +146,14 @@ namespace _espe.conf "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")); @@ -137,10 +163,53 @@ namespace _espe.conf }) (conf_raw["database"] ?? {}) ), "email_sending": ( - ((node_email_sending) => ({ - "mode": (node_email_sending["mode"] ?? "regular"), - "smtp_credentials": (node_email_sending["smtp_credentials"] ?? null), - })) (conf_raw["email_sending"] ?? {}) + ((node_email_sending) => { + const kind : string = (node_email_sending["kind"] ?? "regular"); + const data_raw = (node_email_sending["data"] ?? {}); + switch (kind) { + case "regular": { + return { + "kind": kind, + "data": { + "smtp_credentials": data_raw["smtp_credentials"], + "sender": data_raw["sender"], + } + }; + break; + } + case "redirect": { + return { + "kind": kind, + "data": { + "smtp_credentials": data_raw["smtp_credentials"], + "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) => ({ @@ -152,6 +221,10 @@ namespace _espe.conf ((node_settings) => ({ "target_domain": (node_settings["target_domain"] ?? "example.org"), "prefix_for_numberbased_email_addresses": (node_settings["prefix_for_numberbased_email_addresses"] ?? "member-"), + "registration_email": { + "subject": ((node_settings["registration_email"] ?? {})["subject"] ?? "Registration"), + "body": ((node_settings["registration_email"] ?? {})["body"] ?? "URL: {{url}}"), + }, })) (conf_raw["settings"] ?? {}) ), "admins": (conf_raw["admins"] ?? []), diff --git a/source/helpers.ts b/source/helpers.ts index 05b2802..1c5d467 100644 --- a/source/helpers.ts +++ b/source/helpers.ts @@ -99,6 +99,61 @@ 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( @@ -107,7 +162,7 @@ namespace _espe.helpers content : string ) : Promise { - const mode : string = _espe.conf.get().email_sending.mode; + const mode : string = _espe.conf.get().email_sending.kind; lib_plankton.log.info( "email_send", { @@ -118,20 +173,42 @@ namespace _espe.helpers ); switch (mode) { case "regular": { - if (_espe.conf.get().email_sending.smtp_credentials === null) { - return Promise.reject("no smtp credentials specified; add in conf as 'email_sending.smtp_credentials'!"); + if (_espe.conf.get().email_sending.data["smtp_credentials"] === null) { + return Promise.reject("no smtp credentials specified; add in conf as 'email_sending.data.smtp_credentials'!"); } else { - // TODO + return email_send_real( + _espe.conf.get().email_sending.data["smtp_credentials"], + receivers, + subject, + content, + { + "sender": _espe.conf.get().email_sending.data["sender"], + } + ); } break; } case "redirect": { - if (_espe.conf.get().email_sending.smtp_credentials === null) { - return Promise.reject("no smtp credentials specified; add in conf as 'email_sending.smtp_credentials'!"); + if (_espe.conf.get().email_sending.data["smtp_credentials"] === null) { + return Promise.reject("no smtp credentials specified; add in conf as 'email_sending.data.smtp_credentials'!"); } else { - // TODO + return email_send_real( + _espe.conf.get().email_sending.data["smtp_credentials"], + [_espe.conf.get().email_sending.data["target"]], + lib_plankton.string.coin( + "[REDIRECTION] {{receivers}} | {{original_subject}}", + { + "receivers": receivers.join(", "), + "original_subject": subject + } + ), + content, + { + "sender": _espe.conf.get().email_sending.data["sender"], + } + ); } break; } diff --git a/tools/makefile b/tools/makefile index 8cdf9da..9259f28 100644 --- a/tools/makefile +++ b/tools/makefile @@ -41,6 +41,7 @@ ${dir_temp}/espe-unlinked.js: \ ${dir_source}/api/actions/member_create.ts \ ${dir_source}/api/actions/member_update.ts \ ${dir_source}/api/actions/member_delete.ts \ + ${dir_source}/api/actions/member_urge_for_registration.ts \ ${dir_source}/api/actions/member_register.ts \ ${dir_source}/api/functions.ts \ ${dir_source}/conf.ts \