Merge branch 'main' into task-193

This commit is contained in:
roydfalk 2025-04-03 06:19:47 +00:00
commit b522514c26
23 changed files with 3919 additions and 3020 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,21 @@
"verbosity": "info",
"verification_secret": null
},
"log": [
{
"kind": "stdout",
"data": {
"threshold": "info"
}
},
{
"kind": "file",
"data": {
"threshold": "notice",
"path": "/tmp/espe/log.jsonl"
}
}
],
"server": {
"port": 4916,
"path_base": ""

View file

@ -19,17 +19,20 @@ namespace _espe.api
/**
*/
export function register_member_delete(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
register<null, null>(
lib_plankton.rest_http.register<
null,
null
>(
rest_subject,
lib_plankton.http.enum_method.delete,
"/member/delete/:id",
_espe.api.full_path("/member/delete/:id"),
{
"description": "löscht ein vorhandenes Mitglied",
"restriction": restriction_logged_in,
"execution": async ({"path_parameters": path_parameters}) => {
"description": () => "löscht ein vorhandenes Mitglied",
"restriction": () => restriction_logged_in,
"execution": () => async ({"path_parameters": path_parameters}) => {
const member_id : _espe.type.member_id = parseInt(path_parameters["id"]);
await _espe.service.member.remove(member_id);
return Promise.resolve({

View file

@ -20,10 +20,10 @@ namespace _espe.api
* @todo zeitliche Begrenzung?
*/
export function register_member_info(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
register<
lib_plankton.rest_http.register<
int,
(
null
@ -39,9 +39,9 @@ namespace _espe.api
>(
rest_subject,
lib_plankton.http.enum_method.get,
"/member/info/:id",
_espe.api.full_path("/member/info/:id"),
{
"description": "gibt Angaben über ein Mitglied aus, die für die Registrierung verwendet werden dürfen",
"description": () => "gibt Angaben über ein Mitglied aus, die für die Registrierung verwendet werden dürfen",
"input_schema": () => ({
"type": "number",
"nullable": false,
@ -80,18 +80,18 @@ namespace _espe.api
"email_address_nominal",
]
}),
"query_parameters": [
"query_parameters": () => [
{
"name": "key",
"required": true,
"description": "Zugriffs-Schlüssel",
},
],
"restriction": restriction_verification(
"restriction": () => restriction_verification(
stuff => parseInt(stuff.path_parameters["id"]),
stuff => stuff.query_parameters["key"]
),
"execution": async ({"path_parameters": path_parameters, "input": input}) => {
"execution": () => async ({"path_parameters": path_parameters, "input": input}) => {
const member_id : _espe.type.member_id = parseInt(path_parameters["id"]);
const data : (
null

View file

@ -19,10 +19,10 @@ namespace _espe.api
/**
*/
export function register_member_list(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
register<
lib_plankton.rest_http.register<
null,
Array<
{
@ -37,10 +37,10 @@ namespace _espe.api
>(
rest_subject,
lib_plankton.http.enum_method.get,
"/member/list",
_espe.api.full_path("/member/list"),
{
"description": "listet alle Mitglieder auf",
"query_parameters": [
"description": () => "listet alle Mitglieder auf",
"query_parameters": () => [
{
"name": "search_term",
"required": false,
@ -88,8 +88,8 @@ namespace _espe.api
],
}
}),
"restriction": restriction_logged_in,
"execution": ({"query_parameters": query_parameters}) => (
"restriction": () => restriction_logged_in,
"execution": () => ({"query_parameters": query_parameters}) => (
_espe.service.member.list(query_parameters["search_term"] ?? null)
.then(
data => Promise.resolve({

View file

@ -19,10 +19,10 @@ namespace _espe.api
/**
*/
export function register_member_modify(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
register<
lib_plankton.rest_http.register<
{
email_address_private : (null | string);
groups ?: Array<string>;
@ -33,9 +33,9 @@ namespace _espe.api
>(
rest_subject,
lib_plankton.http.enum_method.patch,
"/member/modify/:id",
_espe.api.full_path("/member/modify/:id"),
{
"description": "ändert die Angaben eines vorhandenen Mitglieds",
"description": () => "ändert die Angaben eines vorhandenen Mitglieds",
"input_schema": () => ({
"nullable": false,
"type": "object",
@ -71,8 +71,8 @@ namespace _espe.api
"output_schema": () => ({
"nullable": true,
}),
"restriction": restriction_logged_in,
"execution": async ({"path_parameters": path_parameters, "input": input}) => {
"restriction": () => restriction_logged_in,
"execution": () => async ({"path_parameters": path_parameters, "input": input}) => {
if (input === null) {
return Promise.reject(new Error("impossible"));
}

View file

@ -20,10 +20,10 @@ namespace _espe.api
* @todo ausgeklügelte Durchsatzratenbegrenzung
*/
export function register_member_password_change_execute(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
register<
lib_plankton.rest_http.register<
{
token : string;
password_new : string;
@ -37,9 +37,9 @@ namespace _espe.api
>(
rest_subject,
lib_plankton.http.enum_method.patch,
"/member/password_change/execute/:id",
_espe.api.full_path("/member/password_change/execute/:id"),
{
"description": "Führt eine Passwort-Änderung für ein Mitglied durch",
"description": () => "Führt eine Passwort-Änderung für ein Mitglied durch",
"input_schema": () => ({
"nullable": false,
"type": "object",
@ -88,8 +88,8 @@ namespace _espe.api
]
}
}),
"restriction": restriction_none,
"execution": ({"path_parameters": path_parameters, "input": input}) => {
"restriction": () => restriction_none,
"execution": () => ({"path_parameters": path_parameters, "input": input}) => {
if (input === null) {
return Promise.reject(new Error("impossible"));
}

View file

@ -21,10 +21,10 @@ namespace _espe.api
* @todo captcha
*/
export function register_member_password_change_initialize(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
register<
lib_plankton.rest_http.register<
{
identifier : string;
url_template : string;
@ -33,9 +33,9 @@ namespace _espe.api
>(
rest_subject,
lib_plankton.http.enum_method.post,
"/member/password_change/initialize",
_espe.api.full_path("/member/password_change/initialize"),
{
"description": "Versucht dem gegebenen Identifikator ein Mitglied zuzuordnen und sendet dem ermittelten Mitglied einen Passwort-Änderungs-Verweis an die hinterlegte private E-Mail-Adresse",
"description": () => "Versucht dem gegebenen Identifikator ein Mitglied zuzuordnen und sendet dem ermittelten Mitglied einen Passwort-Änderungs-Verweis an die hinterlegte private E-Mail-Adresse",
"input_schema": () => ({
"nullable": false,
"type": "object",
@ -60,8 +60,8 @@ namespace _espe.api
"output_schema": () => ({
"nullable": true
}),
"restriction": restriction_none,
"execution": async ({"input": input}) => {
"restriction": () => restriction_none,
"execution": () => async ({"input": input}) => {
if (input === null) {
return Promise.reject(new Error("impossible"));
}

View file

@ -19,10 +19,10 @@ namespace _espe.api
/**
*/
export function register_member_project(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
register<
lib_plankton.rest_http.register<
{
membership_number : (null | string);
name_real_value : string;
@ -38,9 +38,9 @@ namespace _espe.api
>(
rest_subject,
lib_plankton.http.enum_method.post,
"/member/project",
_espe.api.full_path("/member/project"),
{
"description": "erstellt ein neues Mitglied und gibt die erzeugte ID aus",
"description": () => "erstellt ein neues Mitglied und gibt die erzeugte ID aus",
"input_schema": () => ({
"type": "object",
"nullable": false,
@ -84,8 +84,8 @@ namespace _espe.api
"type": "number",
"nullable": false,
}),
"restriction": restriction_logged_in,
"execution": async ({"input": input}) => {
"restriction": () => restriction_logged_in,
"execution": () => async ({"input": input}) => {
if (input === null) {
return Promise.reject(new Error("impossible"));
}

View file

@ -19,10 +19,10 @@ namespace _espe.api
/**
*/
export function register_member_read(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
register<
lib_plankton.rest_http.register<
null,
{
membership_number : (null | string);
@ -44,9 +44,9 @@ namespace _espe.api
>(
rest_subject,
lib_plankton.http.enum_method.get,
"/member/read/:id",
_espe.api.full_path("/member/read/:id"),
{
"description": "gibt ein Mitglied anhand seiner ID aus",
"description": () => "gibt ein Mitglied anhand seiner ID aus",
"output_schema": () => ({
"nullable": false,
"type": "object",
@ -135,8 +135,8 @@ namespace _espe.api
"name_login",
]
}),
"restriction": restriction_logged_in,
"execution": async ({"path_parameters": path_parameters, "input": input}) => {
"restriction": () => restriction_logged_in,
"execution": () => async ({"path_parameters": path_parameters, "input": input}) => {
const member_id : _espe.type.member_id = parseInt(path_parameters["id"]);
const member_object : _espe.type.member_object = await _espe.service.member.get(member_id);
return Promise.resolve({

View file

@ -20,10 +20,10 @@ namespace _espe.api
* @todo zeitliche Begrenzung?
*/
export function register_member_register(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
register<
lib_plankton.rest_http.register<
{
email_use_veiled_address : boolean;
email_use_nominal_address : boolean;
@ -40,9 +40,9 @@ namespace _espe.api
>(
rest_subject,
lib_plankton.http.enum_method.post,
"/member/register/:id",
_espe.api.full_path("/member/register/:id"),
{
"description": "nimmt zusätzliche Angaben eines Mitglieds entgegen",
"description": () => "nimmt zusätzliche Angaben eines Mitglieds entgegen",
"input_schema": () => ({
"type": "object",
"nullable": false,
@ -109,18 +109,18 @@ namespace _espe.api
]
}
}),
"query_parameters": [
"query_parameters": () => [
{
"name": "key",
"required": true,
"description": "Zugriffs-Schlüssel",
},
],
"restriction": restriction_verification(
"restriction": () => restriction_verification(
stuff => parseInt(stuff.path_parameters["id"]),
stuff => stuff.query_parameters["key"]
),
"execution": ({"path_parameters": path_parameters, "input": input}) => {
"execution": () => ({"path_parameters": path_parameters, "input": input}) => {
if (input === null) {
return Promise.reject(new Error("impossible"));
}

View file

@ -19,10 +19,10 @@ namespace _espe.api
/**
*/
export function register_member_summon(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
lib_plankton.rest.register<
lib_plankton.rest_http.register<
{
url_template : string;
},
@ -36,9 +36,9 @@ namespace _espe.api
lib_plankton.http.enum_method.post,
_espe.conf.get().server.path_base + "/member/summon/:id",
{
"description": "sendet an ein Mitglied eine E-Mail mit Aufforderung zur Registrierung",
"restriction": restriction_logged_in,
"execution": async ({"path_parameters": path_parameters, "input": input}) => {
"description": () => "sendet an ein Mitglied eine E-Mail mit Aufforderung zur Registrierung",
"restriction": () => restriction_logged_in,
"execution": () => async ({"path_parameters": path_parameters, "input": input}) => {
if (input === null) {
return Promise.reject(new Error("impossible"));
}

View file

@ -19,10 +19,10 @@ namespace _espe.api
/**
*/
export function register_meta_ping(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
lib_plankton.rest.register<
lib_plankton.rest_http.register<
null,
string
>
@ -31,7 +31,7 @@ namespace _espe.api
lib_plankton.http.enum_method.get,
_espe.conf.get().server.path_base + "/meta/ping",
{
"description": "sendet ein 'pong' zurück; gedacht um die Erreichbarkeit des Backends zu prüfen",
"description": () => "sendet ein 'pong' zurück; gedacht um die Erreichbarkeit des Backends zu prüfen",
"input_schema": () => ({
"nullable": true,
}),
@ -39,8 +39,8 @@ namespace _espe.api
"nullable": false,
"type": "string",
}),
"restriction": restriction_none,
"execution": () => {
"restriction": () => restriction_none,
"execution": () => () => {
return Promise.resolve({
"status_code": 200,
"data": "pong",

View file

@ -19,10 +19,10 @@ namespace _espe.api
/**
*/
export function register_meta_spec(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
lib_plankton.rest.register<
lib_plankton.rest_http.register<
null,
any
>
@ -31,17 +31,17 @@ namespace _espe.api
lib_plankton.http.enum_method.get,
_espe.conf.get().server.path_base + "/meta/spec",
{
"description": "gibt die API-Spezifikation im OpenAPI-Format aus",
"description": () => "gibt die API-Spezifikation im OpenAPI-Format aus",
"input_schema": () => ({
"nullable": true,
}),
"output_schema": () => ({
}),
"restriction": restriction_none,
"execution": () => {
"restriction": () => restriction_none,
"execution": () => () => {
return Promise.resolve({
"status_code": 200,
"data": lib_plankton.rest.to_oas(rest_subject),
"data": lib_plankton.rest_http.to_oas(rest_subject),
});
},
}

View file

@ -19,10 +19,10 @@ namespace _espe.api
/**
*/
export function register_session_begin(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
lib_plankton.rest.register<
lib_plankton.rest_http.register<
{
name : string;
password : string;
@ -37,7 +37,7 @@ namespace _espe.api
lib_plankton.http.enum_method.post,
_espe.conf.get().server.path_base + "/session/begin",
{
"description": "führt die Anmeldung am System aus um geschützte Aktionen nutzen zu können",
"description": () => "führt die Anmeldung am System aus um geschützte Aktionen nutzen zu können",
"input_schema": () => ({
"type": "object",
"properties": {
@ -58,8 +58,8 @@ namespace _espe.api
"type": "string",
"description": "der Sitzungs-Schlüssel, der als Header 'X-Session-Key' gesetzt werden muss um Erlaubnis zur Ausführung geschützter Aktionen zu erhalten",
}),
"restriction": restriction_none,
"execution": async ({"input": input}) => {
"restriction": () => restriction_none,
"execution": () => async ({"input": input}) => {
if (input === null) {
return Promise.reject(new Error("impossible"));
}

View file

@ -19,23 +19,26 @@ namespace _espe.api
/**
*/
export function register_session_end(
rest_subject : lib_plankton.rest.type_rest
rest_subject : lib_plankton.rest_http.type_rest
) : void
{
register<null, null>(
lib_plankton.rest_http.register<
null,
null
>(
rest_subject,
lib_plankton.http.enum_method.delete,
"/session/end",
{
"description": "beendet eine Sitzung",
"description": () => "beendet eine Sitzung",
"input_schema": () => ({
"type": "null",
}),
"output_schema": () => ({
"type": "null",
}),
"restriction": restriction_logged_in,
"execution": async (stuff) => {
"restriction": () => restriction_logged_in,
"execution": () => async (stuff) => {
const session : {key : string; value : lib_plankton.session.type_session} = await session_from_stuff(stuff);
await lib_plankton.session.end(session.key);
return Promise.resolve({

View file

@ -20,7 +20,7 @@ namespace _espe.api
* @todo zu plankton auslagern?
*/
type type_stuff = {
version: (null | string);
// version: (null | string);
headers: Record<string, string>;
path_parameters: Record<string, string>;
query_parameters: Record<string, string>;
@ -43,9 +43,9 @@ namespace _espe.api
*/
/*
export function restriction_disjunction(
left : lib_plankton.rest.type_restriction<any>,
right : lib_plankton.rest.type_restriction<any>
) : lib_plankton.rest.type_restriction<any>
left : lib_plankton.rest_http.type_restriction<any>,
right : lib_plankton.rest_http.type_restriction<any>
) : lib_plankton.rest_http.type_restriction<any>
{
return (
(stuff) => Promise.any<boolean>(
@ -61,14 +61,14 @@ namespace _espe.api
/**
*/
export const restriction_none : lib_plankton.rest.type_restriction<any> = (
export const restriction_none : lib_plankton.rest_http.type_restriction<any> = (
(stuff) => Promise.resolve<boolean>(true)
);
/**
*/
export const restriction_logged_in : lib_plankton.rest.type_restriction<any> = (
export const restriction_logged_in : lib_plankton.rest_http.type_restriction<any> = (
(stuff) => (
session_from_stuff(stuff)
.then(() => Promise.resolve<boolean>(true))
@ -82,7 +82,7 @@ namespace _espe.api
export function restriction_verification(
extract_data : ((stuff : type_stuff) => any),
extract_verification : ((stuff : type_stuff) => string)
) : lib_plankton.rest.type_restriction<any>
) : lib_plankton.rest_http.type_restriction<any>
{
return (
(stuff) => _espe.helpers.verification_check(
@ -91,18 +91,28 @@ namespace _espe.api
)
);
}
/**
*/
export function full_path(
path : string
) : string
{
return (_espe.conf.get().server.path_base + path);
}
/**
*/
/*
export function register<type_input, type_output>(
rest_subject : lib_plankton.rest.type_rest,
rest_subject : lib_plankton.rest_http.type_rest,
http_method : lib_plankton.http.enum_method,
path : string,
options : {
active ?: ((version : string) => boolean);
restriction ?: (null | lib_plankton.rest.type_restriction<type_input>);
execution ?: lib_plankton.rest.type_execution<type_input, type_output>;
active ?: ((version : (null | string)) => boolean);
restriction ?: (null | lib_plankton.rest_http.type_restriction<type_input>);
execution ?: lib_plankton.rest_http.type_execution<type_input, type_output>;
title ?: (null | string);
description ?: (null | string);
query_parameters ?: Array<
@ -112,8 +122,8 @@ namespace _espe.api
required : boolean;
}
>;
input_schema ?: ((version: (null | string)) => lib_plankton.rest.type_oas_schema);
output_schema ?: ((version: (null | string)) => lib_plankton.rest.type_oas_schema);
input_schema ?: ((version: (null | string)) => lib_plankton.rest_http.type_oas_schema);
output_schema ?: ((version: (null | string)) => lib_plankton.rest_http.type_oas_schema);
request_body_mimetype ?: string;
request_body_decode ?: ((http_request_body : Buffer, http_request_header_content_type : (null | string)) => any);
response_body_mimetype ?: string;
@ -121,17 +131,12 @@ namespace _espe.api
} = {}
) : void
{
options = Object.assign(
{
},
options
);
lib_plankton.rest.register<type_input, type_output>(
lib_plankton.rest_http.register<type_input, type_output>(
rest_subject,
http_method,
(_espe.conf.get().server.path_base + path),
options
);
}
*/
}

View file

@ -19,9 +19,9 @@ namespace _espe.api
/**
*/
export function make(
) : lib_plankton.rest.type_rest
) : lib_plankton.rest_http.type_rest
{
const rest_subject : lib_plankton.rest.type_rest = lib_plankton.rest.make(
const rest_subject : lib_plankton.rest_http.type_rest = lib_plankton.rest_http.make(
{
"title": "espe",
"versioning_method": "header",
@ -29,7 +29,7 @@ namespace _espe.api
"set_access_control_headers": true,
"authentication": {
"kind": "key_header",
"parameters": {"name": "X-Session-Key"}
"data": {"name": "X-Session-Key"}
},
}
);

View file

@ -40,6 +40,41 @@ namespace _espe.conf
);
/**
*/
export type type_log_channel = (
{
kind : "stdout";
data : {
threshold : type_log_threshold;
};
}
|
{
kind : "file";
data : {
threshold : type_log_threshold;
path : string;
};
}
|
{
kind : "email";
data : {
threshold : type_log_threshold;
smtp_credentials : {
host : string;
port : int;
username : string;
password : string;
};
sender : string;
receivers : Array<string>;
};
}
);
/**
*/
export type type_conf = {
@ -48,35 +83,7 @@ namespace _espe.conf
verification_secret : (null | string);
};
log : Array<
{
kind : "stdout";
data : {
threshold : type_log_threshold;
};
}
|
{
kind : "file";
data : {
threshold : type_log_threshold;
path : string;
};
}
|
{
kind : "email";
data : {
threshold : type_log_threshold;
smtp_credentials : {
host : string;
port : int;
username : string;
password : string;
};
sender : string;
receivers : Array<string>;
};
}
type_log_channel
>;
server : {
host : string;

View file

@ -13,424 +13,520 @@ You should have received a copy of the GNU General Public License along with thi
<https://www.gnu.org/licenses/>.
*/
/**
*/
async function main(
args_raw : Array<string>
) : Promise<void>
namespace _espe
{
// init
lib_plankton.log.conf_push(
[
lib_plankton.log.channel_make(
/**
*/
function setup_log_fallback(
) : void
{
lib_plankton.log.set_main_logger(
[
{
"kind": "stdout",
"kind": "filtered",
"data": {
"threshold": "notice",
// "format": "human_readable",
"core": {
"kind": "std",
"data": {
"target": "stdout",
"format": {
"kind": "human_readable",
"data": {}
}
}
},
"predicate": [
[
{
"item": {
"kind": "level",
"data": {"threshold": "info"}
},
}
]
],
}
},
]
);
}
/**
*/
function setup_log_from_conf(
) : void
{
lib_plankton.log.set_main_logger(
_espe.conf.get().log.map(
log_channel => {
switch (log_channel.kind) {
case "stdout": {
return {
"kind": "filtered",
"data": {
"core": {
"kind": "std",
"data": {
"target": "stdout",
"format": {"kind": "human_readable", "data": {}}
}
},
"predicate": [
[
{"item": {"kind": "level", "data": {"threshold": log_channel.data.threshold}}}
]
],
}
};
break;
}
case "file": {
return {
"kind": "filtered",
"data": {
"core": {
"kind": "file",
"data": {
"path": log_channel.data.path,
"format": {"kind": "jsonl", "data": {"structured": false}}
}
},
"predicate": [
[
{"item": {"kind": "level", "data": {"threshold": log_channel.data.threshold}}}
]
],
}
};
break;
}
case "email": {
return {
"kind": "filtered",
"data": {
"core": {
"kind": "email",
"data": log_channel.data
},
"predicate": [
[
{"item": {"kind": "level", "data": {"threshold": log_channel.data.threshold}}}
]
],
}
};
break;
}
default: {
throw (new Error("unhandled log channel"));
break;
}
}
}
),
]
);
const language_codes : Array<string> = [
"deu",
"eng",
];
await (
Promise.all(
language_codes
.map(
language_code => (
lib_plankton.file.read(
lib_plankton.string.coin(
"data/localization/{{language_code}}.loc.json",
{
"language_code": language_code,
}
)
);
}
/**
*/
export async function main(
args_raw : Array<string>
) : Promise<void>
{
// init
setup_log_fallback();
const language_codes : Array<string> = [
"deu",
"eng",
];
await (
Promise.all(
language_codes
.map(
language_code => (
lib_plankton.file.read(
lib_plankton.string.coin(
"data/localization/{{language_code}}.loc.json",
{
"language_code": language_code,
}
)
)
)
.then<any>(
content => (new Promise<any>(
(resolve, reject) => {
try {
resolve(JSON.parse(content));
.then<any>(
content => (new Promise<any>(
(resolve, reject) => {
try {
resolve(JSON.parse(content));
}
catch (error) {
reject(error);
}
}
catch (error) {
reject(error);
}
}
))
))
)
)
)
)
)
.then(
packages => lib_plankton.translate.initialize(
{
"verbosity": 1,
"packages": packages,
"order": language_codes,
"autopromote": false,
}
.then(
packages => lib_plankton.translate.initialize(
{
"verbosity": 1,
"packages": packages,
"order": language_codes,
"autopromote": false,
}
)
)
)
);
// args
const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler({
"action": lib_plankton.args.class_argument.positional({
"index": 0,
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": "serve",
"name": "action",
"info": lib_plankton.string.coin(
"{{description}}:\n{{options}}\n\t\t",
{
"description": lib_plankton.translate.get("help.args.action.options.serve"),
"options": (
[
{
"name": "serve",
"description": lib_plankton.translate.get("help.args.action.options.serve"),
},
{
"name": "api-doc",
"description": lib_plankton.translate.get("help.args.action.options.api_doc")
},
{
"name": "email-test",
"description": lib_plankton.translate.get("help.args.action.options.email_test")
},
{
"name": "expose-conf",
"description": lib_plankton.translate.get("help.args.action.options.expose_conf")
},
{
"name": "password-image",
"description": lib_plankton.translate.get("help.args.action.options.password_image")
},
{
"name": "admin-add",
"description": lib_plankton.translate.get("help.args.action.options.admin_add")
},
{
"name": "export-authelia",
"description": lib_plankton.translate.get("help.args.action.options.export_authelia")
},
{
"name": "help",
"description": lib_plankton.translate.get("help.args.action.options.help")
},
]
.map(
entry => lib_plankton.string.coin(
"\t\t- {{name}}\n\t\t\t{{description}}\n",
);
// args
const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler({
"action": lib_plankton.args.class_argument.positional({
"index": 0,
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": "serve",
"name": "action",
"info": lib_plankton.string.coin(
"{{description}}:\n{{options}}\n\t\t",
{
"description": lib_plankton.translate.get("help.args.action.options.serve"),
"options": (
[
{
"name": entry.name,
"description": entry.description,
}
"name": "serve",
"description": lib_plankton.translate.get("help.args.action.options.serve"),
},
{
"name": "api-doc",
"description": lib_plankton.translate.get("help.args.action.options.api_doc")
},
{
"name": "email-test",
"description": lib_plankton.translate.get("help.args.action.options.email_test")
},
{
"name": "expose-conf",
"description": lib_plankton.translate.get("help.args.action.options.expose_conf")
},
{
"name": "password-image",
"description": lib_plankton.translate.get("help.args.action.options.password_image")
},
{
"name": "admin-add",
"description": lib_plankton.translate.get("help.args.action.options.admin_add")
},
{
"name": "export-authelia",
"description": lib_plankton.translate.get("help.args.action.options.export_authelia")
},
{
"name": "help",
"description": lib_plankton.translate.get("help.args.action.options.help")
},
]
.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,
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": null,
// "info": null,
"name": "arg1",
"hidden": true,
}),
"arg2": lib_plankton.args.class_argument.positional({
"index": 2,
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": null,
// "info": null,
"name": "arg2",
"hidden": true,
}),
"arg3": lib_plankton.args.class_argument.positional({
"index": 3,
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": null,
// "info": null,
"name": "arg3",
"hidden": true,
}),
"conf_path": lib_plankton.args.class_argument.volatile({
"indicators_long": ["conf_path"],
"indicators_short": ["c"],
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": "conf.json",
"info": lib_plankton.translate.get("help.args.conf_path.description"),
"name": "conf-path",
}),
"help": lib_plankton.args.class_argument.volatile({
"indicators_long": ["help"],
"indicators_short": ["h"],
"type": lib_plankton.args.enum_type.boolean,
"mode": lib_plankton.args.enum_mode.replace,
"default": false,
"info": lib_plankton.translate.get("help.args.help.description"),
"name": "help",
}),
});
const args : Record<string, any> = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" "));
// init
await _espe.conf.load(args["conf_path"]);
{
const language : (null | string) = _espe.conf.get().general.language;
if (language === null) {
// do nothing
.join("")
),
}
),
}),
"arg1": lib_plankton.args.class_argument.positional({
"index": 1,
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": null,
// "info": null,
"name": "arg1",
"hidden": true,
}),
"arg2": lib_plankton.args.class_argument.positional({
"index": 2,
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": null,
// "info": null,
"name": "arg2",
"hidden": true,
}),
"arg3": lib_plankton.args.class_argument.positional({
"index": 3,
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": null,
// "info": null,
"name": "arg3",
"hidden": true,
}),
"conf_path": lib_plankton.args.class_argument.volatile({
"indicators_long": ["conf_path"],
"indicators_short": ["c"],
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": "conf.json",
"info": lib_plankton.translate.get("help.args.conf_path.description"),
"name": "conf-path",
}),
"help": lib_plankton.args.class_argument.volatile({
"indicators_long": ["help"],
"indicators_short": ["h"],
"type": lib_plankton.args.enum_type.boolean,
"mode": lib_plankton.args.enum_mode.replace,
"default": false,
"info": lib_plankton.translate.get("help.args.help.description"),
"name": "help",
}),
});
const args : Record<string, any> = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" "));
// init
await _espe.conf.load(args["conf_path"]);
{
const language : (null | string) = _espe.conf.get().general.language;
if (language === null) {
// do nothing
}
else {
lib_plankton.translate.promote(language);
}
}
setup_log_from_conf();
// exec
if (args["help"] || (args["action"] === "help")) {
process.stdout.write(
arg_handler.generate_help(
{
"programname": "espe",
"description": "Espe | Backend",
"executable": "espe",
}
)
+
"\n"
);
}
else {
lib_plankton.translate.promote(language);
}
}
lib_plankton.log.conf_push(
_espe.conf.get().log.map(
log_output => lib_plankton.log.channel_make(
{
"kind": log_output.kind,
"data": log_output.data
switch (args["action"]) {
default: {
process.stderr.write("invalid action: " + args["action"] + "\n");
break;
}
)
)
);
// exec
if (args["help"] || (args["action"] === "help")) {
process.stdout.write(
arg_handler.generate_help(
{
"programname": "espe",
"description": "Espe | Backend",
"executable": "espe",
}
)
+
"\n"
);
}
else {
switch (args["action"]) {
default: {
process.stderr.write("invalid action: " + args["action"] + "\n");
break;
}
case "password-image": {
const input : (null | string) = args["arg1"];
if (input === null) {
throw (new Error("SYNTAX: password-image <password>"));
}
else {
const result : string = await _espe.helpers.bcrypt_compute(input);
process.stdout.write(result + "\n")
}
break;
}
case "expose-conf": {
process.stdout.write(
JSON.stringify(
_espe.conf.get(),
undefined,
"\t"
)
+
"\n"
);
break;
}
case "api-doc": {
lib_plankton.log.conf_push([]);
const rest_subject : lib_plankton.rest.type_rest = _espe.api.make();
lib_plankton.log.conf_pop();
process.stdout.write(
JSON.stringify(
lib_plankton.rest.to_oas(rest_subject),
undefined,
"\t"
)
);
break;
}
case "email-test": {
await _espe.helpers.email_send(
(
(args["arg1"] !== null)
? [args["arg1"]]
: (await _espe.service.admin.email_addresses())
),
lib_plankton.string.coin(
"{{head}} | Test",
{
"head": _espe.conf.get().settings.organisation.name,
}
),
"This is a test e-mail"
);
break;
}
case "admin-add": {
const name : (null | string) = args["arg1"];
const email_address : (null | string) = args["arg2"];
const password : (null | string) = args["arg3"];
if ((name === null) || (email_address === null) || (password === null)) {
throw (new Error("SYNTAX: admin-add <name> <email-address> <password>"));
}
else {
const admin_id : _espe.type.admin_id = await _espe.service.admin.add(
name,
email_address,
password
);
process.stdout.write(admin_id.toFixed(0) + "\n")
}
break;
}
case "serve": {
// prepare database
await _espe.database.check();
await lib_plankton.session.setup(
{
"data_chest": (
_espe.conf.get().session_management.in_memory
? lib_plankton.storage.memory.implementation_chest<lib_plankton.session.type_session>({})
: lib_plankton.call.convey(
lib_plankton.storage.sql_table_common.chest(
{
"database_implementation": _espe.helpers.database_implementation(),
"table_name": "sessions",
"key_names": ["key"],
}
),
[
(core) => ({
"setup": (input) => core.setup(undefined),
"clear": () => core.clear(),
"write": (key, value) => core.write([key], {"data": JSON.stringify(value)}),
"delete": (key) => core.delete([key]),
"read": (key) => core.read([key]).then(row => JSON.parse(row["data"])),
// "search": (term) => core.search(term).then(() => []),
"search": (term) => Promise.reject(new Error("not implemented")),
}),
]
)
),
"default_lifetime": _espe.conf.get().session_management.lifetime,
case "password-image": {
const input : (null | string) = args["arg1"];
if (input === null) {
throw (new Error("SYNTAX: password-image <password>"));
}
);
// outputs
_espe.service.member.listen_change(
() => {
lib_plankton.log.info(
"member_change",
else {
const result : string = await _espe.helpers.bcrypt_compute(input);
process.stdout.write(result + "\n")
}
break;
}
case "expose-conf": {
process.stdout.write(
JSON.stringify(
_espe.conf.get(),
undefined,
"\t"
)
+
"\n"
);
break;
}
case "api-doc": {
// lib_plankton.log.conf_push([]);
const rest_subject : lib_plankton.rest_http.type_rest = _espe.api.make();
// lib_plankton.log.conf_pop();
process.stdout.write(
JSON.stringify(
lib_plankton.rest_http.to_oas(rest_subject),
undefined,
"\t"
)
);
break;
}
case "email-test": {
await _espe.helpers.email_send(
(
(args["arg1"] !== null)
? [args["arg1"]]
: (await _espe.service.admin.email_addresses())
),
lib_plankton.string.coin(
"{{head}} | Test",
{
"head": _espe.conf.get().settings.organisation.name,
}
),
"This is a test e-mail"
);
break;
}
case "admin-add": {
const name : (null | string) = args["arg1"];
const email_address : (null | string) = args["arg2"];
const password : (null | string) = args["arg3"];
if ((name === null) || (email_address === null) || (password === null)) {
throw (new Error("SYNTAX: admin-add <name> <email-address> <password>"));
}
else {
const admin_id : _espe.type.admin_id = await _espe.service.admin.add(
name,
email_address,
password
);
_espe.conf.get().outputs.forEach(
output_description => {
const procedure : (() => Promise<void>) = (
(() => {
switch (output_description.kind) {
default: {
process.stdout.write(admin_id.toFixed(0) + "\n")
}
break;
}
case "serve": {
// prepare database
await _espe.database.check();
await lib_plankton.session.setup(
{
"data_chest": (
_espe.conf.get().session_management.in_memory
? lib_plankton.storage.memory.implementation_chest<lib_plankton.session.type_session>({})
: lib_plankton.call.convey(
lib_plankton.storage.sql_table_common.chest(
{
"database_implementation": _espe.helpers.database_implementation(),
"table_name": "sessions",
"key_names": ["key"],
}
),
[
(core) => ({
"setup": (input) => core.setup(undefined),
"clear": () => core.clear(),
"write": (key, value) => core.write([key], {"data": JSON.stringify(value)}),
"delete": (key) => core.delete([key]),
"read": (key) => core.read([key]).then(row => JSON.parse(row["data"])),
// "search": (term) => core.search(term).then(() => []),
"search": (term) => Promise.reject(new Error("not implemented")),
}),
]
)
),
"default_lifetime": _espe.conf.get().session_management.lifetime,
}
);
// outputs
_espe.service.member.listen_change(
() => {
lib_plankton.log.info(
"member_change",
{
}
);
_espe.conf.get().outputs.forEach(
output_description => {
const procedure : (() => Promise<void>) = (
(() => {
switch (output_description.kind) {
default: {
lib_plankton.log.warning(
"output_kind_unhandled",
{
"description": output_description,
}
);
return (() => Promise.resolve<void>(undefined));
break;
}
case "authelia_file": {
return (() => _espe.service.member.output_authelia_file(output_description.data));
break;
}
case "http": {
return (() => _espe.service.member.output_http(output_description.data));
break;
}
case "arc": {
return (() => _espe.service.member.output_arc(output_description.data));
break;
}
}
}) ()
);
(
procedure()
.then(
() => {
}
)
.catch(
(error) => {
lib_plankton.log.warning(
"output_kind_unhandled",
"output_procedure_failed",
{
"description": output_description,
"error": String(error),
}
);
return (() => Promise.resolve<void>(undefined));
break;
}
case "authelia_file": {
return (() => _espe.service.member.output_authelia_file(output_description.data));
break;
}
case "http": {
return (() => _espe.service.member.output_http(output_description.data));
break;
}
case "arc": {
return (() => _espe.service.member.output_arc(output_description.data));
break;
}
}
}) ()
);
(
procedure()
.then(
() => {
}
)
.catch(
(error) => {
lib_plankton.log.warning(
"output_procedure_failed",
{
"error": String(error),
}
);
}
)
);
}
);
}
);
const rest_subject : lib_plankton.rest.type_rest = _espe.api.make();
const server : lib_plankton.server.type_subject = lib_plankton.server.make(
async (input, metadata) => {
const http_request : lib_plankton.http.type_request = lib_plankton.http.decode_request(input.toString());
const http_response : lib_plankton.http.type_response = await lib_plankton.rest.call(
rest_subject,
http_request,
{
"checklevel_restriction": lib_plankton.api.enum_checklevel.hard,
// "checklevel_input": lib_plankton.api.enum_checklevel.soft,
// "checklevel_output": lib_plankton.api.enum_checklevel.soft,
}
);
const output : string = lib_plankton.http.encode_response(http_response);
return output;
},
{
"host": _espe.conf.get().server.host,
"port": _espe.conf.get().server.port,
// DANGER! DANGER!
"threshold": 0.125,
}
);
lib_plankton.server.start(server);
break;
}
case "export-authelia": {
process.stdout.write(await _espe.service.member.export_authelia_user_file() + "\n");
break;
}
}
)
);
}
);
}
);
const rest_subject : lib_plankton.rest_http.type_rest = _espe.api.make();
const server : lib_plankton.server.type_subject = lib_plankton.server.make(
async (input, metadata) => {
const http_request : lib_plankton.http.type_request = lib_plankton.http.decode_request(input.toString());
const http_response : lib_plankton.http.type_response = await lib_plankton.rest_http.call(
rest_subject,
http_request,
{
"checklevel_restriction": lib_plankton.api.enum_checklevel.hard,
// "checklevel_input": lib_plankton.api.enum_checklevel.soft,
// "checklevel_output": lib_plankton.api.enum_checklevel.soft,
}
);
const output : string = lib_plankton.http.encode_response(http_response);
return output;
},
{
"host": _espe.conf.get().server.host,
"port": _espe.conf.get().server.port,
// DANGER! DANGER!
"threshold": 0.125,
}
);
lib_plankton.server.start(server);
break;
}
case "export-authelia": {
process.stdout.write(await _espe.service.member.export_authelia_user_file() + "\n");
break;
}
}
}
}
}
(
main(process.argv.slice(2))
_espe.main(process.argv.slice(2))
.then(
() => {
}

View file

@ -156,7 +156,9 @@ namespace _espe.repository.member
"email_address_private": dispersal.core_row["email_address_private"],
"groups": lib_plankton.list.sorted<string>(
dispersal.group_rows.map(row => row["group_name"]),
(group1, group2) => ((group1 <= group2) ? 0 : 1)
{
"compare_element": (group1, group2) => (group1 <= group2)
}
),
"registered": (dispersal.core_row["registered"] > 0),
"enabled": (dispersal.core_row["enabled"] > 0),

View file

@ -16,8 +16,8 @@ modules="${modules} storage"
modules="${modules} session"
modules="${modules} json"
modules="${modules} api"
modules="${modules} rest"
modules="${modules} http"
modules="${modules} rest_http"
modules="${modules} server"
modules="${modules} email"
modules="${modules} args"