Compare commits
37 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4733260de1 | ||
|
|
2c019723bf | ||
|
|
bef69283f6 | ||
|
|
2a672381dd | ||
|
|
24322588d7 | ||
|
|
2eed983131 | ||
|
|
3c49c744b3 | ||
|
|
78729c6111 | ||
|
|
45acbbbcd9 | ||
|
|
d8c0e55340 | ||
|
|
127c676fd1 | ||
|
|
e6571aeff4 | ||
|
|
6c548272fe | ||
|
|
e552560887 | ||
|
|
944043d873 | ||
|
|
60f1c5a0de | ||
|
|
e66da38cb1 | ||
|
|
bf4f2fab6e | ||
|
|
439f046b1d | ||
|
|
8163a12a68 | ||
|
|
f5963c3553 | ||
|
|
243f1bb155 | ||
|
|
e33fb8bc53 | ||
|
|
0e8e9ec68b | ||
|
|
d8a74f8e37 | ||
|
|
477eeadc14 | ||
|
|
8b480ecc92 | ||
|
|
002e057d45 | ||
|
|
49b9481701 | ||
|
|
671cbfea01 | ||
|
|
efb7b09785 | ||
|
|
0e6be12680 | ||
|
|
3aac310a65 | ||
|
|
115a128d62 | ||
|
|
d38e1da8f9 | ||
|
|
663b6b85b5 | ||
|
|
f90567d043 |
|
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"log": [
|
|
||||||
{
|
|
||||||
"kind": "stdout",
|
|
||||||
"data": {
|
|
||||||
"threshold": "info"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"session_management": {
|
|
||||||
"in_memory": false,
|
|
||||||
"lifetime": 3600
|
|
||||||
},
|
|
||||||
"authentication": {
|
|
||||||
"kind": "internal",
|
|
||||||
"data": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
# Konzept
|
|
||||||
|
|
||||||
## Notizen
|
|
||||||
|
|
||||||
- Kalender sollen unabhängig von Nutzern bestehen können
|
|
||||||
- Zugriffs-Stufen
|
|
||||||
- `none`: kein Zugriff
|
|
||||||
- `view`: nur lesend
|
|
||||||
- `edit`: lesend und schreibend
|
|
||||||
- `admin`: kann alles (auch Kalender löschen)
|
|
||||||
- einem Kalender können beliebig viele Nutzer zugeordnet werden, die jeweils bestimmte Zugriffs-Stufen zugewiesen haben
|
|
||||||
- Veranstaltungen bilden keine eigene Domäne
|
|
||||||
- es gibt verschiedene Arten von Quellen:
|
|
||||||
- lokal
|
|
||||||
- enthält Veranstaltungen
|
|
||||||
- caldav
|
|
||||||
- enthält keine eigenen Veranstaltungen
|
|
||||||
- sollte read-only- und read/write-Modus haben
|
|
||||||
- nach dem Anmelden sieht man eine Kalender-Ansicht mit folgenden Kalendern kombiniert angezeigt:
|
|
||||||
- öffentliche Kalender
|
|
||||||
- nicht öffentliche Kalendar, bei welchen man mindestens Lese-Berechtigung hat
|
|
||||||
- öffentliche Kalendar können ohne Anmeldung betrachtet werden, jedoch nur mit einem schwer bis gar nicht erratbaren Link
|
|
||||||
|
|
||||||
|
|
||||||
## Zugriffssteuerung
|
|
||||||
|
|
||||||
Es gibt folgende Zugriffsstufen :
|
|
||||||
|
|
||||||
- `none`: kein Zugriff
|
|
||||||
- `view`: nur Lese-Zugriff
|
|
||||||
- `edit`: Lese- und Schreib-Zugriff
|
|
||||||
- `admin`: Lese- und Schreib-Zugriff + Bearbeitung von Kalender-Eigenschaften
|
|
||||||
|
|
||||||
Zur Bestimmung welche Zugriffsstufe ein Nutzer auf einen Kalender/Termin hat, werden drei Werte betrachtet:
|
|
||||||
|
|
||||||
- Öffentlichkeit des Kalenders (`public`)
|
|
||||||
- Standard-Zugriffsstufe des Kalenders (`default_level`)
|
|
||||||
- dem Nutzer zugewiesene Zugriffsstufe für den Kalender (`attributed_level`)
|
|
||||||
|
|
||||||
Diese Angaben fließen in folgende Formel ein:
|
|
||||||
|
|
||||||
```
|
|
||||||
access_level = max(
|
|
||||||
(if public then 'view' else 'none'),
|
|
||||||
(if (attributed = NULL) then default_level else attributed_level)
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
… wobei die Ordnung der Zugriffsstufen der oben ersichtlichen Reihenfolge entspricht.
|
|
||||||
|
|
||||||
Einzeln aufgeführt ergibt das:
|
|
||||||
|
|
||||||
| public | default level | attributed level | result |
|
|
||||||
|-- |-- |-- |-- |
|
|
||||||
| no | none | - | none |
|
|
||||||
| no | none | none | none |
|
|
||||||
| no | none | view | view |
|
|
||||||
| no | none | edit | edit |
|
|
||||||
| no | none | admin | admin |
|
|
||||||
| no | view | - | view |
|
|
||||||
| no | view | none | none |
|
|
||||||
| no | view | view | view |
|
|
||||||
| no | view | edit | edit |
|
|
||||||
| no | view | admin | admin |
|
|
||||||
| no | edit | - | edit |
|
|
||||||
| no | edit | none | none |
|
|
||||||
| no | edit | view | view |
|
|
||||||
| no | edit | edit | edit |
|
|
||||||
| no | edit | admin | admin |
|
|
||||||
| no | admin | - | admin |
|
|
||||||
| no | admin | none | none |
|
|
||||||
| no | admin | view | view |
|
|
||||||
| no | admin | edit | edit |
|
|
||||||
| no | admin | admin | admin |
|
|
||||||
| yes | none | - | view |
|
|
||||||
| yes | none | none | view |
|
|
||||||
| yes | none | view | view |
|
|
||||||
| yes | none | edit | edit |
|
|
||||||
| yes | none | admin | admin |
|
|
||||||
| yes | view | - | view |
|
|
||||||
| yes | view | none | view |
|
|
||||||
| yes | view | view | view |
|
|
||||||
| yes | view | edit | edit |
|
|
||||||
| yes | view | admin | admin |
|
|
||||||
| yes | edit | - | edit |
|
|
||||||
| yes | edit | none | view |
|
|
||||||
| yes | edit | view | view |
|
|
||||||
| yes | edit | edit | edit |
|
|
||||||
| yes | edit | admin | admin |
|
|
||||||
| yes | admin | - | admin |
|
|
||||||
| yes | admin | none | none |
|
|
||||||
| yes | admin | view | view |
|
|
||||||
| yes | admin | edit | edit |
|
|
||||||
| yes | admin | admin | admin |
|
|
||||||
121
lib/plankton/plankton.d.ts
vendored
121
lib/plankton/plankton.d.ts
vendored
|
|
@ -1,11 +1,11 @@
|
||||||
/**
|
/**
|
||||||
* @author fenris
|
* @author fenris
|
||||||
*/
|
*/
|
||||||
declare type int = number;
|
type int = number;
|
||||||
/**
|
/**
|
||||||
* @author fenris
|
* @author fenris
|
||||||
*/
|
*/
|
||||||
declare type float = number;
|
type float = number;
|
||||||
declare var process: any;
|
declare var process: any;
|
||||||
declare var require: any;
|
declare var require: any;
|
||||||
declare class Buffer {
|
declare class Buffer {
|
||||||
|
|
@ -22,7 +22,7 @@ declare namespace lib_plankton.base {
|
||||||
/**
|
/**
|
||||||
* @author fenris
|
* @author fenris
|
||||||
*/
|
*/
|
||||||
declare type type_pseudopointer<type_value> = {
|
type type_pseudopointer<type_value> = {
|
||||||
value: type_value;
|
value: type_value;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
|
|
@ -229,9 +229,10 @@ declare namespace lib_plankton.base {
|
||||||
function object_merge(core: Record<string, any>, mantle: Record<string, any>): Record<string, any>;
|
function object_merge(core: Record<string, any>, mantle: Record<string, any>): Record<string, any>;
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
function buffer_show(buffer: Buffer, { "block_size": option_block_size, "break_char": option_break_char, }?: {
|
function buffer_show(buffer: Buffer, { "block_size": option_block_size, "break_char": option_break_char, "render_readable_characters": render_readable_characters, }?: {
|
||||||
block_size?: int;
|
block_size?: int;
|
||||||
break_char?: string;
|
break_char?: string;
|
||||||
|
render_readable_characters?: boolean;
|
||||||
}): string;
|
}): string;
|
||||||
}
|
}
|
||||||
declare module lib_plankton.pod {
|
declare module lib_plankton.pod {
|
||||||
|
|
@ -586,6 +587,10 @@ declare namespace lib_plankton.call {
|
||||||
* @author fenris
|
* @author fenris
|
||||||
*/
|
*/
|
||||||
export function timeout(procedure: (() => void), delay_in_seconds: float): int;
|
export function timeout(procedure: (() => void), delay_in_seconds: float): int;
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
export function loop(procedure: (() => void), delay_in_seconds: float): int;
|
||||||
/**
|
/**
|
||||||
* Promise version of "setTimeout"
|
* Promise version of "setTimeout"
|
||||||
*
|
*
|
||||||
|
|
@ -653,6 +658,9 @@ declare namespace lib_plankton.call {
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export function sleep(seconds: float): Promise<void>;
|
export function sleep(seconds: float): Promise<void>;
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function null_prop<type_value_from, type_value_to>(value_from: (null | type_value_from), function_: ((value: type_value_from) => type_value_to)): (null | type_value_to);
|
||||||
export {};
|
export {};
|
||||||
}
|
}
|
||||||
declare namespace lib_plankton.email {
|
declare namespace lib_plankton.email {
|
||||||
|
|
@ -2315,7 +2323,7 @@ declare namespace lib_plankton.storage.memory {
|
||||||
clear(): Promise<void>;
|
clear(): Promise<void>;
|
||||||
write(key: any, value: any): Promise<boolean>;
|
write(key: any, value: any): Promise<boolean>;
|
||||||
delete(key: any): Promise<void>;
|
delete(key: any): Promise<void>;
|
||||||
read(key: any): Promise<type_item>;
|
read(key: any): Promise<Awaited<type_item>>;
|
||||||
search(term: any): Promise<{
|
search(term: any): Promise<{
|
||||||
key: string;
|
key: string;
|
||||||
preview: string;
|
preview: string;
|
||||||
|
|
@ -3073,26 +3081,73 @@ declare namespace lib_plankton.session {
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
function begin(name: string, options?: {
|
function begin(name: string, { "lifetime": lifetime, "data": data, }?: {
|
||||||
lifetime?: int;
|
lifetime?: int;
|
||||||
data?: any;
|
data?: any;
|
||||||
}): Promise<string>;
|
}): Promise<string>;
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
function get(key: string): Promise<type_session>;
|
function get(key: string, { "prolongation": prolongation, }?: {
|
||||||
|
prolongation?: (null | int);
|
||||||
|
}): Promise<type_session>;
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
function end(key: string): Promise<void>;
|
function end(key: string): Promise<void>;
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
function setup(options?: {
|
function setup({ "key_length": key_length, "key_max_attempts": key_max_attempts, "default_lifetime": default_lifetime, "default_prolongation": default_prolongation, "data_chest": data_chest, "clear": clear, }?: {
|
||||||
key_length?: int;
|
key_length?: int;
|
||||||
key_max_attempts?: int;
|
key_max_attempts?: int;
|
||||||
default_lifetime?: int;
|
default_lifetime?: int;
|
||||||
|
default_prolongation?: (null | int);
|
||||||
data_chest?: lib_plankton.storage.type_chest<string, any, void, string, string>;
|
data_chest?: lib_plankton.storage.type_chest<string, any, void, string, string>;
|
||||||
clear?: boolean;
|
clear?: boolean;
|
||||||
}): Promise<void>;
|
}): Promise<void>;
|
||||||
}
|
}
|
||||||
|
declare namespace lib_plankton.base64 {
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
type type_source = string;
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
type type_target = string;
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
export function encode(source: type_source): type_target;
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
export function decode(target: type_target): type_source;
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
export function implementation_code(): lib_plankton.code.type_code<type_source, type_target>;
|
||||||
|
export {};
|
||||||
|
}
|
||||||
|
declare namespace lib_plankton.base64 {
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
class class_base64 implements lib_plankton.code.interface_code<string, string> {
|
||||||
|
/**
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
constructor();
|
||||||
|
/**
|
||||||
|
* @implementation
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
encode(x: string): string;
|
||||||
|
/**
|
||||||
|
* @implementation
|
||||||
|
* @author fenris
|
||||||
|
*/
|
||||||
|
decode(x: string): string;
|
||||||
|
}
|
||||||
|
}
|
||||||
declare namespace lib_plankton {
|
declare namespace lib_plankton {
|
||||||
namespace order {
|
namespace order {
|
||||||
/**
|
/**
|
||||||
|
|
@ -4077,7 +4132,7 @@ declare namespace lib_plankton.server {
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
function make(handle: ((input: string, metadata?: type_metadata) => Promise<string>), options?: {
|
function make(handle: ((input: string, metadata?: type_metadata) => Promise<string>), { "host": host, "port": port, "threshold": threshold, }?: {
|
||||||
host?: string;
|
host?: string;
|
||||||
port?: int;
|
port?: int;
|
||||||
threshold?: (null | float);
|
threshold?: (null | float);
|
||||||
|
|
@ -4501,50 +4556,6 @@ declare namespace lib_plankton.map.collatemap {
|
||||||
export function implementation_map<type_key, type_value>(subject: type_subject<type_key, type_value>): type_map<type_key, type_value>;
|
export function implementation_map<type_key, type_value>(subject: type_subject<type_key, type_value>): type_map<type_key, type_value>;
|
||||||
export {};
|
export {};
|
||||||
}
|
}
|
||||||
declare namespace lib_plankton.base64 {
|
|
||||||
/**
|
|
||||||
* @author fenris
|
|
||||||
*/
|
|
||||||
type type_source = string;
|
|
||||||
/**
|
|
||||||
* @author fenris
|
|
||||||
*/
|
|
||||||
type type_target = string;
|
|
||||||
/**
|
|
||||||
* @author fenris
|
|
||||||
*/
|
|
||||||
export function encode(source: type_source): type_target;
|
|
||||||
/**
|
|
||||||
* @author fenris
|
|
||||||
*/
|
|
||||||
export function decode(target: type_target): type_source;
|
|
||||||
/**
|
|
||||||
* @author fenris
|
|
||||||
*/
|
|
||||||
export function implementation_code(): lib_plankton.code.type_code<type_source, type_target>;
|
|
||||||
export {};
|
|
||||||
}
|
|
||||||
declare namespace lib_plankton.base64 {
|
|
||||||
/**
|
|
||||||
* @author fenris
|
|
||||||
*/
|
|
||||||
class class_base64 implements lib_plankton.code.interface_code<string, string> {
|
|
||||||
/**
|
|
||||||
* @author fenris
|
|
||||||
*/
|
|
||||||
constructor();
|
|
||||||
/**
|
|
||||||
* @implementation
|
|
||||||
* @author fenris
|
|
||||||
*/
|
|
||||||
encode(x: string): string;
|
|
||||||
/**
|
|
||||||
* @implementation
|
|
||||||
* @author fenris
|
|
||||||
*/
|
|
||||||
decode(x: string): string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
declare namespace lib_plankton.auth {
|
declare namespace lib_plankton.auth {
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
|
@ -4586,9 +4597,11 @@ declare namespace lib_plankton.auth.oidc {
|
||||||
type type_token = string;
|
type type_token = string;
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
type type_userinfo = {
|
export type type_userinfo = {
|
||||||
name: (null | string);
|
name: (null | string);
|
||||||
|
label: (null | string);
|
||||||
email: (null | string);
|
email: (null | string);
|
||||||
|
groups: (null | Array<string>);
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
70
misc/conf-example.json
Normal file
70
misc/conf-example.json
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"log": [
|
||||||
|
{
|
||||||
|
"kind": "file",
|
||||||
|
"data": {
|
||||||
|
"path": "/tmp/zeitbild/log.jsonl",
|
||||||
|
"threshold": "info",
|
||||||
|
"format": "jsonl_structured"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "stdout",
|
||||||
|
"data": {
|
||||||
|
"threshold": "info",
|
||||||
|
"format": "jsonl_structured"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"session_management": {
|
||||||
|
"in_memory": false,
|
||||||
|
"lifetime": 3600
|
||||||
|
},
|
||||||
|
"authentication": {
|
||||||
|
"kind": "internal",
|
||||||
|
"data": {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"database": {
|
||||||
|
"kind": "sqlite",
|
||||||
|
"data": {
|
||||||
|
"path": "../zeitbild.sqlite"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"caldav": {
|
||||||
|
"address": "http://localhost:8000/calendars/-/demo",
|
||||||
|
"username": "demo-{{username}}",
|
||||||
|
"password": "{{password}}",
|
||||||
|
"setup_hints": [
|
||||||
|
{
|
||||||
|
"label": "Liste von Clients",
|
||||||
|
"link": "https://devguide.calconnect.org/CalDAV/Client-Implementations/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Android",
|
||||||
|
"link": "https://goneuland.de/davx5-android-kalender-per-caldav-integrieren-z-b-nextcloud/",
|
||||||
|
"remark": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "iOS",
|
||||||
|
"link": "https://all-inkl.com/wichtig/anleitungen/programme/e-mail/caldav-kalenderfunktion/ios-mail_460.html",
|
||||||
|
"remark": "eigentlich für Server 'all-inkl.com' — Zugangsdaten müssen entsprechend geändert werden"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Thunderbird",
|
||||||
|
"link": "https://www.uni-bielefeld.de/einrichtungen/bits/services/kuz/e-mail-und-kalender/anleitung/kalender-konfiguration-unter-thunderbird/",
|
||||||
|
"remark": "eigentlich für Server 'uni-bielefeld.de' — Zugangsdaten müssen entsprechend geändert werden"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Evolution",
|
||||||
|
"link": "https://help.gnome.org/users/evolution/stable/calendar-caldav.html.de"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "MS Outlook",
|
||||||
|
"link": "https://all-inkl.com/wichtig/anleitungen/programme/e-mail/caldav-kalenderfunktion/outlook-2019_468.html",
|
||||||
|
"remark": "eigentlich für Server 'all-inkl.com' — Zugangsdaten müssen entsprechend geändert werden"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
328
misc/data-house_and_garden.json
Normal file
328
misc/data-house_and_garden.json
Normal file
|
|
@ -0,0 +1,328 @@
|
||||||
|
{
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "gaertner",
|
||||||
|
"label": "Gärtner"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "bewohner",
|
||||||
|
"label": "Bewohner"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "alice",
|
||||||
|
"groups": [1],
|
||||||
|
"email_address": "alice@example.org",
|
||||||
|
"dav_token": null,
|
||||||
|
"password": "alice"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "bob",
|
||||||
|
"groups": [2],
|
||||||
|
"email_address": "bob@example.org",
|
||||||
|
"dav_token": null,
|
||||||
|
"password": "bob"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "charlie",
|
||||||
|
"groups": [1, 2],
|
||||||
|
"email_address": "charlie@example.org",
|
||||||
|
"dav_token": "charlie_dav",
|
||||||
|
"password": "charlie"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"calendars": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Feiertage",
|
||||||
|
"hue": 0.0000,
|
||||||
|
"access": {
|
||||||
|
"public": true,
|
||||||
|
"default_level": "view",
|
||||||
|
"attributed_group": [
|
||||||
|
],
|
||||||
|
"attributed_user": [
|
||||||
|
{
|
||||||
|
"user_id": 3,
|
||||||
|
"level": "admin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"kind": "local",
|
||||||
|
"data": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"name": "Tag des Wassers",
|
||||||
|
"begin": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": [0]},
|
||||||
|
{"action": "shift_week", "args": [-1]},
|
||||||
|
{"action": "shift_day", "args": [1]}
|
||||||
|
],
|
||||||
|
"time": null
|
||||||
|
},
|
||||||
|
"end": null,
|
||||||
|
"location": null,
|
||||||
|
"description": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tag der Erde",
|
||||||
|
"begin": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": [0]},
|
||||||
|
{"action": "shift_week", "args": [0]},
|
||||||
|
{"action": "shift_day", "args": [6]}
|
||||||
|
],
|
||||||
|
"time": null
|
||||||
|
},
|
||||||
|
"end": null,
|
||||||
|
"location": null,
|
||||||
|
"description": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tag der Luft",
|
||||||
|
"begin": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": [0]},
|
||||||
|
{"action": "shift_week", "args": [2]},
|
||||||
|
{"action": "shift_day", "args": [4]}
|
||||||
|
],
|
||||||
|
"time": null
|
||||||
|
},
|
||||||
|
"end": null,
|
||||||
|
"location": null,
|
||||||
|
"description": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Garten",
|
||||||
|
"hue": 0.3333,
|
||||||
|
"access": {
|
||||||
|
"public": false,
|
||||||
|
"default_level": "none",
|
||||||
|
"attributed_group": [
|
||||||
|
{
|
||||||
|
"group_id": 1,
|
||||||
|
"level": "view"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attributed_user": [
|
||||||
|
{
|
||||||
|
"user_id": 1,
|
||||||
|
"level": "admin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"kind": "local",
|
||||||
|
"data": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"name": "Unkraut jähten",
|
||||||
|
"begin": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [-1]},
|
||||||
|
{"action": "shift_day", "args": [3]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 10, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [-1]},
|
||||||
|
{"action": "shift_day", "args": [3]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 11, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"location": "Drosselweg 7",
|
||||||
|
"description": "Giersch und Rainkohl haben wider gewuchert"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Holz hacken",
|
||||||
|
"begin": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [0]},
|
||||||
|
{"action": "shift_day", "args": [0]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 13, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [0]},
|
||||||
|
{"action": "shift_day", "args": [0]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 14, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"location": "Drosselweg 7",
|
||||||
|
"description": "die alte Birke ist bereits in Segmente geschnitten und muss nun noch in handliche Scheite zerlegt werden"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Blumen gießen",
|
||||||
|
"begin": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [1]},
|
||||||
|
{"action": "shift_day", "args": [2]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 18, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [1]},
|
||||||
|
{"action": "shift_day", "args": [2]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 19, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"location": "Drosselweg 7",
|
||||||
|
"description": "erst in der Abendzeit, damit die Sonne tief steht und das Gießen sich lohnt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Grill-Fete",
|
||||||
|
"begin": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [1]},
|
||||||
|
{"action": "shift_day", "args": [5]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 18, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [1]},
|
||||||
|
{"action": "shift_day", "args": [5]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 23, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"location": "Drosselweg 7",
|
||||||
|
"description": "Grillgut ist selbst mitzubringen; Getränke sind bereits vor Ort"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "Haus",
|
||||||
|
"hue": 0.6667,
|
||||||
|
"access": {
|
||||||
|
"public": false,
|
||||||
|
"default_level": "none",
|
||||||
|
"attributed_group": [
|
||||||
|
{
|
||||||
|
"group_id": 2,
|
||||||
|
"level": "view"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attributed_user": [
|
||||||
|
{
|
||||||
|
"user_id": 2,
|
||||||
|
"level": "admin"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"kind": "local",
|
||||||
|
"data": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"name": "Bad putzen",
|
||||||
|
"begin": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [-1]},
|
||||||
|
{"action": "shift_day", "args": [4]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 8, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [-1]},
|
||||||
|
{"action": "shift_day", "args": [4]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 9, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"location": "Eschenhain 11",
|
||||||
|
"description": "das Waschbecken sollte unbedingt von Keimen befreit werden"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Abstellkammer aufräumen",
|
||||||
|
"begin": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [1]},
|
||||||
|
{"action": "shift_day", "args": [0]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 12, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [1]},
|
||||||
|
{"action": "shift_day", "args": [0]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 13, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"location": "Eschenhain 11",
|
||||||
|
"description": "einiges davon kann vermutlich gleich entsorgt werden"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Treppe wischen",
|
||||||
|
"begin": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [2]},
|
||||||
|
{"action": "shift_day", "args": [1]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 14, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"timezone_shift": 0,
|
||||||
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [2]},
|
||||||
|
{"action": "shift_day", "args": [1]}
|
||||||
|
],
|
||||||
|
"time": {"hour": 15, "minute": 0, "second": 0}
|
||||||
|
},
|
||||||
|
"location": "Eschenhain 11",
|
||||||
|
"description": "die dreckigen Schuhe haben ihre Spuren hinterlassen"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -4,18 +4,21 @@
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "alice",
|
"name": "alice",
|
||||||
"email_address": "alice@example.org",
|
"email_address": "alice@example.org",
|
||||||
|
"dav_token": null,
|
||||||
"password": "alice"
|
"password": "alice"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"name": "bob",
|
"name": "bob",
|
||||||
"email_address": "bob@example.org",
|
"email_address": "bob@example.org",
|
||||||
|
"dav_token": "bob_dav",
|
||||||
"password": "bob"
|
"password": "bob"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"name": "charlie",
|
"name": "charlie",
|
||||||
"email_address": "charlie@example.org",
|
"email_address": "charlie@example.org",
|
||||||
|
"dav_token": null,
|
||||||
"password": "charlie"
|
"password": "charlie"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -23,7 +26,9 @@
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "LV Lampukistan",
|
"name": "LV Lampukistan",
|
||||||
|
"hue": 0.0000,
|
||||||
"access": {
|
"access": {
|
||||||
|
"public": true,
|
||||||
"default_level": "view",
|
"default_level": "view",
|
||||||
"attributed": [
|
"attributed": [
|
||||||
{
|
{
|
||||||
|
|
@ -44,12 +49,20 @@
|
||||||
"name": "Aufstand: Mieten",
|
"name": "Aufstand: Mieten",
|
||||||
"begin": {
|
"begin": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 9, "day": 14},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [-1]},
|
||||||
|
{"action": "shift_day", "args": [6]}
|
||||||
|
],
|
||||||
"time": {"hour": 12, "minute": 0, "second": 0}
|
"time": {"hour": 12, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"end": {
|
"end": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 9, "day": 14},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [-1]},
|
||||||
|
{"action": "shift_day", "args": [6]}
|
||||||
|
],
|
||||||
"time": {"hour": 15, "minute": 0, "second": 0}
|
"time": {"hour": 15, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"location": "Porada Ninfu, Haupt-Markt",
|
"location": "Porada Ninfu, Haupt-Markt",
|
||||||
|
|
@ -59,12 +72,20 @@
|
||||||
"name": "Aufstand: Waffen",
|
"name": "Aufstand: Waffen",
|
||||||
"begin": {
|
"begin": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 9, "day": 21},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [0]},
|
||||||
|
{"action": "shift_day", "args": [6]}
|
||||||
|
],
|
||||||
"time": {"hour": 12, "minute": 0, "second": 0}
|
"time": {"hour": 12, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"end": {
|
"end": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 9, "day": 21},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [0]},
|
||||||
|
{"action": "shift_day", "args": [6]}
|
||||||
|
],
|
||||||
"time": {"hour": 15, "minute": 0, "second": 0}
|
"time": {"hour": 15, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"location": "Tandreell, Stoiber-Platz",
|
"location": "Tandreell, Stoiber-Platz",
|
||||||
|
|
@ -74,12 +95,20 @@
|
||||||
"name": "Aufstand: Essen",
|
"name": "Aufstand: Essen",
|
||||||
"begin": {
|
"begin": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 10, "day": 28},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [1]},
|
||||||
|
{"action": "shift_day", "args": [6]}
|
||||||
|
],
|
||||||
"time": {"hour": 12, "minute": 0, "second": 0}
|
"time": {"hour": 12, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"end": {
|
"end": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 10, "day": 28},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [1]},
|
||||||
|
{"action": "shift_day", "args": [6]}
|
||||||
|
],
|
||||||
"time": {"hour": 15, "minute": 0, "second": 0}
|
"time": {"hour": 15, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"location": "Kawanda, Nord-Bahnhof",
|
"location": "Kawanda, Nord-Bahnhof",
|
||||||
|
|
@ -92,7 +121,9 @@
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"name": "KV Zepettel-Region",
|
"name": "KV Zepettel-Region",
|
||||||
|
"hue": 0.3333,
|
||||||
"access": {
|
"access": {
|
||||||
|
"public": false,
|
||||||
"default_level": "view",
|
"default_level": "view",
|
||||||
"attributed": [
|
"attributed": [
|
||||||
{
|
{
|
||||||
|
|
@ -113,12 +144,20 @@
|
||||||
"name": "Feier: Bier",
|
"name": "Feier: Bier",
|
||||||
"begin": {
|
"begin": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 9, "day": 18},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [0]},
|
||||||
|
{"action": "shift_day", "args": [3]}
|
||||||
|
],
|
||||||
"time": {"hour": 18, "minute": 0, "second": 0}
|
"time": {"hour": 18, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"end": {
|
"end": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 9, "day": 18},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [0]},
|
||||||
|
{"action": "shift_day", "args": [3]}
|
||||||
|
],
|
||||||
"time": {"hour": 23, "minute": 0, "second": 0}
|
"time": {"hour": 23, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"location": "Rudschadinedschad, Schlamm-Park",
|
"location": "Rudschadinedschad, Schlamm-Park",
|
||||||
|
|
@ -128,12 +167,20 @@
|
||||||
"name": "Feier: Schnapps",
|
"name": "Feier: Schnapps",
|
||||||
"begin": {
|
"begin": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 10, "day": 1},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [2]},
|
||||||
|
{"action": "shift_day", "args": [2]}
|
||||||
|
],
|
||||||
"time": {"hour": 18, "minute": 0, "second": 0}
|
"time": {"hour": 18, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"end": {
|
"end": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 10, "day": 1},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [2]},
|
||||||
|
{"action": "shift_day", "args": [2]}
|
||||||
|
],
|
||||||
"time": {"hour": 23, "minute": 0, "second": 0}
|
"time": {"hour": 23, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"location": "Kawanda, Ratten-Platz",
|
"location": "Kawanda, Ratten-Platz",
|
||||||
|
|
@ -146,7 +193,9 @@
|
||||||
{
|
{
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"name": "OV Kawanda",
|
"name": "OV Kawanda",
|
||||||
|
"hue": 0.6667,
|
||||||
"access": {
|
"access": {
|
||||||
|
"public": false,
|
||||||
"default_level": "view",
|
"default_level": "view",
|
||||||
"attributed": [
|
"attributed": [
|
||||||
{
|
{
|
||||||
|
|
@ -163,12 +212,20 @@
|
||||||
"name": "Aufräumen: Flaschen",
|
"name": "Aufräumen: Flaschen",
|
||||||
"begin": {
|
"begin": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 9, "day": 24},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [1]},
|
||||||
|
{"action": "shift_day", "args": [2]}
|
||||||
|
],
|
||||||
"time": {"hour": 15, "minute": 0, "second": 0}
|
"time": {"hour": 15, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"end": {
|
"end": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 9, "day": 24},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [1]},
|
||||||
|
{"action": "shift_day", "args": [2]}
|
||||||
|
],
|
||||||
"time": {"hour": 17, "minute": 0, "second": 0}
|
"time": {"hour": 17, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"location": "Kawanda, Penner-Allee",
|
"location": "Kawanda, Penner-Allee",
|
||||||
|
|
@ -181,7 +238,9 @@
|
||||||
{
|
{
|
||||||
"id": 4,
|
"id": 4,
|
||||||
"name": "KV Zepettel-Region | intern",
|
"name": "KV Zepettel-Region | intern",
|
||||||
|
"hue": 0.8333,
|
||||||
"access": {
|
"access": {
|
||||||
|
"public": false,
|
||||||
"default_level": "none",
|
"default_level": "none",
|
||||||
"attributed": [
|
"attributed": [
|
||||||
{
|
{
|
||||||
|
|
@ -202,12 +261,20 @@
|
||||||
"name": "Infostand",
|
"name": "Infostand",
|
||||||
"begin": {
|
"begin": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 9, "day": 16},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [0]},
|
||||||
|
{"action": "shift_day", "args": [1]}
|
||||||
|
],
|
||||||
"time": {"hour": 10, "minute": 0, "second": 0}
|
"time": {"hour": 10, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"end": {
|
"end": {
|
||||||
"timezone_shift": 2,
|
"timezone_shift": 2,
|
||||||
"date": {"year": 2025, "month": 9, "day": 16},
|
"date_relative": [
|
||||||
|
{"action": "trunc_week", "args": []},
|
||||||
|
{"action": "shift_week", "args": [0]},
|
||||||
|
{"action": "shift_day", "args": [1]}
|
||||||
|
],
|
||||||
"time": {"hour": 14, "minute": 0, "second": 0}
|
"time": {"hour": 14, "minute": 0, "second": 0}
|
||||||
},
|
},
|
||||||
"location": "Rudschadinedschad, Schabracken-Markt",
|
"location": "Rudschadinedschad, Schabracken-Markt",
|
||||||
13
misc/data-minimal.json
Normal file
13
misc/data-minimal.json
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "user",
|
||||||
|
"email_address": "user@example.org",
|
||||||
|
"dav_token": null,
|
||||||
|
"password": "user"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"calendars": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Beschreibung
|
## Beschreibung
|
||||||
|
|
||||||
- Hintergrund-Dienst für Kalendar-Erstellung und -Darstellung
|
- Hintergrund-Dienst für [zeitbild](/zeitbild/meta)
|
||||||
|
|
||||||
|
|
||||||
## Erstellung
|
## Erstellung
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
@ -14,7 +33,13 @@ namespace _zeitbild.api
|
||||||
access : {
|
access : {
|
||||||
public : boolean;
|
public : boolean;
|
||||||
default_level : string;
|
default_level : string;
|
||||||
attributed : Array<
|
attributed_group : Array<
|
||||||
|
{
|
||||||
|
group_id : int;
|
||||||
|
level : string;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
attributed_user : Array<
|
||||||
{
|
{
|
||||||
user_id : int;
|
user_id : int;
|
||||||
level : string;
|
level : string;
|
||||||
|
|
@ -29,14 +54,14 @@ namespace _zeitbild.api
|
||||||
}
|
}
|
||||||
|
|
|
|
||||||
{
|
{
|
||||||
kind : "caldav";
|
kind : "ics_feed";
|
||||||
data : {
|
data : {
|
||||||
url : string;
|
url : string;
|
||||||
read_only : boolean;
|
|
||||||
from_fucked_up_wordpress : boolean;
|
from_fucked_up_wordpress : boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
hue : float;
|
||||||
},
|
},
|
||||||
int
|
int
|
||||||
>(
|
>(
|
||||||
|
|
@ -75,12 +100,11 @@ namespace _zeitbild.api
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "caldav": {
|
case "ics_feed": {
|
||||||
resource_object = {
|
resource_object = {
|
||||||
"kind": "caldav",
|
"kind": "ics_feed",
|
||||||
"data": {
|
"data": {
|
||||||
"url": stuff.input.resource.data.url,
|
"url": stuff.input.resource.data.url,
|
||||||
"read_only": stuff.input.resource.data.read_only,
|
|
||||||
"from_fucked_up_wordpress": stuff.input.resource.data.from_fucked_up_wordpress,
|
"from_fucked_up_wordpress": stuff.input.resource.data.from_fucked_up_wordpress,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -94,17 +118,33 @@ namespace _zeitbild.api
|
||||||
"name": stuff.input.name,
|
"name": stuff.input.name,
|
||||||
"access": {
|
"access": {
|
||||||
"public": stuff.input.access.public,
|
"public": stuff.input.access.public,
|
||||||
"default_level": _zeitbild.value_object.access_level.from_string(stuff.input.access.default_level),
|
"default_level": _zeitbild.access_level_from_string(stuff.input.access.default_level),
|
||||||
"attributed": lib_plankton.map.hashmap.implementation_map(
|
"attributed_group": lib_plankton.map.hashmap.implementation_map(
|
||||||
lib_plankton.map.hashmap.make(
|
lib_plankton.map.hashmap.make(
|
||||||
x => x.toFixed(0),
|
x => x.toFixed(0),
|
||||||
{
|
{
|
||||||
"pairs": (
|
"pairs": (
|
||||||
stuff.input.access.attributed
|
stuff.input.access.attributed_group
|
||||||
|
.map(
|
||||||
|
(entry) => ({
|
||||||
|
"key": entry.group_id,
|
||||||
|
"value": _zeitbild.access_level_from_string(entry.level),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"attributed_user": lib_plankton.map.hashmap.implementation_map(
|
||||||
|
lib_plankton.map.hashmap.make(
|
||||||
|
x => x.toFixed(0),
|
||||||
|
{
|
||||||
|
"pairs": (
|
||||||
|
stuff.input.access.attributed_user
|
||||||
.map(
|
.map(
|
||||||
(entry) => ({
|
(entry) => ({
|
||||||
"key": entry.user_id,
|
"key": entry.user_id,
|
||||||
"value": _zeitbild.value_object.access_level.from_string(entry.level),
|
"value": _zeitbild.access_level_from_string(entry.level),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.concat(
|
.concat(
|
||||||
|
|
@ -120,7 +160,8 @@ namespace _zeitbild.api
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
"resource_id": resource_id
|
"resource_id": resource_id,
|
||||||
|
"hue": stuff.input.hue,
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
_zeitbild.service.calendar.add(calendar_object)
|
_zeitbild.service.calendar.add(calendar_object)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
@ -11,13 +30,20 @@ namespace _zeitbild.api
|
||||||
register<
|
register<
|
||||||
{
|
{
|
||||||
name : string;
|
name : string;
|
||||||
|
hue : float;
|
||||||
access : {
|
access : {
|
||||||
public : boolean;
|
public : boolean;
|
||||||
default_level : ("none" | "view" | "edit" | "admin");
|
default_level : string;
|
||||||
attributed : Array<
|
attributed_group : Array<
|
||||||
|
{
|
||||||
|
group_id : int;
|
||||||
|
level : string;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
attributed_user : Array<
|
||||||
{
|
{
|
||||||
user_id : int;
|
user_id : int;
|
||||||
level : ("none" | "view" | "edit" | "admin");
|
level : string;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
|
|
@ -54,19 +80,36 @@ namespace _zeitbild.api
|
||||||
);
|
);
|
||||||
const calendar_object_new : _zeitbild.type_calendar_object = {
|
const calendar_object_new : _zeitbild.type_calendar_object = {
|
||||||
"name": stuff.input.name,
|
"name": stuff.input.name,
|
||||||
|
"hue": stuff.input.hue,
|
||||||
"access": {
|
"access": {
|
||||||
"public": stuff.input.access.public,
|
"public": stuff.input.access.public,
|
||||||
"default_level": _zeitbild.value_object.access_level.from_string(stuff.input.access.default_level),
|
"default_level": _zeitbild.access_level_from_string(stuff.input.access.default_level),
|
||||||
"attributed": lib_plankton.map.hashmap.implementation_map(
|
"attributed_group": lib_plankton.map.hashmap.implementation_map(
|
||||||
lib_plankton.map.hashmap.make(
|
lib_plankton.map.hashmap.make(
|
||||||
x => x.toFixed(0),
|
x => x.toFixed(0),
|
||||||
{
|
{
|
||||||
"pairs": (
|
"pairs": (
|
||||||
stuff.input.access.attributed
|
stuff.input.access.attributed_group
|
||||||
|
.map(
|
||||||
|
(entry) => ({
|
||||||
|
"key": entry.group_id,
|
||||||
|
"value": _zeitbild.access_level_from_string(entry.level),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"attributed_user": lib_plankton.map.hashmap.implementation_map(
|
||||||
|
lib_plankton.map.hashmap.make(
|
||||||
|
x => x.toFixed(0),
|
||||||
|
{
|
||||||
|
"pairs": (
|
||||||
|
stuff.input.access.attributed_user
|
||||||
.map(
|
.map(
|
||||||
(entry) => ({
|
(entry) => ({
|
||||||
"key": entry.user_id,
|
"key": entry.user_id,
|
||||||
"value": _zeitbild.value_object.access_level.from_string(entry.level),
|
"value": _zeitbild.access_level_from_string(entry.level),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
@ -74,7 +117,7 @@ namespace _zeitbild.api
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
"resource_id": calendar_object_old.resource_id
|
"resource_id": calendar_object_old.resource_id,
|
||||||
};
|
};
|
||||||
await _zeitbild.service.calendar.change(
|
await _zeitbild.service.calendar.change(
|
||||||
calendar_id,
|
calendar_id,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
@ -11,7 +30,10 @@ namespace _zeitbild.api
|
||||||
register<
|
register<
|
||||||
_zeitbild.type_event_object, // TODO aufdröseln
|
_zeitbild.type_event_object, // TODO aufdröseln
|
||||||
(
|
(
|
||||||
null
|
{
|
||||||
|
local_resource_event_id : (null | int);
|
||||||
|
hash : string;
|
||||||
|
}
|
||||||
|
|
|
|
||||||
string
|
string
|
||||||
)
|
)
|
||||||
|
|
@ -69,10 +91,12 @@ namespace _zeitbild.api
|
||||||
const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff);
|
const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff);
|
||||||
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(session.value.name);
|
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(session.value.name);
|
||||||
|
|
||||||
if (stuff.input === null) {
|
if (stuff.input === null)
|
||||||
|
{
|
||||||
return Promise.reject(new Error("impossible"));
|
return Promise.reject(new Error("impossible"));
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
return (
|
return (
|
||||||
_zeitbild.service.calendar.event_add(
|
_zeitbild.service.calendar.event_add(
|
||||||
parseInt(stuff.path_parameters["calendar_id"]),
|
parseInt(stuff.path_parameters["calendar_id"]),
|
||||||
|
|
@ -80,9 +104,9 @@ namespace _zeitbild.api
|
||||||
user_id
|
user_id
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
() => Promise.resolve({
|
(data) => Promise.resolve({
|
||||||
"status_code": 200,
|
"status_code": 200,
|
||||||
"data": null,
|
"data": data,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
// TODO distinguish
|
// TODO distinguish
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
@ -69,10 +88,12 @@ namespace _zeitbild.api
|
||||||
const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff);
|
const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff);
|
||||||
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(session.value.name);
|
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(session.value.name);
|
||||||
|
|
||||||
if (stuff.input === null) {
|
if (stuff.input === null)
|
||||||
|
{
|
||||||
return Promise.reject(new Error("impossible"));
|
return Promise.reject(new Error("impossible"));
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
return (
|
return (
|
||||||
_zeitbild.service.calendar.event_change(
|
_zeitbild.service.calendar.event_change(
|
||||||
parseInt(stuff.path_parameters["calendar_id"]),
|
parseInt(stuff.path_parameters["calendar_id"]),
|
||||||
|
|
@ -81,17 +102,21 @@ namespace _zeitbild.api
|
||||||
user_id
|
user_id
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
() => Promise.resolve({
|
() => Promise.resolve(
|
||||||
|
{
|
||||||
"status_code": 200,
|
"status_code": 200,
|
||||||
"data": null,
|
"data": null,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
// TODO distinguish
|
// TODO distinguish
|
||||||
.catch(
|
.catch(
|
||||||
(reason) => Promise.resolve({
|
(reason) => Promise.resolve(
|
||||||
|
{
|
||||||
"status_code": 403,
|
"status_code": 403,
|
||||||
"data": String(reason),
|
"data": String(reason),
|
||||||
})
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
@ -12,14 +31,22 @@ namespace _zeitbild.api
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
name : string;
|
name : string;
|
||||||
|
hue : float;
|
||||||
access : {
|
access : {
|
||||||
|
public : boolean;
|
||||||
default_level : string;
|
default_level : string;
|
||||||
attributed : Array<
|
attributed_group : Array<
|
||||||
|
{
|
||||||
|
group_id : int;
|
||||||
|
level : string;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
attributed_user : Array<
|
||||||
{
|
{
|
||||||
user_id : int;
|
user_id : int;
|
||||||
level : string;
|
level : string;
|
||||||
}
|
}
|
||||||
>
|
>;
|
||||||
};
|
};
|
||||||
resource_id : int;
|
resource_id : int;
|
||||||
}
|
}
|
||||||
|
|
@ -44,11 +71,27 @@ namespace _zeitbild.api
|
||||||
);
|
);
|
||||||
const result = {
|
const result = {
|
||||||
"name": calendar_object.name,
|
"name": calendar_object.name,
|
||||||
|
"hue": calendar_object.hue,
|
||||||
"access": {
|
"access": {
|
||||||
"public": calendar_object.access.public,
|
"public": calendar_object.access.public,
|
||||||
"default_level": _zeitbild.api.access_level_encode(calendar_object.access.default_level),
|
"default_level": _zeitbild.api.access_level_encode(calendar_object.access.default_level),
|
||||||
"attributed": lib_plankton.call.convey(
|
"attributed_group": lib_plankton.call.convey(
|
||||||
calendar_object.access.attributed,
|
calendar_object.access.attributed_group,
|
||||||
|
[
|
||||||
|
lib_plankton.map.dump,
|
||||||
|
(pairs : Array<{key : _zeitbild.type_group_id; value : _zeitbild.enum_access_level;}>) => (
|
||||||
|
pairs
|
||||||
|
.map(
|
||||||
|
(pair : {key : _zeitbild.type_group_id; value : _zeitbild.enum_access_level;}) => ({
|
||||||
|
"group_id": pair.key,
|
||||||
|
"level": _zeitbild.api.access_level_encode(pair.value)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"attributed_user": lib_plankton.call.convey(
|
||||||
|
calendar_object.access.attributed_user,
|
||||||
[
|
[
|
||||||
lib_plankton.map.dump,
|
lib_plankton.map.dump,
|
||||||
(pairs : Array<{key : _zeitbild.type_user_id; value : _zeitbild.enum_access_level;}>) => (
|
(pairs : Array<{key : _zeitbild.type_user_id; value : _zeitbild.enum_access_level;}>) => (
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
@ -14,6 +33,7 @@ namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
id : int;
|
id : int;
|
||||||
name : string;
|
name : string;
|
||||||
|
hue : float;
|
||||||
access_level : string;
|
access_level : string;
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
@ -40,6 +60,10 @@ namespace _zeitbild.api
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": false,
|
"nullable": false,
|
||||||
},
|
},
|
||||||
|
"hue": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
"access_level": {
|
"access_level": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
|
|
@ -49,8 +73,8 @@ namespace _zeitbild.api
|
||||||
"required": [
|
"required": [
|
||||||
"id",
|
"id",
|
||||||
"name",
|
"name",
|
||||||
"public",
|
"hue",
|
||||||
"role",
|
"access_level",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
@ -76,7 +100,8 @@ namespace _zeitbild.api
|
||||||
(entry) => ({
|
(entry) => ({
|
||||||
"id": entry.id,
|
"id": entry.id,
|
||||||
"name": entry.name,
|
"name": entry.name,
|
||||||
"access_level": _zeitbild.value_object.access_level.to_string(entry.access_level),
|
"hue": entry.hue,
|
||||||
|
"access_level": _zeitbild.access_level_to_string(entry.access_level),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
@ -13,8 +32,10 @@ namespace _zeitbild.api
|
||||||
(
|
(
|
||||||
Array<
|
Array<
|
||||||
{
|
{
|
||||||
|
hash : _zeitbild.type_event_hash;
|
||||||
calendar_id : int;
|
calendar_id : int;
|
||||||
calendar_name : string;
|
calendar_name : string;
|
||||||
|
hue : float;
|
||||||
access_level : string;
|
access_level : string;
|
||||||
event_id : (null | int);
|
event_id : (null | int);
|
||||||
event_object : _zeitbild.type_event_object;
|
event_object : _zeitbild.type_event_object;
|
||||||
|
|
@ -53,6 +74,10 @@ namespace _zeitbild.api
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"hash": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
"calendar_id": {
|
"calendar_id": {
|
||||||
"nullable": false,
|
"nullable": false,
|
||||||
"type": "number",
|
"type": "number",
|
||||||
|
|
@ -61,6 +86,10 @@ namespace _zeitbild.api
|
||||||
"nullable": false,
|
"nullable": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
|
"hue": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
"access_level": {
|
"access_level": {
|
||||||
"nullable": false,
|
"nullable": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|
@ -167,8 +196,11 @@ namespace _zeitbild.api
|
||||||
data
|
data
|
||||||
.map(
|
.map(
|
||||||
(entry) => ({
|
(entry) => ({
|
||||||
|
// todo
|
||||||
|
"hash": entry.hash,
|
||||||
"calendar_id": entry.calendar_id,
|
"calendar_id": entry.calendar_id,
|
||||||
"calendar_name": entry.calendar_name,
|
"calendar_name": entry.calendar_name,
|
||||||
|
"hue": entry.hue,
|
||||||
"access_level": _zeitbild.api.access_level_encode(entry.access_level),
|
"access_level": _zeitbild.api.access_level_encode(entry.access_level),
|
||||||
"event_id": entry.event_id,
|
"event_id": entry.event_id,
|
||||||
"event_object": entry.event_object,
|
"event_object": entry.event_object,
|
||||||
|
|
|
||||||
146
source/api/actions/export_ics.ts
Normal file
146
source/api/actions/export_ics.ts
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace _zeitbild.api
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function register_export_ics(
|
||||||
|
rest_subject : lib_plankton.rest_http.type_rest
|
||||||
|
) : void
|
||||||
|
{
|
||||||
|
register<
|
||||||
|
null,
|
||||||
|
(
|
||||||
|
lib_plankton.ical.type_vcalendar
|
||||||
|
|
|
||||||
|
string
|
||||||
|
)
|
||||||
|
>(
|
||||||
|
rest_subject,
|
||||||
|
lib_plankton.http.enum_method.get,
|
||||||
|
"/export/ics",
|
||||||
|
{
|
||||||
|
"description": "trägt Veranstaltungen aus verschiedenen Kalendern zusammen im ics-Format",
|
||||||
|
"query_parameters": () => ([
|
||||||
|
{
|
||||||
|
"name": "from",
|
||||||
|
"required": false,
|
||||||
|
"description": "UNIX timestamp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "to",
|
||||||
|
"required": false,
|
||||||
|
"description": "UNIX timestamp",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "calendar_ids",
|
||||||
|
"required": false,
|
||||||
|
"description": "comma separated",
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
"output_schema": () => ({
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string",
|
||||||
|
}),
|
||||||
|
"response_body_mimetype": "text/calendar",
|
||||||
|
"response_body_encode": (output) => Buffer.from(
|
||||||
|
(typeof(output) === "string")
|
||||||
|
?
|
||||||
|
output
|
||||||
|
:
|
||||||
|
lib_plankton.ical.ics_encode(output)
|
||||||
|
),
|
||||||
|
"restriction": restriction_web_auth,
|
||||||
|
"execution": async (stuff) => {
|
||||||
|
const user : {id : _zeitbild.type_user_id; object : _zeitbild.type_user_object;} = await _zeitbild.api.user_from_web_auth(stuff);
|
||||||
|
|
||||||
|
const from : lib_plankton.pit.type_pit = (
|
||||||
|
("from" in stuff.query_parameters)
|
||||||
|
?
|
||||||
|
parseInt(stuff.query_parameters["from"])
|
||||||
|
:
|
||||||
|
lib_plankton.pit.shift_week(
|
||||||
|
lib_plankton.pit.now(),
|
||||||
|
-2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const to : lib_plankton.pit.type_pit = (
|
||||||
|
("to" in stuff.query_parameters)
|
||||||
|
?
|
||||||
|
parseInt(stuff.query_parameters["to"])
|
||||||
|
:
|
||||||
|
lib_plankton.pit.shift_week(
|
||||||
|
lib_plankton.pit.now(),
|
||||||
|
+6
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const calendar_ids_wanted : (null | Array<_zeitbild.type_calendar_id>) = (
|
||||||
|
(
|
||||||
|
("calendar_ids" in stuff.query_parameters)
|
||||||
|
&&
|
||||||
|
(stuff.query_parameters["calendar_ids"] !== null)
|
||||||
|
)
|
||||||
|
?
|
||||||
|
lib_plankton.call.convey(
|
||||||
|
stuff.query_parameters["calendar_ids"],
|
||||||
|
[
|
||||||
|
(x : string) => x.split(","),
|
||||||
|
(x : Array<string>) => x.map(parseInt),
|
||||||
|
(x : Array<int>) => x.filter(y => (! isNaN(y)))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
:
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
_zeitbild.service.calendar.gather_events(
|
||||||
|
calendar_ids_wanted,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
user.id
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
(events_extended) => Promise.resolve(
|
||||||
|
{
|
||||||
|
"status_code": 200,
|
||||||
|
"data": _zeitbild.helpers.icalendar_vcalendar_from_own_event_list(
|
||||||
|
events_extended
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.catch(
|
||||||
|
(reason) => Promise.resolve(
|
||||||
|
{
|
||||||
|
"status_code": 403,
|
||||||
|
"data": String(reason),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
110
source/api/actions/group_list.ts
Normal file
110
source/api/actions/group_list.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace _zeitbild.api
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function register_group_list(
|
||||||
|
rest_subject : lib_plankton.rest_http.type_rest
|
||||||
|
) : void
|
||||||
|
{
|
||||||
|
register<
|
||||||
|
null,
|
||||||
|
Array<
|
||||||
|
{
|
||||||
|
id : int;
|
||||||
|
name : string;
|
||||||
|
label : string;
|
||||||
|
}
|
||||||
|
>
|
||||||
|
>(
|
||||||
|
rest_subject,
|
||||||
|
lib_plankton.http.enum_method.get,
|
||||||
|
"/groups",
|
||||||
|
{
|
||||||
|
"description": "listet alle Gruppen auf",
|
||||||
|
"query_parameters": () => ([
|
||||||
|
{
|
||||||
|
"name": "term",
|
||||||
|
"required": false,
|
||||||
|
"description": "search term",
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
"output_schema": () => ({
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "number",
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"label",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"restriction": restriction_logged_in,
|
||||||
|
"execution": async (stuff) => {
|
||||||
|
const result : Array<
|
||||||
|
{
|
||||||
|
id : _zeitbild.type_group_id;
|
||||||
|
name : string;
|
||||||
|
label : string;
|
||||||
|
}
|
||||||
|
> = (
|
||||||
|
(await _zeitbild.service.group.list())
|
||||||
|
.map(
|
||||||
|
entry => (
|
||||||
|
{
|
||||||
|
"id": entry.id,
|
||||||
|
"name": entry.object.name,
|
||||||
|
"label": entry.object.label,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return Promise.resolve(
|
||||||
|
{
|
||||||
|
"status_code": 200,
|
||||||
|
"data": result,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
@ -46,23 +65,30 @@ namespace _zeitbild.api
|
||||||
}),
|
}),
|
||||||
"restriction": () => restriction_none,
|
"restriction": () => restriction_none,
|
||||||
"execution": () => async ({"input": input}) => {
|
"execution": () => async ({"input": input}) => {
|
||||||
if (input === null) {
|
if (input === null)
|
||||||
|
{
|
||||||
return Promise.reject(new Error("impossible"));
|
return Promise.reject(new Error("impossible"));
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
const passed : boolean = await _zeitbild.service.auth_internal.check(input.name, input.password);
|
const passed : boolean = await _zeitbild.service.auth_internal.check(input.name, input.password);
|
||||||
if (! passed) {
|
if (! passed)
|
||||||
return Promise.resolve({
|
{
|
||||||
|
return Promise.resolve(
|
||||||
|
{
|
||||||
"status_code": 403,
|
"status_code": 403,
|
||||||
"data": null,
|
"data": null,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const session_key : string = await lib_plankton.session.begin(input.name);
|
const session_key : string = await lib_plankton.session.begin(input.name);
|
||||||
return Promise.resolve({
|
return Promise.resolve(
|
||||||
|
{
|
||||||
"status_code": 201,
|
"status_code": 201,
|
||||||
"data": session_key,
|
"data": session_key,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
@ -24,10 +43,12 @@ namespace _zeitbild.api
|
||||||
"execution": async (stuff) => {
|
"execution": async (stuff) => {
|
||||||
const session : {key : string; value : lib_plankton.session.type_session} = await session_from_stuff(stuff);
|
const session : {key : string; value : lib_plankton.session.type_session} = await session_from_stuff(stuff);
|
||||||
await lib_plankton.session.end(session.key);
|
await lib_plankton.session.end(session.key);
|
||||||
return Promise.resolve({
|
return Promise.resolve(
|
||||||
|
{
|
||||||
"status_code": 200,
|
"status_code": 200,
|
||||||
"data": null,
|
"data": null,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
@ -52,42 +71,17 @@ namespace _zeitbild.api
|
||||||
"execution": async (stuff) => {
|
"execution": async (stuff) => {
|
||||||
const data : {
|
const data : {
|
||||||
token : string;
|
token : string;
|
||||||
userinfo : {
|
userinfo : lib_plankton.auth.oidc.type_userinfo;
|
||||||
name : (null | string);
|
|
||||||
email : (null | string);
|
|
||||||
};
|
|
||||||
redirect_uri_template : string;
|
redirect_uri_template : string;
|
||||||
} = await _zeitbild.auth.oidc_handle_authorization_callback(
|
} = await _zeitbild.auth.oidc_handle_authorization_callback(
|
||||||
(stuff.headers["Cookie"] ?? stuff.headers["cookie"] ?? null),
|
(stuff.headers["Cookie"] ?? stuff.headers["cookie"] ?? null),
|
||||||
stuff.query_parameters
|
stuff.query_parameters
|
||||||
);
|
);
|
||||||
if (data.userinfo.name === null) {
|
|
||||||
return Promise.reject(
|
const user = await _zeitbild.auth.oidc_adapt_user(data.userinfo);
|
||||||
new Error(
|
|
||||||
"IDP did not return user name"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
await _zeitbild.service.user.add(
|
|
||||||
{
|
|
||||||
"name": data.userinfo.name,
|
|
||||||
"email_address": data.userinfo.email,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
lib_plankton.log.info(
|
|
||||||
"user_provisioned",
|
|
||||||
{
|
|
||||||
"name": data.userinfo.name,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
const session_key : string = await lib_plankton.session.begin(
|
const session_key : string = await lib_plankton.session.begin(
|
||||||
data.userinfo.name,
|
user.object.name,
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"oidc_token": data.token,
|
"oidc_token": data.token,
|
||||||
|
|
@ -110,7 +104,6 @@ namespace _zeitbild.api
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
|
||||||
94
source/api/actions/session_status.ts
Normal file
94
source/api/actions/session_status.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace _zeitbild.api
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function register_session_status(
|
||||||
|
rest_subject : lib_plankton.rest_http.type_rest
|
||||||
|
) : void
|
||||||
|
{
|
||||||
|
register<
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
logged_in : boolean;
|
||||||
|
name : (null | string);
|
||||||
|
}
|
||||||
|
>(
|
||||||
|
rest_subject,
|
||||||
|
lib_plankton.http.enum_method.get,
|
||||||
|
"/session/status",
|
||||||
|
{
|
||||||
|
"description": "gibt Information über den Nutzer aus",
|
||||||
|
"output_schema": () => ({
|
||||||
|
"nullable": false,
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"logged_in": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "boolean",
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"nullable": true,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"logged_in",
|
||||||
|
"name",
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
"restriction": restriction_none,
|
||||||
|
"execution": async (stuff) => {
|
||||||
|
const user_id : (null | _zeitbild.type_user_id) = await (
|
||||||
|
session_from_stuff(stuff)
|
||||||
|
.then(
|
||||||
|
(session : {key : string; value : lib_plankton.session.type_session;}) => (
|
||||||
|
_zeitbild.service.user.identify(session.value.name)
|
||||||
|
.catch(x => Promise.resolve(null))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.catch(x => Promise.resolve(null))
|
||||||
|
);
|
||||||
|
const user_object : (null | _zeitbild.type_user_object) = (
|
||||||
|
(user_id === null)
|
||||||
|
?
|
||||||
|
null
|
||||||
|
:
|
||||||
|
(await _zeitbild.service.user.get(user_id))
|
||||||
|
);
|
||||||
|
return Promise.resolve(
|
||||||
|
{
|
||||||
|
"status_code": 200,
|
||||||
|
"data": {
|
||||||
|
"logged_in": (user_id !== null),
|
||||||
|
"name": ((user_object === null) ? null : user_object.name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
172
source/api/actions/user_dav_conf.ts
Normal file
172
source/api/actions/user_dav_conf.ts
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace _zeitbild.api
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function register_user_dav_conf(
|
||||||
|
rest_subject : lib_plankton.rest_http.type_rest
|
||||||
|
) : void
|
||||||
|
{
|
||||||
|
register<
|
||||||
|
null,
|
||||||
|
(
|
||||||
|
null
|
||||||
|
|
|
||||||
|
{
|
||||||
|
address : string;
|
||||||
|
username : string;
|
||||||
|
password : (null | string);
|
||||||
|
setup_hints : Array<
|
||||||
|
{
|
||||||
|
label : string;
|
||||||
|
link : string;
|
||||||
|
remark : (null | string);
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
>(
|
||||||
|
rest_subject,
|
||||||
|
lib_plankton.http.enum_method.get,
|
||||||
|
"/user_dav_conf",
|
||||||
|
{
|
||||||
|
"description": "gibt die CalDAV-Zugangsdaten eines Nutzers aus",
|
||||||
|
"output_schema": () => ({
|
||||||
|
"nullable": true,
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"address": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"nullable": true,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"setup_hints": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"label": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"link": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"remark": {
|
||||||
|
"nullable": true,
|
||||||
|
"type": "string",
|
||||||
|
"default": null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"label",
|
||||||
|
"link",
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"address",
|
||||||
|
"username",
|
||||||
|
"password",
|
||||||
|
"setup_hints",
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}),
|
||||||
|
"restriction": restriction_logged_in,
|
||||||
|
"execution": async (stuff) => {
|
||||||
|
let result : (
|
||||||
|
null
|
||||||
|
|
|
||||||
|
{
|
||||||
|
address : string;
|
||||||
|
username : string;
|
||||||
|
password : (null | string);
|
||||||
|
setup_hints : Array<
|
||||||
|
{
|
||||||
|
label : string;
|
||||||
|
link : string;
|
||||||
|
remark : (null | string);
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
) = null;
|
||||||
|
const raw : (null | any) = _zeitbild.conf.get()["caldav"];
|
||||||
|
if (raw === null)
|
||||||
|
{
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff);
|
||||||
|
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(session.value.name);
|
||||||
|
const user_object : _zeitbild.type_user_object = await _zeitbild.service.user.get(user_id);
|
||||||
|
const arguments_ : Record<string, string> = Object.fromEntries(
|
||||||
|
[
|
||||||
|
{"key": "username", "value": user_object.name},
|
||||||
|
{"key": "password", "value": user_object.dav_token},
|
||||||
|
]
|
||||||
|
.filter(
|
||||||
|
entry => (entry.value !== null)
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
entry => ([entry.key, entry.value as string])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
result = {
|
||||||
|
"address": lib_plankton.string.coin(raw["address"], arguments_),
|
||||||
|
"username": lib_plankton.string.coin(raw["username"], arguments_),
|
||||||
|
"password": (
|
||||||
|
(user_object.dav_token === null)
|
||||||
|
?
|
||||||
|
null
|
||||||
|
:
|
||||||
|
lib_plankton.string.coin(raw["password"], arguments_)
|
||||||
|
),
|
||||||
|
"setup_hints": raw["setup_hints"],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Promise.resolve(
|
||||||
|
{
|
||||||
|
"status_code": 200,
|
||||||
|
"data": result,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
73
source/api/actions/user_dav_token.ts
Normal file
73
source/api/actions/user_dav_token.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace _zeitbild.api
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function register_user_dav_token(
|
||||||
|
rest_subject : lib_plankton.rest_http.type_rest
|
||||||
|
) : void
|
||||||
|
{
|
||||||
|
register<
|
||||||
|
// string,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
>(
|
||||||
|
rest_subject,
|
||||||
|
lib_plankton.http.enum_method.patch,
|
||||||
|
"/user_dav_token",
|
||||||
|
{
|
||||||
|
"description": "setzt/überschreibt den DAV-Token eines Nutzers",
|
||||||
|
/*
|
||||||
|
"input_schema": () => ({
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string"
|
||||||
|
}),
|
||||||
|
*/
|
||||||
|
"input_schema": () => ({
|
||||||
|
"nullable": true,
|
||||||
|
}),
|
||||||
|
"output_schema": () => ({
|
||||||
|
"nullable": true
|
||||||
|
}),
|
||||||
|
"restriction": restriction_logged_in,
|
||||||
|
"execution": async (stuff) => {
|
||||||
|
const session : {key : string; value : lib_plankton.session.type_session;} = await session_from_stuff(stuff);
|
||||||
|
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(session.value.name);
|
||||||
|
// TODO: outsource to user service?
|
||||||
|
const user_object : _zeitbild.type_user_object = await _zeitbild.service.user.get(user_id);
|
||||||
|
// user_object.dav_token = stuff.input;
|
||||||
|
user_object.dav_token = lib_plankton.random.generate_string({"length": 12});
|
||||||
|
await _zeitbild.service.user.change(user_id, user_object);
|
||||||
|
return Promise.resolve(
|
||||||
|
{
|
||||||
|
"status_code": 200,
|
||||||
|
"data": null,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
@ -30,6 +49,20 @@ namespace _zeitbild.api
|
||||||
_zeitbild.api.register_session_begin(rest_subject);
|
_zeitbild.api.register_session_begin(rest_subject);
|
||||||
_zeitbild.api.register_session_end(rest_subject);
|
_zeitbild.api.register_session_end(rest_subject);
|
||||||
_zeitbild.api.register_session_oidc(rest_subject);
|
_zeitbild.api.register_session_oidc(rest_subject);
|
||||||
|
_zeitbild.api.register_session_status(rest_subject);
|
||||||
|
}
|
||||||
|
// groups
|
||||||
|
{
|
||||||
|
_zeitbild.api.register_group_list(rest_subject);
|
||||||
|
}
|
||||||
|
// user
|
||||||
|
{
|
||||||
|
_zeitbild.api.register_users(rest_subject);
|
||||||
|
// caldav
|
||||||
|
{
|
||||||
|
_zeitbild.api.register_user_dav_conf(rest_subject);
|
||||||
|
_zeitbild.api.register_user_dav_token(rest_subject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// calendar
|
// calendar
|
||||||
{
|
{
|
||||||
|
|
@ -46,13 +79,15 @@ namespace _zeitbild.api
|
||||||
_zeitbild.api.register_calendar_event_remove(rest_subject);
|
_zeitbild.api.register_calendar_event_remove(rest_subject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// export
|
||||||
|
{
|
||||||
|
_zeitbild.api.register_export_ics(rest_subject);
|
||||||
|
}
|
||||||
// misc
|
// misc
|
||||||
{
|
{
|
||||||
_zeitbild.api.register_users(rest_subject);
|
|
||||||
_zeitbild.api.register_events(rest_subject);
|
_zeitbild.api.register_events(rest_subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return rest_subject;
|
return rest_subject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.api
|
namespace _zeitbild.api
|
||||||
{
|
{
|
||||||
|
|
|
||||||
217
source/auth.ts
217
source/auth.ts
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.auth
|
namespace _zeitbild.auth
|
||||||
{
|
{
|
||||||
|
|
@ -33,10 +52,12 @@ namespace _zeitbild.auth
|
||||||
key : string
|
key : string
|
||||||
) : string
|
) : string
|
||||||
{
|
{
|
||||||
if (_oidc_redict_uri_template_map === null) {
|
if (_oidc_redict_uri_template_map === null)
|
||||||
|
{
|
||||||
throw (new Error("apparently not initialized yet"));
|
throw (new Error("apparently not initialized yet"));
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
return _oidc_redict_uri_template_map.get(key);
|
return _oidc_redict_uri_template_map.get(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +69,8 @@ namespace _zeitbild.auth
|
||||||
) : Promise<void>
|
) : Promise<void>
|
||||||
{
|
{
|
||||||
switch (_zeitbild.conf.get().authentication.kind) {
|
switch (_zeitbild.conf.get().authentication.kind) {
|
||||||
case "internal": {
|
case "internal":
|
||||||
|
{
|
||||||
_subject = lib_plankton.auth.internal.implementation_auth(
|
_subject = lib_plankton.auth.internal.implementation_auth(
|
||||||
{
|
{
|
||||||
"password_image_chest": {
|
"password_image_chest": {
|
||||||
|
|
@ -64,7 +86,8 @@ namespace _zeitbild.auth
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "oidc": {
|
case "oidc":
|
||||||
|
{
|
||||||
_subject_oidc = lib_plankton.auth.oidc.make(
|
_subject_oidc = lib_plankton.auth.oidc.make(
|
||||||
{
|
{
|
||||||
"url_authorization": _zeitbild.conf.get().authentication.data.url_authorization,
|
"url_authorization": _zeitbild.conf.get().authentication.data.url_authorization,
|
||||||
|
|
@ -77,6 +100,7 @@ namespace _zeitbild.auth
|
||||||
"openid",
|
"openid",
|
||||||
"profile",
|
"profile",
|
||||||
"email",
|
"email",
|
||||||
|
"groups",
|
||||||
],
|
],
|
||||||
"label": _zeitbild.conf.get().authentication.data.label,
|
"label": _zeitbild.conf.get().authentication.data.label,
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +113,8 @@ namespace _zeitbild.auth
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default:
|
||||||
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -104,12 +129,16 @@ namespace _zeitbild.auth
|
||||||
input : any
|
input : any
|
||||||
) : Promise<{kind : string; data : any;}>
|
) : Promise<{kind : string; data : any;}>
|
||||||
{
|
{
|
||||||
switch (_zeitbild.conf.get().authentication.kind) {
|
switch (_zeitbild.conf.get().authentication.kind)
|
||||||
case "oidc": {
|
{
|
||||||
if ((_subject_oidc === null) || (_oidc_redict_uri_template_map === null)) {
|
case "oidc":
|
||||||
|
{
|
||||||
|
if ((_subject_oidc === null) || (_oidc_redict_uri_template_map === null))
|
||||||
|
{
|
||||||
throw (new Error("not initialized yet"));
|
throw (new Error("not initialized yet"));
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
const stuff : {state : string; authorization_url : string;} = lib_plankton.auth.oidc.prepare_login(_subject_oidc);
|
const stuff : {state : string; authorization_url : string;} = lib_plankton.auth.oidc.prepare_login(_subject_oidc);
|
||||||
_oidc_redict_uri_template_map.set(
|
_oidc_redict_uri_template_map.set(
|
||||||
stuff.state,
|
stuff.state,
|
||||||
|
|
@ -127,11 +156,14 @@ namespace _zeitbild.auth
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default:
|
||||||
if (_subject === null) {
|
{
|
||||||
|
if (_subject === null)
|
||||||
|
{
|
||||||
return Promise.reject(new Error("not initialized yet"));
|
return Promise.reject(new Error("not initialized yet"));
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
return (
|
return (
|
||||||
_subject.login_prepare()
|
_subject.login_prepare()
|
||||||
.then(
|
.then(
|
||||||
|
|
@ -156,25 +188,21 @@ namespace _zeitbild.auth
|
||||||
) : Promise<
|
) : Promise<
|
||||||
{
|
{
|
||||||
token : string;
|
token : string;
|
||||||
userinfo : {
|
userinfo : lib_plankton.auth.oidc.type_userinfo;
|
||||||
name : (null | string);
|
|
||||||
email : (null | string);
|
|
||||||
};
|
|
||||||
redirect_uri_template : string;
|
redirect_uri_template : string;
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
if ((_subject_oidc === null) || (_oidc_redict_uri_template_map === null)) {
|
if ((_subject_oidc === null) || (_oidc_redict_uri_template_map === null))
|
||||||
|
{
|
||||||
throw (new Error("not initialized yet"));
|
throw (new Error("not initialized yet"));
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
const state : string = data["state"];
|
const state : string = data["state"];
|
||||||
const result : {
|
const result : {
|
||||||
token : string;
|
token : string;
|
||||||
userinfo : {
|
userinfo : lib_plankton.auth.oidc.type_userinfo;
|
||||||
name : (null | string);
|
|
||||||
email : (null | string);
|
|
||||||
};
|
|
||||||
} = await lib_plankton.auth.oidc.handle_authorization_callback(
|
} = await lib_plankton.auth.oidc.handle_authorization_callback(
|
||||||
_subject_oidc,
|
_subject_oidc,
|
||||||
cookie,
|
cookie,
|
||||||
|
|
@ -183,10 +211,7 @@ namespace _zeitbild.auth
|
||||||
return Promise.resolve<
|
return Promise.resolve<
|
||||||
{
|
{
|
||||||
token : string;
|
token : string;
|
||||||
userinfo : {
|
userinfo : lib_plankton.auth.oidc.type_userinfo;
|
||||||
name : (null | string);
|
|
||||||
email : (null | string);
|
|
||||||
};
|
|
||||||
redirect_uri_template : string;
|
redirect_uri_template : string;
|
||||||
}
|
}
|
||||||
>(
|
>(
|
||||||
|
|
@ -199,4 +224,144 @@ namespace _zeitbild.auth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo switch for enabling/disabling auto provisioning
|
||||||
|
*/
|
||||||
|
export async function oidc_adapt_user(
|
||||||
|
userinfo : lib_plankton.auth.oidc.type_userinfo
|
||||||
|
)
|
||||||
|
: Promise<
|
||||||
|
{
|
||||||
|
id : _zeitbild.type_user_id;
|
||||||
|
object : _zeitbild.type_user_object;
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
if (userinfo.name === null)
|
||||||
|
{
|
||||||
|
return Promise.reject(new Error("IDP did not return user name"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// groups
|
||||||
|
const group_ids : Array<_zeitbild.type_group_id> = await (async () => {
|
||||||
|
const derive_name : ((group_name_raw : string) => string) = (
|
||||||
|
(group_name_raw) => lib_plankton.string.coin(
|
||||||
|
"auto-{{name_raw}}",
|
||||||
|
{
|
||||||
|
"name_raw": group_name_raw,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const derive_label : ((group_name_raw : string) => string) = (
|
||||||
|
(group_name_raw) => lib_plankton.string.coin(
|
||||||
|
"{{name_raw}}",
|
||||||
|
{
|
||||||
|
"name_raw": group_name_raw,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return Promise.all<_zeitbild.type_group_id>(
|
||||||
|
(userinfo.groups ?? [])
|
||||||
|
.map(
|
||||||
|
async (group_name_raw) => {
|
||||||
|
const group_name : string = derive_name(group_name_raw);
|
||||||
|
const group_id_raw : (null | _zeitbild.type_group_id) = await (
|
||||||
|
_zeitbild.repository.group.identify(group_name)
|
||||||
|
.catch(() => Promise.resolve(null))
|
||||||
|
);
|
||||||
|
if (group_id_raw === null)
|
||||||
|
{
|
||||||
|
// create
|
||||||
|
const group_id : _zeitbild.type_group_id = await _zeitbild.service.group.add(
|
||||||
|
{
|
||||||
|
"name": group_name,
|
||||||
|
"label": derive_label(group_name_raw),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
lib_plankton.log.info(
|
||||||
|
"zeitbild.oidc_adapt_user.auto_provisioned_group",
|
||||||
|
{
|
||||||
|
"id": group_id,
|
||||||
|
"name": group_name,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return group_id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// update
|
||||||
|
const group_id : _zeitbild.type_group_id = group_id_raw;
|
||||||
|
await _zeitbild.service.group.change(
|
||||||
|
group_id,
|
||||||
|
{
|
||||||
|
"name": group_name,
|
||||||
|
"label": derive_label(group_name_raw),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return group_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}) ();
|
||||||
|
|
||||||
|
// user
|
||||||
|
const user : {
|
||||||
|
id : _zeitbild.type_user_id;
|
||||||
|
object : _zeitbild.type_user_object;
|
||||||
|
} = await (async () => {
|
||||||
|
const user_id_raw : (null | _zeitbild.type_user_id) = await (
|
||||||
|
_zeitbild.service.user.identify(userinfo.name as string)
|
||||||
|
.catch(() => Promise.resolve(null))
|
||||||
|
);
|
||||||
|
if (user_id_raw === null)
|
||||||
|
{
|
||||||
|
// provision
|
||||||
|
const user_object : _zeitbild.type_user_object = {
|
||||||
|
"name": (userinfo.name as string),
|
||||||
|
"groups": group_ids,
|
||||||
|
"email_address": userinfo.email,
|
||||||
|
"dav_token": null,
|
||||||
|
};
|
||||||
|
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.add(
|
||||||
|
user_object
|
||||||
|
);
|
||||||
|
lib_plankton.log.info(
|
||||||
|
"user_provisioned",
|
||||||
|
{
|
||||||
|
"id": user_id,
|
||||||
|
"name": user_object.name,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return {"id": user_id, "object": user_object};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// update
|
||||||
|
const user_id : _zeitbild.type_user_id = user_id_raw;
|
||||||
|
const user_object : _zeitbild.type_user_object = await _zeitbild.service.user.get(user_id);
|
||||||
|
user_object.name = (userinfo.name as string);
|
||||||
|
user_object.groups = group_ids;
|
||||||
|
user_object.email_address = userinfo.email;
|
||||||
|
await _zeitbild.service.user.change(
|
||||||
|
user_id,
|
||||||
|
user_object
|
||||||
|
);
|
||||||
|
lib_plankton.log.info(
|
||||||
|
"user_updated",
|
||||||
|
{
|
||||||
|
"id": user_id,
|
||||||
|
"name": user_object.name,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return {"id": user_id, "object": user_object};
|
||||||
|
}
|
||||||
|
}) ();
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,23 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild
|
namespace _zeitbild
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
||||||
163
source/conf.ts
163
source/conf.ts
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.conf
|
namespace _zeitbild.conf
|
||||||
{
|
{
|
||||||
|
|
@ -57,7 +76,51 @@ namespace _zeitbild.conf
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["file"]
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"threshold": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"debug",
|
||||||
|
"info",
|
||||||
|
"notice",
|
||||||
|
"warning",
|
||||||
|
"error"
|
||||||
|
],
|
||||||
|
"default": "info"
|
||||||
|
},
|
||||||
|
"format": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"human_readable",
|
||||||
|
"jsonl",
|
||||||
|
"jsonl_structured",
|
||||||
|
],
|
||||||
|
"default": "human_readable",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"default": [
|
"default": [
|
||||||
|
|
@ -188,8 +251,13 @@ namespace _zeitbild.conf
|
||||||
"lifetime": {
|
"lifetime": {
|
||||||
"nullable": false,
|
"nullable": false,
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"default": 900
|
"default": 3600,
|
||||||
}
|
},
|
||||||
|
"prolongation": {
|
||||||
|
"nullable": true,
|
||||||
|
"type": "integer",
|
||||||
|
"default": 300,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
],
|
],
|
||||||
|
|
@ -267,7 +335,8 @@ namespace _zeitbild.conf
|
||||||
"url_token",
|
"url_token",
|
||||||
"url_userinfo",
|
"url_userinfo",
|
||||||
"client_id",
|
"client_id",
|
||||||
"client_secret"
|
"client_secret",
|
||||||
|
"backend_url_base"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -282,8 +351,96 @@ namespace _zeitbild.conf
|
||||||
"data": {
|
"data": {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"external_resources": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"lifetime": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "integer",
|
||||||
|
"default": 14400
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
],
|
||||||
|
"default": {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"caldav": {
|
||||||
|
"nullable": true,
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"address": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"setup_hints": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"label": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"link": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"remark": {
|
||||||
|
"nullable": true,
|
||||||
|
"type": "string",
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"label",
|
||||||
|
"link",
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"address",
|
||||||
|
"username",
|
||||||
|
"password"
|
||||||
|
],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"default": null
|
||||||
|
},
|
||||||
|
"misc": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
/**
|
||||||
|
* @todo make mandatory
|
||||||
|
*/
|
||||||
|
"auth_salt": {
|
||||||
|
"nullable": false,
|
||||||
|
"type": "string",
|
||||||
|
"default": "unsafe_auth_salt"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"default": {}
|
||||||
|
},
|
||||||
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"version"
|
"version"
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.database
|
namespace _zeitbild.database
|
||||||
{
|
{
|
||||||
|
|
@ -5,7 +24,7 @@ namespace _zeitbild.database
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
const _compatible_revisions : Array<string> = [
|
const _compatible_revisions : Array<string> = [
|
||||||
"r4",
|
"r6",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
|
@ -7,7 +26,7 @@ namespace _zeitbild.helpers
|
||||||
/**
|
/**
|
||||||
* @todo timezone
|
* @todo timezone
|
||||||
*/
|
*/
|
||||||
function ical_datetime_to_own_datetime(
|
function icalendar_datetime_to_own_datetime(
|
||||||
ical_datetime : lib_plankton.ical.type_datetime
|
ical_datetime : lib_plankton.ical.type_datetime
|
||||||
) : lib_plankton.pit.type_datetime
|
) : lib_plankton.pit.type_datetime
|
||||||
{
|
{
|
||||||
|
|
@ -36,7 +55,7 @@ namespace _zeitbild.helpers
|
||||||
/**
|
/**
|
||||||
* @todo timezone
|
* @todo timezone
|
||||||
*/
|
*/
|
||||||
export function ical_dt_to_own_datetime(
|
export function icalendar_dt_to_own_datetime(
|
||||||
ical_dt: lib_plankton.ical.type_dt
|
ical_dt: lib_plankton.ical.type_dt
|
||||||
) : lib_plankton.pit.type_datetime
|
) : lib_plankton.pit.type_datetime
|
||||||
{
|
{
|
||||||
|
|
@ -58,6 +77,130 @@ namespace _zeitbild.helpers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function icalendar_dt_from_own_datetime(
|
||||||
|
datetime : lib_plankton.pit.type_datetime
|
||||||
|
) : lib_plankton.ical.type_dt
|
||||||
|
{
|
||||||
|
const datetime_normalized : lib_plankton.pit.type_datetime = lib_plankton.pit.to_datetime(
|
||||||
|
lib_plankton.pit.from_datetime(datetime),
|
||||||
|
{
|
||||||
|
"timezone_shift": 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
"tzid": "UTC",
|
||||||
|
"value": {
|
||||||
|
"date": {
|
||||||
|
"year": datetime_normalized.date.year,
|
||||||
|
"month": datetime_normalized.date.month,
|
||||||
|
"day": datetime_normalized.date.day,
|
||||||
|
},
|
||||||
|
"time": (
|
||||||
|
(datetime_normalized.time === null)
|
||||||
|
?
|
||||||
|
null
|
||||||
|
:
|
||||||
|
{
|
||||||
|
"utc": true,
|
||||||
|
"hour": datetime_normalized.time.hour,
|
||||||
|
"minute": datetime_normalized.time.minute,
|
||||||
|
"second": datetime_normalized.time.second,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function icalendar_vevent_from_own_event(
|
||||||
|
event_extended : _zeitbild.type_event_extended,
|
||||||
|
index : int,
|
||||||
|
{
|
||||||
|
"stamp": stamp = "adhoc",
|
||||||
|
}
|
||||||
|
:
|
||||||
|
{
|
||||||
|
stamp ?: string;
|
||||||
|
}
|
||||||
|
=
|
||||||
|
{
|
||||||
|
}
|
||||||
|
) : lib_plankton.ical.type_vevent
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"uid": lib_plankton.string.coin(
|
||||||
|
"zeitbild_{{stamp}}_{{id}}",
|
||||||
|
{
|
||||||
|
"stamp": stamp,
|
||||||
|
// "id": event_extended.event_id.toFixed(0),
|
||||||
|
"id": index.toFixed(0),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
"dtstamp": icalendar_dt_from_own_datetime(event_extended.event_object.begin).value,
|
||||||
|
"dtstart": icalendar_dt_from_own_datetime(event_extended.event_object.begin),
|
||||||
|
"dtend": (
|
||||||
|
(event_extended.event_object.end === null)
|
||||||
|
?
|
||||||
|
undefined
|
||||||
|
:
|
||||||
|
icalendar_dt_from_own_datetime(event_extended.event_object.end)
|
||||||
|
),
|
||||||
|
"location": (event_extended.event_object.location ?? undefined),
|
||||||
|
"summary": event_extended.event_object.name,
|
||||||
|
"url": (event_extended.event_object.link ?? undefined),
|
||||||
|
"description": (event_extended.event_object.description ?? undefined),
|
||||||
|
/**
|
||||||
|
* @todo transform name
|
||||||
|
*/
|
||||||
|
"categories": [event_extended.calendar_name],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo assign better uids
|
||||||
|
*/
|
||||||
|
export function icalendar_vcalendar_from_own_event_list(
|
||||||
|
events_extended : Array<_zeitbild.type_event_extended>
|
||||||
|
) : lib_plankton.ical.type_vcalendar
|
||||||
|
{
|
||||||
|
const pit_now : lib_plankton.pit.type_pit = lib_plankton.pit.now();
|
||||||
|
const datetime_now : lib_plankton.pit.type_datetime = lib_plankton.pit.to_datetime(pit_now);
|
||||||
|
const stamp : string = lib_plankton.string.coin(
|
||||||
|
"{{year}}{{month}}{{day}}",
|
||||||
|
{
|
||||||
|
"year": datetime_now.date.year.toFixed(0).padStart(4, "0"),
|
||||||
|
"month": datetime_now.date.month.toFixed(0).padStart(2, "0"),
|
||||||
|
"day": datetime_now.date.day.toFixed(0).padStart(2, "0"),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
"version": "2.0",
|
||||||
|
"prodid": "",
|
||||||
|
"vevents": (
|
||||||
|
events_extended
|
||||||
|
.map<lib_plankton.ical.type_vevent>(
|
||||||
|
(event_extended, index) => icalendar_vevent_from_own_event(
|
||||||
|
event_extended,
|
||||||
|
index,
|
||||||
|
{
|
||||||
|
"stamp": stamp,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"method": "PUBLISH",
|
||||||
|
"vtimezone": {
|
||||||
|
"tzid": "Europe/Berlin",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export async function template_coin(
|
export async function template_coin(
|
||||||
|
|
|
||||||
199
source/logic.ts
Normal file
199
source/logic.ts
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
namespace _zeitbild
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function access_level_to_string(
|
||||||
|
access_level : _zeitbild.enum_access_level
|
||||||
|
)
|
||||||
|
: string
|
||||||
|
{
|
||||||
|
switch (access_level)
|
||||||
|
{
|
||||||
|
case _zeitbild.enum_access_level.none: {return "none";}
|
||||||
|
case _zeitbild.enum_access_level.view: {return "view";}
|
||||||
|
case _zeitbild.enum_access_level.edit: {return "edit";}
|
||||||
|
case _zeitbild.enum_access_level.admin: {return "admin";}
|
||||||
|
default: {throw (new Error("invalid access level: " + String(access_level)));}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function access_level_from_string(
|
||||||
|
representation : string
|
||||||
|
)
|
||||||
|
: _zeitbild.enum_access_level
|
||||||
|
{
|
||||||
|
switch (representation)
|
||||||
|
{
|
||||||
|
case "none": {return _zeitbild.enum_access_level.none;}
|
||||||
|
case "view": {return _zeitbild.enum_access_level.view;}
|
||||||
|
case "edit": {return _zeitbild.enum_access_level.edit;}
|
||||||
|
case "admin": {return _zeitbild.enum_access_level.admin;}
|
||||||
|
default: {throw (new Error("invalid encoded access level: " + String(representation)));}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function access_level_order(
|
||||||
|
x : _zeitbild.enum_access_level,
|
||||||
|
y : _zeitbild.enum_access_level
|
||||||
|
)
|
||||||
|
: boolean
|
||||||
|
{
|
||||||
|
const list : Array<_zeitbild.enum_access_level> = [
|
||||||
|
_zeitbild.enum_access_level.none,
|
||||||
|
_zeitbild.enum_access_level.view,
|
||||||
|
_zeitbild.enum_access_level.edit,
|
||||||
|
_zeitbild.enum_access_level.admin,
|
||||||
|
];
|
||||||
|
return (list.indexOf(x) <= list.indexOf(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function access_level_determine_raw(
|
||||||
|
public_ : boolean,
|
||||||
|
access_level_attributed : (
|
||||||
|
null
|
||||||
|
|
|
||||||
|
{
|
||||||
|
default : _zeitbild.enum_access_level,
|
||||||
|
group : Array<_zeitbild.enum_access_level>;
|
||||||
|
user : (null | _zeitbild.enum_access_level);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: _zeitbild.enum_access_level
|
||||||
|
{
|
||||||
|
return lib_plankton.call.convey(
|
||||||
|
_zeitbild.enum_access_level.none,
|
||||||
|
[
|
||||||
|
// if public
|
||||||
|
(x : _zeitbild.enum_access_level) => (
|
||||||
|
public_
|
||||||
|
?
|
||||||
|
_zeitbild.enum_access_level.view
|
||||||
|
:
|
||||||
|
x
|
||||||
|
),
|
||||||
|
// if logged in
|
||||||
|
(x : _zeitbild.enum_access_level) => (
|
||||||
|
(access_level_attributed === null)
|
||||||
|
?
|
||||||
|
x
|
||||||
|
:
|
||||||
|
lib_plankton.call.convey(
|
||||||
|
x,
|
||||||
|
[
|
||||||
|
// default
|
||||||
|
(y : _zeitbild.enum_access_level) => access_level_attributed.default,
|
||||||
|
// group
|
||||||
|
(y : _zeitbild.enum_access_level) => (
|
||||||
|
lib_plankton.call.null_prop(
|
||||||
|
lib_plankton.list.max<_zeitbild.enum_access_level, _zeitbild.enum_access_level>(
|
||||||
|
access_level_attributed.group,
|
||||||
|
z => z,
|
||||||
|
{
|
||||||
|
"compare_value": _zeitbild.access_level_order,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
z => z.value
|
||||||
|
)
|
||||||
|
??
|
||||||
|
y
|
||||||
|
),
|
||||||
|
// user
|
||||||
|
(y : _zeitbild.enum_access_level) => (
|
||||||
|
(access_level_attributed.user === null)
|
||||||
|
?
|
||||||
|
y
|
||||||
|
:
|
||||||
|
access_level_attributed.user
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function access_level_determine(
|
||||||
|
calendar_object : _zeitbild.type_calendar_object,
|
||||||
|
user : (
|
||||||
|
null
|
||||||
|
|
|
||||||
|
{
|
||||||
|
id : _zeitbild.type_user_id;
|
||||||
|
object : _zeitbild.type_user_object;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: _zeitbild.enum_access_level
|
||||||
|
{
|
||||||
|
return access_level_determine_raw(
|
||||||
|
calendar_object.access.public,
|
||||||
|
(
|
||||||
|
(user === null)
|
||||||
|
?
|
||||||
|
null
|
||||||
|
:
|
||||||
|
{
|
||||||
|
"default": calendar_object.access.default_level,
|
||||||
|
"group": (
|
||||||
|
user.object.groups
|
||||||
|
.map<(null | _zeitbild.enum_access_level)>(
|
||||||
|
group_id => (
|
||||||
|
calendar_object.access.attributed_group.has(group_id)
|
||||||
|
?
|
||||||
|
calendar_object.access.attributed_group.get(group_id)
|
||||||
|
:
|
||||||
|
null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.filter(
|
||||||
|
x => (x !== null)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"user": (
|
||||||
|
lib_plankton.call.try_catch_wrap<_zeitbild.enum_access_level>(
|
||||||
|
() => calendar_object.access.attributed_user.get(user.id)
|
||||||
|
).value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
249
source/main.ts
249
source/main.ts
|
|
@ -1,162 +1,23 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
/**
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
type type_data = {
|
|
||||||
users : Array<
|
|
||||||
{
|
|
||||||
id : int;
|
|
||||||
name : string;
|
|
||||||
email_address : string;
|
|
||||||
password : string;
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
calendars : Array<
|
|
||||||
{
|
|
||||||
id : int;
|
|
||||||
name : string;
|
|
||||||
access : {
|
|
||||||
public ?: boolean;
|
|
||||||
default_level : ("none" | "view" | "edit" | "admin");
|
|
||||||
attributed : Array<
|
|
||||||
{
|
|
||||||
user_id : int;
|
|
||||||
level : ("none" | "view" | "edit" | "admin");
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
};
|
|
||||||
resource : (
|
|
||||||
{
|
|
||||||
kind : "local";
|
|
||||||
data : {
|
|
||||||
events : Array<
|
|
||||||
_zeitbild.type_event_object
|
|
||||||
>
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
||||||
{
|
|
||||||
kind : "caldav";
|
|
||||||
data : {
|
|
||||||
url : string;
|
|
||||||
read_only : boolean;
|
|
||||||
from_fucked_up_wordpress ?: boolean;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
async function data_init(
|
|
||||||
data : type_data
|
|
||||||
) : Promise<void>
|
|
||||||
{
|
|
||||||
let track : {
|
|
||||||
user : Record<
|
|
||||||
int,
|
|
||||||
_zeitbild.type_user_id
|
|
||||||
>;
|
|
||||||
calendar : Record<
|
|
||||||
int,
|
|
||||||
_zeitbild.type_user_id
|
|
||||||
>;
|
|
||||||
} = {
|
|
||||||
"user": {},
|
|
||||||
"calendar": {},
|
|
||||||
};
|
|
||||||
for await (const user_raw of data.users)
|
|
||||||
{
|
|
||||||
const user_object : _zeitbild.type_user_object = {
|
|
||||||
"name": user_raw.name,
|
|
||||||
"email_address": user_raw.email_address,
|
|
||||||
};
|
|
||||||
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.add(
|
|
||||||
user_object
|
|
||||||
);
|
|
||||||
await _zeitbild.service.auth_internal.set(
|
|
||||||
user_raw.name,
|
|
||||||
user_raw.password
|
|
||||||
);
|
|
||||||
track.user[user_raw.id] = user_id;
|
|
||||||
}
|
|
||||||
for await (const calendar_raw of data.calendars)
|
|
||||||
{
|
|
||||||
let resource_object : _zeitbild.type_resource_object;
|
|
||||||
let resource_id : _zeitbild.type_resource_id;
|
|
||||||
switch (calendar_raw.resource.kind)
|
|
||||||
{
|
|
||||||
case "local":
|
|
||||||
{
|
|
||||||
resource_object = {
|
|
||||||
"kind": "local",
|
|
||||||
"data": {
|
|
||||||
"event_ids": [],
|
|
||||||
}
|
|
||||||
};
|
|
||||||
resource_id = await _zeitbild.service.resource.add(
|
|
||||||
resource_object
|
|
||||||
);
|
|
||||||
/*const event_ids : Array<_zeitbild.type_local_resource_event_id> = */await Promise.all(
|
|
||||||
calendar_raw.resource.data.events
|
|
||||||
.map(
|
|
||||||
(event_raw : _zeitbild.type_event_object) => _zeitbild.service.resource.event_add(resource_id, event_raw)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "caldav":
|
|
||||||
{
|
|
||||||
resource_object = {
|
|
||||||
"kind": "caldav",
|
|
||||||
"data": {
|
|
||||||
"url": calendar_raw.resource.data.url,
|
|
||||||
"read_only": calendar_raw.resource.data.read_only,
|
|
||||||
"from_fucked_up_wordpress": (calendar_raw.resource.data.from_fucked_up_wordpress ?? false),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
resource_id = await _zeitbild.service.resource.add(
|
|
||||||
resource_object
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const calendar_object : _zeitbild.type_calendar_object =
|
|
||||||
{
|
|
||||||
"name": calendar_raw.name,
|
|
||||||
"access": {
|
|
||||||
"public": (calendar_raw.access.public ?? false),
|
|
||||||
"default_level": _zeitbild.value_object.access_level.from_string(calendar_raw.access.default_level),
|
|
||||||
"attributed": lib_plankton.map.hashmap.implementation_map(
|
|
||||||
lib_plankton.map.hashmap.make(
|
|
||||||
x => x.toFixed(0),
|
|
||||||
{
|
|
||||||
"pairs": (
|
|
||||||
calendar_raw.access.attributed
|
|
||||||
.map(
|
|
||||||
(entry) => ({
|
|
||||||
"key": track.user[entry.user_id],
|
|
||||||
"value": _zeitbild.value_object.access_level.from_string(entry.level),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
),
|
|
||||||
},
|
|
||||||
"resource_id": resource_id,
|
|
||||||
};
|
|
||||||
const calendar_id : _zeitbild.type_calendar_id = await _zeitbild.service.calendar.add(
|
|
||||||
calendar_object
|
|
||||||
);
|
|
||||||
track.calendar[calendar_raw.id] = calendar_id;
|
|
||||||
}
|
|
||||||
return Promise.resolve<void>(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
|
@ -208,8 +69,8 @@ async function main(
|
||||||
"description": "conf-expose"
|
"description": "conf-expose"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "fill",
|
"name": "sample",
|
||||||
"description": "fill"
|
"description": "sample"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "help",
|
"name": "help",
|
||||||
|
|
@ -311,6 +172,46 @@ async function main(
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "file": {
|
||||||
|
return {
|
||||||
|
"kind": "minlevel",
|
||||||
|
"data": {
|
||||||
|
"core": {
|
||||||
|
"kind": "file",
|
||||||
|
"data": {
|
||||||
|
"path": log_output.data.path,
|
||||||
|
"format": lib_plankton.call.distinguish(
|
||||||
|
{
|
||||||
|
"kind": log_output.data.format,
|
||||||
|
"data": null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jsonl": () => ({
|
||||||
|
"kind": "jsonl",
|
||||||
|
"data": {
|
||||||
|
"structured": false,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"jsonl_structured": () => ({
|
||||||
|
"kind": "jsonl",
|
||||||
|
"data": {
|
||||||
|
"structured": true,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
"human_readable": () => ({
|
||||||
|
"kind": "human_readable",
|
||||||
|
"data": {
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"threshold": log_output.data.threshold,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
throw (new Error("unhandled"));
|
throw (new Error("unhandled"));
|
||||||
break;
|
break;
|
||||||
|
|
@ -347,8 +248,10 @@ async function main(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
switch (args["action"]) {
|
switch (args["action"])
|
||||||
default: {
|
{
|
||||||
|
default:
|
||||||
|
{
|
||||||
lib_plankton.log.error(
|
lib_plankton.log.error(
|
||||||
"main_invalid_action",
|
"main_invalid_action",
|
||||||
{
|
{
|
||||||
|
|
@ -357,7 +260,8 @@ async function main(
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "conf-schema": {
|
case "conf-schema":
|
||||||
|
{
|
||||||
process.stdout.write(
|
process.stdout.write(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
_zeitbild.conf.schema(),
|
_zeitbild.conf.schema(),
|
||||||
|
|
@ -369,7 +273,8 @@ async function main(
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "conf-expose": {
|
case "conf-expose":
|
||||||
|
{
|
||||||
process.stdout.write(
|
process.stdout.write(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
_zeitbild.conf.get(),
|
_zeitbild.conf.get(),
|
||||||
|
|
@ -381,10 +286,10 @@ async function main(
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "api-doc": {
|
case "api-doc":
|
||||||
|
{
|
||||||
lib_plankton.log.set_main_logger([]);
|
lib_plankton.log.set_main_logger([]);
|
||||||
const rest_subject : lib_plankton.rest_http.type_rest = _zeitbild.api.make();
|
const rest_subject : lib_plankton.rest_http.type_rest = _zeitbild.api.make();
|
||||||
lib_plankton.log.conf_pop();
|
|
||||||
process.stdout.write(
|
process.stdout.write(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
lib_plankton.rest_http.to_oas(rest_subject),
|
lib_plankton.rest_http.to_oas(rest_subject),
|
||||||
|
|
@ -394,8 +299,9 @@ async function main(
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "fill": {
|
case "sample":
|
||||||
await data_init(
|
{
|
||||||
|
await _zeitbild.sample.init(
|
||||||
lib_plankton.json.decode(
|
lib_plankton.json.decode(
|
||||||
await lib_plankton.file.read(args.data_path)
|
await lib_plankton.file.read(args.data_path)
|
||||||
)
|
)
|
||||||
|
|
@ -403,16 +309,23 @@ async function main(
|
||||||
process.stdout.write("-- done\n");
|
process.stdout.write("-- done\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "serve": {
|
case "serve":
|
||||||
|
{
|
||||||
// prepare database
|
// prepare database
|
||||||
await _zeitbild.database.check();
|
await _zeitbild.database.check();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo clear old sessions
|
||||||
|
*/
|
||||||
|
|
||||||
await lib_plankton.session.setup(
|
await lib_plankton.session.setup(
|
||||||
{
|
{
|
||||||
"data_chest": (
|
"data_chest": (
|
||||||
_zeitbild.conf.get().session_management.in_memory
|
_zeitbild.conf.get().session_management.in_memory
|
||||||
? lib_plankton.storage.memory.implementation_chest<lib_plankton.session.type_session>({})
|
?
|
||||||
: lib_plankton.call.convey(
|
lib_plankton.storage.memory.implementation_chest<lib_plankton.session.type_session>({})
|
||||||
|
:
|
||||||
|
lib_plankton.call.convey(
|
||||||
lib_plankton.storage.sql_table_common.chest(
|
lib_plankton.storage.sql_table_common.chest(
|
||||||
{
|
{
|
||||||
"database_implementation": _zeitbild.database.get_implementation(),
|
"database_implementation": _zeitbild.database.get_implementation(),
|
||||||
|
|
@ -434,6 +347,7 @@ async function main(
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"default_lifetime": _zeitbild.conf.get().session_management.lifetime,
|
"default_lifetime": _zeitbild.conf.get().session_management.lifetime,
|
||||||
|
"default_prolongation": _zeitbild.conf.get().session_management.prolongation,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -480,6 +394,7 @@ async function main(
|
||||||
)
|
)
|
||||||
.catch(
|
.catch(
|
||||||
(error) => {
|
(error) => {
|
||||||
|
// console.error(error);
|
||||||
process.stderr.write(String(error) + "\n");
|
process.stderr.write(String(error) + "\n");
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.repository.auth_internal
|
namespace _zeitbild.repository.auth_internal
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,85 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.repository.calendar
|
namespace _zeitbild.repository.calendar
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
type type_dispersal = {
|
type type_core_row = {
|
||||||
core_row : Record<
|
name : string;
|
||||||
string,
|
hue : int;
|
||||||
any
|
access_public : boolean;
|
||||||
>;
|
access_level_default : int;
|
||||||
access_attributed_rows : Array<
|
resource_id : int;
|
||||||
Record<
|
|
||||||
string,
|
|
||||||
any
|
|
||||||
>
|
|
||||||
>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_access_attributed_group_row = {
|
||||||
|
// calendar_id : int;
|
||||||
|
group_id : int;
|
||||||
|
level : int;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_access_attributed_group_preview = {
|
||||||
|
group_id : int;
|
||||||
|
level : int;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_access_attributed_user_row = {
|
||||||
|
// calendar_id : int;
|
||||||
|
user_id : int;
|
||||||
|
level : int;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_access_attributed_user_preview = {
|
||||||
|
user_id : int;
|
||||||
|
level : int;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_dispersal = {
|
||||||
|
core_row : type_core_row;
|
||||||
|
access_attributed_group_rows : Array<type_access_attributed_group_row>;
|
||||||
|
access_attributed_user_rows : Array<type_access_attributed_user_row>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
const hue_scaling : int = 0xFFFF;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
var _core_store : (
|
var _core_store : (
|
||||||
|
|
@ -35,7 +97,22 @@ namespace _zeitbild.repository.calendar
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
var _access_attributed_chest : (
|
var _access_attributed_group_chest : (
|
||||||
|
null
|
||||||
|
|
|
||||||
|
lib_plankton.storage.type_chest<
|
||||||
|
Array<any>,
|
||||||
|
Record<string, any>,
|
||||||
|
lib_plankton.database.type_description_create_table,
|
||||||
|
lib_plankton.storage.sql_table_common.type_sql_table_common_search_term,
|
||||||
|
Record<string, any>
|
||||||
|
>
|
||||||
|
) = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
var _access_attributed_user_chest : (
|
||||||
null
|
null
|
||||||
|
|
|
|
||||||
lib_plankton.storage.type_chest<
|
lib_plankton.storage.type_chest<
|
||||||
|
|
@ -51,7 +128,8 @@ namespace _zeitbild.repository.calendar
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
function get_core_store(
|
function get_core_store(
|
||||||
) : lib_plankton.storage.type_store<
|
)
|
||||||
|
: lib_plankton.storage.type_store<
|
||||||
_zeitbild.type_calendar_id,
|
_zeitbild.type_calendar_id,
|
||||||
Record<string, any>,
|
Record<string, any>,
|
||||||
{},
|
{},
|
||||||
|
|
@ -59,7 +137,8 @@ namespace _zeitbild.repository.calendar
|
||||||
Record<string, any>
|
Record<string, any>
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
if (_core_store === null) {
|
if (_core_store === null)
|
||||||
|
{
|
||||||
_core_store = lib_plankton.storage.sql_table_autokey_store(
|
_core_store = lib_plankton.storage.sql_table_autokey_store(
|
||||||
{
|
{
|
||||||
"database_implementation": _zeitbild.database.get_implementation(),
|
"database_implementation": _zeitbild.database.get_implementation(),
|
||||||
|
|
@ -68,7 +147,8 @@ namespace _zeitbild.repository.calendar
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
return _core_store;
|
return _core_store;
|
||||||
|
|
@ -77,7 +157,7 @@ namespace _zeitbild.repository.calendar
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
function get_access_attributed_chest(
|
function get_access_attributed_group_chest(
|
||||||
) : lib_plankton.storage.type_chest<
|
) : lib_plankton.storage.type_chest<
|
||||||
Array<any>,
|
Array<any>,
|
||||||
Record<string, any>,
|
Record<string, any>,
|
||||||
|
|
@ -86,19 +166,51 @@ namespace _zeitbild.repository.calendar
|
||||||
Record<string, any>
|
Record<string, any>
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
if (_access_attributed_chest === null) {
|
if (_access_attributed_group_chest === null)
|
||||||
_access_attributed_chest = lib_plankton.storage.sql_table_common.chest(
|
{
|
||||||
|
_access_attributed_group_chest = lib_plankton.storage.sql_table_common.chest(
|
||||||
{
|
{
|
||||||
"database_implementation": _zeitbild.database.get_implementation(),
|
"database_implementation": _zeitbild.database.get_implementation(),
|
||||||
"table_name": "calendar_access_attributed",
|
"table_name": "calendar_access_attributed_group",
|
||||||
|
"key_names": ["calendar_id","group_id"],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
return _access_attributed_group_chest;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function get_access_attributed_user_chest(
|
||||||
|
)
|
||||||
|
: lib_plankton.storage.type_chest<
|
||||||
|
Array<any>,
|
||||||
|
Record<string, any>,
|
||||||
|
lib_plankton.database.type_description_create_table,
|
||||||
|
lib_plankton.storage.sql_table_common.type_sql_table_common_search_term,
|
||||||
|
Record<string, any>
|
||||||
|
>
|
||||||
|
{
|
||||||
|
if (_access_attributed_user_chest === null)
|
||||||
|
{
|
||||||
|
_access_attributed_user_chest = lib_plankton.storage.sql_table_common.chest(
|
||||||
|
{
|
||||||
|
"database_implementation": _zeitbild.database.get_implementation(),
|
||||||
|
"table_name": "calendar_access_attributed_user",
|
||||||
"key_names": ["calendar_id","user_id"],
|
"key_names": ["calendar_id","user_id"],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
return _access_attributed_chest;
|
return _access_attributed_user_chest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -140,17 +252,29 @@ namespace _zeitbild.repository.calendar
|
||||||
*/
|
*/
|
||||||
function encode(
|
function encode(
|
||||||
object : _zeitbild.type_calendar_object
|
object : _zeitbild.type_calendar_object
|
||||||
) : type_dispersal
|
)
|
||||||
|
: type_dispersal
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
"core_row": {
|
"core_row": {
|
||||||
"name": object.name,
|
"name": object.name,
|
||||||
|
"hue": Math.floor(object.hue * hue_scaling),
|
||||||
"access_public": object.access.public,
|
"access_public": object.access.public,
|
||||||
"access_level_default": encode_access_level(object.access.default_level),
|
"access_level_default": encode_access_level(object.access.default_level),
|
||||||
"resource_id": object.resource_id,
|
"resource_id": object.resource_id,
|
||||||
},
|
},
|
||||||
"access_attributed_rows": (
|
"access_attributed_group_rows": (
|
||||||
lib_plankton.map.dump(object.access.attributed)
|
lib_plankton.map.dump(object.access.attributed_group)
|
||||||
|
.map(
|
||||||
|
({"key": group_id, "value": level}) => ({
|
||||||
|
// "calendar_id": calendar_id,
|
||||||
|
"group_id": group_id,
|
||||||
|
"level": encode_access_level(level),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"access_attributed_user_rows": (
|
||||||
|
lib_plankton.map.dump(object.access.attributed_user)
|
||||||
.map(
|
.map(
|
||||||
({"key": user_id, "value": level}) => ({
|
({"key": user_id, "value": level}) => ({
|
||||||
// "calendar_id": calendar_id,
|
// "calendar_id": calendar_id,
|
||||||
|
|
@ -167,24 +291,47 @@ namespace _zeitbild.repository.calendar
|
||||||
*/
|
*/
|
||||||
function decode(
|
function decode(
|
||||||
dispersal : type_dispersal
|
dispersal : type_dispersal
|
||||||
) : _zeitbild.type_calendar_object
|
)
|
||||||
|
: _zeitbild.type_calendar_object
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
"name": dispersal.core_row["name"],
|
"name": dispersal.core_row.name,
|
||||||
|
"hue": (dispersal.core_row.hue / hue_scaling),
|
||||||
"access": {
|
"access": {
|
||||||
"public": dispersal.core_row["access_public"],
|
"public": dispersal.core_row.access_public,
|
||||||
"default_level": decode_access_level(dispersal.core_row["access_level_default"]),
|
"default_level": decode_access_level(dispersal.core_row.access_level_default),
|
||||||
"attributed": lib_plankton.map.hashmap.implementation_map(
|
"attributed_group": lib_plankton.map.hashmap.implementation_map(
|
||||||
|
lib_plankton.map.hashmap.make<_zeitbild.type_group_id, _zeitbild.enum_access_level>(
|
||||||
|
x => x.toFixed(0),
|
||||||
|
{
|
||||||
|
"pairs": (
|
||||||
|
dispersal.access_attributed_group_rows
|
||||||
|
.map(
|
||||||
|
(access_attributed_group_row) => ({
|
||||||
|
// "calendar_id": access_attributed_group_row["calendar_id"],
|
||||||
|
// "key": access_attributed_group_row["preview"]["user_id"],
|
||||||
|
"key": access_attributed_group_row.group_id,
|
||||||
|
// "value": decode_access_level(access_attributed_group_row["preview"]["level"]),
|
||||||
|
"value": decode_access_level(access_attributed_group_row.level),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"attributed_user": lib_plankton.map.hashmap.implementation_map(
|
||||||
lib_plankton.map.hashmap.make<_zeitbild.type_user_id, _zeitbild.enum_access_level>(
|
lib_plankton.map.hashmap.make<_zeitbild.type_user_id, _zeitbild.enum_access_level>(
|
||||||
x => x.toFixed(0),
|
x => x.toFixed(0),
|
||||||
{
|
{
|
||||||
"pairs": (
|
"pairs": (
|
||||||
dispersal.access_attributed_rows
|
dispersal.access_attributed_user_rows
|
||||||
.map(
|
.map(
|
||||||
(access_attributed_row) => ({
|
(access_attributed_user_row) => ({
|
||||||
// "calendar_id": access_attributed_row["calendar_id"],
|
// "calendar_id": access_attributed_user_row["calendar_id"],
|
||||||
"key": access_attributed_row["preview"]["user_id"],
|
// "key": access_attributed_user_row["preview"]["user_id"],
|
||||||
"value": decode_access_level(access_attributed_row["preview"]["level"]),
|
"key": access_attributed_user_row.user_id,
|
||||||
|
// "value": decode_access_level(access_attributed_user_row["preview"]["level"]),
|
||||||
|
"value": decode_access_level(access_attributed_user_row.level),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
@ -192,22 +339,21 @@ namespace _zeitbild.repository.calendar
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
"resource_id": dispersal.core_row["resource_id"],
|
"resource_id": dispersal.core_row.resource_id,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export function read(
|
export async function read(
|
||||||
id : _zeitbild.type_calendar_id
|
id : _zeitbild.type_calendar_id
|
||||||
) : Promise<_zeitbild.type_calendar_object>
|
)
|
||||||
|
: Promise<_zeitbild.type_calendar_object>
|
||||||
{
|
{
|
||||||
return (
|
const core_row : type_core_row = ((await get_core_store().read(id)) as type_core_row);
|
||||||
get_core_store().read(id)
|
const access_attributed_group_rows : Array<type_access_attributed_group_row> = await (
|
||||||
.then(
|
get_access_attributed_group_chest().search(
|
||||||
(core_row) => (
|
|
||||||
get_access_attributed_chest().search(
|
|
||||||
{
|
{
|
||||||
"expression": "(calendar_id = $calendar_id)",
|
"expression": "(calendar_id = $calendar_id)",
|
||||||
"arguments": {
|
"arguments": {
|
||||||
|
|
@ -216,21 +362,51 @@ namespace _zeitbild.repository.calendar
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
(access_attributed_rows) => Promise.resolve<type_dispersal>(
|
(hits) => Promise.resolve<Array<type_access_attributed_group_row>>(
|
||||||
|
hits
|
||||||
|
.map(
|
||||||
|
hit => (
|
||||||
{
|
{
|
||||||
"core_row": core_row,
|
// "calendar_id": null,
|
||||||
"access_attributed_rows": access_attributed_rows,
|
"group_id": hit.preview.group_id,
|
||||||
|
"level": hit.preview.level,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.then(
|
|
||||||
(dispersal) => Promise.resolve<_zeitbild.type_calendar_object>(
|
|
||||||
decode(dispersal)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
const access_attributed_user_rows : Array<type_access_attributed_user_row> = await (
|
||||||
|
get_access_attributed_user_chest().search(
|
||||||
|
{
|
||||||
|
"expression": "(calendar_id = $calendar_id)",
|
||||||
|
"arguments": {
|
||||||
|
"calendar_id": id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
(hits) => Promise.resolve<Array<type_access_attributed_user_row>>(
|
||||||
|
hits
|
||||||
|
.map(
|
||||||
|
hit => (
|
||||||
|
{
|
||||||
|
// "calendar_id": null,
|
||||||
|
"user_id": hit.preview.user_id,
|
||||||
|
"level": hit.preview.level,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const dispersal : type_dispersal = {
|
||||||
|
"core_row": core_row,
|
||||||
|
"access_attributed_group_rows": access_attributed_group_rows,
|
||||||
|
"access_attributed_user_rows": access_attributed_user_rows,
|
||||||
|
};
|
||||||
|
const calendar_object : _zeitbild.type_calendar_object = decode(dispersal);
|
||||||
|
return calendar_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -245,10 +421,18 @@ namespace _zeitbild.repository.calendar
|
||||||
const calendar_id : _zeitbild.type_calendar_id = await core_store.create(
|
const calendar_id : _zeitbild.type_calendar_id = await core_store.create(
|
||||||
dispersal.core_row
|
dispersal.core_row
|
||||||
);
|
);
|
||||||
for await (const access_attributed_row of dispersal.access_attributed_rows) {
|
for await (const access_attributed_group_row of dispersal.access_attributed_group_rows)
|
||||||
get_access_attributed_chest().write(
|
{
|
||||||
[calendar_id, access_attributed_row["user_id"]],
|
get_access_attributed_group_chest().write(
|
||||||
{"level": access_attributed_row["level"]}
|
[calendar_id, access_attributed_group_row.group_id],
|
||||||
|
{"level": access_attributed_group_row.level}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for await (const access_attributed_user_row of dispersal.access_attributed_user_rows)
|
||||||
|
{
|
||||||
|
get_access_attributed_user_chest().write(
|
||||||
|
[calendar_id, access_attributed_user_row.user_id],
|
||||||
|
{"level": access_attributed_user_row.level}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await lib_plankton.cache.clear(_zeitbild.cache_regular);
|
await lib_plankton.cache.clear(_zeitbild.cache_regular);
|
||||||
|
|
@ -272,44 +456,117 @@ namespace _zeitbild.repository.calendar
|
||||||
dispersal.core_row
|
dispersal.core_row
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// attributed access
|
// attributed:group
|
||||||
{
|
{
|
||||||
const access_attributed_chest = get_access_attributed_chest();
|
const access_attributed_group_chest = get_access_attributed_group_chest();
|
||||||
const hits : Array<Record<string, any>> = await access_attributed_chest.search(
|
const hits : Array<{key : Array<any>; preview : type_access_attributed_group_row;}> = (
|
||||||
|
(await access_attributed_group_chest.search(
|
||||||
{
|
{
|
||||||
"expression": "(calendar_id = $calendar_id)",
|
"expression": "(calendar_id = $calendar_id)",
|
||||||
"arguments": {
|
"arguments": {
|
||||||
"calendar_id": calendar_id,
|
"calendar_id": calendar_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
))
|
||||||
|
.map(
|
||||||
|
hit => (
|
||||||
|
{
|
||||||
|
"key": hit.key,
|
||||||
|
"preview": {
|
||||||
|
"group_id": hit.preview["group_id"],
|
||||||
|
"level": hit.preview["level"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
const contrast = lib_plankton.list.contrast<
|
const contrast = lib_plankton.list.contrast<
|
||||||
Record<string, any>,
|
{key : Array<any>; preview : type_access_attributed_group_row;},
|
||||||
Record<string, any>
|
type_access_attributed_group_row
|
||||||
>(
|
>(
|
||||||
hits,
|
hits,
|
||||||
hit => hit["user_id"],
|
hit => hit.preview.group_id.toFixed(0),
|
||||||
dispersal.access_attributed_rows,
|
dispersal.access_attributed_group_rows,
|
||||||
row => row["user_id"]
|
row => row.group_id.toFixed(0)
|
||||||
);
|
);
|
||||||
// delete
|
// delete
|
||||||
for await (const entry of contrast.only_left) {
|
for await (const entry of contrast.only_left)
|
||||||
await access_attributed_chest.delete(
|
{
|
||||||
[calendar_id, entry.left["user_id"]]
|
await access_attributed_group_chest.delete(
|
||||||
|
[calendar_id, entry.left.preview.group_id]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// update
|
// update
|
||||||
for await (const entry of contrast.both) {
|
for await (const entry of contrast.both)
|
||||||
await access_attributed_chest.write(
|
{
|
||||||
[calendar_id, entry.right["user_id"]],
|
await access_attributed_group_chest.write(
|
||||||
{"level": entry.right["level"]}
|
[calendar_id, entry.right.group_id],
|
||||||
|
{"level": entry.right.level}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// create
|
// create
|
||||||
for await (const entry of contrast.only_right) {
|
for await (const entry of contrast.only_right)
|
||||||
await access_attributed_chest.write(
|
{
|
||||||
[calendar_id, entry.right["user_id"]],
|
await access_attributed_group_chest.write(
|
||||||
{"level": entry.right["level"]}
|
[calendar_id, entry.right.group_id],
|
||||||
|
{"level": entry.right.level}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// attributed:user
|
||||||
|
{
|
||||||
|
const access_attributed_user_chest = get_access_attributed_user_chest();
|
||||||
|
const hits : Array<{key : Array<any>; preview : type_access_attributed_user_row;}> = (
|
||||||
|
(await access_attributed_user_chest.search(
|
||||||
|
{
|
||||||
|
"expression": "(calendar_id = $calendar_id)",
|
||||||
|
"arguments": {
|
||||||
|
"calendar_id": calendar_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
.map(
|
||||||
|
hit => (
|
||||||
|
{
|
||||||
|
"key": hit.key,
|
||||||
|
"preview": {
|
||||||
|
"user_id": hit.preview["user_id"],
|
||||||
|
"level": hit.preview["level"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const contrast = lib_plankton.list.contrast<
|
||||||
|
{key : Array<any>; preview : type_access_attributed_user_row;},
|
||||||
|
type_access_attributed_user_row
|
||||||
|
>(
|
||||||
|
hits,
|
||||||
|
hit => hit.preview.user_id.toFixed(0),
|
||||||
|
dispersal.access_attributed_user_rows,
|
||||||
|
row => row.user_id.toFixed(0)
|
||||||
|
);
|
||||||
|
// delete
|
||||||
|
for await (const entry of contrast.only_left)
|
||||||
|
{
|
||||||
|
await access_attributed_user_chest.delete(
|
||||||
|
[calendar_id, entry.left.preview.user_id]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// update
|
||||||
|
for await (const entry of contrast.both)
|
||||||
|
{
|
||||||
|
await access_attributed_user_chest.write(
|
||||||
|
[calendar_id, entry.right.user_id],
|
||||||
|
{"level": entry.right.level}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// create
|
||||||
|
for await (const entry of contrast.only_right)
|
||||||
|
{
|
||||||
|
await access_attributed_user_chest.write(
|
||||||
|
[calendar_id, entry.right.user_id],
|
||||||
|
{"level": entry.right.level}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -320,17 +577,25 @@ namespace _zeitbild.repository.calendar
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo remove events from resource?
|
* @todo remove events from resource?
|
||||||
|
* @todo remove resource
|
||||||
*/
|
*/
|
||||||
export async function delete_(
|
export async function delete_(
|
||||||
calendar_id : _zeitbild.type_calendar_id
|
calendar_id : _zeitbild.type_calendar_id
|
||||||
) : Promise<void>
|
)
|
||||||
|
: Promise<void>
|
||||||
{
|
{
|
||||||
await lib_plankton.cache.clear(_zeitbild.cache_regular);
|
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_user_chest = get_access_attributed_user_chest();
|
||||||
// attributed access
|
// attributed:user
|
||||||
{
|
{
|
||||||
const hits : Array<Record<string, any>> = await access_attributed_chest.search(
|
const chest = get_access_attributed_user_chest();
|
||||||
|
const hits : Array<
|
||||||
|
{
|
||||||
|
key : Array<any>;
|
||||||
|
preview : Record<string, any>;
|
||||||
|
}
|
||||||
|
> = await chest.search(
|
||||||
{
|
{
|
||||||
"expression": "(calendar_id = $calendar_id)",
|
"expression": "(calendar_id = $calendar_id)",
|
||||||
"arguments": {
|
"arguments": {
|
||||||
|
|
@ -338,10 +603,30 @@ namespace _zeitbild.repository.calendar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
for await (const hit of hits) {
|
for (const hit of hits)
|
||||||
await access_attributed_chest.delete(
|
{
|
||||||
[calendar_id, hit["user_id"]]
|
await chest.delete(hit.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// attributed:group
|
||||||
|
{
|
||||||
|
const chest = get_access_attributed_group_chest();
|
||||||
|
const hits : Array<
|
||||||
|
{
|
||||||
|
key : Array<any>;
|
||||||
|
preview : Record<string, any>;
|
||||||
|
}
|
||||||
|
> = await chest.search(
|
||||||
|
{
|
||||||
|
"expression": "(calendar_id = $calendar_id)",
|
||||||
|
"arguments": {
|
||||||
|
"calendar_id": calendar_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
for (const hit of hits)
|
||||||
|
{
|
||||||
|
await chest.delete(hit.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// core
|
// core
|
||||||
|
|
@ -359,74 +644,151 @@ namespace _zeitbild.repository.calendar
|
||||||
type type_overview_entry = {
|
type type_overview_entry = {
|
||||||
id : _zeitbild.type_calendar_id;
|
id : _zeitbild.type_calendar_id;
|
||||||
name : string;
|
name : string;
|
||||||
|
hue : float;
|
||||||
access_level : _zeitbild.enum_access_level;
|
access_level : _zeitbild.enum_access_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo caching
|
|
||||||
*/
|
*/
|
||||||
export async function overview(
|
export async function overview(
|
||||||
user_id : (null | _zeitbild.type_user_id)
|
user_id : (null | _zeitbild.type_user_id)
|
||||||
) : Promise<
|
)
|
||||||
|
: Promise<
|
||||||
Array<
|
Array<
|
||||||
type_overview_entry
|
type_overview_entry
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
|
type type_data = {
|
||||||
|
hits_core : Array<
|
||||||
|
{
|
||||||
|
key : int;
|
||||||
|
preview : Record<string, any>;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
hits_access_attributed_group : Array<
|
||||||
|
{
|
||||||
|
key : int;
|
||||||
|
preview : Record<string, any>;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
hits_access_attributed_user : Array<
|
||||||
|
{
|
||||||
|
key : int;
|
||||||
|
preview : Record<string, any>;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
};
|
||||||
return lib_plankton.cache.get_complex<any, Array<type_overview_entry>>(
|
return lib_plankton.cache.get_complex<any, Array<type_overview_entry>>(
|
||||||
_zeitbild.cache_regular,
|
_zeitbild.cache_regular,
|
||||||
"calendar_overview",
|
"calendar_overview",
|
||||||
{
|
{
|
||||||
"user_id": user_id,
|
"user_id": user_id,
|
||||||
},
|
},
|
||||||
null,
|
60,
|
||||||
() => (
|
async () => lib_plankton.call.convey(
|
||||||
lib_plankton.file.read("sql/calendar_overview.sql")
|
|
||||||
.then(
|
|
||||||
(template) => _zeitbild.database.get_implementation().query_free_get(
|
|
||||||
{
|
{
|
||||||
"template": template,
|
"hits_core": await get_core_store().search(
|
||||||
"arguments": {
|
{
|
||||||
"user_id": user_id,
|
"expression": "TRUE",
|
||||||
|
"arguments": {}
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
"hits_access_attributed_group": await get_access_attributed_group_chest().search(
|
||||||
|
(user_id === null)
|
||||||
|
?
|
||||||
|
{
|
||||||
|
"expression": "TRUE",
|
||||||
|
"arguments": {}
|
||||||
}
|
}
|
||||||
)
|
:
|
||||||
)
|
{
|
||||||
.then(
|
"expression": "(group_id IN (SELECT group_id FROM user_groups WHERE (user_id = $user_id)))",
|
||||||
(rows) => Promise.resolve(
|
"arguments": {"user_id": user_id}
|
||||||
lib_plankton.call.convey(
|
}
|
||||||
rows,
|
),
|
||||||
|
"hits_access_attributed_user": await get_access_attributed_user_chest().search(
|
||||||
|
(user_id === null)
|
||||||
|
?
|
||||||
|
{
|
||||||
|
"expression": "TRUE",
|
||||||
|
"arguments": {}
|
||||||
|
}
|
||||||
|
:
|
||||||
|
{
|
||||||
|
"expression": "(user_id = $user_id)",
|
||||||
|
"arguments": {"user_id": user_id}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
},
|
||||||
[
|
[
|
||||||
(x : Array<Record<string, any>>) => x.map(
|
// transform
|
||||||
(row : Record<string, any>) => ({
|
(data : type_data) => data.hits_core.map(
|
||||||
"id": row["id"],
|
(hit_core) => {
|
||||||
"name": row["name"],
|
const calendar_id : _zeitbild.type_calendar_id = hit_core.key;
|
||||||
/**
|
return {
|
||||||
* @todo unite with _zeitbild.service.calendar.get_access_level
|
"id": calendar_id,
|
||||||
*/
|
"name": hit_core.preview["name"],
|
||||||
"access_level": decode_access_level(
|
"hue": (hit_core.preview["hue"] / hue_scaling),
|
||||||
Math.max(
|
"access_level": _zeitbild.access_level_determine_raw(
|
||||||
(row["access_public"] ? 1 : 0),
|
hit_core.preview["access_public"],
|
||||||
(
|
(
|
||||||
(user_id === null)
|
(user_id === null)
|
||||||
?
|
?
|
||||||
0
|
null
|
||||||
:
|
:
|
||||||
(row["access_level_attributed"] ?? row["access_level_default"])
|
{
|
||||||
)
|
"default": decode_access_level(hit_core.preview["access_level_default"]),
|
||||||
|
"group": lib_plankton.call.convey(
|
||||||
|
data.hits_access_attributed_group,
|
||||||
|
[
|
||||||
|
(x : Array<{key : int; preview : Record<string, any>}>) => x.filter(
|
||||||
|
hit_access_attributed_group => (
|
||||||
|
(hit_access_attributed_group.preview.calendar_id === calendar_id)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
})
|
(x : Array<{key : int; preview : Record<string, any>}>) => x.map(
|
||||||
|
hit_access_attributed_group => hit_access_attributed_group.preview.level
|
||||||
),
|
),
|
||||||
|
(x : Array<int>) => x.map(
|
||||||
|
decode_access_level
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"user": lib_plankton.call.convey(
|
||||||
|
data.hits_access_attributed_user,
|
||||||
|
[
|
||||||
|
(x : Array<{key : int; preview : Record<string, any>}>) => x.filter(
|
||||||
|
hits_access_attributed_user => (
|
||||||
|
(hits_access_attributed_user.preview.calendar_id === calendar_id)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(x : Array<{key : int; preview : Record<string, any>}>) => x.map(
|
||||||
|
hits_access_attributed_user => hits_access_attributed_user.preview.level
|
||||||
|
),
|
||||||
|
(x : Array<int>) => x.map(
|
||||||
|
decode_access_level
|
||||||
|
),
|
||||||
|
(x : Array<_zeitbild.enum_access_level>) => (x[0] ?? null),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// only keep visible calendars
|
||||||
(x : Array<type_overview_entry>) => x.filter(
|
(x : Array<type_overview_entry>) => x.filter(
|
||||||
(row) => (
|
(row) => (
|
||||||
! _zeitbild.value_object.access_level.order(
|
! _zeitbild.access_level_order(
|
||||||
row.access_level,
|
row.access_level,
|
||||||
_zeitbild.enum_access_level.none
|
_zeitbild.enum_access_level.none
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
// sort by access level and name
|
||||||
(x : Array<type_overview_entry>) => lib_plankton.list.sorted<type_overview_entry>(
|
(x : Array<type_overview_entry>) => lib_plankton.list.sorted<type_overview_entry>(
|
||||||
x,
|
x,
|
||||||
{
|
{
|
||||||
|
|
@ -434,7 +796,7 @@ namespace _zeitbild.repository.calendar
|
||||||
row => row.access_level,
|
row => row.access_level,
|
||||||
row => row.id,
|
row => row.id,
|
||||||
{
|
{
|
||||||
"order_first": (a, b) => _zeitbild.value_object.access_level.order(b, a),
|
"order_first": (a, b) => _zeitbild.access_level_order(b, a),
|
||||||
"order_second": (a, b) => (a <= b)
|
"order_second": (a, b) => (a <= b)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
@ -442,10 +804,8 @@ namespace _zeitbild.repository.calendar
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
207
source/repositories/group.ts
Normal file
207
source/repositories/group.ts
Normal file
|
|
@ -0,0 +1,207 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace _zeitbild.repository.group
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_row = {
|
||||||
|
name : string;
|
||||||
|
label : string;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_preview = {
|
||||||
|
name : string;
|
||||||
|
label : string;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
var _store : (
|
||||||
|
null
|
||||||
|
|
|
||||||
|
lib_plankton.storage.type_store<
|
||||||
|
_zeitbild.type_user_id,
|
||||||
|
/*type_row*/Record<string, any>,
|
||||||
|
{},
|
||||||
|
lib_plankton.storage.type_sql_table_autokey_search_term,
|
||||||
|
/*type_preview*/Record<string, any>
|
||||||
|
>
|
||||||
|
) = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function get_store(
|
||||||
|
)
|
||||||
|
: lib_plankton.storage.type_store<
|
||||||
|
_zeitbild.type_user_id,
|
||||||
|
/*type_row*/Record<string, any>,
|
||||||
|
{},
|
||||||
|
lib_plankton.storage.type_sql_table_autokey_search_term,
|
||||||
|
/*type_preview*/Record<string, any>
|
||||||
|
>
|
||||||
|
{
|
||||||
|
if (_store === null)
|
||||||
|
{
|
||||||
|
_store = lib_plankton.storage.sql_table_autokey_store(
|
||||||
|
{
|
||||||
|
"database_implementation": _zeitbild.database.get_implementation(),
|
||||||
|
"table_name": "groups",
|
||||||
|
"key_name": "id",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
return _store;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function encode(
|
||||||
|
group_object : _zeitbild.type_group_object
|
||||||
|
)
|
||||||
|
: type_row
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"name": group_object.name,
|
||||||
|
"label": group_object.label,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function decode(
|
||||||
|
row : type_row
|
||||||
|
)
|
||||||
|
: _zeitbild.type_group_object
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"name": row.name,
|
||||||
|
"label": row.label,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export async function list(
|
||||||
|
)
|
||||||
|
: Promise<
|
||||||
|
Array<
|
||||||
|
{
|
||||||
|
id : _zeitbild.type_group_id;
|
||||||
|
object : _zeitbild.type_group_object;
|
||||||
|
}
|
||||||
|
>
|
||||||
|
>
|
||||||
|
{
|
||||||
|
const hits : Array<{key : int; preview : /*type_preview*/Record<string, any>;}> = await get_store().search({"expression": "TRUE", "arguments": {}});
|
||||||
|
return Promise.resolve(
|
||||||
|
hits
|
||||||
|
.map(
|
||||||
|
(hit) => ({
|
||||||
|
"id": hit.key,
|
||||||
|
"object": {
|
||||||
|
"name": hit.preview.name,
|
||||||
|
"label": hit.preview.label,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export async function read(
|
||||||
|
group_id : _zeitbild.type_group_id
|
||||||
|
)
|
||||||
|
: Promise<_zeitbild.type_group_object>
|
||||||
|
{
|
||||||
|
const row : type_row = ((await get_store().read(group_id)) as type_row);
|
||||||
|
const group_object : _zeitbild.type_group_object = decode(row);
|
||||||
|
return Promise.resolve<_zeitbild.type_group_object>(group_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export async function create(
|
||||||
|
group_object : _zeitbild.type_group_object
|
||||||
|
)
|
||||||
|
: Promise<_zeitbild.type_group_id>
|
||||||
|
{
|
||||||
|
const row : type_row = encode(group_object);
|
||||||
|
const group_id : _zeitbild.type_group_id = await get_store().create(row);
|
||||||
|
return Promise.resolve<_zeitbild.type_group_id>(group_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export async function update(
|
||||||
|
group_id : _zeitbild.type_group_id,
|
||||||
|
group_object : _zeitbild.type_group_object
|
||||||
|
)
|
||||||
|
: Promise<void>
|
||||||
|
{
|
||||||
|
const row : type_row = encode(group_object);
|
||||||
|
await get_store().update(group_id, row);
|
||||||
|
return Promise.resolve<void>(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export async function identify(
|
||||||
|
name : string
|
||||||
|
)
|
||||||
|
: Promise<_zeitbild.type_group_id>
|
||||||
|
{
|
||||||
|
const hits : Array<{key : _zeitbild.type_group_id; preview : /*type_preview*/Record<string, any>;}> = await get_store().search(
|
||||||
|
{
|
||||||
|
"expression": "(name = $name)",
|
||||||
|
"arguments": {
|
||||||
|
"name": name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (hits.length <= 0)
|
||||||
|
{
|
||||||
|
return Promise.reject<_zeitbild.type_group_id>(new Error("not found"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Promise.resolve<_zeitbild.type_group_id>(hits[0].key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,42 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.repository.resource
|
namespace _zeitbild.repository.resource
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_row = {
|
||||||
|
kind : string;
|
||||||
|
sub_id : int;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_preview = {
|
||||||
|
kind : string;
|
||||||
|
sub_id : int;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
type type_local_resource_event_stuff = {
|
type type_local_resource_event_stuff = {
|
||||||
|
|
@ -42,7 +77,7 @@ namespace _zeitbild.repository.resource
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
var _caldav_resource_store : (
|
var _ics_feed_resource_store : (
|
||||||
null
|
null
|
||||||
|
|
|
|
||||||
lib_plankton.storage.type_store<
|
lib_plankton.storage.type_store<
|
||||||
|
|
@ -62,10 +97,10 @@ namespace _zeitbild.repository.resource
|
||||||
|
|
|
|
||||||
lib_plankton.storage.type_store<
|
lib_plankton.storage.type_store<
|
||||||
_zeitbild.type_resource_id,
|
_zeitbild.type_resource_id,
|
||||||
Record<string, any>,
|
/*type_row*/Record<string, any>,
|
||||||
{},
|
{},
|
||||||
lib_plankton.storage.type_sql_table_autokey_search_term,
|
lib_plankton.storage.type_sql_table_autokey_search_term,
|
||||||
Record<string, any>
|
/*type_preview*/Record<string, any>
|
||||||
>
|
>
|
||||||
) = null;
|
) = null;
|
||||||
|
|
||||||
|
|
@ -126,7 +161,7 @@ namespace _zeitbild.repository.resource
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
function get_caldav_resource_store(
|
function get_ics_feed_resource_store(
|
||||||
) : lib_plankton.storage.type_store<
|
) : lib_plankton.storage.type_store<
|
||||||
int,
|
int,
|
||||||
Record<string, any>,
|
Record<string, any>,
|
||||||
|
|
@ -135,11 +170,11 @@ namespace _zeitbild.repository.resource
|
||||||
Record<string, any>
|
Record<string, any>
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
if (_caldav_resource_store === null) {
|
if (_ics_feed_resource_store === null) {
|
||||||
_caldav_resource_store = lib_plankton.storage.sql_table_autokey_store(
|
_ics_feed_resource_store = lib_plankton.storage.sql_table_autokey_store(
|
||||||
{
|
{
|
||||||
"database_implementation": _zeitbild.database.get_implementation(),
|
"database_implementation": _zeitbild.database.get_implementation(),
|
||||||
"table_name": "caldav_resources",
|
"table_name": "ics_feed_resources",
|
||||||
"key_name": "id",
|
"key_name": "id",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -147,7 +182,7 @@ namespace _zeitbild.repository.resource
|
||||||
else {
|
else {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
return _caldav_resource_store;
|
return _ics_feed_resource_store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -358,15 +393,14 @@ namespace _zeitbild.repository.resource
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case "caldav": {
|
case "ics_feed": {
|
||||||
const dataset_extra_caldav : Record<string, any> = await get_caldav_resource_store().read(dataset_core.sub_id);
|
const dataset_extra_ics_feed : Record<string, any> = await get_ics_feed_resource_store().read(dataset_core.sub_id);
|
||||||
return Promise.resolve<_zeitbild.type_resource_object>(
|
return Promise.resolve<_zeitbild.type_resource_object>(
|
||||||
{
|
{
|
||||||
"kind": "caldav",
|
"kind": "ics_feed",
|
||||||
"data": {
|
"data": {
|
||||||
"url": dataset_extra_caldav["url"],
|
"url": dataset_extra_ics_feed["url"],
|
||||||
"read_only": dataset_extra_caldav["read_only"],
|
"from_fucked_up_wordpress": dataset_extra_ics_feed["from_fucked_up_wordpress"],
|
||||||
"from_fucked_up_wordpress": dataset_extra_caldav["from_fucked_up_wordpress"],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -405,18 +439,17 @@ namespace _zeitbild.repository.resource
|
||||||
return Promise.resolve<_zeitbild.type_resource_id>(resource_id);
|
return Promise.resolve<_zeitbild.type_resource_id>(resource_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "caldav": {
|
case "ics_feed": {
|
||||||
const caldav_resource_id : int = await get_caldav_resource_store().create(
|
const ics_feed_resource_id : int = await get_ics_feed_resource_store().create(
|
||||||
{
|
{
|
||||||
"url": resource_object.data.url,
|
"url": resource_object.data.url,
|
||||||
"read_only": resource_object.data.read_only,
|
|
||||||
"from_fucked_up_wordpress": resource_object.data.from_fucked_up_wordpress,
|
"from_fucked_up_wordpress": resource_object.data.from_fucked_up_wordpress,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const resource_id : _zeitbild.type_resource_id = await get_resource_core_store().create(
|
const resource_id : _zeitbild.type_resource_id = await get_resource_core_store().create(
|
||||||
{
|
{
|
||||||
"kind": "caldav",
|
"kind": "ics_feed",
|
||||||
"sub_id": caldav_resource_id,
|
"sub_id": ics_feed_resource_id,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
await lib_plankton.cache.clear(_zeitbild.cache_regular);
|
await lib_plankton.cache.clear(_zeitbild.cache_regular);
|
||||||
|
|
@ -491,12 +524,12 @@ namespace _zeitbild.repository.resource
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "caldav": {
|
case "ics_feed": {
|
||||||
await get_caldav_resource_store().update(
|
await get_ics_feed_resource_store().update(
|
||||||
dataset_core["sub_id"],
|
dataset_core["sub_id"],
|
||||||
{
|
{
|
||||||
"url": resource_object.data.url,
|
"url": resource_object.data.url,
|
||||||
"read_only": resource_object.data.read_only,
|
"from_fucked_up_wordpress": resource_object.data.from_fucked_up_wordpress,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
await lib_plankton.cache.clear(_zeitbild.cache_regular);
|
await lib_plankton.cache.clear(_zeitbild.cache_regular);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
-- Für gewöhnlich würde man hier gruppieren. Aufgrund des UNIQUE-constraints in "calendar_access_attributed" ist das
|
|
||||||
-- jedoch nicht nötig, da dadurch für jeden Eintrag in "calendar" mit gegebener "user_id" höchstens ein Eintrag in
|
|
||||||
-- "calendar_access_attributed" passt und da es ein LEFT OUTER JOIN ist, wird es _genau_ ein Eintrag sein
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
x.id AS id,
|
|
||||||
x.name AS name,
|
|
||||||
x.access_public AS access_public,
|
|
||||||
x.access_level_default AS access_level_default,
|
|
||||||
y.level AS access_level_attributed
|
|
||||||
FROM
|
|
||||||
calendars AS x
|
|
||||||
LEFT OUTER JOIN calendar_access_attributed AS y ON ((x.id = y.calendar_id) AND (y.user_id = $user_id))
|
|
||||||
;
|
|
||||||
|
|
@ -1,15 +1,88 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.repository.user
|
namespace _zeitbild.repository.user
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
var _store : (
|
type type_core_row = {
|
||||||
|
name : string;
|
||||||
|
email_address : (null | string);
|
||||||
|
dav_token : (null | string);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_group_row_slim = {
|
||||||
|
group_id : int;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_group_row_fat = {
|
||||||
|
user_id : int;
|
||||||
|
group_id : int;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_preview = {
|
||||||
|
name : string;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_dispersal = {
|
||||||
|
core : type_core_row;
|
||||||
|
groups : Array<type_group_row_slim>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
var _store_core : (
|
||||||
null
|
null
|
||||||
|
|
|
|
||||||
lib_plankton.storage.type_store<
|
lib_plankton.storage.type_store<
|
||||||
_zeitbild.type_user_id,
|
_zeitbild.type_user_id,
|
||||||
Record<string, any>,
|
/*type_core_row*/Record<string, any>,
|
||||||
|
{},
|
||||||
|
lib_plankton.storage.type_sql_table_autokey_search_term,
|
||||||
|
/*type_preview*/Record<string, any>
|
||||||
|
>
|
||||||
|
) = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
var _store_groups : (
|
||||||
|
null
|
||||||
|
|
|
||||||
|
lib_plankton.storage.type_store<
|
||||||
|
int,
|
||||||
|
/*type_group_row_fat*/Record<string, any>,
|
||||||
{},
|
{},
|
||||||
lib_plankton.storage.type_sql_table_autokey_search_term,
|
lib_plankton.storage.type_sql_table_autokey_search_term,
|
||||||
Record<string, any>
|
Record<string, any>
|
||||||
|
|
@ -19,17 +92,19 @@ namespace _zeitbild.repository.user
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
function get_store(
|
function get_store_core(
|
||||||
) : lib_plankton.storage.type_store<
|
)
|
||||||
|
: lib_plankton.storage.type_store<
|
||||||
_zeitbild.type_user_id,
|
_zeitbild.type_user_id,
|
||||||
Record<string, any>,
|
/*type_core_row*/Record<string, any>,
|
||||||
{},
|
{},
|
||||||
lib_plankton.storage.type_sql_table_autokey_search_term,
|
lib_plankton.storage.type_sql_table_autokey_search_term,
|
||||||
Record<string, any>
|
/*type_preview*/Record<string, any>
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
if (_store === null) {
|
if (_store_core === null)
|
||||||
_store = lib_plankton.storage.sql_table_autokey_store(
|
{
|
||||||
|
_store_core = lib_plankton.storage.sql_table_autokey_store(
|
||||||
{
|
{
|
||||||
"database_implementation": _zeitbild.database.get_implementation(),
|
"database_implementation": _zeitbild.database.get_implementation(),
|
||||||
"table_name": "users",
|
"table_name": "users",
|
||||||
|
|
@ -37,10 +112,41 @@ namespace _zeitbild.repository.user
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
return _store;
|
return _store_core;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function get_store_groups(
|
||||||
|
)
|
||||||
|
: lib_plankton.storage.type_store<
|
||||||
|
int,
|
||||||
|
/*type_group_row_fat*/Record<string, any>,
|
||||||
|
{},
|
||||||
|
lib_plankton.storage.type_sql_table_autokey_search_term,
|
||||||
|
Record<string, any>
|
||||||
|
>
|
||||||
|
{
|
||||||
|
if (_store_groups === null)
|
||||||
|
{
|
||||||
|
_store_groups = lib_plankton.storage.sql_table_autokey_store(
|
||||||
|
{
|
||||||
|
"database_implementation": _zeitbild.database.get_implementation(),
|
||||||
|
"table_name": "user_groups",
|
||||||
|
"key_name": "id",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
return _store_groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -48,11 +154,24 @@ namespace _zeitbild.repository.user
|
||||||
*/
|
*/
|
||||||
function encode(
|
function encode(
|
||||||
user_object : _zeitbild.type_user_object
|
user_object : _zeitbild.type_user_object
|
||||||
) : Record<string, any>
|
)
|
||||||
|
: type_dispersal
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
"core": {
|
||||||
"name": user_object.name,
|
"name": user_object.name,
|
||||||
"email_address": user_object.email_address,
|
"email_address": user_object.email_address,
|
||||||
|
"dav_token": user_object.dav_token,
|
||||||
|
},
|
||||||
|
"groups": (
|
||||||
|
user_object.groups.map(
|
||||||
|
group_id => (
|
||||||
|
{
|
||||||
|
"group_id": group_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,12 +179,19 @@ namespace _zeitbild.repository.user
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
function decode(
|
function decode(
|
||||||
row : Record<string, any>
|
dispersal : type_dispersal
|
||||||
) : _zeitbild.type_user_object
|
)
|
||||||
|
: _zeitbild.type_user_object
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
"name": row["name"],
|
"name": dispersal.core.name,
|
||||||
"email_address": row["email_address"],
|
"groups": (
|
||||||
|
dispersal.groups.map(
|
||||||
|
group_row => group_row.group_id,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"email_address": dispersal.core.email_address,
|
||||||
|
"dav_token": dispersal.core.dav_token,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +199,8 @@ namespace _zeitbild.repository.user
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export async function list(
|
export async function list(
|
||||||
) : Promise<
|
)
|
||||||
|
: Promise<
|
||||||
Array<
|
Array<
|
||||||
{
|
{
|
||||||
id : _zeitbild.type_user_id;
|
id : _zeitbild.type_user_id;
|
||||||
|
|
@ -82,7 +209,12 @@ namespace _zeitbild.repository.user
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
const hits : Array<{key : int; preview : Record<string, any>;}> = await get_store().search({"expression": "TRUE", "arguments": {}});
|
const hits : Array<{key : int; preview : /*type_preview*/Record<string, any>;}> = await get_store_core().search(
|
||||||
|
{
|
||||||
|
"expression": "TRUE",
|
||||||
|
"arguments": {}
|
||||||
|
}
|
||||||
|
);
|
||||||
return Promise.resolve(
|
return Promise.resolve(
|
||||||
hits
|
hits
|
||||||
.map(
|
.map(
|
||||||
|
|
@ -99,10 +231,43 @@ namespace _zeitbild.repository.user
|
||||||
*/
|
*/
|
||||||
export async function read(
|
export async function read(
|
||||||
user_id : _zeitbild.type_user_id
|
user_id : _zeitbild.type_user_id
|
||||||
) : Promise<_zeitbild.type_user_object>
|
)
|
||||||
|
: Promise<_zeitbild.type_user_object>
|
||||||
{
|
{
|
||||||
const row : Record<string, any> = await get_store().read(user_id);
|
const core_row : type_core_row = ((await get_store_core().read(user_id)) as type_core_row);
|
||||||
const user_object : _zeitbild.type_user_object = decode(row);
|
const group_rows : Array<type_group_row_fat> = (
|
||||||
|
(
|
||||||
|
await get_store_groups().search(
|
||||||
|
{
|
||||||
|
"expression": "(user_id = $user_id)",
|
||||||
|
"arguments": {
|
||||||
|
"user_id": user_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
hit => (
|
||||||
|
{
|
||||||
|
"user_id": hit.preview.user_id,
|
||||||
|
"group_id": hit.preview.group_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const dispersal : type_dispersal = {
|
||||||
|
"core": core_row,
|
||||||
|
"groups": (
|
||||||
|
group_rows.map(
|
||||||
|
group_row_fat => (
|
||||||
|
{
|
||||||
|
"group_id": group_row_fat.group_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
const user_object : _zeitbild.type_user_object = decode(dispersal);
|
||||||
return Promise.resolve<_zeitbild.type_user_object>(user_object);
|
return Promise.resolve<_zeitbild.type_user_object>(user_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,21 +276,100 @@ namespace _zeitbild.repository.user
|
||||||
*/
|
*/
|
||||||
export async function create(
|
export async function create(
|
||||||
user_object : _zeitbild.type_user_object
|
user_object : _zeitbild.type_user_object
|
||||||
) : Promise<_zeitbild.type_user_id>
|
)
|
||||||
|
: Promise<_zeitbild.type_user_id>
|
||||||
{
|
{
|
||||||
const row : Record<string, any> = encode(user_object);
|
const dispersal : type_dispersal = encode(user_object);
|
||||||
const user_id : _zeitbild.type_user_id = await get_store().create(row);
|
// core
|
||||||
|
const user_id : _zeitbild.type_user_id = await (() => {
|
||||||
|
return get_store_core().create(dispersal.core);
|
||||||
|
}) ();
|
||||||
|
// groups
|
||||||
|
{
|
||||||
|
for (const group_row_slim of dispersal.groups)
|
||||||
|
{
|
||||||
|
const group_row_fat : type_group_row_fat = {
|
||||||
|
"user_id": user_id,
|
||||||
|
"group_id": group_row_slim.group_id,
|
||||||
|
};
|
||||||
|
await get_store_groups().create(group_row_fat);
|
||||||
|
}
|
||||||
|
}
|
||||||
return Promise.resolve<_zeitbild.type_user_id>(user_id);
|
return Promise.resolve<_zeitbild.type_user_id>(user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export async function update(
|
||||||
|
user_id : _zeitbild.type_user_id,
|
||||||
|
user_object : _zeitbild.type_user_object
|
||||||
|
)
|
||||||
|
: Promise<void>
|
||||||
|
{
|
||||||
|
const dispersal : type_dispersal = encode(user_object);
|
||||||
|
// core
|
||||||
|
{
|
||||||
|
await get_store_core().update(user_id, dispersal.core);
|
||||||
|
}
|
||||||
|
// groups
|
||||||
|
{
|
||||||
|
const hits : Array<{key : int; preview : Record<string, any>;}> = await get_store_groups().search(
|
||||||
|
{
|
||||||
|
"expression": "(user_id = $user_id)",
|
||||||
|
"arguments": {
|
||||||
|
"user_id": user_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const contrast = lib_plankton.list.contrast(
|
||||||
|
hits,
|
||||||
|
hit => hit.preview.group_id.toFixed(0),
|
||||||
|
dispersal.groups,
|
||||||
|
group_row_slim => group_row_slim.group_id.toFixed(0)
|
||||||
|
);
|
||||||
|
// delete
|
||||||
|
{
|
||||||
|
for (const entry of contrast.only_left)
|
||||||
|
{
|
||||||
|
await get_store_groups().delete(entry.left.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update
|
||||||
|
{
|
||||||
|
for (const entry of contrast.both)
|
||||||
|
{
|
||||||
|
const row_group_fat : type_group_row_fat = {
|
||||||
|
"user_id": user_id,
|
||||||
|
"group_id": entry.right.group_id,
|
||||||
|
};
|
||||||
|
await get_store_groups().update(entry.left.key, row_group_fat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create
|
||||||
|
{
|
||||||
|
for (const entry of contrast.only_right)
|
||||||
|
{
|
||||||
|
const row_group_fat : type_group_row_fat = {
|
||||||
|
"user_id": user_id,
|
||||||
|
"group_id": entry.right.group_id,
|
||||||
|
};
|
||||||
|
await get_store_groups().create(row_group_fat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.resolve<void>(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export async function identify(
|
export async function identify(
|
||||||
name : string
|
name : string
|
||||||
) : Promise<_zeitbild.type_user_id>
|
)
|
||||||
|
: Promise<_zeitbild.type_user_id>
|
||||||
{
|
{
|
||||||
const hits : Array<{key : _zeitbild.type_user_id; preview : any;}> = await get_store().search(
|
const hits : Array<{key : _zeitbild.type_user_id; preview : /*type_preview*/Record<string, any>;}> = await get_store_core().search(
|
||||||
{
|
{
|
||||||
"expression": "(name = $name)",
|
"expression": "(name = $name)",
|
||||||
"arguments": {
|
"arguments": {
|
||||||
|
|
@ -133,10 +377,12 @@ namespace _zeitbild.repository.user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (hits.length <= 0) {
|
if (hits.length <= 0)
|
||||||
|
{
|
||||||
return Promise.reject<_zeitbild.type_user_id>(new Error("not found"));
|
return Promise.reject<_zeitbild.type_user_id>(new Error("not found"));
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
return Promise.resolve<_zeitbild.type_user_id>(hits[0].key);
|
return Promise.resolve<_zeitbild.type_user_id>(hits[0].key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
403
source/sample.ts
Normal file
403
source/sample.ts
Normal file
|
|
@ -0,0 +1,403 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace _zeitbild.sample
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_date_absolute = {
|
||||||
|
year : int;
|
||||||
|
month : int;
|
||||||
|
day : int;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_date_relative = Array<
|
||||||
|
{
|
||||||
|
action : "trunc_week";
|
||||||
|
args : [int];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
{
|
||||||
|
action : "shift_week";
|
||||||
|
args : [int];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
{
|
||||||
|
action : "shift_day";
|
||||||
|
args : [int];
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_ywd = {
|
||||||
|
year : int;
|
||||||
|
week : int;
|
||||||
|
day : int;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_time = {
|
||||||
|
hour : int;
|
||||||
|
minute : int;
|
||||||
|
second : int;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_datetime = (
|
||||||
|
{
|
||||||
|
timezone_shift : int;
|
||||||
|
time : (null | type_time);
|
||||||
|
}
|
||||||
|
&
|
||||||
|
(
|
||||||
|
{
|
||||||
|
date_absolute : type_date_absolute;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
{
|
||||||
|
date_relative : type_date_relative;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
type type_data = {
|
||||||
|
groups : Array<
|
||||||
|
{
|
||||||
|
id : int;
|
||||||
|
name : string;
|
||||||
|
label ?: string;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
users : Array<
|
||||||
|
{
|
||||||
|
id : int;
|
||||||
|
name : string;
|
||||||
|
groups ?: Array<int>;
|
||||||
|
email_address : string;
|
||||||
|
dav_token ?: (null | string);
|
||||||
|
password : string;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
calendars : Array<
|
||||||
|
{
|
||||||
|
id : int;
|
||||||
|
name : string;
|
||||||
|
access : {
|
||||||
|
public ?: boolean;
|
||||||
|
default_level : ("none" | "view" | "edit" | "admin");
|
||||||
|
attributed_group ?: Array<
|
||||||
|
{
|
||||||
|
group_id : int;
|
||||||
|
level : ("none" | "view" | "edit" | "admin");
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
attributed_user ?: Array<
|
||||||
|
{
|
||||||
|
user_id : int;
|
||||||
|
level : ("none" | "view" | "edit" | "admin");
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
resource : (
|
||||||
|
{
|
||||||
|
kind : "local";
|
||||||
|
data : {
|
||||||
|
events : Array<
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @todo rename to "title"
|
||||||
|
*/
|
||||||
|
name : string;
|
||||||
|
begin : type_datetime;
|
||||||
|
end : (
|
||||||
|
null
|
||||||
|
|
|
||||||
|
type_datetime
|
||||||
|
);
|
||||||
|
location : (
|
||||||
|
null
|
||||||
|
|
|
||||||
|
string
|
||||||
|
);
|
||||||
|
link : (
|
||||||
|
null
|
||||||
|
|
|
||||||
|
string
|
||||||
|
);
|
||||||
|
description : (
|
||||||
|
null
|
||||||
|
|
|
||||||
|
string
|
||||||
|
);
|
||||||
|
}
|
||||||
|
>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
{
|
||||||
|
kind : "ics_feed";
|
||||||
|
data : {
|
||||||
|
url : string;
|
||||||
|
from_fucked_up_wordpress ?: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
hue ?: (null | float);
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
const phi : float = ((Math.sqrt(5) - 1) / 2);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function decode_datetime(
|
||||||
|
datetime : type_datetime
|
||||||
|
) : lib_plankton.pit.type_datetime
|
||||||
|
{
|
||||||
|
if ("date_relative" in datetime)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"timezone_shift": datetime.timezone_shift,
|
||||||
|
"date": lib_plankton.call.convey(
|
||||||
|
lib_plankton.pit.now(),
|
||||||
|
(
|
||||||
|
datetime.date_relative.map<(x : any) => any>(
|
||||||
|
entry => {
|
||||||
|
switch (entry.action)
|
||||||
|
{
|
||||||
|
// default: {throw (new Error("unhandled action: " + entry.action));}
|
||||||
|
case "trunc_week": {return (x => lib_plankton.pit.trunc_week(x));}
|
||||||
|
case "shift_week": {return (x => lib_plankton.pit.shift_week(x, entry.args[0]));}
|
||||||
|
case "shift_day": {return (x => lib_plankton.pit.shift_day(x, entry.args[0]));}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.concat(
|
||||||
|
[
|
||||||
|
lib_plankton.pit.to_datetime,
|
||||||
|
x => x.date,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"time": datetime.time,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"timezone_shift": datetime.timezone_shift,
|
||||||
|
"date": datetime.date_absolute,
|
||||||
|
"time": datetime.time,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export async function init(
|
||||||
|
data : type_data
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
let track : {
|
||||||
|
group : Record<
|
||||||
|
int,
|
||||||
|
_zeitbild.type_group_id
|
||||||
|
>;
|
||||||
|
user : Record<
|
||||||
|
int,
|
||||||
|
_zeitbild.type_user_id
|
||||||
|
>;
|
||||||
|
calendar : Record<
|
||||||
|
int,
|
||||||
|
_zeitbild.type_user_id
|
||||||
|
>;
|
||||||
|
} = {
|
||||||
|
"group": {},
|
||||||
|
"user": {},
|
||||||
|
"calendar": {},
|
||||||
|
};
|
||||||
|
// groups
|
||||||
|
{
|
||||||
|
for await (const group_raw of data.groups)
|
||||||
|
{
|
||||||
|
const group_object : _zeitbild.type_group_object = {
|
||||||
|
"name": group_raw.name,
|
||||||
|
"label": (group_raw.label ?? group_raw.name),
|
||||||
|
};
|
||||||
|
const group_id : _zeitbild.type_group_id = await _zeitbild.service.group.add(
|
||||||
|
group_object
|
||||||
|
);
|
||||||
|
track.group[group_raw.id] = group_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// users
|
||||||
|
{
|
||||||
|
for await (const user_raw of data.users)
|
||||||
|
{
|
||||||
|
const user_object : _zeitbild.type_user_object = {
|
||||||
|
"name": user_raw.name,
|
||||||
|
"groups": (user_raw.groups ?? []),
|
||||||
|
"email_address": user_raw.email_address,
|
||||||
|
"dav_token": (user_raw.dav_token ?? null),
|
||||||
|
};
|
||||||
|
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.add(
|
||||||
|
user_object
|
||||||
|
);
|
||||||
|
await _zeitbild.service.auth_internal.set(
|
||||||
|
user_raw.name,
|
||||||
|
user_raw.password
|
||||||
|
);
|
||||||
|
track.user[user_raw.id] = user_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// calendars
|
||||||
|
{
|
||||||
|
for await (const calendar_raw of data.calendars)
|
||||||
|
{
|
||||||
|
let resource_object : _zeitbild.type_resource_object;
|
||||||
|
let resource_id : _zeitbild.type_resource_id;
|
||||||
|
switch (calendar_raw.resource.kind)
|
||||||
|
{
|
||||||
|
case "local":
|
||||||
|
{
|
||||||
|
resource_object = {
|
||||||
|
"kind": "local",
|
||||||
|
"data": {
|
||||||
|
"event_ids": [],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
resource_id = await _zeitbild.service.resource.add(
|
||||||
|
resource_object
|
||||||
|
);
|
||||||
|
/*const event_ids : Array<_zeitbild.type_local_resource_event_id> = */await Promise.all(
|
||||||
|
calendar_raw.resource.data.events
|
||||||
|
.map(
|
||||||
|
(event_raw) => {
|
||||||
|
const event : _zeitbild.type_event_object = {
|
||||||
|
"name": event_raw.name,
|
||||||
|
"begin": decode_datetime(event_raw.begin),
|
||||||
|
"end": (
|
||||||
|
(event_raw.end === null)
|
||||||
|
?
|
||||||
|
null
|
||||||
|
:
|
||||||
|
decode_datetime(event_raw.end)
|
||||||
|
),
|
||||||
|
"location": event_raw.location,
|
||||||
|
"link": event_raw.link,
|
||||||
|
"description": event_raw.description,
|
||||||
|
};
|
||||||
|
return _zeitbild.service.resource.event_add(resource_id, event);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "ics_feed":
|
||||||
|
{
|
||||||
|
resource_object = {
|
||||||
|
"kind": "ics_feed",
|
||||||
|
"data": {
|
||||||
|
"url": calendar_raw.resource.data.url,
|
||||||
|
"from_fucked_up_wordpress": (calendar_raw.resource.data.from_fucked_up_wordpress ?? false),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
resource_id = await _zeitbild.service.resource.add(
|
||||||
|
resource_object
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const calendar_object : _zeitbild.type_calendar_object =
|
||||||
|
{
|
||||||
|
"name": calendar_raw.name,
|
||||||
|
"hue": (
|
||||||
|
calendar_raw.hue
|
||||||
|
??
|
||||||
|
((calendar_raw.id * phi) % 1)
|
||||||
|
),
|
||||||
|
"access": {
|
||||||
|
"public": (calendar_raw.access.public ?? false),
|
||||||
|
"default_level": _zeitbild.access_level_from_string(calendar_raw.access.default_level),
|
||||||
|
"attributed_group": lib_plankton.map.hashmap.implementation_map(
|
||||||
|
lib_plankton.map.hashmap.make(
|
||||||
|
x => x.toFixed(0),
|
||||||
|
{
|
||||||
|
"pairs": (
|
||||||
|
(calendar_raw.access.attributed_group ?? [])
|
||||||
|
.map(
|
||||||
|
(entry) => ({
|
||||||
|
"key": track.user[entry.group_id],
|
||||||
|
"value": _zeitbild.access_level_from_string(entry.level),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"attributed_user": lib_plankton.map.hashmap.implementation_map(
|
||||||
|
lib_plankton.map.hashmap.make(
|
||||||
|
x => x.toFixed(0),
|
||||||
|
{
|
||||||
|
"pairs": (
|
||||||
|
(calendar_raw.access.attributed_user ?? [])
|
||||||
|
.map(
|
||||||
|
(entry) => ({
|
||||||
|
"key": track.user[entry.user_id],
|
||||||
|
"value": _zeitbild.access_level_from_string(entry.level),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"resource_id": resource_id,
|
||||||
|
};
|
||||||
|
const calendar_id : _zeitbild.type_calendar_id = await _zeitbild.service.calendar.add(
|
||||||
|
calendar_object
|
||||||
|
);
|
||||||
|
track.calendar[calendar_raw.id] = calendar_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.resolve<void>(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.service.auth_internal
|
namespace _zeitbild.service.auth_internal
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,46 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.service.calendar
|
namespace _zeitbild.service.calendar
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
function get_access_level(
|
async function get_access_level(
|
||||||
calendar_object : _zeitbild.type_calendar_object,
|
calendar_object : _zeitbild.type_calendar_object,
|
||||||
user_id : (null | _zeitbild.type_user_id)
|
user_id : (null | _zeitbild.type_user_id)
|
||||||
) : _zeitbild.enum_access_level
|
)
|
||||||
|
: Promise<_zeitbild.enum_access_level>
|
||||||
{
|
{
|
||||||
return (
|
return _zeitbild.access_level_determine(
|
||||||
lib_plankton.list.max<_zeitbild.enum_access_level, _zeitbild.enum_access_level>(
|
calendar_object,
|
||||||
[
|
|
||||||
(
|
|
||||||
calendar_object.access.public
|
|
||||||
?
|
|
||||||
_zeitbild.enum_access_level.view
|
|
||||||
:
|
|
||||||
_zeitbild.enum_access_level.none
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
(user_id === null)
|
(user_id === null)
|
||||||
?
|
?
|
||||||
_zeitbild.enum_access_level.none
|
null
|
||||||
:
|
:
|
||||||
calendar_object.access.attributed.get(
|
|
||||||
user_id,
|
|
||||||
lib_plankton.pod.make_filled<_zeitbild.enum_access_level>(
|
|
||||||
calendar_object.access.default_level
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
x => x,
|
|
||||||
{
|
{
|
||||||
"compare_value": _zeitbild.value_object.access_level.order,
|
"id": user_id,
|
||||||
|
"object": (await _zeitbild.service.user.get(user_id)),
|
||||||
}
|
}
|
||||||
)?.value
|
)
|
||||||
??
|
|
||||||
_zeitbild.enum_access_level.none
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,7 +48,7 @@ namespace _zeitbild.service.calendar
|
||||||
/**
|
/**
|
||||||
* checks if a user has a sufficient access level
|
* checks if a user has a sufficient access level
|
||||||
*/
|
*/
|
||||||
function wrap_check_access_level<type_result>(
|
async function wrap_check_access_level<type_result>(
|
||||||
calendar_object : _zeitbild.type_calendar_object,
|
calendar_object : _zeitbild.type_calendar_object,
|
||||||
user_id : (null | _zeitbild.type_user_id),
|
user_id : (null | _zeitbild.type_user_id),
|
||||||
threshold : _zeitbild.enum_access_level,
|
threshold : _zeitbild.enum_access_level,
|
||||||
|
|
@ -55,26 +57,29 @@ namespace _zeitbild.service.calendar
|
||||||
=>
|
=>
|
||||||
Promise<type_result>
|
Promise<type_result>
|
||||||
)
|
)
|
||||||
) : Promise<type_result>
|
)
|
||||||
|
: Promise<type_result>
|
||||||
{
|
{
|
||||||
const access_level : _zeitbild.enum_access_level = get_access_level(
|
const access_level : _zeitbild.enum_access_level = await get_access_level(
|
||||||
calendar_object,
|
calendar_object,
|
||||||
user_id
|
user_id
|
||||||
);
|
);
|
||||||
if (! _zeitbild.value_object.access_level.order(threshold, access_level)) {
|
if (! _zeitbild.access_level_order(threshold, access_level))
|
||||||
|
{
|
||||||
return Promise.reject<type_result>(
|
return Promise.reject<type_result>(
|
||||||
new Error(
|
new Error(
|
||||||
lib_plankton.string.coin(
|
lib_plankton.string.coin(
|
||||||
"insufficient access level; at least required: {{threshold}}, actual: {{actual}}",
|
"insufficient access level; at least required: {{threshold}}, actual: {{actual}}",
|
||||||
{
|
{
|
||||||
"threshold": _zeitbild.value_object.access_level.to_string(threshold),
|
"threshold": _zeitbild.access_level_to_string(threshold),
|
||||||
"actual": _zeitbild.value_object.access_level.to_string(access_level),
|
"actual": _zeitbild.access_level_to_string(access_level),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
return success_handler(access_level);
|
return success_handler(access_level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -90,6 +95,7 @@ namespace _zeitbild.service.calendar
|
||||||
id : _zeitbild.type_calendar_id;
|
id : _zeitbild.type_calendar_id;
|
||||||
name : string;
|
name : string;
|
||||||
access_level : _zeitbild.enum_access_level;
|
access_level : _zeitbild.enum_access_level;
|
||||||
|
hue : float;
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
|
|
@ -192,21 +198,31 @@ namespace _zeitbild.service.calendar
|
||||||
calendar_id : _zeitbild.type_calendar_id,
|
calendar_id : _zeitbild.type_calendar_id,
|
||||||
event_object : _zeitbild.type_event_object,
|
event_object : _zeitbild.type_event_object,
|
||||||
user_id : _zeitbild.type_user_id
|
user_id : _zeitbild.type_user_id
|
||||||
) : Promise<void>
|
) : Promise<
|
||||||
|
{
|
||||||
|
local_resource_event_id : (null | type_local_resource_event_id);
|
||||||
|
hash : type_event_hash;
|
||||||
|
}
|
||||||
|
>
|
||||||
{
|
{
|
||||||
const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read(
|
const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read(
|
||||||
calendar_id
|
calendar_id
|
||||||
);
|
);
|
||||||
return wrap_check_access_level<void>(
|
return wrap_check_access_level(
|
||||||
calendar_object,
|
calendar_object,
|
||||||
user_id,
|
user_id,
|
||||||
_zeitbild.enum_access_level.edit,
|
_zeitbild.enum_access_level.edit,
|
||||||
async () => {
|
async () => {
|
||||||
/*const event_id : _zeitbild.type_local_resource_event_id = */await _zeitbild.service.resource.event_add(
|
const local_resource_event_id : _zeitbild.type_local_resource_event_id = await _zeitbild.service.resource.event_add(
|
||||||
calendar_object.resource_id,
|
calendar_object.resource_id,
|
||||||
event_object
|
event_object
|
||||||
);
|
);
|
||||||
return Promise.resolve<void>(undefined);
|
return Promise.resolve(
|
||||||
|
{
|
||||||
|
"local_resource_event_id": local_resource_event_id,
|
||||||
|
"hash": get_event_hash_local(calendar_id, local_resource_event_id),
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -266,6 +282,45 @@ namespace _zeitbild.service.calendar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function get_event_hash_local(
|
||||||
|
calendar_id : _zeitbild.type_calendar_id,
|
||||||
|
local_resource_event_id : _zeitbild.type_local_resource_event_id
|
||||||
|
) : string
|
||||||
|
{
|
||||||
|
return lib_plankton.string.coin(
|
||||||
|
"{{calendar_id}}:{{event_id}}",
|
||||||
|
{
|
||||||
|
"calendar_id": calendar_id.toFixed(0),
|
||||||
|
"event_id": local_resource_event_id.toFixed(0),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
function get_event_hash_ics_feed(
|
||||||
|
calendar_id : _zeitbild.type_calendar_id,
|
||||||
|
event_object : _zeitbild.type_event_object
|
||||||
|
) : string
|
||||||
|
{
|
||||||
|
return lib_plankton.string.coin(
|
||||||
|
"{{calendar_id}}~{{hash}}",
|
||||||
|
{
|
||||||
|
"calendar_id": calendar_id.toFixed(0),
|
||||||
|
"hash": lib_plankton.call.convey(
|
||||||
|
event_object,
|
||||||
|
[
|
||||||
|
(x : any) => lib_plankton.json.encode(x),
|
||||||
|
(x : string) => lib_plankton.base64.encode(x),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo optimize by reducing the number of database queries
|
* @todo optimize by reducing the number of database queries
|
||||||
*/
|
*/
|
||||||
|
|
@ -278,13 +333,14 @@ namespace _zeitbild.service.calendar
|
||||||
Array<
|
Array<
|
||||||
{
|
{
|
||||||
id : (null | _zeitbild.type_local_resource_event_id);
|
id : (null | _zeitbild.type_local_resource_event_id);
|
||||||
|
hash : _zeitbild.type_event_hash;
|
||||||
object : _zeitbild.type_event_object;
|
object : _zeitbild.type_event_object;
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read(calendar_id);
|
const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read(calendar_id);
|
||||||
return wrap_check_access_level<Array<{id : (null | _zeitbild.type_local_resource_event_id); object : _zeitbild.type_event_object;}>>(
|
return wrap_check_access_level<Array<{id : (null | _zeitbild.type_local_resource_event_id); hash : _zeitbild.type_event_hash; object : _zeitbild.type_event_object;}>>(
|
||||||
calendar_object,
|
calendar_object,
|
||||||
user_id,
|
user_id,
|
||||||
_zeitbild.enum_access_level.view,
|
_zeitbild.enum_access_level.view,
|
||||||
|
|
@ -305,6 +361,10 @@ namespace _zeitbild.service.calendar
|
||||||
(event_object) => Promise.resolve(
|
(event_object) => Promise.resolve(
|
||||||
{
|
{
|
||||||
"id": event_id,
|
"id": event_id,
|
||||||
|
"hash": get_event_hash_local(
|
||||||
|
calendar_id,
|
||||||
|
event_id,
|
||||||
|
),
|
||||||
"object": event_object,
|
"object": event_object,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -327,8 +387,13 @@ namespace _zeitbild.service.calendar
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "caldav": {
|
case "ics_feed": {
|
||||||
// TODO readonly
|
// TODO readonly
|
||||||
|
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(
|
const url : lib_plankton.url.type_url = lib_plankton.url.decode(
|
||||||
resource_object.data.url
|
resource_object.data.url
|
||||||
);
|
);
|
||||||
|
|
@ -342,13 +407,18 @@ namespace _zeitbild.service.calendar
|
||||||
"headers": {},
|
"headers": {},
|
||||||
"body": null,
|
"body": null,
|
||||||
};
|
};
|
||||||
// TODO: cache?
|
|
||||||
const http_response : lib_plankton.http.type_response = await lib_plankton.http.call(
|
const http_response : lib_plankton.http.type_response = await lib_plankton.http.call(
|
||||||
http_request,
|
http_request,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const ics_raw : string = http_response.body.toString();
|
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(
|
const vcalendar_list : Array<lib_plankton.ical.type_vcalendar> = lib_plankton.ical.ics_decode_multi(
|
||||||
ics_raw,
|
ics_raw,
|
||||||
{
|
{
|
||||||
|
|
@ -362,6 +432,9 @@ namespace _zeitbild.service.calendar
|
||||||
"prodid": vcalendar_list[0].prodid,
|
"prodid": vcalendar_list[0].prodid,
|
||||||
"vevents": vcalendar_list.map(x => x.vevents).reduce((x, y) => x.concat(y), []),
|
"vevents": vcalendar_list.map(x => x.vevents).reduce((x, y) => x.concat(y), []),
|
||||||
};
|
};
|
||||||
|
return Promise.resolve<lib_plankton.ical.type_vcalendar>(vcalendar);
|
||||||
|
}
|
||||||
|
);
|
||||||
return Promise.resolve(
|
return Promise.resolve(
|
||||||
vcalendar.vevents
|
vcalendar.vevents
|
||||||
.map(
|
.map(
|
||||||
|
|
@ -376,11 +449,11 @@ namespace _zeitbild.service.calendar
|
||||||
:
|
:
|
||||||
"???"
|
"???"
|
||||||
),
|
),
|
||||||
"begin": _zeitbild.helpers.ical_dt_to_own_datetime(vevent.dtstart),
|
"begin": _zeitbild.helpers.icalendar_dt_to_own_datetime(vevent.dtstart),
|
||||||
"end": (
|
"end": (
|
||||||
(vevent.dtend !== undefined)
|
(vevent.dtend !== undefined)
|
||||||
?
|
?
|
||||||
_zeitbild.helpers.ical_dt_to_own_datetime(vevent.dtend)
|
_zeitbild.helpers.icalendar_dt_to_own_datetime(vevent.dtend)
|
||||||
:
|
:
|
||||||
null
|
null
|
||||||
),
|
),
|
||||||
|
|
@ -416,6 +489,7 @@ namespace _zeitbild.service.calendar
|
||||||
.map(
|
.map(
|
||||||
(event) => ({
|
(event) => ({
|
||||||
"id": null,
|
"id": null,
|
||||||
|
"hash": get_event_hash_ics_feed(calendar_id, event),
|
||||||
"object": event,
|
"object": event,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
@ -441,19 +515,6 @@ namespace _zeitbild.service.calendar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
type type_gather_events_result = Array<
|
|
||||||
{
|
|
||||||
calendar_id : _zeitbild.type_calendar_id;
|
|
||||||
calendar_name : string;
|
|
||||||
access_level : _zeitbild.enum_access_level;
|
|
||||||
event_id : (null | _zeitbild.type_local_resource_event_id);
|
|
||||||
event_object : _zeitbild.type_event_object;
|
|
||||||
}
|
|
||||||
>;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export async function gather_events(
|
export async function gather_events(
|
||||||
|
|
@ -461,7 +522,7 @@ namespace _zeitbild.service.calendar
|
||||||
from_pit : lib_plankton.pit.type_pit,
|
from_pit : lib_plankton.pit.type_pit,
|
||||||
to_pit : lib_plankton.pit.type_pit,
|
to_pit : lib_plankton.pit.type_pit,
|
||||||
user_id : (null | _zeitbild.type_user_id)
|
user_id : (null | _zeitbild.type_user_id)
|
||||||
) : Promise<type_gather_events_result>
|
) : Promise<Array<_zeitbild.type_event_extended>>
|
||||||
{
|
{
|
||||||
const calendar_ids_allowed : Array<_zeitbild.type_calendar_id> = (
|
const calendar_ids_allowed : Array<_zeitbild.type_calendar_id> = (
|
||||||
(await overview(user_id))
|
(await overview(user_id))
|
||||||
|
|
@ -483,8 +544,8 @@ 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, Array<_zeitbild.type_event_extended>>(
|
||||||
_zeitbild.cache,
|
_zeitbild.cache_regular,
|
||||||
"gather_events",
|
"gather_events",
|
||||||
{
|
{
|
||||||
"user_id": user_id,
|
"user_id": user_id,
|
||||||
|
|
@ -492,6 +553,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
|
||||||
|
|
@ -500,13 +565,14 @@ namespace _zeitbild.service.calendar
|
||||||
const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read(
|
const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read(
|
||||||
calendar_id
|
calendar_id
|
||||||
);
|
);
|
||||||
const access_level : _zeitbild.enum_access_level = get_access_level(
|
const access_level : _zeitbild.enum_access_level = await get_access_level(
|
||||||
calendar_object,
|
calendar_object,
|
||||||
user_id
|
user_id
|
||||||
);
|
);
|
||||||
const events : Array<
|
const events : Array<
|
||||||
{
|
{
|
||||||
id : (null | _zeitbild.type_local_resource_event_id);
|
id : (null | _zeitbild.type_local_resource_event_id);
|
||||||
|
hash : _zeitbild.type_event_hash;
|
||||||
object : _zeitbild.type_event_object;
|
object : _zeitbild.type_event_object;
|
||||||
}
|
}
|
||||||
> = await get_events(
|
> = await get_events(
|
||||||
|
|
@ -519,8 +585,10 @@ namespace _zeitbild.service.calendar
|
||||||
events
|
events
|
||||||
.map(
|
.map(
|
||||||
(event_entry) => ({
|
(event_entry) => ({
|
||||||
|
"hash": event_entry.hash,
|
||||||
"calendar_id": calendar_id,
|
"calendar_id": calendar_id,
|
||||||
"calendar_name": calendar_object.name,
|
"calendar_name": calendar_object.name,
|
||||||
|
"hue": calendar_object.hue,
|
||||||
"access_level": access_level,
|
"access_level": access_level,
|
||||||
"event_id": event_entry.id,
|
"event_id": event_entry.id,
|
||||||
"event_object": event_entry.object,
|
"event_object": event_entry.object,
|
||||||
|
|
|
||||||
63
source/services/group.ts
Normal file
63
source/services/group.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace _zeitbild.service.group
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function list(
|
||||||
|
)
|
||||||
|
: Promise<
|
||||||
|
Array<
|
||||||
|
{
|
||||||
|
id : _zeitbild.type_group_id;
|
||||||
|
object : _zeitbild.type_group_object;
|
||||||
|
}
|
||||||
|
>
|
||||||
|
>
|
||||||
|
{
|
||||||
|
return _zeitbild.repository.group.list();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function add(
|
||||||
|
group_object : _zeitbild.type_group_object
|
||||||
|
)
|
||||||
|
: Promise<_zeitbild.type_group_id>
|
||||||
|
{
|
||||||
|
return _zeitbild.repository.group.create(group_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function change(
|
||||||
|
group_id : _zeitbild.type_group_id,
|
||||||
|
group_object : _zeitbild.type_group_object
|
||||||
|
)
|
||||||
|
: Promise<void>
|
||||||
|
{
|
||||||
|
return _zeitbild.repository.group.update(group_id, group_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.service.resource
|
namespace _zeitbild.service.resource
|
||||||
{
|
{
|
||||||
|
|
@ -31,7 +50,7 @@ namespace _zeitbild.service.resource
|
||||||
return Promise.resolve<_zeitbild.type_event_object>(event_object);
|
return Promise.resolve<_zeitbild.type_event_object>(event_object);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "caldav": {
|
case "ics_feed": {
|
||||||
// TODO
|
// TODO
|
||||||
return Promise.reject(new Error("not implemented"));
|
return Promise.reject(new Error("not implemented"));
|
||||||
break;
|
break;
|
||||||
|
|
@ -63,14 +82,8 @@ namespace _zeitbild.service.resource
|
||||||
return Promise.resolve<_zeitbild.type_local_resource_event_id>(local_resource_event_id);
|
return Promise.resolve<_zeitbild.type_local_resource_event_id>(local_resource_event_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "caldav": {
|
case "ics_feed": {
|
||||||
if (resource_object.data.read_only) {
|
return Promise.reject(new Error("unavailable"));
|
||||||
return Promise.reject(new Error("can not add event to read only caldav resource"));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// TODO
|
|
||||||
return Promise.reject(new Error("not implemented"));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
|
@ -102,14 +115,8 @@ namespace _zeitbild.service.resource
|
||||||
return Promise.resolve<void>(undefined);
|
return Promise.resolve<void>(undefined);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "caldav": {
|
case "ics_feed": {
|
||||||
if (resource_object.data.read_only) {
|
return Promise.reject(new Error("unavailable"));
|
||||||
return Promise.reject(new Error("can not change event of read only caldav resource"));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// TODO
|
|
||||||
return Promise.reject(new Error("not implemented"));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
|
@ -139,14 +146,8 @@ namespace _zeitbild.service.resource
|
||||||
return Promise.resolve<void>(undefined);
|
return Promise.resolve<void>(undefined);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "caldav": {
|
case "ics_feed": {
|
||||||
if (resource_object.data.read_only) {
|
return Promise.reject(new Error("unavailable"));
|
||||||
return Promise.reject(new Error("can not delete event from read only caldav resource"));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// TODO
|
|
||||||
return Promise.reject(new Error("not implemented"));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
namespace _zeitbild.service.user
|
namespace _zeitbild.service.user
|
||||||
{
|
{
|
||||||
|
|
@ -28,6 +47,16 @@ namespace _zeitbild.service.user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function get(
|
||||||
|
user_id : _zeitbild.type_user_id
|
||||||
|
) : Promise<_zeitbild.type_user_object>
|
||||||
|
{
|
||||||
|
return _zeitbild.repository.user.read(user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export function add(
|
export function add(
|
||||||
|
|
@ -37,4 +66,15 @@ namespace _zeitbild.service.user
|
||||||
return _zeitbild.repository.user.create(user_object);
|
return _zeitbild.repository.user.create(user_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export function change(
|
||||||
|
user_id : _zeitbild.type_user_id,
|
||||||
|
user_object : _zeitbild.type_user_object
|
||||||
|
) : Promise<void>
|
||||||
|
{
|
||||||
|
return _zeitbild.repository.user.update(user_id, user_object);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
This file is part of »zeitbild«.
|
||||||
|
|
||||||
|
Copyright 2025 'kcf' <fenris@folksprak.org>
|
||||||
|
|
||||||
|
»zeitbild« 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.
|
||||||
|
|
||||||
|
»zeitbild« 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 »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
|
@ -14,6 +33,19 @@ namespace _zeitbild
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export type type_group_id = int;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export type type_group_object = {
|
||||||
|
name : string;
|
||||||
|
label : string;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export type type_user_id = int;
|
export type type_user_id = int;
|
||||||
|
|
@ -23,17 +55,28 @@ namespace _zeitbild
|
||||||
*/
|
*/
|
||||||
export type type_user_object = {
|
export type type_user_object = {
|
||||||
name : string;
|
name : string;
|
||||||
|
groups : Array<
|
||||||
|
type_group_id
|
||||||
|
>;
|
||||||
email_address : (
|
email_address : (
|
||||||
null
|
null
|
||||||
|
|
|
|
||||||
string
|
string
|
||||||
);
|
);
|
||||||
|
dav_token : (
|
||||||
|
null
|
||||||
|
|
|
||||||
|
string
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export type type_event_object = {
|
export type type_event_object = {
|
||||||
|
/**
|
||||||
|
* @todo rename to "title"
|
||||||
|
*/
|
||||||
name : string;
|
name : string;
|
||||||
begin : lib_plankton.pit.type_datetime;
|
begin : lib_plankton.pit.type_datetime;
|
||||||
end : (
|
end : (
|
||||||
|
|
@ -82,10 +125,9 @@ namespace _zeitbild
|
||||||
}
|
}
|
||||||
|
|
|
|
||||||
{
|
{
|
||||||
kind : "caldav";
|
kind : "ics_feed";
|
||||||
data : {
|
data : {
|
||||||
url : string;
|
url : string;
|
||||||
read_only : boolean;
|
|
||||||
from_fucked_up_wordpress : boolean;
|
from_fucked_up_wordpress : boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -101,10 +143,15 @@ namespace _zeitbild
|
||||||
*/
|
*/
|
||||||
export type type_calendar_object = {
|
export type type_calendar_object = {
|
||||||
name : string;
|
name : string;
|
||||||
|
hue : float;
|
||||||
access : {
|
access : {
|
||||||
public : boolean;
|
public : boolean;
|
||||||
default_level : enum_access_level;
|
default_level : enum_access_level;
|
||||||
attributed : lib_plankton.map.type_map<
|
attributed_group : lib_plankton.map.type_map<
|
||||||
|
type_group_id,
|
||||||
|
enum_access_level
|
||||||
|
>;
|
||||||
|
attributed_user : lib_plankton.map.type_map<
|
||||||
type_user_id,
|
type_user_id,
|
||||||
enum_access_level
|
enum_access_level
|
||||||
>;
|
>;
|
||||||
|
|
@ -112,4 +159,22 @@ namespace _zeitbild
|
||||||
resource_id : type_resource_id;
|
resource_id : type_resource_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export type type_event_hash = string;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export type type_event_extended = {
|
||||||
|
hash : type_event_hash;
|
||||||
|
calendar_id : type_calendar_id;
|
||||||
|
calendar_name : string;
|
||||||
|
hue : float;
|
||||||
|
access_level : enum_access_level;
|
||||||
|
event_id : (null | type_local_resource_event_id);
|
||||||
|
event_object : type_event_object;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
namespace _zeitbild.value_object.access_level
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
export function to_string(
|
|
||||||
access_level : _zeitbild.enum_access_level
|
|
||||||
) : string
|
|
||||||
{
|
|
||||||
switch (access_level) {
|
|
||||||
case _zeitbild.enum_access_level.none: {return "none";}
|
|
||||||
case _zeitbild.enum_access_level.view: {return "view";}
|
|
||||||
case _zeitbild.enum_access_level.edit: {return "edit";}
|
|
||||||
case _zeitbild.enum_access_level.admin: {return "admin";}
|
|
||||||
default: {throw (new Error("invalid access level: " + String(access_level)));}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
export function from_string(
|
|
||||||
access_level_ : string
|
|
||||||
) : _zeitbild.enum_access_level
|
|
||||||
{
|
|
||||||
switch (access_level_) {
|
|
||||||
case "none": {return _zeitbild.enum_access_level.none;}
|
|
||||||
case "view": {return _zeitbild.enum_access_level.view;}
|
|
||||||
case "edit": {return _zeitbild.enum_access_level.edit;}
|
|
||||||
case "admin": {return _zeitbild.enum_access_level.admin;}
|
|
||||||
default: {throw (new Error("invalid encoded access level: " + String(access_level_)));}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
export function order(
|
|
||||||
x : _zeitbild.enum_access_level,
|
|
||||||
y : _zeitbild.enum_access_level
|
|
||||||
) : boolean
|
|
||||||
{
|
|
||||||
const list : Array<_zeitbild.enum_access_level> = [
|
|
||||||
_zeitbild.enum_access_level.none,
|
|
||||||
_zeitbild.enum_access_level.view,
|
|
||||||
_zeitbild.enum_access_level.edit,
|
|
||||||
_zeitbild.enum_access_level.admin,
|
|
||||||
];
|
|
||||||
return (list.indexOf(x) <= list.indexOf(y));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -47,6 +47,7 @@ def main():
|
||||||
"--verbose",
|
"--verbose",
|
||||||
"--exclude='conf.json'",
|
"--exclude='conf.json'",
|
||||||
"--exclude='data.sqlite'",
|
"--exclude='data.sqlite'",
|
||||||
|
"--exclude='log.jsonl'",
|
||||||
("%s/" % args.build_directory),
|
("%s/" % args.build_directory),
|
||||||
(
|
(
|
||||||
("%s" % args.target_directory)
|
("%s" % args.target_directory)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ cmd_tsc := ${dir_tools}/typescript/node_modules/.bin/tsc
|
||||||
## rules
|
## rules
|
||||||
|
|
||||||
.PHONY: default
|
.PHONY: default
|
||||||
default: node_modules sql ${dir_build}/zeitbild node_modules
|
default: node_modules ${dir_build}/zeitbild node_modules
|
||||||
|
|
||||||
.PHONY: node_modules
|
.PHONY: node_modules
|
||||||
node_modules:
|
node_modules:
|
||||||
|
|
@ -26,13 +26,6 @@ node_modules:
|
||||||
@ ${cmd_log} "node modules …"
|
@ ${cmd_log} "node modules …"
|
||||||
@ ${cmd_cp} -r -u ${dir_lib}/node/node_modules/* ${dir_build}/node_modules/
|
@ ${cmd_cp} -r -u ${dir_lib}/node/node_modules/* ${dir_build}/node_modules/
|
||||||
|
|
||||||
.PHONY: sql
|
|
||||||
sql: \
|
|
||||||
$(wildcard ${dir_source}/repositories/sql/*)
|
|
||||||
@ ${cmd_log} "sql …"
|
|
||||||
@ ${cmd_mkdir} ${dir_build}/sql
|
|
||||||
@ ${cmd_cp} -r -u $^ ${dir_build}/sql/
|
|
||||||
|
|
||||||
${dir_temp}/conf.ts: \
|
${dir_temp}/conf.ts: \
|
||||||
${dir_source}/conf.ts.tpl \
|
${dir_source}/conf.ts.tpl \
|
||||||
${dir_source}/conf.schema.json
|
${dir_source}/conf.schema.json
|
||||||
|
|
@ -47,12 +40,14 @@ ${dir_temp}/zeitbild-unlinked.js: \
|
||||||
${dir_source}/database.ts \
|
${dir_source}/database.ts \
|
||||||
${dir_source}/auth.ts \
|
${dir_source}/auth.ts \
|
||||||
${dir_source}/types.ts \
|
${dir_source}/types.ts \
|
||||||
${dir_source}/value_objects/access_level.ts \
|
${dir_source}/logic.ts \
|
||||||
${dir_source}/repositories/auth_internal.ts \
|
${dir_source}/repositories/auth_internal.ts \
|
||||||
|
${dir_source}/repositories/group.ts \
|
||||||
${dir_source}/repositories/user.ts \
|
${dir_source}/repositories/user.ts \
|
||||||
${dir_source}/repositories/resource.ts \
|
${dir_source}/repositories/resource.ts \
|
||||||
${dir_source}/repositories/calendar.ts \
|
${dir_source}/repositories/calendar.ts \
|
||||||
${dir_source}/services/auth_internal.ts \
|
${dir_source}/services/auth_internal.ts \
|
||||||
|
${dir_source}/services/group.ts \
|
||||||
${dir_source}/services/user.ts \
|
${dir_source}/services/user.ts \
|
||||||
${dir_source}/services/resource.ts \
|
${dir_source}/services/resource.ts \
|
||||||
${dir_source}/services/calendar.ts \
|
${dir_source}/services/calendar.ts \
|
||||||
|
|
@ -64,7 +59,11 @@ ${dir_temp}/zeitbild-unlinked.js: \
|
||||||
${dir_source}/api/actions/session_begin.ts \
|
${dir_source}/api/actions/session_begin.ts \
|
||||||
${dir_source}/api/actions/session_oidc.ts \
|
${dir_source}/api/actions/session_oidc.ts \
|
||||||
${dir_source}/api/actions/session_end.ts \
|
${dir_source}/api/actions/session_end.ts \
|
||||||
|
${dir_source}/api/actions/session_status.ts \
|
||||||
|
${dir_source}/api/actions/group_list.ts \
|
||||||
${dir_source}/api/actions/users.ts \
|
${dir_source}/api/actions/users.ts \
|
||||||
|
${dir_source}/api/actions/user_dav_conf.ts \
|
||||||
|
${dir_source}/api/actions/user_dav_token.ts \
|
||||||
${dir_source}/api/actions/calendar_list.ts \
|
${dir_source}/api/actions/calendar_list.ts \
|
||||||
${dir_source}/api/actions/calendar_get.ts \
|
${dir_source}/api/actions/calendar_get.ts \
|
||||||
${dir_source}/api/actions/calendar_add.ts \
|
${dir_source}/api/actions/calendar_add.ts \
|
||||||
|
|
@ -75,7 +74,9 @@ ${dir_temp}/zeitbild-unlinked.js: \
|
||||||
${dir_source}/api/actions/calendar_event_change.ts \
|
${dir_source}/api/actions/calendar_event_change.ts \
|
||||||
${dir_source}/api/actions/calendar_event_remove.ts \
|
${dir_source}/api/actions/calendar_event_remove.ts \
|
||||||
${dir_source}/api/actions/events.ts \
|
${dir_source}/api/actions/events.ts \
|
||||||
|
${dir_source}/api/actions/export_ics.ts \
|
||||||
${dir_source}/api/functions.ts \
|
${dir_source}/api/functions.ts \
|
||||||
|
${dir_source}/sample.ts \
|
||||||
${dir_source}/main.ts
|
${dir_source}/main.ts
|
||||||
@ ${cmd_log} "compile …"
|
@ ${cmd_log} "compile …"
|
||||||
@ ${cmd_mkdir} $(dir $@)
|
@ ${cmd_mkdir} $(dir $@)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ modules="${modules} session"
|
||||||
modules="${modules} file"
|
modules="${modules} file"
|
||||||
modules="${modules} string"
|
modules="${modules} string"
|
||||||
modules="${modules} json"
|
modules="${modules} json"
|
||||||
|
modules="${modules} base64"
|
||||||
modules="${modules} list"
|
modules="${modules} list"
|
||||||
modules="${modules} order"
|
modules="${modules} order"
|
||||||
modules="${modules} ical"
|
modules="${modules} ical"
|
||||||
|
|
@ -43,8 +44,8 @@ mkdir -p ${dir}
|
||||||
mkdir /tmp/sandbox -p
|
mkdir /tmp/sandbox -p
|
||||||
cd /tmp/sandbox
|
cd /tmp/sandbox
|
||||||
ptk fetch node ${modules}
|
ptk fetch node ${modules}
|
||||||
schwamm --include=/tmp/sandboxplankton.swm.json --output=dump:logic-decl > ${dir}/plankton.d.ts
|
schwamm --include=/tmp/sandbox/plankton.swm.json --output=dump:logic-decl > ${dir}/plankton.d.ts
|
||||||
schwamm --include=/tmp/sandboxplankton.swm.json --output=dump:logic-impl > ${dir}/plankton.js
|
schwamm --include=/tmp/sandbox/plankton.swm.json --output=dump:logic-impl > ${dir}/plankton.js
|
||||||
exit
|
exit
|
||||||
|
|
||||||
mkdir -p ${dir}
|
mkdir -p ${dir}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue