Merge branch 'dev-auto_register'

This commit is contained in:
roydfalk 2024-06-23 13:53:48 +02:00
commit 2b1e99d1d6
15 changed files with 874 additions and 322 deletions

View file

@ -923,6 +923,16 @@ declare namespace lib_plankton.file {
*/
function delete_(path: string): Promise<void>;
}
declare namespace lib_plankton.email {
/**
*/
function send(smtp_credentials: {
host: string;
port: int;
username: string;
password: string;
}, sender: string, receivers: Array<string>, subject: string, content: string): Promise<void>;
}
declare namespace lib_plankton.log {
/**
*/
@ -996,10 +1006,40 @@ declare namespace lib_plankton.log {
* the path of the log file
*/
private path;
/**
*/
private human_readable;
/**
* [constructor]
*/
constructor(path: string);
constructor(path: string, human_readable: boolean);
/**
*/
add(entry: type_entry): void;
}
}
declare namespace lib_plankton.log {
/**
*/
class class_channel_email extends class_channel {
/**
*/
private smtp_credentials;
/**
*/
private sender;
/**
*/
private receivers;
/**
* [constructor]
*/
constructor(smtp_credentials: {
host: string;
port: int;
username: string;
password: string;
}, sender: string, receivers: Array<string>);
/**
*/
add(entry: type_entry): void;

View file

@ -2173,6 +2173,100 @@ var lib_plankton;
file.delete_ = delete_;
})(file = lib_plankton.file || (lib_plankton.file = {}));
})(lib_plankton || (lib_plankton = {}));
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
/*
This file is part of »bacterio-plankton:email«.
Copyright 2016-2023 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'
<info@greenscale.de>
»bacterio-plankton:email« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»bacterio-plankton:lang« 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »bacterio-plankton:email«. If not, see <http://www.gnu.org/licenses/>.
*/
var lib_plankton;
(function (lib_plankton) {
var email;
(function (email) {
/**
*/
function send(smtp_credentials, sender, receivers, subject, content) {
return __awaiter(this, void 0, void 0, function () {
var nm_nodemailer, transporter, info;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
nm_nodemailer = require("nodemailer");
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
});
return [4 /*yield*/, transporter.sendMail({
"from": sender,
"to": receivers.join(", "),
"subject": subject,
"text": content
})];
case 1:
info = _a.sent();
return [2 /*return*/];
}
});
});
}
email.send = send;
})(email = lib_plankton.email || (lib_plankton.email = {}));
})(lib_plankton || (lib_plankton = {}));
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
@ -2445,9 +2539,10 @@ var lib_plankton;
/**
* [constructor]
*/
function class_channel_file(path) {
function class_channel_file(path, human_readable) {
var _this = _super.call(this) || this;
_this.path = path;
_this.human_readable = human_readable;
return _this;
}
/**
@ -2455,9 +2550,8 @@ var lib_plankton;
class_channel_file.prototype.add = function (entry) {
var _this = this;
var nm_fs = require("fs");
nm_fs.writeFile(this.path, {
"flag": "a+"
}, (("<" + (new Date(Date.now())).toISOString().slice(0, 19) + ">")
var line = (this.human_readable
? (("<" + (new Date(Date.now())).toISOString().slice(0, 19) + ">")
+
" "
+
@ -2471,8 +2565,25 @@ var lib_plankton;
+
JSON.stringify(entry.details, undefined, " ")
+
"\n"), function (error) {
"\n")
: (JSON.stringify({
"timestamp": lib_plankton.base.get_current_timestamp(),
"level_number": entry.level,
"level_name": log.level_show(entry.level),
"incident": entry.incident,
"details": entry.details
})
+
"\n"));
nm_fs.writeFile(this.path, line, {
"flag": "a+"
}, function (error) {
if (error !== null) {
process.stderr.write('-- [plankton] could not add log entry to file ' + _this.path + "\n");
}
else {
// do nothing
}
});
};
return class_channel_file;
@ -2496,6 +2607,58 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »bacterio-plankton:log«. If not, see <http://www.gnu.org/licenses/>.
*/
var lib_plankton;
(function (lib_plankton) {
var log;
(function (log) {
/**
*/
var class_channel_email = /** @class */ (function (_super) {
__extends(class_channel_email, _super);
/**
* [constructor]
*/
function class_channel_email(smtp_credentials, sender, receivers) {
var _this = _super.call(this) || this;
_this.smtp_credentials = smtp_credentials;
_this.sender = sender;
_this.receivers = receivers;
return _this;
}
/**
*/
class_channel_email.prototype.add = function (entry) {
var nm_fs = require("fs");
lib_plankton.email.send(this.smtp_credentials, this.sender, this.receivers, (("[" + log.level_show(entry.level) + "]")
+
" "
+
("" + entry.incident + "")), JSON.stringify(entry.details, undefined, " "));
};
return class_channel_email;
}(log.class_channel));
log.class_channel_email = class_channel_email;
})(log = lib_plankton.log || (lib_plankton.log = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:log«.
Copyright 2016-2023 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'
<info@greenscale.de>
»bacterio-plankton:log« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»bacterio-plankton:lang« 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »bacterio-plankton:log«. If not, see <http://www.gnu.org/licenses/>.
*/
@ -2635,7 +2798,7 @@ var lib_plankton;
/**
*/
function channel_make(description) {
var _a, _b, _c, _d;
var _a, _b, _c, _d, _e;
switch (description.kind) {
default: {
throw (new Error("unhandled log channel kind: " + description.kind));
@ -2646,11 +2809,15 @@ var lib_plankton;
break;
}
case "file": {
return (new log.class_channel_minlevel(new log.class_channel_file((_b = description.data["path"]) !== null && _b !== void 0 ? _b : "/tmp/plankton.log"), translate_level((_c = description.data["threshold"]) !== null && _c !== void 0 ? _c : "debug")));
return (new log.class_channel_minlevel(new log.class_channel_file(((_b = description.data["path"]) !== null && _b !== void 0 ? _b : "/tmp/plankton.log"), false), translate_level((_c = description.data["threshold"]) !== null && _c !== void 0 ? _c : "debug")));
break;
}
case "email": {
return (new log.class_channel_minlevel(new log.class_channel_email(description.data["smtp_credentials"], description.data["sender"], description.data["receivers"]), translate_level((_d = description.data["threshold"]) !== null && _d !== void 0 ? _d : "debug")));
break;
}
case "notify": {
return (new log.class_channel_minlevel(new log.class_channel_notify(), translate_level((_d = description.data["threshold"]) !== null && _d !== void 0 ? _d : "debug")));
return (new log.class_channel_minlevel(new log.class_channel_notify(), translate_level((_e = description.data["threshold"]) !== null && _e !== void 0 ? _e : "debug")));
break;
}
}
@ -10453,7 +10620,7 @@ var lib_plankton;
// "input_shape": options.input_type,
// "output_shape": options.output_type,
});
lib_plankton.log.info("rest_route_added", {
lib_plankton.log.debug("rest_route_added", {
"http_method": http_method,
"path": path,
// "routetree": rest.routetree,

69
misc/conf.example.json Normal file
View file

@ -0,0 +1,69 @@
{
"general": {
"language": null,
"verbosity": "info",
"verification_secret": null
},
"server": {
"port": 4916,
"path_base": ""
},
"database": {
"kind": "sqlite",
"data": {
"path": "data.sqlite"
}
},
"email_sending": {
"kind": "console",
"data": {
}
},
"session_management": {
"in_memory": false,
"drop_all_at_start": false,
"lifetime": 7200
},
"settings": {
"organisation": {
"name": "Example Orginsation",
"domain": "example.org"
},
"misc": {
"prefix_for_veiled_email_addresses": "member-",
"facultative_membership_number": true,
"auto_register": true
},
"summon_email": {
"remark": null
},
"password_policy": {
"minimum_length": 4,
"maximum_length": 12,
"must_contain_letter": true,
"must_contain_number": false,
"must_contain_special_character": false
},
"password_change": {
"cooldown_time": 300
},
"name_index": {
"veil": false,
"salt": null
},
"connections": {
"frontend_url_base": null,
"login_url": null
}
},
"admins": [
{
"name": "admin",
"password_image": "$2b$12$xOa6iWPOMjiwJ3oIOZWDGu/w2Ca/eKLHWE7aDItkNsP/79nJk065i",
"email_address": "espe-admin@example.org"
}
],
"output": {
"authelia": "/tmp/authelia-users.yml"
}
}

View file

@ -22,15 +22,15 @@ namespace _espe.api
rest_subject : lib_plankton.rest.type_rest
) : void
{
register<_espe.service.member.type_value, null>(
register<null, null>(
rest_subject,
lib_plankton.http.enum_method.delete,
"/member/:id",
"/member/delete/:id",
{
"description": "löscht ein vorhandenes Mitglied",
"restriction": restriction_logged_in,
"execution": async ({"path_parameters": path_parameters}) => {
const member_id : _espe.service.member.type_id = parseInt(path_parameters["id"]);
const member_id : _espe.type.member_id = parseInt(path_parameters["id"]);
await _espe.service.member.remove(member_id);
return Promise.resolve({
"status_code": 200,

View file

@ -27,6 +27,7 @@ namespace _espe.api
membership_number : (null | string);
name_real_value : string;
email_address_private : (null | string);
notification_target_url_template ?: (null | string);
},
(
string
@ -59,6 +60,11 @@ namespace _espe.api
"nullable": true,
"description": "private E-Mail-Adresse"
},
"notification_target_url_template": {
"type": "string",
"nullable": true,
"description": "Platz-Halter: id"
},
},
"required": [
"membership_number",
@ -105,6 +111,24 @@ namespace _espe.api
),
}
);
if (! _espe.conf.get().settings.misc.auto_register) {
// do nothing
}
else {
// TODO: Werte in Konfiguration auslagern
await _espe.service.member.register(
member_id,
{
"email_use_veiled_address": false,
"email_use_nominal_address": false,
"email_redirect_to_private_address": false,
"password": null,
},
{
"notification_target_url_template": input.notification_target_url_template,
}
);
}
return Promise.resolve({
"status_code": 201,
"data": member_id

View file

@ -52,7 +52,7 @@ namespace _espe.api
_espe.api.register_member_list(rest_subject);
_espe.api.register_member_read(rest_subject);
_espe.api.register_member_modify(rest_subject);
// _espe.api.register_member_delete(rest_subject);
_espe.api.register_member_delete(rest_subject);
// password_change
{
_espe.api.register_member_password_change_initialize(rest_subject);

View file

@ -18,24 +18,66 @@ namespace _espe.conf
/**
*/
export type type_conf = {
general : {
language : (null | string);
verbosity : (
"none"
|
type type_log_threshold = (
"debug"
|
"notice"
|
"info"
|
"notice"
|
"warning"
|
"error"
);
/**
*/
type type_log_format = (
"jsonl"
|
"human_readable"
);
/**
*/
export type type_conf = {
general : {
language : (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 : {
port : int;
path_base : string;
@ -102,6 +144,7 @@ namespace _espe.conf
misc : {
prefix_for_veiled_email_addresses : string;
facultative_membership_number : boolean;
auto_register : boolean;
};
summon_email : {
remark : string;
@ -150,17 +193,26 @@ namespace _espe.conf
conf_raw : any
) : void
{
switch (conf_raw["version"]) {
case 1: {
_data = {
"general": (
((node_general) => ({
"language": (node_general["language"] ?? null),
"verbosity": (node_general["verbosity"] ?? "notice"),
"verification_secret": (node_general["verification_secret"] ?? null),
})) (conf_raw["general"] ?? {})
),
"log": [
{
"kind": "stdout",
"data": {
"threshold": ((conf_raw["general"] ?? {})["verbosity"] ?? "notice"),
}
},
],
"server": (
((node_server) => ({
"port": (node_server["port"] ?? 3593),
"port": (node_server["port"] ?? 4916),
"path_base": (node_server["path_base"] ?? ""),
})) (conf_raw["server"] ?? {})
),
@ -194,7 +246,7 @@ namespace _espe.conf
),
"email_sending": (
((node_email_sending) => {
const kind : string = (node_email_sending["kind"] ?? "regular");
const kind : string = (node_email_sending["kind"] ?? "console");
const data_raw = (node_email_sending["data"] ?? {});
switch (kind) {
case "regular": {
@ -258,6 +310,7 @@ namespace _espe.conf
((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": (
@ -289,8 +342,8 @@ namespace _espe.conf
),
"name_index": (
((node_settings_password_policy) => ({
"veil": (node_settings_password_policy["veil"] ?? false),
"salt": (node_settings_password_policy["salt"] ?? null),
"veil": (node_settings_password_policy["veil"] ?? true),
"salt": (node_settings_password_policy["salt"] ?? ""),
})) (node_settings["name_index"] ?? {})
),
"connections": (
@ -308,6 +361,198 @@ namespace _espe.conf
})) (conf_raw["output"] ?? {})
),
};
break;
}
case 2: {
_data = {
"general": (
((node_general) => ({
"language": (node_general["language"] ?? null),
"verification_secret": (node_general["verification_secret"] ?? null),
})) (conf_raw["general"] ?? {})
),
"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": {
}
},
]
)
),
"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;
}
}
}

View file

@ -8,7 +8,8 @@
"email.registration.subject": "Registrierung erfolgt",
"email.registration.body": "Das Mitglied '{{name_display}}' hat sich soeben registriert:\n\n{{url}}",
"email.activation.subject": "Freischaltung erfolgt",
"email.activation.body": "Hi, {{name_display}}\n\nDein Mitglieder-Konto wurde gerade freigeschalten. Du kannst dich nun anmelden:\n\nURL: {{url}}\nAnmelde-Name: {{name_login}}",
"email.activation.body": "Hi, {{name_display}}\n\nDein Mitglieder-Konto wurde gerade freigeschalten. Du kannst dich nun anmelden:\n\nURL: {{url}}\nAnmelde-Name: {{name_login}}\n{{password_info}}",
"email.activation.password_info": "Passwort: {{password}}\n\nBitte ändere dein Passwort zeitnah!",
"email.password_change.initialization.subject": "Passwort-Änderung eingeleitet",
"email.password_change.initialization.body": "Hi, {{name}}\n\nDie Funktion zum Ändern deines Passwortes wurde aufgerufen. Wenn du dein Passwort ändern willst, rufe folgenden Link auf:\n\n{{url}}\n",
"email.password_change.execution.subject": "Passwort-Änderung abgeschlossen",

View file

@ -8,7 +8,8 @@
"email.registration.subject": "Registration received",
"email.registration.body": "The member '{{name_display}}' just registered:\n\n{{url}}",
"email.activation.subject": "Activated",
"email.activation.body": "Hi, {{name_display}}\n\nYour account has just been activated. You may login now:\n\nURL: {{url}}\nLogin name: {{name_login}}",
"email.activation.body": "Hi, {{name_display}}\n\nYour account has just been activated. You may login now:\n\nURL: {{url}}\nLogin name: {{name_login}}\n{{password_info}}",
"email.activation.password_info": "Password: {{password}}\n\nPlease change your password soon!",
"email.password_change.initialization.subject": "Password change initialized",
"email.password_change.initialization.body": "Hi, {{name}}\n\nThe function for changing your password has been triggered. If you want to change your password, open the folloling link:\n\n{{url}}",
"email.password_change.execution.subject": "Password change concluded",

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(
@ -207,14 +152,12 @@ namespace _espe.helpers
return Promise.reject<void>("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<void>("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;

View file

@ -21,16 +21,61 @@ async function main(
) : Promise<void>
{
// init
await lib_plankton.translate.initialize(
lib_plankton.log.conf_push(
[
lib_plankton.log.channel_make(
{
"kind": "stdout",
"data": {
"threshold": "notice",
// "format": "human_readable",
}
}
),
]
);
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": [
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"],
"packages": packages,
"order": language_codes,
"autopromote": false,
}
)
)
);
// args
@ -138,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

View file

@ -191,9 +191,18 @@ namespace _espe.service.member
/**
*/
async function send_activation_email(
member_object : _espe.type.member_object
member_object : _espe.type.member_object,
options : {
password ?: (null | string);
} = {}
) : Promise<void>
{
options = Object.assign(
{
"password": null,
},
options
);
if (! member_object.enabled) {
// do nothing
}
@ -219,6 +228,20 @@ namespace _espe.service.member
"name_display": name_display(member_object),
"name_login": name_login(member_object),
"url": (_espe.conf.get().settings.connections.login_url ?? "--"),
"password_info": (
(
(options.password === undefined)
||
(options.password === null)
)
? ""
: lib_plankton.string.coin(
lib_plankton.translate.get("email.activation.password_info"),
{
"password": options.password,
}
)
),
}
)
);
@ -424,28 +447,31 @@ namespace _espe.service.member
);
const member_object : _espe.type.member_object = await get(member_id);
let flaws : Array<{incident : string; details : Record<string, any>;}> = [];
const password_set : boolean = (
(data.password !== null)
&&
(data.password !== "")
);
let password_value : string;
let password_generated : boolean;
if (member_object.registered) {
flaws.push({"incident": "already_registered", "details": {}});
password_value = "";
password_generated = false;
}
else {
if (
password_set
&&
(data.password !== null)
&&
(data.password !== "")
) {
flaws = flaws.concat(
validate_password(data.password)
.map(flaw => ({"incident": ("password_" + flaw.incident), "details": flaw.details}))
);
password_value = data.password;
password_generated = false;
}
else {
// do nothing
password_value = generate_password();
password_generated = true;
}
}
@ -456,7 +482,7 @@ namespace _espe.service.member
member_object.email_use_veiled_address = data.email_use_veiled_address;
member_object.email_use_nominal_address = data.email_use_nominal_address;
member_object.email_redirect_to_private_address = data.email_redirect_to_private_address;
member_object.password_image = await password_image(data.password);
member_object.password_image = await password_image(password_value);
member_object.registered = true;
await _espe.repository.member.update(member_id, member_object);
signal_change();
@ -475,10 +501,6 @@ namespace _espe.service.member
}
)
);
if (url === null) {
// do nothing
}
else {
/*await*/ _espe.helpers.notify_admins(
lib_plankton.string.coin(
"{{head}} | {{core}}",
@ -491,13 +513,12 @@ namespace _espe.service.member
lib_plankton.translate.get("email.registration.body"),
{
"name_display": name_display(member_object),
"url": url,
"url": (url ?? "?"),
}
)
);
}
}
/*await*/ send_activation_email(member_object);
/*await*/ send_activation_email(member_object, {"password": password_generated ? password_value : null});
}
return Promise.resolve(flaws);
@ -538,6 +559,17 @@ namespace _espe.service.member
}
/**
*/
export async function remove(
id : _espe.type.member_id
) : Promise<void>
{
await _espe.repository.member.delete_(id);
signal_change();
}
/**
* bereitet eine Passwort-Rücksetzung für Mitglieder vor
*
@ -741,17 +773,6 @@ namespace _espe.service.member
}
/*
export async function remove(
id : _espe.type.member_id
) : Promise<void>
{
await _espe.repository.member.delete(id);
signal_change();
}
*/
/**
* @todo check validity (e.g. username characters)
*/

View file

@ -46,6 +46,7 @@ def main():
"--update",
"--verbose",
"--exclude='conf.json'",
"--exclude='data.sqlite'",
("%s/" % args.build_directory),
(
("%s" % args.target_directory)

View file

@ -65,6 +65,7 @@ ${dir_temp}/espe-core.js ${dir_temp}/espe-core.d.ts: \
${dir_source}/api/actions/member_list.ts \
${dir_source}/api/actions/member_read.ts \
${dir_source}/api/actions/member_modify.ts \
${dir_source}/api/actions/member_delete.ts \
${dir_source}/api/actions/member_password_change_initialize.ts \
${dir_source}/api/actions/member_password_change_execute.ts \
${dir_source}/api/functions.ts \

View file

@ -19,6 +19,7 @@ modules="${modules} api"
modules="${modules} rest"
modules="${modules} http"
modules="${modules} server"
modules="${modules} email"
modules="${modules} args"
modules="${modules} translate"
modules="${modules} log"