This commit is contained in:
Fenris Wolf 2025-08-29 10:52:37 +00:00
parent 64652af779
commit 00f416a126
14 changed files with 418 additions and 21 deletions

View file

@ -17,5 +17,11 @@
"kind": "internal", "kind": "internal",
"data": { "data": {
} }
},
"database": {
"kind": "sqlite",
"data": {
"path": "../zeitbild.sqlite"
}
} }
} }

View file

@ -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": "a5f10bc2d4ded8c5a07c5cb1c4e8b74363abce59d626574f0a83e67d499d9d5f",
"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"
} }
], ],

View file

@ -0,0 +1,75 @@
namespace _zeitbild.api
{
/**
*/
export function register_davina_calendars(
rest_subject : lib_plankton.rest_caldav.type_rest
) : void
{
register<
null,
null | Array<
{
id : int;
name : string;
access_level : string;
}
>
>
(
rest_subject,
lib_plankton.http.enum_method.get,
"/davina/calendars",
{
"input_schema": () => ({
"nullable": true,
}),
"output_schema": () => ({
"nullable": false,
"type": "boolean",
}),
"query_parameters": () => ([
{
"name": "username",
"required": true,
"description": "username",
},
]),
/**
* @todo
*/
"restriction": restriction_none,
"execution": async ({"query_parameters": query_parameters}) => {
const username : string = query_parameters["username"];
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(username);
return (
_zeitbild.service.calendar.overview(user_id)
.then(
(data_raw) => Promise.resolve(
data_raw
.map(
(entry) => ({
"id": entry.id,
"name": entry.name,
"access_level": _zeitbild.value_object.access_level.to_string(entry.access_level),
})
)
)
)
.then(
(data) => Promise.resolve({
"status_code": 200,
"data": data,
})
)
);
},
}
);
}
}

View file

@ -0,0 +1,57 @@
namespace _zeitbild.api
{
/**
*/
export function register_davina_check(
rest_subject : lib_plankton.rest_caldav.type_rest
) : void
{
register<
null,
boolean
>
(
rest_subject,
lib_plankton.http.enum_method.get,
"/davina/check",
{
"input_schema": () => ({
"nullable": true,
}),
"output_schema": () => ({
"nullable": false,
"type": "boolean",
}),
"query_parameters": () => ([
{
"name": "username",
"required": true,
"description": "username",
},
{
"name": "password",
"required": true,
"description": "password",
},
]),
"restriction": restriction_none,
"execution": async ({"query_parameters": query_parameters}) => {
const username : string = query_parameters["username"];
const password : string = query_parameters["password"];
/**
* @todo [important] rectify
*/
const valid : boolean = (username === password);
return Promise.resolve({
"status_code": 200,
"data": valid,
});
},
}
);
}
}

View file

@ -0,0 +1,78 @@
namespace _zeitbild.api
{
/**
*/
export function register_davina_event_get(
rest_subject : lib_plankton.rest_caldav.type_rest
) : void
{
register<
null,
lib_plankton.ical.type_vcalendar
>(
rest_subject,
lib_plankton.http.enum_method.get,
"/davina/event_get",
{
"query_parameters": () => ([
{
"name": "username",
"required": true,
"description": "user name",
},
{
"name": "calendar_id",
"required": true,
"description": "calendar ID",
},
{
"name": "event_id",
"required": true,
"description": "event ID",
},
]),
"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)
/**
* @todo add event encoder function to plankton
*/
// .replace(new RegExp("[\\s\\S]*BEGIN:VEVENT([\\s\\S]*)END:VEVENT[\\s\\S]*", "m"), "BEGIN:VEVENT$1END:VEVENT")
)
),
/**
* @todo
*/
"restriction": restriction_none,
"execution": async (stuff) => {
const username : string = stuff.query_parameters["username"];
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(username);
const calendar_id : int = parseInt(stuff.query_parameters["calendar_id"]);
const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.service.calendar.get(calendar_id, user_id);
const event_id : int = parseInt(stuff.query_parameters["event_id"]);
const event_object : _zeitbild.type_event_object = await _zeitbild.service.resource.event_get(calendar_object.resource_id, event_id);
return Promise.resolve(
{
"status_code": 200,
"data": _zeitbild.helpers.ical_vcalendar_from_own_event_list([event_object]),
}
);
}
}
);
}
}

View file

