Compare commits

...

1 commit

Author SHA1 Message Date
Christian Fraß 2b7d791da2 [mod] datenbank-steuerung:automatische migrationen 2024-04-22 21:19:04 +02:00
5 changed files with 282 additions and 18 deletions

View file

@ -111,10 +111,30 @@ namespace _espe.conf
})) (conf_raw["server"] ?? {}) })) (conf_raw["server"] ?? {})
), ),
"database": ( "database": (
((node_database) => ({ ((node_database) => {
"kind": (node_database["kind"] ?? "sqlite"), const kind : string = (node_database["kind"] ?? "sqlite");
"data": node_database["data"], const node_database_data_raw = (node_database["data"] ?? {});
})) (conf_raw["database"] ?? {}) switch (kind) {
case "sqlite": {
return {
"kind": kind,
"data": {
"path": (node_database_data_raw["path"] ?? "data.sqlite"),
}
};
}
case "postgresql": {
return {
"kind": kind,
"data": node_database_data_raw,
};
}
default: {
throw (new Error("unhandled"));
break;
}
}
}) (conf_raw["database"] ?? {})
), ),
"email_sending": ( "email_sending": (
((node_email_sending) => ({ ((node_email_sending) => ({

251
source/database.ts Normal file
View file

@ -0,0 +1,251 @@
namespace _espe.database
{
/**
*/
enum enum_revision {
r0 = 0,
r1 = 1,
}
/**
*/
const _revision_order : Array<enum_revision> = [
enum_revision.r0,
enum_revision.r1,
];
/**
*/
export function get_implementation(
) : lib_plankton.database.type_database
{
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));
}
}
}
/**
*/
function get_revision(
) : Promise<enum_revision>
{
return (
(
get_implementation().query_select(
{
"source": "meta",
"fields": ["revision"],
}
)
.then<int>(
(rows) => Promise.resolve<int>(rows[0]["revision"])
)
.catch<int>(
() => Promise.resolve<int>(0)
)
)
.then<enum_revision>(
(revision_raw) => Promise.resolve<enum_revision>(revision_raw)
)
);
}
/**
*/
async function advance(
) : Promise<boolean>
{
const implementation : lib_plankton.database.type_database = get_implementation();
const revision_current : enum_revision = await get_revision();
const index_current : int = _revision_order.indexOf(revision_current);
const index_next : (null | int) = (
(index_current < (_revision_order.length - 1))
? (index_current + 1)
: null
);
const revision_next : (null | enum_revision) = (
(index_next !== null)
? _revision_order[index_next]
: null
);
if (revision_next !== null) {
lib_plankton.log.notice(
"database_advancement_necessary",
{
"revision_current": revision_current,
"revision_next": revision_next,
}
);
switch (revision_next) {
case enum_revision.r1: {
// structure
{
await implementation.query_create_table(
{
"name": "meta",
"data_fields": [
{
"name": "revision",
"type": lib_plankton.database.enum_type.integer,
"nullable": false,
},
],
}
);
await implementation.query_create_table(
{
"name": "members",
"key_field": {
"name": "id"
},
"data_fields": [
{
"name": "membership_number",
"type": lib_plankton.database.enum_type.string_short,
"nullable": false
},
{
"name": "enabled",
"type": lib_plankton.database.enum_type.boolean,
"nullable": false
},
{
"name": "name_login",
"type": lib_plankton.database.enum_type.string_short,
"nullable": true
},
{
"name": "password_image",
"type": lib_plankton.database.enum_type.string_medium,
"nullable": true
},
{
"name": "name_real_value",
"type": lib_plankton.database.enum_type.string_medium,
"nullable": false
},
{
"name": "name_real_extension",
"type": lib_plankton.database.enum_type.string_short,
"nullable": true
},
{
"name": "name_display",
"type": lib_plankton.database.enum_type.string_medium,
"nullable": true
},
{
"name": "salutation",
"type": lib_plankton.database.enum_type.string_short,
"nullable": true
},
{
"name": "email_address_private_value",
"type": lib_plankton.database.enum_type.string_short,
"nullable": true
},
{
"name": "email_address_numberbased_use",
"type": lib_plankton.database.enum_type.boolean,
"nullable": false
},
{
"name": "email_address_namebased_use",
"type": lib_plankton.database.enum_type.boolean,
"nullable": false
},
{
"name": "email_redirect_to_private",
"type": lib_plankton.database.enum_type.boolean,
"nullable": false
}
],
"constraints": [
{
"kind": "unique",
"parameters": {
"fields": ["membership_number"]
}
},
{
"kind": "unique",
"parameters": {
"fields": ["name_real_value","name_real_extension"]
}
},
{
"kind": "unique",
"parameters": {
"fields": ["name_login"]
}
}
]
}
);
}
// data
{
await implementation.query_insert(
{
"table_name": "meta",
"values": {
"revision": revision_next,
}
}
);
}
return Promise.resolve<boolean>(true);
break;
}
default: {
return Promise.reject<boolean>("unhandled advancement step");
break;
}
}
}
else {
lib_plankton.log.notice(
"database_up_to_date",
{
"revision_current": revision_current,
}
);
return Promise.resolve<boolean>(false);
}
}
/**
*/
export async function advance_all(
) : Promise<void>
{
while (true) {
const modified : boolean = await advance();
if (modified) {
// do nothing
}
else {
break;
}
}
}
}

View file

@ -50,24 +50,12 @@ namespace _espe.helpers
/** /**
* @deprecated
*/ */
export function database_implementation( export function database_implementation(
) : lib_plankton.database.type_database ) : lib_plankton.database.type_database
{ {
switch (_espe.conf.get().database.kind) { return _espe.database.get_implementation();
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));
}
}
} }

View file

@ -125,6 +125,10 @@ async function main(
), ),
] ]
); );
// prepare database
await _espe.database.advance_all();
switch (args["action"]) { switch (args["action"]) {
default: { default: {
process.stderr.write("invalid action: " + args["action"] + "\n"); process.stderr.write("invalid action: " + args["action"] + "\n");

View file

@ -27,6 +27,7 @@ node_modules:
${dir_temp}/espe-unlinked.js: \ ${dir_temp}/espe-unlinked.js: \
${dir_lib}/plankton/plankton.d.ts \ ${dir_lib}/plankton/plankton.d.ts \
${dir_source}/helpers.ts \ ${dir_source}/helpers.ts \
${dir_source}/database.ts \
${dir_source}/service-member.ts \ ${dir_source}/service-member.ts \
${dir_source}/service-admin.ts \ ${dir_source}/service-admin.ts \
${dir_source}/api/base.ts \ ${dir_source}/api/base.ts \