[task-192]
## Tasks - [192](https://vikunja.ramsch.sx/tasks/192) ## Zugehörige MRs - [datamodel](misc/zeitbild-datamodel#1) - [frontend-dali](misc/zeitbild-frontend-dali#1) Reviewed-on: misc/zeitbild-backend#1 Co-authored-by: Fenris Wolf <fenris@folksprak.org> Co-committed-by: Fenris Wolf <fenris@folksprak.org>
This commit is contained in:
parent
39fbe6af80
commit
f90567d043
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"version": 1,
|
||||
"log": [
|
||||
{
|
||||
"kind": "stdout",
|
||||
"data": {
|
||||
"threshold": "info"
|
||||
}
|
||||
}
|
||||
],
|
||||
"session_management": {
|
||||
"in_memory": false,
|
||||
"lifetime": 3600
|
||||
},
|
||||
"authentication": {
|
||||
"kind": "internal",
|
||||
"data": {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,19 +3,22 @@
|
|||
{
|
||||
"id": 1,
|
||||
"name": "alice",
|
||||
"email_address": "alice@example.org",
|
||||
"email_address": "alice@example.org",
|
||||
"dav_token": null,
|
||||
"password": "alice"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "bob",
|
||||
"email_address": "bob@example.org",
|
||||
"email_address": "bob@example.org",
|
||||
"dav_token": "bob_dav",
|
||||
"password": "bob"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "charlie",
|
||||
"email_address": "charlie@example.org",
|
||||
"email_address": "charlie@example.org",
|
||||
"dav_token": null,
|
||||
"password": "charlie"
|
||||
}
|
||||
],
|
||||
|
|
|
|||
58
misc/conf-example.json
Normal file
58
misc/conf-example.json
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"version": 1,
|
||||
"log": [
|
||||
{
|
||||
"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/-/lampukistan",
|
||||
"username": "lampukistan-{{username}}",
|
||||
"password": "{{password}}",
|
||||
"setup_hints": [
|
||||
{
|
||||
"label": "Android",
|
||||
"link": "https://www.android-user.de/caldavcarddav-kalender-und-adressbuecher-ohne-google-synchronisieren/",
|
||||
"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://www.united-domains.de/help/faq-article/wie-synchronisiere-ich-meinen-kalender-caldav-mit-ms-outlook/",
|
||||
"remark": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -29,10 +29,9 @@ namespace _zeitbild.api
|
|||
}
|
||||
|
|
||||
{
|
||||
kind : "caldav";
|
||||
kind : "ics_feed";
|
||||
data : {
|
||||
url : string;
|
||||
read_only : boolean;
|
||||
from_fucked_up_wordpress : boolean;
|
||||
};
|
||||
}
|
||||
|
|
@ -75,12 +74,11 @@ namespace _zeitbild.api
|
|||
};
|
||||
break;
|
||||
}
|
||||
case "caldav": {
|
||||
case "ics_feed": {
|
||||
resource_object = {
|
||||
"kind": "caldav",
|
||||
"kind": "ics_feed",
|
||||
"data": {
|
||||
"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,
|
||||
}
|
||||
};
|
||||
|
|
|
|||
127
source/api/actions/export_ics.ts
Normal file
127
source/api/actions/export_ics.ts
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
|
||||
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),
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -74,6 +74,7 @@ namespace _zeitbild.api
|
|||
{
|
||||
"name": data.userinfo.name,
|
||||
"email_address": data.userinfo.email,
|
||||
"dav_token": null,
|
||||
}
|
||||
);
|
||||
lib_plankton.log.info(
|
||||
|
|
|
|||
152
source/api/actions/user_dav_conf.ts
Normal file
152
source/api/actions/user_dav_conf.ts
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
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,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
53
source/api/actions/user_dav_token.ts
Normal file
53
source/api/actions/user_dav_token.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
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,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -31,6 +31,15 @@ namespace _zeitbild.api
|
|||
_zeitbild.api.register_session_end(rest_subject);
|
||||
_zeitbild.api.register_session_oidc(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
|
||||
{
|
||||
_zeitbild.api.register_calendar_list(rest_subject);
|
||||
|
|
@ -46,13 +55,15 @@ namespace _zeitbild.api
|
|||
_zeitbild.api.register_calendar_event_remove(rest_subject);
|
||||
}
|
||||
}
|
||||
// export
|
||||
{
|
||||
_zeitbild.api.register_export_ics(rest_subject);
|
||||
}
|
||||
// misc
|
||||
{
|
||||
_zeitbild.api.register_users(rest_subject);
|
||||
_zeitbild.api.register_events(rest_subject);
|
||||
}
|
||||
|
||||
|
||||
return rest_subject;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -282,7 +282,95 @@ namespace _zeitbild.conf
|
|||
"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": [
|
||||
"version"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace _zeitbild.helpers
|
|||
/**
|
||||
* @todo timezone
|
||||
*/
|
||||
function ical_datetime_to_own_datetime(
|
||||
function icalendar_datetime_to_own_datetime(
|
||||
ical_datetime : lib_plankton.ical.type_datetime
|
||||
) : lib_plankton.pit.type_datetime
|
||||
{
|
||||
|
|
@ -31,12 +31,12 @@ namespace _zeitbild.helpers
|
|||
)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @todo timezone
|
||||
*/
|
||||
export function ical_dt_to_own_datetime(
|
||||
export function icalendar_dt_to_own_datetime(
|
||||
ical_dt: lib_plankton.ical.type_dt
|
||||
) : lib_plankton.pit.type_datetime
|
||||
{
|
||||
|
|
@ -58,6 +58,125 @@ namespace _zeitbild.helpers
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo timezone
|
||||
*/
|
||||
export function icalendar_dt_from_own_datetime(
|
||||
datetime : lib_plankton.pit.type_datetime
|
||||
) : lib_plankton.ical.type_dt
|
||||
{
|
||||
return {
|
||||
"tzid": "Europe/Berlin",
|
||||
"value": {
|
||||
"date": {
|
||||
"year": datetime.date.year,
|
||||
"month": datetime.date.month,
|
||||
"day": datetime.date.day,
|
||||
},
|
||||
"time": (
|
||||
(datetime.time === null)
|
||||
?
|
||||
null
|
||||
:
|
||||
{
|
||||
"utc": true,
|
||||
"hour": datetime.time.hour,
|
||||
"minute": datetime.time.minute,
|
||||
"second": datetime.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(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ type type_data = {
|
|||
id : int;
|
||||
name : string;
|
||||
email_address : string;
|
||||
dav_token : (null | string);
|
||||
password : string;
|
||||
}
|
||||
>;
|
||||
|
|
@ -36,10 +37,9 @@ type type_data = {
|
|||
}
|
||||
|
|
||||
{
|
||||
kind : "caldav";
|
||||
kind : "ics_feed";
|
||||
data : {
|
||||
url : string;
|
||||
read_only : boolean;
|
||||
from_fucked_up_wordpress ?: boolean;
|
||||
};
|
||||
}
|
||||
|
|
@ -73,6 +73,7 @@ async function data_init(
|
|||
const user_object : _zeitbild.type_user_object = {
|
||||
"name": user_raw.name,
|
||||
"email_address": user_raw.email_address,
|
||||
"dav_token": user_raw.dav_token,
|
||||
};
|
||||
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.add(
|
||||
user_object
|
||||
|
|
@ -108,13 +109,12 @@ async function data_init(
|
|||
);
|
||||
break;
|
||||
}
|
||||
case "caldav":
|
||||
case "ics_feed":
|
||||
{
|
||||
resource_object = {
|
||||
"kind": "caldav",
|
||||
"kind": "ics_feed",
|
||||
"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),
|
||||
}
|
||||
};
|
||||
|
|
@ -384,7 +384,6 @@ async function main(
|
|||
case "api-doc": {
|
||||
lib_plankton.log.set_main_logger([]);
|
||||
const rest_subject : lib_plankton.rest_http.type_rest = _zeitbild.api.make();
|
||||
lib_plankton.log.conf_pop();
|
||||
process.stdout.write(
|
||||
JSON.stringify(
|
||||
lib_plankton.rest_http.to_oas(rest_subject),
|
||||
|
|
@ -480,6 +479,7 @@ async function main(
|
|||
)
|
||||
.catch(
|
||||
(error) => {
|
||||
// console.error(error);
|
||||
process.stderr.write(String(error) + "\n");
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ namespace _zeitbild.repository.resource
|
|||
|
||||
/**
|
||||
*/
|
||||
var _caldav_resource_store : (
|
||||
var _ics_feed_resource_store : (
|
||||
null
|
||||
|
|
||||
lib_plankton.storage.type_store<
|
||||
|
|
@ -126,7 +126,7 @@ namespace _zeitbild.repository.resource
|
|||
|
||||
/**
|
||||
*/
|
||||
function get_caldav_resource_store(
|
||||
function get_ics_feed_resource_store(
|
||||
) : lib_plankton.storage.type_store<
|
||||
int,
|
||||
Record<string, any>,
|
||||
|
|
@ -135,11 +135,11 @@ namespace _zeitbild.repository.resource
|
|||
Record<string, any>
|
||||
>
|
||||
{
|
||||
if (_caldav_resource_store === null) {
|
||||
_caldav_resource_store = lib_plankton.storage.sql_table_autokey_store(
|
||||
if (_ics_feed_resource_store === null) {
|
||||
_ics_feed_resource_store = lib_plankton.storage.sql_table_autokey_store(
|
||||
{
|
||||
"database_implementation": _zeitbild.database.get_implementation(),
|
||||
"table_name": "caldav_resources",
|
||||
"table_name": "ics_feed_resources",
|
||||
"key_name": "id",
|
||||
}
|
||||
);
|
||||
|
|
@ -147,7 +147,7 @@ namespace _zeitbild.repository.resource
|
|||
else {
|
||||
// do nothing
|
||||
}
|
||||
return _caldav_resource_store;
|
||||
return _ics_feed_resource_store;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -358,15 +358,14 @@ namespace _zeitbild.repository.resource
|
|||
}
|
||||
);
|
||||
}
|
||||
case "caldav": {
|
||||
const dataset_extra_caldav : Record<string, any> = await get_caldav_resource_store().read(dataset_core.sub_id);
|
||||
case "ics_feed": {
|
||||
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>(
|
||||
{
|
||||
"kind": "caldav",
|
||||
"kind": "ics_feed",
|
||||
"data": {
|
||||
"url": dataset_extra_caldav["url"],
|
||||
"read_only": dataset_extra_caldav["read_only"],
|
||||
"from_fucked_up_wordpress": dataset_extra_caldav["from_fucked_up_wordpress"],
|
||||
"url": dataset_extra_ics_feed["url"],
|
||||
"from_fucked_up_wordpress": dataset_extra_ics_feed["from_fucked_up_wordpress"],
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
@ -405,18 +404,17 @@ namespace _zeitbild.repository.resource
|
|||
return Promise.resolve<_zeitbild.type_resource_id>(resource_id);
|
||||
break;
|
||||
}
|
||||
case "caldav": {
|
||||
const caldav_resource_id : int = await get_caldav_resource_store().create(
|
||||
case "ics_feed": {
|
||||
const ics_feed_resource_id : int = await get_ics_feed_resource_store().create(
|
||||
{
|
||||
"url": resource_object.data.url,
|
||||
"read_only": resource_object.data.read_only,
|
||||
"from_fucked_up_wordpress": resource_object.data.from_fucked_up_wordpress,
|
||||
}
|
||||
);
|
||||
const resource_id : _zeitbild.type_resource_id = await get_resource_core_store().create(
|
||||
{
|
||||
"kind": "caldav",
|
||||
"sub_id": caldav_resource_id,
|
||||
"kind": "ics_feed",
|
||||
"sub_id": ics_feed_resource_id,
|
||||
}
|
||||
);
|
||||
await lib_plankton.cache.clear(_zeitbild.cache_regular);
|
||||
|
|
@ -491,12 +489,12 @@ namespace _zeitbild.repository.resource
|
|||
*/
|
||||
break;
|
||||
}
|
||||
case "caldav": {
|
||||
await get_caldav_resource_store().update(
|
||||
case "ics_feed": {
|
||||
await get_ics_feed_resource_store().update(
|
||||
dataset_core["sub_id"],
|
||||
{
|
||||
"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);
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ namespace _zeitbild.repository.user
|
|||
return {
|
||||
"name": user_object.name,
|
||||
"email_address": user_object.email_address,
|
||||
"dav_token": user_object.dav_token,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -66,6 +67,7 @@ namespace _zeitbild.repository.user
|
|||
return {
|
||||
"name": row["name"],
|
||||
"email_address": row["email_address"],
|
||||
"dav_token": row["dav_token"],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +121,19 @@ namespace _zeitbild.repository.user
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function update(
|
||||
user_id : _zeitbild.type_user_id,
|
||||
user_object : _zeitbild.type_user_object
|
||||
) : Promise<void>
|
||||
{
|
||||
const dispersal : Record<string, any> = encode(user_object);
|
||||
await get_store().update(user_id, dispersal);
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function identify(
|
||||
|
|
|
|||
|
|
@ -327,41 +327,54 @@ namespace _zeitbild.service.calendar
|
|||
);
|
||||
break;
|
||||
}
|
||||
case "caldav": {
|
||||
case "ics_feed": {
|
||||
// TODO readonly
|
||||
const url : lib_plankton.url.type_url = lib_plankton.url.decode(
|
||||
resource_object.data.url
|
||||
);
|
||||
const http_request : lib_plankton.http.type_request = {
|
||||
"version": "HTTP/2",
|
||||
"scheme": ((url.scheme === "https") ? "https" : "http"),
|
||||
"host": url.host,
|
||||
"path": (url.path ?? "/"),
|
||||
"query": url.query,
|
||||
"method": lib_plankton.http.enum_method.get,
|
||||
"headers": {},
|
||||
"body": null,
|
||||
};
|
||||
// TODO: cache?
|
||||
const http_response : lib_plankton.http.type_response = await lib_plankton.http.call(
|
||||
http_request,
|
||||
{
|
||||
const vcalendar : lib_plankton.ical.type_vcalendar = await lib_plankton.cache.get<lib_plankton.ical.type_vcalendar>(
|
||||
_zeitbild.cache_external_resources,
|
||||
resource_object.data.url,
|
||||
_zeitbild.conf.get().external_resources.lifetime,
|
||||
async () => {
|
||||
const url : lib_plankton.url.type_url = lib_plankton.url.decode(
|
||||
resource_object.data.url
|
||||
);
|
||||
const http_request : lib_plankton.http.type_request = {
|
||||
"version": "HTTP/2",
|
||||
"scheme": ((url.scheme === "https") ? "https" : "http"),
|
||||
"host": url.host,
|
||||
"path": (url.path ?? "/"),
|
||||
"query": url.query,
|
||||
"method": lib_plankton.http.enum_method.get,
|
||||
"headers": {},
|
||||
"body": null,
|
||||
};
|
||||
const http_response : lib_plankton.http.type_response = await lib_plankton.http.call(
|
||||
http_request,
|
||||
{
|
||||
}
|
||||
);
|
||||
const ics_raw : string = (
|
||||
(http_response.body === null)
|
||||
?
|
||||
""
|
||||
:
|
||||
http_response.body.toString()
|
||||
);
|
||||
const vcalendar_list : Array<lib_plankton.ical.type_vcalendar> = lib_plankton.ical.ics_decode_multi(
|
||||
ics_raw,
|
||||
{
|
||||
"ignore_unhandled_instruction_keys": resource_object.data.from_fucked_up_wordpress,
|
||||
"from_fucked_up_wordpress": resource_object.data.from_fucked_up_wordpress,
|
||||
}
|
||||
);
|
||||
const vcalendar : lib_plankton.ical.type_vcalendar = {
|
||||
// required
|
||||
"version": vcalendar_list[0].version,
|
||||
"prodid": vcalendar_list[0].prodid,
|
||||
"vevents": vcalendar_list.map(x => x.vevents).reduce((x, y) => x.concat(y), []),
|
||||
};
|
||||
return Promise.resolve<lib_plankton.ical.type_vcalendar>(vcalendar);
|
||||
}
|
||||
);
|
||||
const ics_raw : string = http_response.body.toString();
|
||||
const vcalendar_list : Array<lib_plankton.ical.type_vcalendar> = lib_plankton.ical.ics_decode_multi(
|
||||
ics_raw,
|
||||
{
|
||||
"ignore_unhandled_instruction_keys": resource_object.data.from_fucked_up_wordpress,
|
||||
"from_fucked_up_wordpress": resource_object.data.from_fucked_up_wordpress,
|
||||
}
|
||||
);
|
||||
const vcalendar : lib_plankton.ical.type_vcalendar = {
|
||||
// required
|
||||
"version": vcalendar_list[0].version,
|
||||
"prodid": vcalendar_list[0].prodid,
|
||||
"vevents": vcalendar_list.map(x => x.vevents).reduce((x, y) => x.concat(y), []),
|
||||
};
|
||||
return Promise.resolve(
|
||||
vcalendar.vevents
|
||||
.map(
|
||||
|
|
@ -376,11 +389,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": (
|
||||
(vevent.dtend !== undefined)
|
||||
?
|
||||
_zeitbild.helpers.ical_dt_to_own_datetime(vevent.dtend)
|
||||
_zeitbild.helpers.icalendar_dt_to_own_datetime(vevent.dtend)
|
||||
:
|
||||
null
|
||||
),
|
||||
|
|
@ -441,19 +454,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(
|
||||
|
|
@ -461,7 +461,7 @@ namespace _zeitbild.service.calendar
|
|||
from_pit : lib_plankton.pit.type_pit,
|
||||
to_pit : lib_plankton.pit.type_pit,
|
||||
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> = (
|
||||
(await overview(user_id))
|
||||
|
|
@ -483,8 +483,8 @@ namespace _zeitbild.service.calendar
|
|||
)
|
||||
);
|
||||
calendar_ids.sort();
|
||||
return lib_plankton.cache.get_complex<any, type_gather_events_result>(
|
||||
_zeitbild.cache,
|
||||
return lib_plankton.cache.get_complex<any, Array<_zeitbild.type_event_extended>>(
|
||||
_zeitbild.cache_regular,
|
||||
"gather_events",
|
||||
{
|
||||
"user_id": user_id,
|
||||
|
|
@ -492,6 +492,10 @@ namespace _zeitbild.service.calendar
|
|||
"to_pit": to_pit,
|
||||
"calendar_ids": calendar_ids,
|
||||
},
|
||||
/**
|
||||
* @todo expire?
|
||||
*/
|
||||
null,
|
||||
() => (
|
||||
Promise.all(
|
||||
calendar_ids
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ namespace _zeitbild.service.resource
|
|||
return Promise.resolve<_zeitbild.type_event_object>(event_object);
|
||||
break;
|
||||
}
|
||||
case "caldav": {
|
||||
case "ics_feed": {
|
||||
// TODO
|
||||
return Promise.reject(new Error("not implemented"));
|
||||
break;
|
||||
|
|
@ -63,14 +63,8 @@ namespace _zeitbild.service.resource
|
|||
return Promise.resolve<_zeitbild.type_local_resource_event_id>(local_resource_event_id);
|
||||
break;
|
||||
}
|
||||
case "caldav": {
|
||||
if (resource_object.data.read_only) {
|
||||
return Promise.reject(new Error("can not add event to read only caldav resource"));
|
||||
}
|
||||
else {
|
||||
// TODO
|
||||
return Promise.reject(new Error("not implemented"));
|
||||
}
|
||||
case "ics_feed": {
|
||||
return Promise.reject(new Error("unavailable"));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
|
@ -102,14 +96,8 @@ namespace _zeitbild.service.resource
|
|||
return Promise.resolve<void>(undefined);
|
||||
break;
|
||||
}
|
||||
case "caldav": {
|
||||
if (resource_object.data.read_only) {
|
||||
return Promise.reject(new Error("can not change event of read only caldav resource"));
|
||||
}
|
||||
else {
|
||||
// TODO
|
||||
return Promise.reject(new Error("not implemented"));
|
||||
}
|
||||
case "ics_feed": {
|
||||
return Promise.reject(new Error("unavailable"));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
|
@ -139,14 +127,8 @@ namespace _zeitbild.service.resource
|
|||
return Promise.resolve<void>(undefined);
|
||||
break;
|
||||
}
|
||||
case "caldav": {
|
||||
if (resource_object.data.read_only) {
|
||||
return Promise.reject(new Error("can not delete event from read only caldav resource"));
|
||||
}
|
||||
else {
|
||||
// TODO
|
||||
return Promise.reject(new Error("not implemented"));
|
||||
}
|
||||
case "ics_feed": {
|
||||
return Promise.reject(new Error("unavailable"));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,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(
|
||||
|
|
@ -37,4 +47,15 @@ namespace _zeitbild.service.user
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ namespace _zeitbild
|
|||
|
|
||||
string
|
||||
);
|
||||
dav_token : (
|
||||
null
|
||||
|
|
||||
string
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -82,10 +87,9 @@ namespace _zeitbild
|
|||
}
|
||||
|
|
||||
{
|
||||
kind : "caldav";
|
||||
kind : "ics_feed";
|
||||
data : {
|
||||
url : string;
|
||||
read_only : boolean;
|
||||
from_fucked_up_wordpress : boolean;
|
||||
};
|
||||
}
|
||||
|
|
@ -111,5 +115,16 @@ namespace _zeitbild
|
|||
};
|
||||
resource_id : type_resource_id;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_event_extended = {
|
||||
calendar_id : type_calendar_id;
|
||||
calendar_name : string;
|
||||
access_level : enum_access_level;
|
||||
event_id : (null | type_local_resource_event_id);
|
||||
event_object : type_event_object;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ ${dir_temp}/zeitbild-unlinked.js: \
|
|||
${dir_source}/api/actions/session_oidc.ts \
|
||||
${dir_source}/api/actions/session_end.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_get.ts \
|
||||
${dir_source}/api/actions/calendar_add.ts \
|
||||
|
|
@ -75,6 +77,7 @@ ${dir_temp}/zeitbild-unlinked.js: \
|
|||
${dir_source}/api/actions/calendar_event_change.ts \
|
||||
${dir_source}/api/actions/calendar_event_remove.ts \
|
||||
${dir_source}/api/actions/events.ts \
|
||||
${dir_source}/api/actions/export_ics.ts \
|
||||
${dir_source}/api/functions.ts \
|
||||
${dir_source}/main.ts
|
||||
@ ${cmd_log} "compile …"
|
||||
|
|
|
|||
Loading…
Reference in a new issue