backend/source/service-member.ts

378 lines
7.7 KiB
TypeScript
Raw Normal View History

2024-04-22 10:02:34 +02:00
namespace _aum.service.member
{
/**
*/
export type type_id = int;
/**
*/
export type type_value = {
membership_number : string;
enabled : boolean;
name_real_value : string;
name_real_extension : (null | string);
name_display : (null | string);
name_login : string;
password_image : (null | string);
email_address_private_value : (null | string);
email_address_numberbased_use : boolean;
email_address_namebased_use : boolean;
email_redirect_to_private : boolean;
salutation : (null | string);
};
/**
*/
var _hooks_change : Array<() => void> = [];
/**
*/
export function listen_change(
procedure : () => void
) : void
{
_hooks_change.push(procedure);
}
/**
*/
function notify_change(
) : void
{
_hooks_change.forEach(
procedure => {
procedure();
}
);
}
/**
*/
function store(
) : lib_plankton.storage.type_store<type_id, Record<string, any>, {}, lib_plankton.storage.type_sql_table_autokey_search_term, Record<string, any>>
{
const store : lib_plankton.storage.type_store<type_id, Record<string, any>, {}, lib_plankton.storage.type_sql_table_autokey_search_term, Record<string, any>> = lib_plankton.storage.sql_table_autokey_store(
{
"database_implementation": _aum.helpers.database_implementation(),
"table_name": "members",
"key_name": "id",
}
);
return store;
}
/**
*/
function encode(
value : type_value
) : Record<string, any>
{
return {
"membership_number": value.membership_number,
"enabled": (value.enabled ? 1 : 0),
"name_real_value": value.name_real_value,
"name_real_extension": value.name_real_extension,
"name_display": value.name_display,
"name_login": value.name_login,
"password_image": value.password_image,
"email_address_private_value": value.email_address_private_value,
"email_address_numberbased_use": (value.email_address_numberbased_use ? 1 : 0),
"email_address_namebased_use": (value.email_address_namebased_use ? 1 : 0),
"email_redirect_to_private": (value.email_redirect_to_private ? 1 : 0),
"salutation": value.salutation,
};
}
/**
*/
function decode(
row : Record<string, any>
) : type_value
{
return {
"membership_number": row["membership_number"],
"enabled": (row["enabled"] > 0),
"name_real_value": row["name_real_value"],
"name_real_extension": row["name_real_extension"],
"name_display": row["name_display"],
"name_login": row["name_login"],
"password_image": row["password_image"],
"email_address_private_value": row["email_address_private_value"],
"email_address_numberbased_use": (row["email_address_numberbased_use"] > 0),
"email_address_namebased_use": (row["email_address_namebased_use"] > 0),
"email_redirect_to_private": (row["email_redirect_to_private"] > 0),
"salutation": row["salutation"],
};
}
/**
*/
export async function dump(
) : Promise<Array<{id : type_id; value : type_value;}>>
{
const my_store = store();
return (
(await my_store.search(null))
.map(
({"key": key, "preview": preview}) => ({
"id": key,
"value": (preview as type_value),
})
)
);
}
/**
*/
export async function list(
) : Promise<Array<{id : type_id; preview : {membership_number : string, name_real_value : string;};}>>
{
const my_store = store();
return (
(await my_store.search(null))
.map(
({"key": key, "preview": preview}) => ({
"id": key,
"preview": {
"membership_number": preview["membership_number"],
"name_real_value": preview["name_real_value"],
}
})
)
);
}
/**
* @todo data
*/
export async function get(
id : type_id
) : Promise<type_value>
{
const my_store = store();
const row : Record<string, any> = await my_store.read(id);
return decode(row);
}
/**
*/
export async function add(
value : type_value
) : Promise<type_id>
{
const my_store = store();
const row : Record<string, any> = encode(value);
const id : type_id = await my_store.create(row);
notify_change();
return id;
}
/**
*/
export async function modify(
id : type_id,
value : type_value
) : Promise<void>
{
const my_store = store();
const row : Record<string, any> = encode(value);
await my_store.update(id, row);
notify_change();
}
/**
*/
export async function remove(
id : type_id
) : Promise<void>
{
const my_store = store();
await my_store.delete(id);
notify_change();
}
/**
*/
export async function register(
id : type_id,
data : {
name_login : string;
name_display : string;
salutation : string;
email_address_numberbased_use : boolean;
email_address_namebased_use : boolean;
email_redirect_to_private : boolean;
password : string;
}
) : Promise<void>
{
const member : type_value = await get(id);
if (member.name_login !== null) {
return Promise.reject(new Error("already registered"));
}
else {
member.name_login = data.name_login;
member.name_display = data.name_display;
member.salutation = data.salutation;
member.email_address_numberbased_use = data.email_address_numberbased_use
member.email_address_namebased_use = data.email_address_namebased_use
member.email_redirect_to_private = data.email_redirect_to_private;
member.password_image = await _aum.helpers.bcrypt_compute(data.password);
await modify(id, member);
}
}
/**
*/
function derive_email_address_namebased(
value : type_value
) : string
{
return lib_plankton.string.coin(
"{{value}}{{extension}}@{{domain}}",
{
"value": value.name_real_value.toLowerCase().replace(new RegExp(" ", "g"), "."),
"extension": (
(
(value.name_real_extension === null)
||
(value.name_real_extension === "")
)
? ""
: ("." + value.name_real_extension)
),
"domain": _aum.conf.get().email_domain,
}
);
}
/**
*/
function derive_email_address_numberbased(
value : type_value
) : string
{
return lib_plankton.string.coin(
"{{prefix}}{{membership_number}}@{{domain}}",
{
"prefix": _aum.conf.get().email_numberbased_address_prefix,
"membership_number": value.membership_number,
"domain": _aum.conf.get().email_domain,
}
);
}
/**
*/
function derive_email_address_to_use(
value : type_value
) : (null | string)
{
return (
value.email_address_namebased_use
? derive_email_address_namebased(value)
: (
value.email_address_numberbased_use
? derive_email_address_numberbased(value)
: value.email_address_private_value
)
);
}
/**
* @todo check validity (e.g. membername characters)
*/
export async function export_authelia_member_file(
) : Promise<string>
{
const nm_yaml = require("yaml");
return lib_plankton.call.convey(
await dump(),
[
x => x.map(
entry => Object.assign(
entry,
{"email_address": derive_email_address_to_use(entry.value)}
)
),
x => x.filter(
entry => (
(
(entry.value.name_login !== null)
&&
(entry.value.name_login !== "")
)
&&
(
(entry.value.password_image !== null)
&&
(entry.value.password_image !== "")
)
&&
(entry.email_address !== null)
)
),
x => x.map(
entry => ([
entry.value.name_login,
{
"disabled": (! entry.value.enabled),
"displayname": (
(
(entry.value.name_display !== null)
&&
(entry.value.name_display !== "")
)
? entry.value.name_display
: entry.value.name_real_value
),
"email": entry.email_address,
"groups": [],
"password": entry.value.password_image,
}
])
),
Object.fromEntries,
x => ({"users": x}),
x => nm_yaml.stringify(x),
]
);
}
}