@ -0,0 +1,114 @@
namespace _zeitbild.api
{
/**
*/
export function register_davina_event_list(
rest_subject : lib_plankton.rest_caldav.type_rest
) : void
{
register<
null,
(
Array<
{
event_id : (null | int);
event_name : string;
}
>
|
string
)
>(
rest_subject,
lib_plankton.http.enum_method.get,
"/davina/event_list",
{
"query_parameters": () => ([
{
"name": "username",
"required": true,
"description": "user name",
},
{
"name": "calendar_id",
"required": true,
"description": "calendar ID",
},
]),
"output_schema": () => ({
"type": "array",
"items": {
"nullable": false,
"type": "object",
"additionalProperties": false,
"properties": {
"event_id": {
"nullable": true,
"type": "number",
},
"event_name": {
"nullable": false,
"type": "string",
},
},
"required": [
"event_id",
"event_name",
],
}
}),
/**
* @todo
*/
"restriction": restriction_none,
"execution": async (stuff) => {
const username : string = stuff.query_parameters["username"];
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.identify(username);
/**
* @todo
*/
const from : lib_plankton.pit.type_pit = 0;
const to : lib_plankton.pit.type_pit = 1800000000;
const calendar_id : int = parseInt(stuff.query_parameters["calendar_id"]);
return (
_zeitbild.service.calendar.gather_events(
[calendar_id],
from,
to,
user_id
)
.then(
(data) => Promise.resolve(
{
"status_code": 200,
"data": (
data
.map(
(entry) => ({
"event_id": entry.event_id,
"event_name": entry.event_object.name,
})
)
),
}
)
)
.catch(
(reason) => Promise.resolve(
{
"status_code": 403,
"data": String(reason),
}
)
)
);
}
}
);
}
}

View file

