This commit is contained in:
Fenris Wolf 2024-12-09 21:40:07 +01:00
parent 9eab32d573
commit 64652af779
9 changed files with 219 additions and 386 deletions

View file

@ -2495,95 +2495,47 @@ declare namespace lib_plankton.storage.sql_table_common {
declare namespace lib_plankton.cache { declare namespace lib_plankton.cache {
/** /**
*/ */
type type_cache<type_value> = { type type_result<type_value> = {
init: (() => Promise<void>); retrieved: boolean;
clear: (() => Promise<void>); value: type_value;
query: ((key: string, retrieve: (() => Promise<type_value>)) => Promise<{
retrieved: boolean;
value: type_value;
}>);
}; };
/**
*/
type type_entry<type_value> = {
value: type_value;
expiry: (null | float);
};
/**
*/
type type_subject<type_value> = lib_plankton.storage.type_chest<string, type_entry<type_value>, void, any, any>;
} }
declare namespace lib_plankton.cache { declare namespace lib_plankton.cache {
/** /**
*/ */
function get<type_value>(cache: type_cache<type_value>, key: string, retrieve: (() => Promise<type_value>)): Promise<type_value>; function make<type_value>({ "chest": chest, }?: {
/** chest?: lib_plankton.storage.type_chest<string, type_entry<type_value>, void, any, any>;
*/
function get_complex<type_input, type_value>(cache: type_cache<type_value>, group: string, input: type_input, retrieve: ((input: type_input) => Promise<type_value>), options?: {
encode_input?: ((input: type_input) => string);
}): Promise<type_value>;
}
declare namespace lib_plankton.cache.never {
/**
* @author fenris
*/
type type_subject<type_value> = {};
/**
* @author fenris
*/
function make<type_value>(): type_subject<type_value>;
/**
*/
function implementation<type_value>(subject: type_subject<type_value>): type_cache<type_value>;
}
declare namespace lib_plankton.cache.always {
/**
* @author fenris
*/
type type_subject<type_value> = {
value: lib_plankton.pod.type_pod<type_value>;
};
/**
* @author fenris
*/
function make<type_value>(value: lib_plankton.pod.type_pod<type_value>): type_subject<type_value>;
/**
* @author fenris
*/
function clear<type_value>(subject: type_subject<type_value>): Promise<void>;
/**
* @author fenris
*/
function query<type_value>(subject: type_subject<type_value>, key: string, retrieve: (() => Promise<type_value>)): Promise<{
retrieved: boolean;
value: type_value;
}>;
/**
*/
function implementation<type_value>(subject: type_subject<type_value>): type_cache<type_value>;
}
declare namespace lib_plankton.cache.chest {
/**
* @author fenris
*/
type type_subject<type_value> = {
chest: lib_plankton.storage.type_chest<string, type_value, void, any, any>;
};
/**
* @author fenris
*/
function make<type_value>(options?: {
chest?: lib_plankton.storage.type_chest<string, type_value, void, any, any>;
}): type_subject<type_value>; }): type_subject<type_value>;
/** /**
* @author fenris
*/ */
function init<type_value>(subject: type_subject<type_value>): Promise<void>; function init<type_value>(subject: type_subject<type_value>): Promise<void>;
/** /**
* @author fenris
*/ */
function clear<type_value>(subject: type_subject<type_value>): Promise<void>; function clear<type_value>(subject: type_subject<type_value>): Promise<void>;
/** /**
* @author fenris
*/ */
function query<type_value>(subject: type_subject<type_value>, key: string, retrieve: (() => Promise<type_value>)): Promise<{ function invalidate<type_value>(subject: type_subject<type_value>, key: string): Promise<void>;
retrieved: boolean;
value: type_value;
}>;
/** /**
*/ */
function implementation<type_value>(subject: type_subject<type_value>): type_cache<type_value>; function query<type_value>(subject: type_subject<type_value>, key: string, lifetime: (null | float), retrieve: (() => Promise<type_value>)): Promise<type_result<type_value>>;
/**
* syntactic sugar: if the information, whether the value was retrieved, is irrelevant
*/
function get<type_value>(subject: type_subject<type_value>, key: string, lifetime: (null | float), retrieve: (() => Promise<type_value>)): Promise<type_value>;
/**
*/
function get_complex<type_input, type_value>(cache: type_subject<type_value>, group: string, input: type_input, lifetime: (null | float), retrieve: ((input: type_input) => Promise<type_value>), { "encode_input": encode_input, }?: {
encode_input?: ((input: type_input) => string);
}): Promise<type_value>;
} }
declare namespace lib_plankton.shape { declare namespace lib_plankton.shape {
/** /**

View file

@ -7675,246 +7675,93 @@ var lib_plankton;
(function (cache_1) { (function (cache_1) {
/** /**
*/ */
function get(cache, key, retrieve) { function make({ "chest": chest = lib_plankton.storage.memory.implementation_chest({}), } = {}) {
return cache.query(key, retrieve).then(result => result.value); return chest;
}
cache_1.make = make;
/**
*/
function init(subject) {
return subject.setup(undefined);
}
cache_1.init = init;
/**
*/
function clear(subject) {
return subject.clear();
}
cache_1.clear = clear;
/**
*/
function invalidate(subject, key) {
return subject.delete(key);
}
cache_1.invalidate = invalidate;
/**
*/
async function query(subject, key, lifetime, retrieve) {
const now = lib_plankton.base.get_current_timestamp();
return ((subject.read(key)
.then((entry) => Promise.resolve({
"found": true,
"entry": entry,
}))
.catch(() => Promise.resolve({
"found": false,
"entry": null,
})))
.then((item) => {
if ((!item.found)
||
((item.entry.expiry !== null)
&&
(item.entry.expiry <= now))) {
lib_plankton.log.info("plankton.cache.unknown_or_expired", {
"key": key,
});
return (retrieve()
.then((value) => (subject.write(key, {
"value": value,
"expiry": ((lifetime === null)
?
null
:
(now + lifetime))
})
.then(() => Promise.resolve({
"retrieved": true,
"value": value
})))));
}
else {
lib_plankton.log.info("plankton.cache.known_and_valid", {
"key": key,
});
return Promise.resolve({
"retrieved": false,
"value": item.entry.value
});
}
}));
}
cache_1.query = query;
/**
* syntactic sugar: if the information, whether the value was retrieved, is irrelevant
*/
function get(subject, key, lifetime, retrieve) {
return (query(subject, key, lifetime, retrieve)
.then(result => Promise.resolve(result.value)));
} }
cache_1.get = get; cache_1.get = get;
/** /**
*/ */
function get_complex(cache, group, input, retrieve, options = {}) { function get_complex(cache, group, input, lifetime, retrieve, { "encode_input": encode_input = (input => JSON.stringify(input)), } = {}) {
options = Object.assign({ return get(cache, (group + "." + encode_input(input)), lifetime, () => retrieve(input));
"encode_input": input => JSON.stringify(input),
}, options);
return get(cache, (group + "." + options.encode_input(input)), () => retrieve(input));
} }
cache_1.get_complex = get_complex; cache_1.get_complex = get_complex;
})(cache = lib_plankton.cache || (lib_plankton.cache = {})); })(cache = lib_plankton.cache || (lib_plankton.cache = {}));
})(lib_plankton || (lib_plankton = {})); })(lib_plankton || (lib_plankton = {}));
/* /*
This file is part of »bacterio-plankton:cache«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'
<info@greenscale.de>
»bacterio-plankton:cache« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»bacterio-plankton:cache« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »bacterio-plankton:cache«. If not, see <http://www.gnu.org/licenses/>.
*/
var lib_plankton;
(function (lib_plankton) {
var cache;
(function (cache) {
var never;
(function (never) {
/**
* @author fenris
*/
function make() {
return {};
}
never.make = make;
/**
* @author fenris
*/
function clear(subject) {
return Promise.resolve(undefined);
}
/**
* @author fenris
*/
async function query(subject, key, retrieve) {
return {
"retrieved": true,
"value": (await retrieve()),
};
}
/**
*/
function implementation(subject) {
return {
"init": () => Promise.resolve(undefined),
"clear": () => clear(subject),
"query": (key, retrieve) => query(subject, key, retrieve),
};
}
never.implementation = implementation;
})(never = cache.never || (cache.never = {}));
})(cache = lib_plankton.cache || (lib_plankton.cache = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:cache«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'
<info@greenscale.de>
»bacterio-plankton:cache« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»bacterio-plankton:cache« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »bacterio-plankton:cache«. If not, see <http://www.gnu.org/licenses/>.
*/
var lib_plankton;
(function (lib_plankton) {
var cache;
(function (cache) {
var always;
(function (always) {
/**
* @author fenris
*/
function make(value) {
return {
"value": value,
};
}
always.make = make;
/**
* @author fenris
*/
function clear(subject) {
return Promise.resolve(undefined);
}
always.clear = clear;
/**
* @author fenris
*/
function query(subject, key, retrieve) {
if (lib_plankton.pod.is_filled(subject.value)) {
return Promise.resolve({
"retrieved": false,
"value": lib_plankton.pod.cull(subject.value),
});
}
else {
return Promise.reject();
}
}
always.query = query;
/**
*/
function implementation(subject) {
return {
"init": () => Promise.resolve(undefined),
"clear": () => clear(subject),
"query": (key, retrieve) => query(subject, key, retrieve),
};
}
always.implementation = implementation;
})(always = cache.always || (cache.always = {}));
})(cache = lib_plankton.cache || (lib_plankton.cache = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:cache«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'
<info@greenscale.de>
»bacterio-plankton:cache« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»bacterio-plankton:cache« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »bacterio-plankton:cache«. If not, see <http://www.gnu.org/licenses/>.
*/
var lib_plankton;
(function (lib_plankton) {
var cache;
(function (cache) {
var chest;
(function (chest) {
/**
* @author fenris
*/
function make(options = {}) {
options = Object.assign({
"chest": lib_plankton.storage.memory.implementation_chest({}),
}, options);
return {
"chest": options.chest,
};
}
chest.make = make;
/**
* @author fenris
*/
function init(subject) {
return subject.chest.setup(undefined);
}
chest.init = init;
/**
* @author fenris
*/
function clear(subject) {
return subject.chest.clear();
}
chest.clear = clear;
/**
* @author fenris
*/
function query(subject, key, retrieve) {
return (subject.chest.read(key)
.then(value => (lib_plankton.log.info("cache.chest_hashed.known", {
"key": key,
})
,
Promise.resolve({
"retrieved": false,
"value": value,
})))
.catch(() => (lib_plankton.log.info("cache.chest_hashed.unknown", {
"key": key,
})
,
(retrieve()
.then((value) => (subject.chest.write(key, value)
.then(() => Promise.resolve({
"retrieved": true,
"value": value,
}))))
/*
.catch(
(reason) => Promise.reject<{retrieved : boolean; value : type_value}>(reason)
)
*/
))));
}
chest.query = query;
/**
*/
function implementation(subject) {
return {
"init": () => init(subject),
"clear": () => clear(subject),
"query": (key, retrieve) => query(subject, key, retrieve),
};
}
chest.implementation = implementation;
})(chest = cache.chest || (cache.chest = {}));
})(cache = lib_plankton.cache || (lib_plankton.cache = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:shape«. This file is part of »bacterio-plankton:shape«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'

View file

@ -3,6 +3,16 @@ namespace _zeitbild
/** /**
*/ */
export var cache : lib_plankton.cache.type_cache<any>; export var cache_regular : lib_plankton.cache.type_subject<any>;
/**
*/
export var cache_external_resources : lib_plankton.cache.type_subject<any>;
/**
*/
export var cache_templates : lib_plankton.cache.type_subject<string>;
} }

View file

@ -283,6 +283,22 @@ namespace _zeitbild.conf
} }
} }
}, },
"external_resources": {
"nullable": false,
"type": "object",
"properties": {
"lifetime": {
"nullable": false,
"type": "integer",
"default": 14400
}
},
"additionalProperties": false,
"required": [
],
"default": {
}
},
"misc": { "misc": {
"nullable": false, "nullable": false,
"type": "object", "type": "object",
@ -300,7 +316,7 @@ namespace _zeitbild.conf
], ],
"additionalProperties": false, "additionalProperties": false,
"default": {} "default": {}
} },
}, },
"required": [ "required": [
"version" "version"

View file

@ -161,37 +161,29 @@ namespace _zeitbild.helpers
/** /**
*/ */
var _template_cache : Record<string, string> = {};
/**
* @todo caching
*/
export async function template_coin( export async function template_coin(
name : string, name : string,
data : Record<string, string> data : Record<string, string>
) : Promise<string> ) : Promise<string>
{ {
let content : string; const content : string = await lib_plankton.cache.get<string>(
if (! (name in _template_cache)) { _zeitbild.cache_templates,
content = ( name,
( null,
await lib_plankton.file.read( () => (
lib_plankton.string.coin( lib_plankton.file.read(
"templates/{{name}}.html.tpl", lib_plankton.string.coin(
{ "templates/{{name}}.html.tpl",
"name": name, {
} "name": name,
) }
) )
) )
.toString() .then(
); x => Promise.resolve<string>(x.toString())
_template_cache[name] = content; )
} )
else { );
content = _template_cache[name];
}
return Promise.resolve<string>( return Promise.resolve<string>(
lib_plankton.string.coin( lib_plankton.string.coin(
content, content,

View file

@ -313,14 +313,18 @@ async function main(
} }
) )
); );
_zeitbild.cache = lib_plankton.cache.chest.implementation<any>( {
lib_plankton.cache.chest.make<any>( _zeitbild.cache_regular = lib_plankton.cache.make<any>();
{ await lib_plankton.cache.init(_zeitbild.cache_regular);
"chest": lib_plankton.storage.memory.implementation_chest<any>({}), }
} {
) _zeitbild.cache_external_resources = lib_plankton.cache.make<any>();
); await lib_plankton.cache.init(_zeitbild.cache_external_resources);
await _zeitbild.cache.init(); }
{
_zeitbild.cache_templates = lib_plankton.cache.make<any>();
await lib_plankton.cache.init(_zeitbild.cache_templates);
}
// exec // exec
if (args["help"]) { if (args["help"]) {

View file

@ -251,7 +251,7 @@ namespace _zeitbild.repository.calendar
{"level": access_attributed_row["level"]} {"level": access_attributed_row["level"]}
); );
} }
await _zeitbild.cache.clear(); await lib_plankton.cache.clear(_zeitbild.cache_regular);
return Promise.resolve<_zeitbild.type_calendar_id>(calendar_id); return Promise.resolve<_zeitbild.type_calendar_id>(calendar_id);
} }
@ -313,7 +313,7 @@ namespace _zeitbild.repository.calendar
); );
} }
} }
await _zeitbild.cache.clear(); await lib_plankton.cache.clear(_zeitbild.cache_regular);
return Promise.resolve<void>(undefined); return Promise.resolve<void>(undefined);
} }
@ -325,7 +325,7 @@ namespace _zeitbild.repository.calendar
calendar_id : _zeitbild.type_calendar_id calendar_id : _zeitbild.type_calendar_id
) : Promise<void> ) : Promise<void>
{ {
await _zeitbild.cache.clear(); await lib_plankton.cache.clear(_zeitbild.cache_regular);
const core_store = get_core_store(); const core_store = get_core_store();
const access_attributed_chest = get_access_attributed_chest(); const access_attributed_chest = get_access_attributed_chest();
// attributed access // attributed access
@ -375,11 +375,12 @@ namespace _zeitbild.repository.calendar
> >
{ {
return lib_plankton.cache.get_complex<any, Array<type_overview_entry>>( return lib_plankton.cache.get_complex<any, Array<type_overview_entry>>(
_zeitbild.cache, _zeitbild.cache_regular,
"calendar_overview", "calendar_overview",
{ {
"user_id": user_id, "user_id": user_id,
}, },
null,
() => ( () => (
lib_plankton.file.read("sql/calendar_overview.sql") lib_plankton.file.read("sql/calendar_overview.sql")
.then( .then(

View file

@ -401,7 +401,7 @@ namespace _zeitbild.repository.resource
"sub_id": local_resource_id, "sub_id": local_resource_id,
} }
); );
await _zeitbild.cache.clear(); await lib_plankton.cache.clear(_zeitbild.cache_regular);
return Promise.resolve<_zeitbild.type_resource_id>(resource_id); return Promise.resolve<_zeitbild.type_resource_id>(resource_id);
break; break;
} }
@ -419,7 +419,7 @@ namespace _zeitbild.repository.resource
"sub_id": caldav_resource_id, "sub_id": caldav_resource_id,
} }
); );
await _zeitbild.cache.clear(); await lib_plankton.cache.clear(_zeitbild.cache_regular);
return Promise.resolve<_zeitbild.type_resource_id>(resource_id); return Promise.resolve<_zeitbild.type_resource_id>(resource_id);
break; break;
} }
@ -499,7 +499,7 @@ namespace _zeitbild.repository.resource
"read_only": resource_object.data.read_only, "read_only": resource_object.data.read_only,
} }
); );
await _zeitbild.cache.clear(); await lib_plankton.cache.clear(_zeitbild.cache_regular);
break; break;
} }
default: { default: {
@ -561,7 +561,7 @@ namespace _zeitbild.repository.resource
} }
) )
); );
await _zeitbild.cache.clear(); await lib_plankton.cache.clear(_zeitbild.cache_regular);
return Promise.resolve(local_resource_event_id); return Promise.resolve(local_resource_event_id);
} }
} }
@ -589,7 +589,7 @@ namespace _zeitbild.repository.resource
} }
) )
); );
await _zeitbild.cache.clear(); await lib_plankton.cache.clear(_zeitbild.cache_regular);
return Promise.resolve<void>(undefined); return Promise.resolve<void>(undefined);
} }
} }
@ -602,7 +602,7 @@ namespace _zeitbild.repository.resource
local_resource_event_id : _zeitbild.type_local_resource_event_id local_resource_event_id : _zeitbild.type_local_resource_event_id
) : Promise<void> ) : Promise<void>
{ {
await _zeitbild.cache.clear(); await lib_plankton.cache.clear(_zeitbild.cache_regular);
const dataset_core : Record<string, any> = await get_resource_core_store().read(resource_id); const dataset_core : Record<string, any> = await get_resource_core_store().read(resource_id);
if (! (dataset_core.kind === "local")) { if (! (dataset_core.kind === "local")) {
throw (new Error("not a local resource")); throw (new Error("not a local resource"));

View file

@ -329,45 +329,52 @@ namespace _zeitbild.service.calendar
} }
case "caldav": { case "caldav": {
// TODO readonly // TODO readonly
const url : lib_plankton.url.type_url = lib_plankton.url.decode( const vcalendar : lib_plankton.ical.type_vcalendar = await lib_plankton.cache.get<lib_plankton.ical.type_vcalendar>(
resource_object.data.url _zeitbild.cache_external_resources,
); resource_object.data.url,
const http_request : lib_plankton.http.type_request = { _zeitbild.conf.get().external_resources.lifetime,
"version": "HTTP/2", async () => {
"scheme": ((url.scheme === "https") ? "https" : "http"), const url : lib_plankton.url.type_url = lib_plankton.url.decode(
"host": url.host, resource_object.data.url
"path": (url.path ?? "/"), );
"query": url.query, const http_request : lib_plankton.http.type_request = {
"method": lib_plankton.http.enum_method.get, "version": "HTTP/2",
"headers": {}, "scheme": ((url.scheme === "https") ? "https" : "http"),
"body": null, "host": url.host,
}; "path": (url.path ?? "/"),
// TODO: cache? "query": url.query,
const http_response : lib_plankton.http.type_response = await lib_plankton.http.call( "method": lib_plankton.http.enum_method.get,
http_request, "headers": {},
{ "body": null,
};
const http_response : lib_plankton.http.type_response = await lib_plankton.http.call(
http_request,
{
}
);
const ics_raw : string = (
(http_response.body === null)
?
""
:
http_response.body.toString()
);
const vcalendar_list : Array<lib_plankton.ical.type_vcalendar> = lib_plankton.ical.ics_decode_multi(
ics_raw,
{
"ignore_unhandled_instruction_keys": resource_object.data.from_fucked_up_wordpress,
"from_fucked_up_wordpress": resource_object.data.from_fucked_up_wordpress,
}
);
const vcalendar : lib_plankton.ical.type_vcalendar = {
// required
"version": vcalendar_list[0].version,
"prodid": vcalendar_list[0].prodid,
"vevents": vcalendar_list.map(x => x.vevents).reduce((x, y) => x.concat(y), []),
};
return Promise.resolve<lib_plankton.ical.type_vcalendar>(vcalendar);
} }
); );
const ics_raw : string = (
(http_response.body === null)
?
""
:
http_response.body.toString()
);
const vcalendar_list : Array<lib_plankton.ical.type_vcalendar> = lib_plankton.ical.ics_decode_multi(
ics_raw,
{
"ignore_unhandled_instruction_keys": resource_object.data.from_fucked_up_wordpress,
"from_fucked_up_wordpress": resource_object.data.from_fucked_up_wordpress,
}
);
const vcalendar : lib_plankton.ical.type_vcalendar = {
// required
"version": vcalendar_list[0].version,
"prodid": vcalendar_list[0].prodid,
"vevents": vcalendar_list.map(x => x.vevents).reduce((x, y) => x.concat(y), []),
};
return Promise.resolve( return Promise.resolve(
vcalendar.vevents vcalendar.vevents
.map( .map(
@ -490,7 +497,7 @@ namespace _zeitbild.service.calendar
); );
calendar_ids.sort(); calendar_ids.sort();
return lib_plankton.cache.get_complex<any, type_gather_events_result>( return lib_plankton.cache.get_complex<any, type_gather_events_result>(
_zeitbild.cache, _zeitbild.cache_regular,
"gather_events", "gather_events",
{ {
"user_id": user_id, "user_id": user_id,
@ -498,6 +505,10 @@ namespace _zeitbild.service.calendar
"to_pit": to_pit, "to_pit": to_pit,
"calendar_ids": calendar_ids, "calendar_ids": calendar_ids,
}, },
/**
* @todo expire?
*/
null,
() => ( () => (
Promise.all( Promise.all(
calendar_ids calendar_ids