diff --git a/readme.md b/readme.md index d36baf5..6c9614f 100644 --- a/readme.md +++ b/readme.md @@ -27,7 +27,6 @@ ### Anweisungen -- `conf.json` im build-Verzeichnis anlegen -- `tools/run` ausführen - - +- ins Erzeugnis-Verzeichnis wechseln +- `./espe -h` ausführen +- für die meisten Anwendungsfälle ist es erforderlich eine Konfigurations-Datei anzulegen diff --git a/source/conf.ts b/source/conf.ts index 06883c4..24e47a7 100644 --- a/source/conf.ts +++ b/source/conf.ts @@ -4,9 +4,45 @@ namespace _espe.conf /** */ export type type_conf = { - port : int; - database_path : string; - email : { + general : { + verbosity : ( + "none" + | + "debug" + | + "notice" + | + "info" + | + "warning" + | + "error" + ); + verification_secret : (null | string); + }; + server : { + port : int; + }; + database : ( + { + kind : "sqlite"; + data : { + path : string; + }; + } + | + { + kind : "postgresql"; + data : { + host : string; + port ?: int; + username : string; + password : string; + schema : string; + }; + } + ); + email_sending : { mode : ( "regular" | @@ -16,35 +52,30 @@ namespace _espe.conf | "drop" ); - smtp_credentials : { - host : string; - port : int; - username : string; - password : string; - }; + smtp_credentials : ( + null + | + { + host : string; + port : int; + username : string; + password : string; + } + ); }; - session_lifetime : int; - session_drop_all_at_start : boolean; - email_domain : string; - email_numberbased_address_prefix : string; - verification_secret : string; - verbosity : ( - "none" - | - "debug" - | - "notice" - | - "info" - | - "warning" - | - "error" - ); + session_management : { + lifetime : int; + drop_all_at_start : boolean; + }; + settings : { + target_domain : string; + prefix_for_numberbased_email_addresses : string; + }; + // TODO: evtl. in Datenbank verlagern admins : Array< { name : string; - password : string; + password_image : string; } >; }; @@ -68,17 +99,44 @@ namespace _espe.conf : {} ); _data = { - "port": (conf_raw["port"] ?? 7979), - "email_domain": (conf_raw["email_domain"] ?? "example.org"), - "email_numberbased_address_prefix": (conf_raw["email_numberbased_address_prefix"] ?? "member-"), - "email": conf_raw["email"], // TODO: feiner - "verification_secret": (conf_raw["verification_secret"] ?? "itsy_bitsy"), - "session_lifetime": (conf_raw["session_lifetime"] ?? 900), - "session_drop_all_at_start": true, - "verbosity": (conf_raw["verbosity"] ?? "notice"), - "database_path": (conf_raw["database_path"] ?? "data.sqlite"), + "general": ( + ((node_general) => ({ + "verbosity": (node_general["verbosity"] ?? "notice"), + "verification_secret": (node_general["verification_secret"] ?? null), + })) (conf_raw["general"] ?? {}) + ), + "server": ( + ((node_server) => ({ + "port": (node_server["port"] ?? 7979), + })) (conf_raw["server"] ?? {}) + ), + "database": ( + ((node_database) => ({ + "kind": (node_database["kind"] ?? "sqlite"), + "data": node_database["data"], + })) (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"] ?? {}) + ), + "session_management": ( + ((node_session_management) => ({ + "lifetime": (node_session_management["lifetime"] ?? 900), + "drop_all_at_start": (node_session_management["drop_all_at_start"] ?? true), + })) (conf_raw["session_management"] ?? {}) + ), + "settings": ( + ((node_settings) => ({ + "target_domain": (node_settings["target_domain"] ?? "example.org"), + "prefix_for_numberbased_email_addresses": (node_settings["prefix_for_numberbased_email_addresses"] ?? "member-"), + })) (conf_raw["settings"] ?? {}) + ), "admins": (conf_raw["admins"] ?? []), }; + // process.stderr.write(JSON.stringify(_data, undefined, "\t")); return Promise.resolve(undefined); } @@ -86,7 +144,7 @@ namespace _espe.conf /** */ export function get( - ) : any + ) : type_conf { if (_data === null) { throw (new Error("conf not loaded yet")); diff --git a/source/helpers.ts b/source/helpers.ts index 122798d..c547baa 100644 --- a/source/helpers.ts +++ b/source/helpers.ts @@ -54,12 +54,20 @@ namespace _espe.helpers export function database_implementation( ) : lib_plankton.database.type_database { - return lib_plankton.database.sqlite_database( - { - "path": _espe.conf.get().database_path, - "verbose": false, + switch (_espe.conf.get().database.kind) { + case "sqlite": { + return lib_plankton.database.sqlite_database( + { + "path": _espe.conf.get().database.data["path"], + "verbose": false, + } + ); + break; } - ); + default: { + throw (new Error("database implementation not available: " + _espe.conf.get().database.kind)); + } + } } @@ -69,10 +77,16 @@ namespace _espe.helpers data : any ) : Promise { - return lib_plankton.sha256.get( - lib_plankton.json.encode(data), - _espe.conf.get().verification_secret - ); + const secret : (null | string) = _espe.conf.get().general.verification_secret; + if (secret === null) { + return Promise.reject(new Error("no verification secret specified; add in conf as 'general.verification_secret'!")); + } + else { + return lib_plankton.sha256.get( + lib_plankton.json.encode(data), + secret + ); + } } @@ -83,11 +97,17 @@ namespace _espe.helpers verification : string ) : Promise { - const verification_expected : string = lib_plankton.sha256.get( - lib_plankton.json.encode(data), - _espe.conf.get().verification_secret - ); - return (verification === verification_expected); + const secret : (null | string) = _espe.conf.get().general.verification_secret; + if (secret === null) { + return Promise.reject(new Error("no verification secret specified; add in conf as 'general.verification_secret'!")); + } + else { + const verification_expected : string = lib_plankton.sha256.get( + lib_plankton.json.encode(data), + secret + ); + return (verification === verification_expected); + } } @@ -99,7 +119,7 @@ namespace _espe.helpers content : string ) : Promise { - const mode : string = _espe.conf.get().email.mode; + const mode : string = _espe.conf.get().email_sending.mode; lib_plankton.log.info( "email_send", { @@ -110,11 +130,21 @@ namespace _espe.helpers ); switch (mode) { case "regular": { - // TODO + if (_espe.conf.get().email_sending.smtp_credentials === null) { + return Promise.reject("no smtp credentials specified; add in conf as 'email_sending.smtp_credentials'!"); + } + else { + // TODO + } break; } case "redirect": { - // TODO + if (_espe.conf.get().email_sending.smtp_credentials === null) { + return Promise.reject("no smtp credentials specified; add in conf as 'email_sending.smtp_credentials'!"); + } + else { + // TODO + } break; } case "console": { diff --git a/source/main.ts b/source/main.ts index d95edec..8905028 100644 --- a/source/main.ts +++ b/source/main.ts @@ -11,8 +11,46 @@ async function main( "type": lib_plankton.args.enum_type.string, "mode": lib_plankton.args.enum_mode.replace, "default": "serve", - "info": "Aktion (serve | doc | password-image | export-authelia | help)", "name": "action", + "info": lib_plankton.string.coin( + "auszuführende Aktion; Auswahl:\n{{selection}}\n\t\t", + { + "selection": ( + [ + { + "name": "serve", + "description": "Server starten", + }, + { + "name": "api-doc", + "description": "API-Dokumentation gemäß OpenAPI Specification auf Standard-Ausgabe schreiben" + }, + { + "name": "password-image", + "description": "Passwort-Abbild errechnen und auf Standard-Ausgabe schreiben" + }, + { + "name": "export-authelia", + "description": "Export der Nutzer-Datenbank im Authelia-user-Datei-Format auf Standard-Ausgabe schreiben" + }, + { + "name": "help", + "description": "Diese Hilfe auf Standard-Ausgabe schreiben" + }, + ] + .map( + entry => lib_plankton.string.coin( + "\t\t- {{name}}\n\t\t\t{{description}}\n", + { + "name": entry.name, + "description": entry.description, + } + ) + ) + .join("") + ), + } + ), }), "arg1": lib_plankton.args.class_argument.positional({ "index": 1, @@ -37,7 +75,7 @@ async function main( "indicators_short": ["c"], "type": lib_plankton.args.enum_type.string, "mode": lib_plankton.args.enum_mode.replace, - "default": "conf.json", + "default": null, "info": "Pfad zur Konfigurations-Datei", "name": "conf-path", }), @@ -83,17 +121,13 @@ async function main( "notice": lib_plankton.log.enum_level.notice, "info": lib_plankton.log.enum_level.info, "debug":lib_plankton.log.enum_level.debug, - }[_espe.conf.get().verbosity] + }[_espe.conf.get().general.verbosity] ), ] ); switch (args["action"]) { default: { - process.stderr.write( - "invalid action: " + args["action"] - + - "\n" - ); + process.stderr.write("invalid action: " + args["action"] + "\n"); break; } case "password-image": { @@ -107,7 +141,7 @@ async function main( } break; } - case "doc": { + case "api-doc": { const rest_subject : lib_plankton.rest.type_rest = _espe.api.make(); process.stdout.write( JSON.stringify( @@ -126,7 +160,7 @@ async function main( lib_plankton.storage.sql_table_common.chest( { "database_implementation": _espe.helpers.database_implementation(), - "table_name": "sessions", + "table_name": "session_management", "key_names": ["key"], } ), @@ -143,7 +177,7 @@ async function main( ] ), */ - "default_lifetime": _espe.conf.get().session_lifetime, + "default_lifetime": _espe.conf.get().session_management.lifetime, } ); @@ -158,14 +192,14 @@ async function main( ); _espe.service.member.listen_change( async () => { - const authelia_export : string = await _espe.service.member.export_authelia_member_file(); + const authelia_export : string = await _espe.service.member.export_authelia_user_file(); process.stdout.write(authelia_export + "\n"); } ); const rest_subject : lib_plankton.rest.type_rest = _espe.api.make(); const server : lib_plankton.server.type_subject = lib_plankton.server.make( - _espe.conf.get().port, + _espe.conf.get().server.port, async (input, metadata) => { const http_request : lib_plankton.http.type_request = lib_plankton.http.decode_request(input); const http_response : lib_plankton.http.type_response = await lib_plankton.rest.call( @@ -186,7 +220,7 @@ async function main( break; } case "export-authelia": { - process.stdout.write(await _espe.service.member.export_authelia_member_file() + "\n"); + process.stdout.write(await _espe.service.member.export_authelia_user_file() + "\n"); break; } } diff --git a/source/service-member.ts b/source/service-member.ts index c8f51b4..72403d8 100644 --- a/source/service-member.ts +++ b/source/service-member.ts @@ -271,7 +271,7 @@ namespace _espe.service.member ? "" : ("." + value.name_real_extension) ), - "domain": _espe.conf.get().email_domain, + "domain": _espe.conf.get().settings.target_domain, } ); } @@ -286,9 +286,9 @@ namespace _espe.service.member return lib_plankton.string.coin( "{{prefix}}{{membership_number}}@{{domain}}", { - "prefix": _espe.conf.get().email_numberbased_address_prefix, + "prefix": _espe.conf.get().settings.prefix_for_numberbased_email_addresses, "membership_number": value.membership_number, - "domain": _espe.conf.get().email_domain, + "domain": _espe.conf.get().settings.target_domain, } ); } @@ -313,9 +313,9 @@ namespace _espe.service.member /** - * @todo check validity (e.g. membername characters) + * @todo check validity (e.g. username characters) */ - export async function export_authelia_member_file( + export async function export_authelia_user_file( ) : Promise { const nm_yaml = require("yaml");