2024-04-22 10:22:18 +02:00
|
|
|
namespace _espe.service.member
|
2024-04-22 10:02:34 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
*/
|
|
|
|
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(
|
|
|
|
{
|
2024-04-22 10:22:18 +02:00
|
|
|
"database_implementation": _espe.helpers.database_implementation(),
|
2024-04-22 10:02:34 +02:00
|
|
|
"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;
|
2024-04-22 10:22:18 +02:00
|
|
|
member.password_image = await _espe.helpers.bcrypt_compute(data.password);
|
2024-04-22 10:02:34 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
),
|
2024-04-22 10:22:18 +02:00
|
|
|
"domain": _espe.conf.get().email_domain,
|
2024-04-22 10:02:34 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
*/
|
|
|
|
function derive_email_address_numberbased(
|
|
|
|
value : type_value
|
|
|
|
) : string
|
|
|
|
{
|
|
|
|
return lib_plankton.string.coin(
|
|
|
|
"{{prefix}}{{membership_number}}@{{domain}}",
|
|
|
|
{
|
2024-04-22 10:22:18 +02:00
|
|
|
"prefix": _espe.conf.get().email_numberbased_address_prefix,
|
2024-04-22 10:02:34 +02:00
|
|
|
"membership_number": value.membership_number,
|
2024-04-22 10:22:18 +02:00
|
|
|
"domain": _espe.conf.get().email_domain,
|
2024-04-22 10:02:34 +02:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
*/
|
|
|
|
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),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|