diff --git a/source/api/actions/member_project.ts b/source/api/actions/member_project.ts index faafec8..99caab2 100644 --- a/source/api/actions/member_project.ts +++ b/source/api/actions/member_project.ts @@ -76,7 +76,7 @@ namespace _espe.api } else { if ( - (! _espe.conf.get().settings.facultative_membership_number) + (! _espe.conf.get().settings.misc.facultative_membership_number) && ( (input.membership_number === null) diff --git a/source/conf.ts b/source/conf.ts index 3229d6c..87febc9 100644 --- a/source/conf.ts +++ b/source/conf.ts @@ -20,6 +20,7 @@ namespace _espe.conf */ export type type_conf = { general : { + language : (null | string); verbosity : ( "none" | @@ -94,22 +95,16 @@ namespace _espe.conf lifetime : int; }; settings : { - target_domain : string; - frontend_url_base : (null | string); - login_url : (null | string); - prefix_for_nominal_email_addresses : string; - facultative_membership_number : boolean; + organisation : { + name : string; + domain : string; + }; + misc : { + prefix_for_veiled_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; + remark : string; }; password_policy : { minimum_length : (null | int); @@ -120,19 +115,15 @@ namespace _espe.conf }; password_change : { cooldown_time : int; - initialization_email : { - subject : string; - body : string; - }; - execution_email : { - subject : string; - body : string; - }; }; name_index : { veil : boolean; salt : (null | string); }; + connections : { + frontend_url_base : (null | string); + login_url : (null | string); + }; }; // TODO: evtl. in Datenbank verlagern admins : Array< @@ -162,6 +153,7 @@ namespace _espe.conf _data = { "general": ( ((node_general) => ({ + "language": (node_general["language"] ?? null), "verbosity": (node_general["verbosity"] ?? "notice"), "verification_secret": (node_general["verification_secret"] ?? null), })) (conf_raw["general"] ?? {}) @@ -258,23 +250,21 @@ namespace _espe.conf ), "settings": ( ((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"] ?? "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"), + "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), + })) (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": ( @@ -295,14 +285,6 @@ namespace _espe.conf "password_change": ( ((node_settings_password_change) => ({ "cooldown_time": (node_settings_password_change["cooldown_time"] ?? 86400), - "initialization_email": { - "subject": ((node_settings_password_change["initialization_email"] ?? {})["subject"] ?? "Password change initialized"), - "body": ((node_settings_password_change["initialization_email"] ?? {})["body"] ?? "{{url}}"), - }, - "execution_email": { - "subject": ((node_settings_password_change["execution_email"] ?? {})["subject"] ?? "Password changed"), - "body": ((node_settings_password_change["execution_email"] ?? {})["body"] ?? ""), - }, })) (node_settings["password_change"] ?? {}) ), "name_index": ( @@ -311,6 +293,12 @@ namespace _espe.conf "salt": (node_settings_password_policy["salt"] ?? null), })) (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"] ?? []), diff --git a/source/data/localization/deu.loc.json b/source/data/localization/deu.loc.json new file mode 100644 index 0000000..3508bd2 --- /dev/null +++ b/source/data/localization/deu.loc.json @@ -0,0 +1,26 @@ +{ + "meta": { + "identifier": "deu" + }, + "tree": { + "email.summon.subject": "Registrierung", + "email.summon.body": "Hi, {{name}}\n\n{{remark}}\n\nWenn du die Dienste nutzen möchtest, rufe bitte folgende Adresse auf:\n\n{{url}}", + "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.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", + "email.password_change.execution.body": "Hi, {{name}}\n\nDein Passwort wurde soeben geändert.\n", + "help.args.action.description": "auszuführende Aktion; Auswahl", + "help.args.action.options.serve": "Server starten", + "help.args.action.options.api_doc": "API-Dokumentation gemäß OpenAPI-Spezifikation auf Standard-Ausgabe schreiben", + "help.args.action.options.expose_conf": "Vollständige Konfiguration ausgeben", + "help.args.action.options.password_image": "Passwort-Abbild errechnen und auf Standard-Ausgabe schreiben", + "help.args.action.options.export_authelia": "Export der Nutzer-Datenbank im Authelia-user-Datei-Format auf Standard-Ausgabe schreiben", + "help.args.action.options.help": "Diese Hilfe auf Standard-Ausgabe schreiben", + "help.args.conf_path.description": "Pfad zur Konfigurations-Datei", + "help.args.help.description": "Hilfe anzeigen" + } +} diff --git a/source/data/localization/eng.loc.json b/source/data/localization/eng.loc.json new file mode 100644 index 0000000..b646b39 --- /dev/null +++ b/source/data/localization/eng.loc.json @@ -0,0 +1,26 @@ +{ + "meta": { + "identifier": "eng" + }, + "tree": { + "email.summon.subject": "Registration", + "email.summon.body": "Hi, {{name}}\n\n{{remark}}\n\nIn case you want to use online service, open the following link:\n\n{{url}}", + "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.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", + "email.password_change.execution.body": "Hi, {{name}}\n\nYour password has just been changed.\n", + "help.args.action.description": "action to executo; options", + "help.args.action.options.serve": "start server", + "help.args.action.options.api_doc": "write API documentation according to OpenAPI specification to stdout", + "help.args.action.options.expose_conf": "write complete configuration to stdout", + "help.args.action.options.password_image": "compute password image and write to stdout", + "help.args.action.options.export_authelia": "export user database in Authelia user file format and write to stdout", + "help.args.action.options.help": "write this help to stdout", + "help.args.conf_path.description": "path to configuration file", + "help.args.help.description": "show help" + } +} diff --git a/source/helpers.ts b/source/helpers.ts index fd76e3a..4896501 100644 --- a/source/helpers.ts +++ b/source/helpers.ts @@ -274,9 +274,9 @@ namespace _espe.helpers export function frontend_url_check( ) : void { - const frontend_url_base : (null | string) = _espe.conf.get().settings.frontend_url_base; + const frontend_url_base : (null | string) = _espe.conf.get().settings.connections.frontend_url_base; if (frontend_url_base === null) { - throw (new Error("no frontend url base set; add in conf as 'settings.frontend_url_base'!")); + throw (new Error("no frontend url base set; add in conf as 'settings.connections.frontend_url_base'!")); } else { // do nothing @@ -291,9 +291,9 @@ namespace _espe.helpers arguments_ : Record ) : (null | string) { - const frontend_url_base : (null | string) = _espe.conf.get().settings.frontend_url_base; + const frontend_url_base : (null | string) = _espe.conf.get().settings.connections.frontend_url_base; if (frontend_url_base === null) { - // throw (new Error("no frontend url base set; add in conf as 'settings.frontend_url_base'!")); + // throw (new Error("no frontend url base set; add in conf as 'settings.connections.frontend_url_base'!")); return null; } else { diff --git a/source/main.ts b/source/main.ts index 9de2b8a..78a59e3 100644 --- a/source/main.ts +++ b/source/main.ts @@ -20,6 +20,19 @@ async function main( args_raw : Array ) : Promise { + // init + await 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"], + "autopromote": false, + } + ); + // args const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler({ "action": lib_plankton.args.class_argument.positional({ @@ -29,33 +42,34 @@ async function main( "default": "serve", "name": "action", "info": lib_plankton.string.coin( - "auszuführende Aktion; Auswahl:\n{{selection}}\n\t\t", + "{{description}}:\n{{options}}\n\t\t", { - "selection": ( + "description": lib_plankton.translate.get("help.args.action.options.serve"), + "options": ( [ { "name": "serve", - "description": "Server starten", + "description": lib_plankton.translate.get("help.args.action.options.serve"), }, { "name": "api-doc", - "description": "API-Dokumentation gemäß OpenAPI Specification auf Standard-Ausgabe schreiben" + "description": lib_plankton.translate.get("help.args.action.options.api_doc") }, { "name": "expose-conf", - "description": "Vollständige Konfiguration ausgeben" + "description": lib_plankton.translate.get("help.args.action.options.expose_conf") }, { "name": "password-image", - "description": "Passwort-Abbild errechnen und auf Standard-Ausgabe schreiben" + "description": lib_plankton.translate.get("help.args.action.options.password_image") }, { "name": "export-authelia", - "description": "Export der Nutzer-Datenbank im Authelia-user-Datei-Format auf Standard-Ausgabe schreiben" + "description": lib_plankton.translate.get("help.args.action.options.export_authelia") }, { "name": "help", - "description": "Diese Hilfe auf Standard-Ausgabe schreiben" + "description": lib_plankton.translate.get("help.args.action.options.help") }, ] .map( @@ -96,7 +110,7 @@ async function main( "type": lib_plankton.args.enum_type.string, "mode": lib_plankton.args.enum_mode.replace, "default": "conf.json", - "info": "Pfad zur Konfigurations-Datei", + "info": lib_plankton.translate.get("help.args.conf_path.description"), "name": "conf-path", }), "help": lib_plankton.args.class_argument.volatile({ @@ -105,14 +119,38 @@ async function main( "type": lib_plankton.args.enum_type.boolean, "mode": lib_plankton.args.enum_mode.replace, "default": false, - "info": "Hilfe anzeigen", + "info": lib_plankton.translate.get("help.args.help.description"), "name": "help", }), }); const args : Record = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" ")); + // init + await _espe.conf.load(args["conf_path"]); + if (_espe.conf.get().general.language === null) { + // do nothing + } + else { + 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(), + { + "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] + ), + ] + ); + // exec - if (args["help"] || (args["action"] == "help")) { + if (args["help"] || (args["action"] === "help")) { process.stdout.write( arg_handler.generate_help( { @@ -126,26 +164,6 @@ async function main( ); } else { - // conf - await _espe.conf.load(args["conf_path"]); - - // setup - lib_plankton.log.conf_push( - [ - new lib_plankton.log.class_channel_minlevel( - new lib_plankton.log.class_channel_stdout(), - { - "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] - ), - ] - ); - switch (args["action"]) { default: { process.stderr.write("invalid action: " + args["action"] + "\n"); diff --git a/source/services/member.ts b/source/services/member.ts index bfdf03f..d2824a3 100644 --- a/source/services/member.ts +++ b/source/services/member.ts @@ -188,9 +188,9 @@ namespace _espe.service.member : lib_plankton.string.coin( "{{prefix}}{{membership_number}}@{{domain}}", { - "prefix": _espe.conf.get().settings.prefix_for_nominal_email_addresses, + "prefix": _espe.conf.get().settings.misc.prefix_for_veiled_email_addresses, "membership_number": object.membership_number, - "domain": _espe.conf.get().settings.target_domain, + "domain": _espe.conf.get().settings.organisation.domain, } ) ); @@ -208,7 +208,7 @@ namespace _espe.service.member "{{user}}@{{domain}}", { "user": name_login(object), - "domain": _espe.conf.get().settings.target_domain, + "domain": _espe.conf.get().settings.organisation.domain, } ); } @@ -270,13 +270,19 @@ namespace _espe.service.member [ member_object.email_address_private, ], - _espe.conf.get().settings.activation_email.subject, lib_plankton.string.coin( - _espe.conf.get().settings.activation_email.body, + "{{head}} | {{core}}", + { + "head": _espe.conf.get().settings.organisation.name, + "core": lib_plankton.translate.get("email.activation.subject"), + } + ), + lib_plankton.string.coin( + lib_plankton.translate.get("email.activation.body"), { "name_display": name_display(member_object), "name_login": name_login(member_object), - "url": (_espe.conf.get().settings.login_url ?? "--"), + "url": (_espe.conf.get().settings.connections.login_url ?? "--"), } ) ); @@ -397,12 +403,23 @@ namespace _espe.service.member [ member_object.email_address_private, ], - _espe.conf.get().settings.summon_email.subject, lib_plankton.string.coin( - _espe.conf.get().settings.summon_email.body, + "{{head}} | {{core}}", + { + "head": _espe.conf.get().settings.organisation.name, + "core": lib_plankton.translate.get("email.summon.subject"), + } + ), + lib_plankton.string.coin( + lib_plankton.translate.get("email.summon.body"), { "name": name_display(member_object), "url": url, + "remark": ( + (_espe.conf.get().settings.summon_email.remark === null) + ? "" + : (_espe.conf.get().settings.summon_email.remark + "\n\n") + ), } ) ); @@ -529,9 +546,15 @@ namespace _espe.service.member } else { /*await*/ _espe.helpers.notify_admins( - _espe.conf.get().settings.registration_email.subject, lib_plankton.string.coin( - _espe.conf.get().settings.registration_email.body, + "{{head}} | {{core}}", + { + "head": _espe.conf.get().settings.organisation.name, + "core": lib_plankton.translate.get("email.registration.subject"), + } + ), + lib_plankton.string.coin( + lib_plankton.translate.get("email.registration.body"), { "name_display": name_display(member_object), "url": url, @@ -680,9 +703,15 @@ namespace _espe.service.member [ member_object_old.email_address_private, ], - _espe.conf.get().settings.password_change.initialization_email.subject, lib_plankton.string.coin( - _espe.conf.get().settings.password_change.initialization_email.body, + "{{head}} | {{core}}", + { + "head": _espe.conf.get().settings.organisation.name, + "core": lib_plankton.translate.get("email.password_change.initialization.subject"), + } + ), + lib_plankton.string.coin( + lib_plankton.translate.get("email.password_change.initialization.body"), { "name": name_display(member_object_old), "url": url, @@ -757,9 +786,15 @@ namespace _espe.service.member [ member_object_old.email_address_private, ], - _espe.conf.get().settings.password_change.execution_email.subject, lib_plankton.string.coin( - _espe.conf.get().settings.password_change.execution_email.body, + "{{head}} | {{core}}", + { + "head": _espe.conf.get().settings.organisation.name, + "core": lib_plankton.translate.get("email.password_change.execution.subject"), + } + ), + lib_plankton.string.coin( + lib_plankton.translate.get("email.password_change.execution.body"), { "name": name_display(member_object_old), } diff --git a/todo.md b/todo.md deleted file mode 100644 index e0653eb..0000000 --- a/todo.md +++ /dev/null @@ -1,4 +0,0 @@ -# ToDo - -- Übersetzungen - diff --git a/tools/makefile b/tools/makefile index 671b6e1..38f63a2 100644 --- a/tools/makefile +++ b/tools/makefile @@ -71,7 +71,7 @@ ${dir_temp}/espe-core.js ${dir_temp}/espe-core.d.ts: \ @ tsc --lib es2020 --strict $^ --outFile ${dir_temp}/espe-core.js --declaration .PHONY: main -main: core ${dir_build}/espe +main: core ${dir_build}/espe data ${dir_temp}/espe-main-raw.js: \ ${dir_lib}/plankton/plankton.d.ts \ @@ -118,3 +118,9 @@ ${dir_build}/espe-test.mocha.js: \ @ ${cmd_mkdir} $(dir $@) @ ${cmd_cat} $^ > $@ @ ${cmd_chmod} +x $@ + +.PHONY: data +data: + @ ${cmd_log} "data …" + @ ${cmd_mkdir} ${dir_build}/data + @ ${cmd_cp} -r -u -v ${dir_source}/data/* ${dir_build}/data/