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 {
/**
*/
type type_cache<type_value> = {
init: (() => Promise<void>);
clear: (() => Promise<void>);
query: ((key: string, retrieve: (() => Promise<type_value>)) => Promise<{
retrieved: boolean;
value: type_value;
}>);
type type_result<type_value> = {
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 {
/**
*/
function get<type_value>(cache: type_cache<type_value>, key: string, retrieve: (() => Promise<type_value>)): Promise<type_value>;
/**
*/
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>;
function make<type_value>({ "chest": chest, }?: {
chest?: lib_plankton.storage.type_chest<string, type_entry<type_value>, void, any, any>;
}): type_subject<type_value>;
/**
* @author fenris
*/
function init<type_value>(subject: type_subject<type_value>): Promise<void>;
/**
* @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 invalidate<type_value>(subject: type_subject<type_value>, key: string): Promise<void>;
/**
*/
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 {
/**

View file

@ -7675,246 +7675,93 @@ var lib_plankton;
(function (cache_1) {
/**
*/
function get(cache, key, retrieve) {
return cache.query(key, retrieve).then(result => result.value);
function make({ "chest": chest = lib_plankton.storage.memory.implementation_chest({}), } = {}) {
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;
/**
*/
function get_complex(cache, group, input, retrieve, options = {}) {
options = Object.assign({
"encode_input": input => JSON.stringify(input),
}, options);
return get(cache, (group + "." + options.encode_input(input)), () => retrieve(input));
function get_complex(cache, group, input, lifetime, retrieve, { "encode_input": encode_input = (input => JSON.stringify(input)), } = {}) {
return get(cache, (group + "." + encode_input(input)), lifetime, () => retrieve(input));
}
cache_1.get_complex = get_complex;
})(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 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«.
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": {
"nullable": false,
"type": "object",
@ -300,7 +316,7 @@ namespace _zeitbild.conf
],
"additionalProperties": false,
"default": {}
}
},
},
"required": [
"version"

View file

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

View file

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

View file

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

View file

@ -401,7 +401,7 @@ namespace _zeitbild.repository.resource
"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);
break;
}
@ -419,7 +419,7 @@ namespace _zeitbild.repository.resource
"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);
break;
}
@ -499,7 +499,7 @@ namespace _zeitbild.repository.resource
"read_only": resource_object.data.read_only,
}
);
await _zeitbild.cache.clear();
await lib_plankton.cache.clear(_zeitbild.cache_regular);
break;
}
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);
}
}
@ -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);
}
}
@ -602,7 +602,7 @@ namespace _zeitbild.repository.resource
local_resource_event_id : _zeitbild.type_local_resource_event_id
) : 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);
if (! (dataset_core.kind === "local")) {
throw (new Error("not a local resource"));

View file

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