@ -29,7 +29,13 @@ namespace _zeitbild.api
* @todo outsource? * @todo outsource?
*/ */
export async function web_auth( export async function web_auth(
authorization_string : (null | string) authorization_string : (null | string),
{
"via_dav_token": via_dav_token = false,
} : {
via_dav_token ?: boolean;
} = {
}
) : Promise<(null | _zeitbild.type_user_id)> ) : Promise<(null | _zeitbild.type_user_id)>
{ {
if (authorization_string === null) { if (authorization_string === null) {
@ -68,9 +74,16 @@ namespace _zeitbild.api
return Promise.resolve<(null | _zeitbild.type_user_id)>(null); return Promise.resolve<(null | _zeitbild.type_user_id)>(null);
} }
else { else {
const password_shall : string = lib_plankton.sha256.get( const user_object : _zeitbild.type_user_object = await _zeitbild.service.user.get(user_id);
username, const password_shall : string = (
_zeitbild.conf.get()["misc"]["auth_salt"] via_dav_token
?
user_object.dav_token
:
lib_plankton.sha256.get(
username,
_zeitbild.conf.get()["misc"]["auth_salt"]
)
); );
if (! (password_is === password_shall)) { if (! (password_is === password_shall)) {
/** /**
@ -96,6 +109,13 @@ namespace _zeitbild.api
} }
/**
*/
export const restriction_none : lib_plankton.rest_caldav.type_restriction<any> = (
(stuff) => Promise.resolve<boolean>(true)
);
/** /**
*/ */
export const restriction_logged_in : lib_plankton.rest_caldav.type_restriction<any> = ( export const restriction_logged_in : lib_plankton.rest_caldav.type_restriction<any> = (
@ -112,11 +132,16 @@ namespace _zeitbild.api
export const restriction_basic_auth : lib_plankton.rest_caldav.type_restriction<any> = ( export const restriction_basic_auth : lib_plankton.rest_caldav.type_restriction<any> = (
(stuff) => ( (stuff) => (
web_auth( web_auth(
stuff.headers["Authorization"] (
?? stuff.headers["Authorization"]
stuff.headers["authorization"] ??
?? stuff.headers["authorization"]
null ??
null
),
{
"via_dav_token": false,
}
) )
.then<boolean>( .then<boolean>(
(user_id) => Promise.resolve<boolean>( (user_id) => Promise.resolve<boolean>(
@ -129,8 +154,26 @@ namespace _zeitbild.api
/** /**
*/ */
export const restriction_none : lib_plankton.rest_caldav.type_restriction<any> = ( export const restriction_dav_token : lib_plankton.rest_caldav.type_restriction<any> = (
(stuff) => Promise.resolve<boolean>(true) (stuff) => (
web_auth(
(
stuff.headers["Authorization"]
??
stuff.headers["authorization"]
??
null
),
{
"via_dav_token": true,
}
)
.then<boolean>(
(user_id) => Promise.resolve<boolean>(
(user_id !== null)
)
)
)
); );

View file

@ -59,6 +59,13 @@ namespace _zeitbild.api
_zeitbild.api.register_caldav_projects(rest_subject); _zeitbild.api.register_caldav_projects(rest_subject);
_zeitbild.api.register_caldav_get(rest_subject); _zeitbild.api.register_caldav_get(rest_subject);
} }
// davina
{
_zeitbild.api.register_davina_check(rest_subject);
_zeitbild.api.register_davina_calendars(rest_subject);
_zeitbild.api.register_davina_event_list(rest_subject);
_zeitbild.api.register_davina_event_get(rest_subject);
}
// misc // misc
{ {
_zeitbild.api.register_users(rest_subject); _zeitbild.api.register_users(rest_subject);

View file

@ -8,6 +8,7 @@ type type_data = {
id : int; id : int;
name : string; name : string;
email_address : string; email_address : string;
dav_token : (null | string);
password : string; password : string;
} }
>; >;
@ -72,6 +73,7 @@ async function data_init(
const user_object : _zeitbild.type_user_object = { const user_object : _zeitbild.type_user_object = {
"name": user_raw.name, "name": user_raw.name,
"email_address": user_raw.email_address, "email_address": user_raw.email_address,
"dav_token": user_raw.dav_token,
}; };
const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.add( const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.add(
user_object user_object

View file

@ -53,6 +53,7 @@ namespace _zeitbild.repository.user
return { return {
"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,
}; };
} }
@ -66,6 +67,7 @@ namespace _zeitbild.repository.user
return { return {
"name": row["name"], "name": row["name"],
"email_address": row["email_address"], "email_address": row["email_address"],
"dav_token": row["dav_token"],
}; };
} }

View file

@ -1,5 +0,0 @@
namespace _zeitbild.service.caldav
{
}

View file

@ -251,6 +251,7 @@ namespace _zeitbild.service.caldav
"propstats": ( "propstats": (
false false
? ?
// probably the wrong way
[ [
{ {
"prop": (props ?? []).map( "prop": (props ?? []).map(
@ -284,6 +285,7 @@ namespace _zeitbild.service.caldav
}, },
] ]
: :
// probably the right way
props.map( props.map(
(prop) => { (prop) => {
const prop_parts : Array<string> = prop.toLowerCase().split(":"); const prop_parts : Array<string> = prop.toLowerCase().split(":");
@ -352,6 +354,7 @@ namespace _zeitbild.service.caldav
user_id : type_user_id user_id : type_user_id
) : Promise<lib_plankton.xml.type_node_data> ) : Promise<lib_plankton.xml.type_node_data>
{ {
const http_protocol : string = "HTTP/1.1";
const answers : Record< const answers : Record<
string, string,
(stuff : any) => lib_plankton.webdav.type_data_prop_value (stuff : any) => lib_plankton.webdav.type_data_prop_value
@ -476,13 +479,13 @@ namespace _zeitbild.service.caldav
? ?
{ {
"prop": group.map(member => member.prop), "prop": group.map(member => member.prop),
"status": "HTTP/1.1 200 OK", "status": (http_protocol + " 200 OK"),
"description": null, "description": null,
} }
: :
{ {
"prop": group.map(member => member.prop), "prop": group.map(member => member.prop),
"status": "HTTP/1.1 404 Not Found", "status": (http_protocol + " 404 Not Found"),
"description": null, "description": null,
} }
) )

View file

@ -28,6 +28,11 @@ namespace _zeitbild
| |
string string
); );
dav_token : (
null
|
string
);
}; };
@ -82,6 +87,9 @@ namespace _zeitbild
} }
| |
{ {
/**
* @todo rename to "ics_feed" or sth.
*/
kind : "caldav"; kind : "caldav";
data : { data : {
url : string; url : string;

View file

@ -83,6 +83,10 @@ ${dir_temp}/zeitbild-unlinked.js: \
${dir_source}/api/actions/caldav_probe_via_well_known.ts \ ${dir_source}/api/actions/caldav_probe_via_well_known.ts \
${dir_source}/api/actions/caldav_projects.ts \ ${dir_source}/api/actions/caldav_projects.ts \
${dir_source}/api/actions/caldav_get.ts \ ${dir_source}/api/actions/caldav_get.ts \
${dir_source}/api/actions/davina_check.ts \
${dir_source}/api/actions/davina_calendars.ts \
${dir_source}/api/actions/davina_event_list.ts \
${dir_source}/api/actions/davina_event_get.ts \
${dir_source}/api/functions.ts \ ${dir_source}/api/functions.ts \
${dir_source}/main.ts ${dir_source}/main.ts
@ ${cmd_log} "compile …" @ ${cmd_log} "compile …"