[mod] notifications

This commit is contained in:
roydfalk 2024-05-27 21:27:36 +02:00
parent 1483bf95a8
commit 8a33c029c7
4 changed files with 252 additions and 119 deletions

View file

@ -29,6 +29,7 @@ namespace _espe.api
email_use_nominal_address : boolean;
email_redirect_to_private_address : boolean;
password : (null | string);
notification_target_url_template ?: (null | string);
},
Array<
{
@ -67,6 +68,11 @@ namespace _espe.api
"nullable": true,
"description": "Passwort für alle Netz-Dienste",
},
"notification_target_url_template": {
"type": "string",
"nullable": true,
"description": "Platz-Halter: id"
},
},
"required": [
"email_use_veiled_address",
@ -128,6 +134,9 @@ namespace _espe.api
"email_use_nominal_address": input.email_use_nominal_address,
"email_redirect_to_private_address": input.email_redirect_to_private_address,
"password": input.password,
},
{
"notification_target_url_template": (input.notification_target_url_template ?? null),
}
)
.then(

View file

@ -96,12 +96,21 @@ namespace _espe.conf
settings : {
target_domain : string;
frontend_url_base : (null | string);
login_url : (null | string);
prefix_for_nominal_email_addresses : string;
facultative_membership_number : boolean;
summon_email : {
subject : string;
body : string;
};
registration_email : {
subject : string;
body : string;
};
activation_email : {
subject : string;
body : string;
};
password_policy : {
minimum_length : (null | int);
maximum_length : (null | int);
@ -248,12 +257,21 @@ namespace _espe.conf
((node_settings) => ({
"target_domain": (node_settings["target_domain"] ?? "example.org"),
"frontend_url_base": (node_settings["frontend_url_base"] ?? null), // TODO: mandatory?
"login_url": (node_settings["login_url"] ?? null),
"prefix_for_nominal_email_addresses": (node_settings["prefix_for_nominal_email_addresses"] ?? "member-"),
"facultative_membership_number": (node_settings["facultative_membership_number"] ?? false),
"summon_email": {
"subject": ((node_settings["summon_email"] ?? {})["subject"] ?? "Please register"),
"body": ((node_settings["summon_email"] ?? {})["body"] ?? "URL: {{url}}"),
},
"registration_email": {
"subject": ((node_settings["registration_email"] ?? {})["subject"] ?? "Registration"),
"subject": ((node_settings["registration_email"] ?? {})["subject"] ?? "Mmeber registered"),
"body": ((node_settings["registration_email"] ?? {})["body"] ?? "URL: {{url}}"),
},
"activation_email": {
"subject": ((node_settings["activation_email"] ?? {})["subject"] ?? "Account activated"),
"body": ((node_settings["activation_email"] ?? {})["body"] ?? "URL: {{url}}\n\nLogin name: {{name_login}}\n\n"),
},
"password_policy": (
((node_settings_password_policy) => ({
"minimum_length": (

View file

@ -268,4 +268,64 @@ namespace _espe.helpers
}
}
/**
*/
export function frontend_url_check(
) : void
{
const frontend_url_base : (null | string) = _espe.conf.get().settings.frontend_url_base;
if (frontend_url_base === null) {
throw (new Error("no frontend url base set; add in conf as 'settings.frontend_url_base'!"));
}
else {
// do nothing
}
}
/**
*/
export function frontend_url_get(
template : string,
arguments_ : Record<string, string>
) : (null | string)
{
const frontend_url_base : (null | string) = _espe.conf.get().settings.frontend_url_base;
if (frontend_url_base === null) {
// throw (new Error("no frontend url base set; add in conf as 'settings.frontend_url_base'!"));
return null;
}
else {
return lib_plankton.string.coin(
"{{base}}/{{rest}}",
{
"base": frontend_url_base,
"rest": lib_plankton.string.coin(template, arguments_),
}
);
}
}
/**
*/
export async function notify_admins(
subject : string,
body : string
) : Promise<void>
{
await _espe.helpers.email_send(
(
(
_espe.conf.get().admins
.map(admin => admin.email_address)
.filter(x => (x !== null))
) as Array<string>
),
subject,
body
);
}
}

View file

@ -33,7 +33,7 @@ namespace _espe.service.member
/**
*/
function notify_change(
function signal_change(
) : void
{
_hooks_change.forEach(
@ -251,6 +251,40 @@ namespace _espe.service.member
);
}
/**
*/
async function send_activation_email(
member_object : _espe.type.member_object
) : Promise<void>
{
if (! member_object.enabled) {
// do nothing
}
else {
if (member_object.email_address_private === null) {
// do nothing
}
else {
await _espe.helpers.email_send(
[
member_object.email_address_private,
],
_espe.conf.get().settings.activation_email.subject,
lib_plankton.string.coin(
_espe.conf.get().settings.activation_email.body,
{
"name_display": name_display(member_object),
"name_login": name_login(member_object),
"url": (_espe.conf.get().settings.login_url ?? "--"),
}
)
);
}
}
}
/**
* gibt die vollständigen Daten aller Mitglieder aus
*/
@ -330,47 +364,49 @@ namespace _espe.service.member
"password_change_token": null,
};
const id : _espe.type.member_id = await _espe.repository.member.create(object);
notify_change();
signal_change();
return id;
}
/**
* sendet an ein Mitglied eine E-Mail mit Aufforderung zur Registrierung
*
* @todo conf:settings.frontend_url_base verwenden
*/
export async function summon(
member_id : _espe.type.member_id,
url_template : string
) : Promise<(null | string)>
{
_espe.helpers.frontend_url_check();
const member_object : _espe.type.member_object = await get(member_id);
if (member_object.email_address_private === null) {
return null;
}
else {
const verification : string = await _espe.helpers.verification_get(member_id);
const url : string = lib_plankton.string.coin(
const url : (null | string) = _espe.helpers.frontend_url_get(
url_template,
{
"verification": verification,
"verification": await _espe.helpers.verification_get(member_id),
}
);
if (url === null) {
// do nothing
}
else {
await _espe.helpers.email_send(
[
member_object.email_address_private,
],
_espe.conf.get().settings.registration_email.subject,
_espe.conf.get().settings.summon_email.subject,
lib_plankton.string.coin(
_espe.conf.get().settings.registration_email.body,
_espe.conf.get().settings.summon_email.body,
{
"name": member_object.name_real_value,
"name": name_display(member_object),
"url": url,
}
)
);
}
return url;
}
}
@ -425,13 +461,13 @@ namespace _espe.service.member
password : (null | string);
},
options : {
notify_admins ?: boolean;
notification_target_url_template ?: (null | string);
} = {}
) : Promise<Array<{incident : string; details : Record<string, any>;}>>
{
options = Object.assign(
{
"notify_admins": false,
"notification_target_url_template": null,
},
options
);
@ -472,26 +508,40 @@ namespace _espe.service.member
member_object.password_image = await password_image(data.password);
member_object.registered = true;
await _espe.repository.member.update(member_id, member_object);
notify_change();
if (! options.notify_admins) {
signal_change();
{
const url : (null | string) = (
(
(options.notification_target_url_template === undefined)
||
(options.notification_target_url_template === null)
)
? null
: _espe.helpers.frontend_url_get(
options.notification_target_url_template,
{
"id": member_id.toFixed(0),
}
)
);
if (url === null) {
// do nothing
}
else {
await _espe.helpers.email_send(
(
(
_espe.conf.get().admins
.map(admin => admin.email_address)
.filter(x => (x !== null))
) as Array<string>
),
"Eingegangene Registrierung", // TODO
("Member-ID: " + member_id.toFixed(0))
/*await*/ _espe.helpers.notify_admins(
_espe.conf.get().settings.registration_email.subject,
lib_plankton.string.coin(
_espe.conf.get().settings.registration_email.body,
{
"name_display": name_display(member_object),
"url": url,
}
)
);
}
}
/*await*/ send_activation_email(member_object);
}
return Promise.resolve(flaws);
}
@ -526,7 +576,8 @@ namespace _espe.service.member
"password_change_token": member_object_old.password_change_token,
};
await _espe.repository.member.update(member_id, member_object_new);
notify_change();
signal_change();
/*await*/ send_activation_email(member_object_new);
}
@ -540,11 +591,7 @@ namespace _espe.service.member
url_template : string
) : Promise<void>
{
const frontend_url_base : (null | string) = _espe.conf.get().settings.frontend_url_base;
if (frontend_url_base === null) {
return Promise.reject<void>(new Error("no frontend url base set; add in conf as 'settings.frontend_url_base'!"));
}
else {
_espe.helpers.frontend_url_check();
const now : int = lib_plankton.base.get_current_timestamp(true);
const cooldown_time : int = _espe.conf.get().settings.password_change.cooldown_time;
const member_ids : Array<_espe.type.member_id> = await (
@ -616,8 +663,19 @@ namespace _espe.service.member
"password_change_token": token,
};
await _espe.repository.member.update(member_id, member_object_new);
// notify_change();
// signal_change();
// do NOT wait in order to reduce information for potential attackers
const url : (null | string) = _espe.helpers.frontend_url_get(
url_template,
{
"id": member_id.toFixed(0),
"token": token,
}
);
if (url === null) {
// do nothing
}
else {
/*await*/ _espe.helpers.email_send(
[
member_object_old.email_address_private,
@ -627,19 +685,7 @@ namespace _espe.service.member
_espe.conf.get().settings.password_change.initialization_email.body,
{
"name": name_display(member_object_old),
"url": lib_plankton.string.coin(
"{{base}}{{rest}}",
{
"base": frontend_url_base,
"rest": lib_plankton.string.coin(
url_template,
{
"id": member_id.toFixed(0),
"token": token,
}
),
}
),
"url": url,
}
)
);
@ -706,7 +752,7 @@ namespace _espe.service.member
"password_change_token": null,
};
await _espe.repository.member.update(member_id, member_object_new);
notify_change();
signal_change();
await _espe.helpers.email_send(
[
member_object_old.email_address_private,
@ -732,7 +778,7 @@ namespace _espe.service.member
) : Promise<void>
{
await _espe.repository.member.delete(id);
notify_change();
signal_change();
}
*/