From eb74ecd66ffb685b265d2469a35cbaab5d52f8ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Fra=C3=9F?= Date: Tue, 1 Apr 2025 04:20:59 +0000 Subject: [PATCH] [task-193] [int] --- source/api/actions/invite_accept.ts | 2 +- source/api/actions/invite_create.ts | 17 ++-- source/api/actions/invite_examine.ts | 29 ++++-- source/helpers.ts | 2 +- source/repositories/invite.ts | 52 ++++++++--- source/services/invite.ts | 133 ++++++++++++++++++++++++--- source/types.ts | 4 +- 7 files changed, 191 insertions(+), 48 deletions(-) diff --git a/source/api/actions/invite_accept.ts b/source/api/actions/invite_accept.ts index c31ae74..9695aa0 100644 --- a/source/api/actions/invite_accept.ts +++ b/source/api/actions/invite_accept.ts @@ -17,7 +17,7 @@ namespace _espe.api { /** */ - export function register_invite_examine( + export function register_invite_accept( rest_subject : lib_plankton.rest.type_rest ) : void { diff --git a/source/api/actions/invite_create.ts b/source/api/actions/invite_create.ts index b432632..db9cd5d 100644 --- a/source/api/actions/invite_create.ts +++ b/source/api/actions/invite_create.ts @@ -30,7 +30,9 @@ namespace _espe.api name_value : string; email_address_mode : int; email_address_value : (null | string); + groups_mode : int; groups_value : Array; + expiry ?: (null | int); // notification_target_url_template ?: (null | string); }, ( @@ -90,6 +92,8 @@ namespace _espe.api "membership_number_value", "name_value", "email_address_value", + "groups_value", + "expiry", ] }), "output_schema": () => ({ @@ -139,17 +143,12 @@ namespace _espe.api "name_mode": _espe.helpers.invite_prefill_mode_decode(input.name_mode), "name_value": input.name_value, "email_address_mode": _espe.helpers.invite_prefill_mode_decode(input.email_address_mode), - "email_address_value": ( - ("email_address_value" in input) - ? ( - (input.email_address_value !== "") - ? input.email_address_value - : null - ) - : null - ), + "email_address_value": input.email_address_value, "groups_mode": _espe.helpers.invite_prefill_mode_decode(input.groups_mode), "groups_value": input.groups_value, + }, + { + "expiry": input.expiry, } ); return Promise.resolve({ diff --git a/source/api/actions/invite_examine.ts b/source/api/actions/invite_examine.ts index 9122018..4de355f 100644 --- a/source/api/actions/invite_examine.ts +++ b/source/api/actions/invite_examine.ts @@ -34,7 +34,7 @@ namespace _espe.api */ "description": "gibt die Daten einer Einladung anhand ihres Schlüssels aus", "input_schema": () => ({ - "type": "string" + "type": "string", "nullable": false, }), /** @@ -44,16 +44,25 @@ namespace _espe.api "nullable": true, }), "restriction": restriction_none, - "execution": async ({"input": input}) => { + "execution": ({"input": input}) => { const invite_key : _espe.type.invite_key = input; - const invite_object : _espe.type.invite_object = await _espe.service.invite.examine(invite_key); - /** - * @todo - */ - return Promise.resolve({ - "status_code": 501, - "data": null - }); + return ( + _espe.service.invite.examine(invite_key) + .then( + (invite_object) => Promise.resolve({ + "status_code": 200, + "data": { + "membership_number_mode": _espe.helpers.invite_prefill_mode_encode(invite_object.membership_number_mode), + } + }) + ) + .catch( + (error) => Promise.resolve({ + "status_code": 404, + "data": "not found" + }) + ) + ); } } ); diff --git a/source/helpers.ts b/source/helpers.ts index 2dffccf..afcdf39 100644 --- a/source/helpers.ts +++ b/source/helpers.ts @@ -270,7 +270,7 @@ namespace _espe.helpers invite_prefill_mode_encoded : int ) : _espe.type.invite_prefill_mode { - switch (invite_prefill_mode) { + switch (invite_prefill_mode_encoded) { case 0: return _espe.type.invite_prefill_mode.hidden; case 1: return _espe.type.invite_prefill_mode.locked; case 2: return _espe.type.invite_prefill_mode.free; diff --git a/source/repositories/invite.ts b/source/repositories/invite.ts index 86e7e4c..7c4b89f 100644 --- a/source/repositories/invite.ts +++ b/source/repositories/invite.ts @@ -112,22 +112,23 @@ namespace _espe.repository.invite /** */ function encode( - object : _espe.type.member_object + object : _espe.type.invite_object ) : type_dispersal { return { "core_row": { "key": object.key, - "membership_number_mode": invite_prefill_mode_encode(object.membership_number_mode), + "expiry": object.expiry, + "membership_number_mode": _espe.helpers.invite_prefill_mode_encode(object.membership_number_mode), "membership_number_value": object.membership_number_value, - "name_mode": invite_prefill_mode_encode(object.name_mode), + "name_mode": _espe.helpers.invite_prefill_mode_encode(object.name_mode), "name_value": object.name_value, - "email_address_mode": invite_prefill_mode_encode(object.email_address_mode), + "email_address_mode": _espe.helpers.invite_prefill_mode_encode(object.email_address_mode), "email_address_value": object.email_address_value, - "groups_mode": invite_prefill_mode_encode(object.groups_mode), + "groups_mode": _espe.helpers.invite_prefill_mode_encode(object.groups_mode), }, "group_rows": ( - object.groups + object.groups_value .map( group => ({ "group_name": group, @@ -142,17 +143,18 @@ namespace _espe.repository.invite */ function decode( dispersal : type_dispersal - ) : _espe.type.member_object + ) : _espe.type.invite_object { return { "key": dispersal.core_row["key"], - "membership_number_mode": invite_prefill_mode_decode(dispersal.core_row["membership_number_mode"]), + "expiry": dispersal.core_row["expiry"], + "membership_number_mode": _espe.helpers.invite_prefill_mode_decode(dispersal.core_row["membership_number_mode"]), "membership_number_value": dispersal.core_row["membership_number_value"], - "name_mode": invite_prefill_mode_decode(dispersal.core_row["name_mode"]), + "name_mode": _espe.helpers.invite_prefill_mode_decode(dispersal.core_row["name_mode"]), "name_value": dispersal.core_row["name_value"], - "email_address_mode": invite_prefill_mode_decode(dispersal.core_row["email_address_mode"]), + "email_address_mode": _espe.helpers.invite_prefill_mode_decode(dispersal.core_row["email_address_mode"]), "email_address_value": dispersal.core_row["email_address_value"], - "groups_mode": invite_prefill_mode_decode(dispersal.core_row["groups_mode"]), + "groups_mode": _espe.helpers.invite_prefill_mode_decode(dispersal.core_row["groups_mode"]), "groups_value": lib_plankton.list.sorted( dispersal.group_rows.map(row => row["group_name"]), (group1, group2) => ((group1 <= group2) ? 0 : 1) @@ -169,7 +171,7 @@ namespace _espe.repository.invite ) : Promise< Array< { - id : _espe.type.inivite_id; + id : _espe.type.invite_id; preview : { name : string; }; @@ -179,6 +181,15 @@ namespace _espe.repository.invite { return ( (await get_core_store().search(null)) + .filter( + ({"key": key, "preview": preview}) => ( + (search_term === null) + ? + true + : + (preview["key"] === search_term) + ) + ) .map( ({"key": key, "preview": preview}) => ({ "id": key, @@ -268,6 +279,23 @@ namespace _espe.repository.invite } + /** + */ + export async function identify( + key : _espe.type.invite_key + ) : Promise<_espe.type.invite_id> + { + const hits : Array<{id : _espe.type.invite_id; preview : any;}> = await list(key); + return ( + (hits.length !== 1) + ? + Promise.reject<_espe.type.invite_id>(new Error("not found")) + : + Promise.resolve<_espe.type.invite_id>(hits[0].id) + ); + } + + /** */ export async function dump( diff --git a/source/services/invite.ts b/source/services/invite.ts index af0254d..4552c32 100644 --- a/source/services/invite.ts +++ b/source/services/invite.ts @@ -18,38 +18,145 @@ namespace _espe.service.invite /** */ - export function create( + export async function create( + { + "membership_number_mode": membership_number_mode, + "membership_number_value": membership_number_value, + "name_mode": name_mode, + "name_value": name_value, + "email_address_mode": email_address_mode, + "email_address_value": email_address_value, + "groups_mode": groups_mode, + "groups_value": groups_value, + } : { + membership_number_mode : _espe.type.invite_prefill_mode; + membership_number_value : (null | string); + name_mode : _espe.type.invite_prefill_mode; + name_value : string; + email_address_mode : _espe.type.invite_prefill_mode; + email_address_value : (null | string); + groups_mode : _espe.type.invite_prefill_mode; + groups_value : Array; + }, + { + "expiry": expiry = -1, + } : { + expiry ?: (null | int); + } = { + } ) : Promise<{id : _espe.type.invite_id; key : _espe.type.invite_key}> { - throw (new Error("not implemented")); + /** + * @todo outsource to conf + */ + const default_lifetime : int = (60 * 60 * 24 * 7 * 2); + /** + * @todo proper salt + */ + const invite_key : _espe.type.invite_key = lib_plankton.sha256.get( + ( + name_value + + + "/" + + + lib_plankton.base.get_current_timestamp().toFixed(0) + ), + "secret" + ); + const invite_object : _espe.type.invite_object = { + "key": invite_key, + "expiry": ( + ((expiry !== null) && (expiry < 0)) + ? + (lib_plankton.base.get_current_timestamp() + default_lifetime) + : + expiry + ), + "membership_number_mode": membership_number_mode, + "membership_number_value": membership_number_value, + "name_mode": name_mode, + "name_value": name_value, + "email_address_mode": email_address_mode, + "email_address_value": email_address_value, + "groups_mode": groups_mode, + "groups_value": groups_value, + }; + const invite_id : _espe.type.invite_id = await _espe.repository.invite.create(invite_object); + return { + "id": invite_id, + "key": invite_key, + }; } /** + * @todo heed expiry */ - export function examine( + export async function examine( key : _espe.type.invite_key - ) : Promise<{id : _espe.type.invite_id; key : _espe.type.invite_key}> + ) : Promise<_espe.type.invite_object> { - throw (new Error("not implemented")); + const invite_id : _espe.type.invite_id = await _espe.repository.invite.identify(key); + const invite_object : _espe.type.invite_object = await _espe.repository.invite.read(invite_id); + return invite_object; } /** + * @todo heed expiry + * @todo password? */ - export function accept( + export async function accept( key : _espe.type.invite_key, membership_number_value : (null | string), name_value : (null | string), - email_address_value : (null | string) + email_address_value : (null | string), + groups_value : Array ) : Promise { - /** - * @todo Nutzer anlegen - * @todo Einladung löschen - */ - throw (new Error("not implemented")); + const invite_id : _espe.type.invite_id = await _espe.repository.invite.identify(key); + const invite_object : _espe.type.invite_object = await _espe.repository.invite.read(invite_id); + const member_id : _espe.type.member_id = await _espe.service.member.project( + { + "membership_number": ( + (invite_object.membership_number_mode === _espe.type.invite_prefill_mode.free) + ? + membership_number_value + : + invite_object.membership_number_value + ), + "name_real_value": ( + ( + (invite_object.name_mode === _espe.type.invite_prefill_mode.free) + && + (name_value !== null) + ) + ? + name_value + : + invite_object.name_value + ), + "email_address_private": ( + ( + (invite_object.email_address_mode === _espe.type.invite_prefill_mode.free) + && + (email_address_value !== null) + ) + ? + email_address_value + : + invite_object.email_address_value + ), + "groups": ( + (invite_object.groups_mode === _espe.type.invite_prefill_mode.free) + ? + groups_value + : + invite_object.groups_value + ), + } + ); + await _espe.repository.invite.delete_(invite_id); } } - diff --git a/source/types.ts b/source/types.ts index 691c55b..907fb40 100644 --- a/source/types.ts +++ b/source/types.ts @@ -68,7 +68,7 @@ namespace _espe.type /** */ - enum type invite_prefill_mode = { + export enum invite_prefill_mode { hidden, locked, free, @@ -76,10 +76,10 @@ namespace _espe.type /** - * @todo expiry timestamp */ export type invite_object = { key : invite_key; + expiry : (null | int); membership_number_mode : invite_prefill_mode; membership_number_value : (null | string); name_mode : invite_prefill_mode;