[ini]
This commit is contained in:
parent
279bedf74e
commit
175e57b1f3
33 changed files with 13494 additions and 7271 deletions
27
.editorconfig
Normal file
27
.editorconfig
Normal file
|
@ -0,0 +1,27 @@
|
|||
# see https://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = tab
|
||||
indent_style = tab
|
||||
tab_width = 4
|
||||
insert_final_newline = true
|
||||
max_line_length = 80
|
||||
trim_trailing_whitespace = true
|
||||
curly_bracket_next_line = false
|
||||
indent_brace_style = K&R
|
||||
spaces_around_operators = true
|
||||
spaces_around_brackets = false
|
||||
quote_type = double
|
||||
|
||||
[*.y{,a}ml{,lint}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
{
|
||||
"view_mode": "table",
|
||||
"timezone_shift": 0
|
||||
"version": 1,
|
||||
"log": [
|
||||
{"kind": "stdout", "data": {"threshold": "debug"}}
|
||||
]
|
||||
}
|
||||
|
|
4161
lib/plankton/plankton.d.ts
vendored
4161
lib/plankton/plankton.d.ts
vendored
File diff suppressed because it is too large
Load diff
14124
lib/plankton/plankton.js
14124
lib/plankton/plankton.js
File diff suppressed because it is too large
Load diff
74
source/api/actions/calendar_list.ts
Normal file
74
source/api/actions/calendar_list.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
|
||||
namespace _zeitbild.api
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export function register_calendar_list(
|
||||
rest_subject : lib_plankton.rest.type_rest
|
||||
) : void
|
||||
{
|
||||
register<
|
||||
null,
|
||||
Array<
|
||||
{
|
||||
id : _zeitbild.type.calendar_id;
|
||||
preview : {
|
||||
name : string;
|
||||
};
|
||||
}
|
||||
>
|
||||
>(
|
||||
rest_subject,
|
||||
lib_plankton.http.enum_method.get,
|
||||
"/calendar/list",
|
||||
{
|
||||
"description": "listet alle Kalender auf",
|
||||
"query_parameters": [
|
||||
],
|
||||
"output_schema": () => ({
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"nullable": false,
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"nullable": false,
|
||||
},
|
||||
"preview": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"nullable": false,
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"preview",
|
||||
],
|
||||
}
|
||||
}),
|
||||
"restriction": restriction_none, // TODO
|
||||
"execution": () => (
|
||||
_zeitbild.service.calendar.list(null)
|
||||
.then(
|
||||
data => Promise.resolve({
|
||||
"status_code": 200,
|
||||
"data": data,
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
40
source/api/actions/meta_ping.ts
Normal file
40
source/api/actions/meta_ping.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
|
||||
namespace _zeitbild.api
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export function register_meta_ping(
|
||||
rest_subject : lib_plankton.rest.type_rest
|
||||
) : void
|
||||
{
|
||||
lib_plankton.rest.register<
|
||||
null,
|
||||
string
|
||||
>
|
||||
(
|
||||
rest_subject,
|
||||
lib_plankton.http.enum_method.get,
|
||||
_zeitbild.conf.get().server.path_base + "/meta/ping",
|
||||
{
|
||||
"description": "sendet ein 'pong' zurück; gedacht um die Erreichbarkeit des Backends zu prüfen",
|
||||
"input_schema": () => ({
|
||||
"nullable": true,
|
||||
}),
|
||||
"output_schema": () => ({
|
||||
"nullable": false,
|
||||
"type": "string",
|
||||
}),
|
||||
"restriction": restriction_none,
|
||||
"execution": () => {
|
||||
return Promise.resolve({
|
||||
"status_code": 200,
|
||||
"data": "pong",
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
37
source/api/actions/meta_spec.ts
Normal file
37
source/api/actions/meta_spec.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
|
||||
namespace _zeitbild.api
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export function register_meta_spec(
|
||||
rest_subject : lib_plankton.rest.type_rest
|
||||
) : void
|
||||
{
|
||||
lib_plankton.rest.register<
|
||||
null,
|
||||
any
|
||||
>
|
||||
(
|
||||
rest_subject,
|
||||
lib_plankton.http.enum_method.get,
|
||||
_zeitbild.conf.get().server.path_base + "/meta/spec",
|
||||
{
|
||||
"description": "gibt die API-Spezifikation im OpenAPI-Format aus",
|
||||
"input_schema": () => ({
|
||||
"nullable": true,
|
||||
}),
|
||||
"output_schema": () => ({
|
||||
}),
|
||||
"restriction": restriction_none,
|
||||
"execution": () => {
|
||||
return Promise.resolve({
|
||||
"status_code": 200,
|
||||
"data": lib_plankton.rest.to_oas(rest_subject),
|
||||
});
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
76
source/api/base.ts
Normal file
76
source/api/base.ts
Normal file
|
@ -0,0 +1,76 @@
|
|||
|
||||
namespace _zeitbild.api
|
||||
{
|
||||
|
||||
/**
|
||||
* @todo zu plankton auslagern?
|
||||
*/
|
||||
type type_stuff = {
|
||||
version: (null | string);
|
||||
headers: Record<string, string>;
|
||||
path_parameters: Record<string, string>;
|
||||
query_parameters: Record<string, string>;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function session_from_stuff(
|
||||
stuff : {headers : Record<string, string>;}
|
||||
) : Promise<{key : string; value : lib_plankton.session.type_session}>
|
||||
{
|
||||
const key : string = (stuff.headers["X-Session-Key"] || stuff.headers["X-Session-Key".toLowerCase()]);
|
||||
const value : lib_plankton.session.type_session = await lib_plankton.session.get(key);
|
||||
return {"key": key, "value": value};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export const restriction_none : lib_plankton.rest.type_restriction<any> = (
|
||||
(stuff) => Promise.resolve<boolean>(true)
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function register<type_input, type_output>(
|
||||
rest_subject : lib_plankton.rest.type_rest,
|
||||
http_method : lib_plankton.http.enum_method,
|
||||
path : string,
|
||||
options : {
|
||||
active ?: ((version : string) => boolean);
|
||||
restriction ?: (null | lib_plankton.rest.type_restriction<type_input>);
|
||||
execution ?: lib_plankton.rest.type_execution<type_input, type_output>;
|
||||
title ?: (null | string);
|
||||
description ?: (null | string);
|
||||
query_parameters ?: Array<
|
||||
{
|
||||
name : string;
|
||||
description : (null | string);
|
||||
required : boolean;
|
||||
}
|
||||
>;
|
||||
input_schema ?: ((version: (null | string)) => lib_plankton.rest.type_oas_schema);
|
||||
output_schema ?: ((version: (null | string)) => lib_plankton.rest.type_oas_schema);
|
||||
request_body_mimetype ?: string;
|
||||
request_body_decode ?: ((http_request_body : Buffer, http_request_header_content_type : (null | string)) => any);
|
||||
response_body_mimetype ?: string;
|
||||
response_body_encode ?: ((output : any) => Buffer);
|
||||
} = {}
|
||||
) : void
|
||||
{
|
||||
options = Object.assign(
|
||||
{
|
||||
},
|
||||
options
|
||||
);
|
||||
lib_plankton.rest.register<type_input, type_output>(
|
||||
rest_subject,
|
||||
http_method,
|
||||
(_zeitbild.conf.get().server.path_base + path),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
}
|
37
source/api/functions.ts
Normal file
37
source/api/functions.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
|
||||
namespace _zeitbild.api
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export function make(
|
||||
) : lib_plankton.rest.type_rest
|
||||
{
|
||||
const rest_subject : lib_plankton.rest.type_rest = lib_plankton.rest.make(
|
||||
{
|
||||
"title": "zeitbild",
|
||||
"versioning_method": "header",
|
||||
"versioning_header_name": "X-Api-Version",
|
||||
"set_access_control_headers": true,
|
||||
"authentication": {
|
||||
"kind": "key_header",
|
||||
"parameters": {"name": "X-Session-Key"}
|
||||
},
|
||||
}
|
||||
);
|
||||
// meta
|
||||
{
|
||||
_zeitbild.api.register_meta_ping(rest_subject);
|
||||
_zeitbild.api.register_meta_spec(rest_subject);
|
||||
}
|
||||
// calendar
|
||||
{
|
||||
_zeitbild.api.register_calendar_list(rest_subject);
|
||||
}
|
||||
|
||||
|
||||
return rest_subject;
|
||||
}
|
||||
|
||||
}
|
||||
|
6
source/backend.ts
Normal file
6
source/backend.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
/**
|
||||
*/
|
||||
namespace _zeitbild.frontend.resources.backend
|
||||
{
|
||||
}
|
237
source/conf.ts
Normal file
237
source/conf.ts
Normal file
|
@ -0,0 +1,237 @@
|
|||
|
||||
namespace _zeitbild.conf
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
type type_log_threshold = (
|
||||
"debug"
|
||||
|
|
||||
"info"
|
||||
|
|
||||
"notice"
|
||||
|
|
||||
"warning"
|
||||
|
|
||||
"error"
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
type type_log_format = (
|
||||
"jsonl"
|
||||
|
|
||||
"human_readable"
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_conf = {
|
||||
general : {
|
||||
language : (null | string);
|
||||
};
|
||||
log : Array<
|
||||
{
|
||||
kind : "stdout";
|
||||
data : {
|
||||
threshold : type_log_threshold;
|
||||
};
|
||||
}
|
||||
|
|
||||
{
|
||||
kind : "file";
|
||||
data : {
|
||||
threshold : type_log_threshold;
|
||||
path : string;
|
||||
};
|
||||
}
|
||||
|
|
||||
{
|
||||
kind : "email";
|
||||
data : {
|
||||
threshold : type_log_threshold;
|
||||
smtp_credentials : {
|
||||
host : string;
|
||||
port : int;
|
||||
username : string;
|
||||
password : string;
|
||||
};
|
||||
sender : string;
|
||||
receivers : Array<string>;
|
||||
};
|
||||
}
|
||||
>;
|
||||
server : {
|
||||
host : string;
|
||||
port : int;
|
||||
path_base : string;
|
||||
};
|
||||
database : (
|
||||
{
|
||||
kind : "sqlite";
|
||||
data : {
|
||||
path : string;
|
||||
};
|
||||
}
|
||||
|
|
||||
{
|
||||
kind : "postgresql";
|
||||
data : {
|
||||
host : string;
|
||||
port ?: int;
|
||||
username : string;
|
||||
password : string;
|
||||
schema : string;
|
||||
};
|
||||
}
|
||||
);
|
||||
session_management : {
|
||||
in_memory : boolean;
|
||||
drop_all_at_start : boolean;
|
||||
lifetime : int;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
var _data : (null | type_conf) = null;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function inject(
|
||||
conf_raw : any
|
||||
) : void
|
||||
{
|
||||
const version : int = (conf_raw["version"] ?? 1);
|
||||
_data = {
|
||||
"general": (
|
||||
((node_general) => ({
|
||||
"language": (node_general["language"] ?? null),
|
||||
})) (conf_raw["general"] ?? {})
|
||||
),
|
||||
"log": (
|
||||
(() => {
|
||||
const node_log = (
|
||||
conf_raw["log"]
|
||||
??
|
||||
[
|
||||
{
|
||||
"kind": "stdout",
|
||||
"data": {
|
||||
}
|
||||
},
|
||||
]
|
||||
);
|
||||
return (
|
||||
node_log.map(
|
||||
(node_log_entry : any) => ({
|
||||
"kind": node_log_entry["kind"],
|
||||
"data": Object.assign(
|
||||
{
|
||||
"format": "human_readable",
|
||||
"threshold": "notice",
|
||||
},
|
||||
(node_log_entry["data"] ?? {})
|
||||
)
|
||||
})
|
||||
)
|
||||
);
|
||||
}) ()
|
||||
),
|
||||
"server": (
|
||||
((node_server) => ({
|
||||
"host": (() => {
|
||||
return (node_server["host"] ?? "::");
|
||||
}) (),
|
||||
"port": (node_server["port"] ?? 7845),
|
||||
"path_base": (node_server["path_base"] ?? ""),
|
||||
})) (conf_raw["server"] ?? {})
|
||||
),
|
||||
"database": (
|
||||
((node_database) => {
|
||||
const kind : string = (node_database["kind"] ?? "sqlite");
|
||||
const node_database_data_raw = (node_database["data"] ?? {});
|
||||
switch (kind) {
|
||||
case "sqlite": {
|
||||
return {
|
||||
"kind": kind,
|
||||
"data": {
|
||||
"path": (node_database_data_raw["path"] ?? "data.sqlite"),
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "postgresql": {
|
||||
return {
|
||||
"kind": kind,
|
||||
"data": node_database_data_raw,
|
||||
};
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw (new Error("unhandled"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}) (conf_raw["database"] ?? {})
|
||||
),
|
||||
"session_management": (
|
||||
((node_session_management) => ({
|
||||
"in_memory": (node_session_management["in_memory"] ?? true),
|
||||
"drop_all_at_start": (node_session_management["drop_all_at_start"] ?? true),
|
||||
"lifetime": (node_session_management["lifetime"] ?? 900),
|
||||
})) (conf_raw["session_management"] ?? {})
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo mandatory fields
|
||||
*/
|
||||
export async function load(
|
||||
path : string
|
||||
) : Promise<void>
|
||||
{
|
||||
let conf_raw : any;
|
||||
if (! (await lib_plankton.file.exists(path))) {
|
||||
// return Promise.reject<void>(new Error("configuration file not found: " + path + "; using fallback"));
|
||||
conf_raw = {};
|
||||
}
|
||||
else {
|
||||
try {
|
||||
conf_raw = lib_plankton.json.decode(await lib_plankton.file.read(path));
|
||||
}
|
||||
catch (error) {
|
||||
conf_raw = null;
|
||||
}
|
||||
}
|
||||
if (conf_raw === null) {
|
||||
return Promise.reject<void>("configuration file could not be read");
|
||||
}
|
||||
else {
|
||||
inject(conf_raw);
|
||||
// process.stderr.write(JSON.stringify(_data, undefined, "\t"));
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function get(
|
||||
) : type_conf
|
||||
{
|
||||
if (_data === null) {
|
||||
throw (new Error("conf not loaded yet"));
|
||||
}
|
||||
else {
|
||||
return _data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
112
source/database.ts
Normal file
112
source/database.ts
Normal file
|
@ -0,0 +1,112 @@
|
|||
|
||||
namespace _zeitbild.database
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
const _compatible_revisions : Array<string> = [
|
||||
"r1",
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function get_implementation(
|
||||
) : lib_plankton.database.type_database
|
||||
{
|
||||
switch (_zeitbild.conf.get().database.kind) {
|
||||
case "sqlite": {
|
||||
type type_parameters = {
|
||||
path : string;
|
||||
};
|
||||
const parameters : type_parameters = (_zeitbild.conf.get().database.data as type_parameters);
|
||||
return lib_plankton.database.sqlite_database(
|
||||
{
|
||||
"path": parameters.path,
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "postgresql": {
|
||||
type type_parameters = {
|
||||
host : string;
|
||||
port ?: int;
|
||||
username : string;
|
||||
password : string;
|
||||
schema : string;
|
||||
};
|
||||
const parameters : type_parameters = (_zeitbild.conf.get().database.data as type_parameters);
|
||||
return lib_plankton.database.postgresql_database(
|
||||
{
|
||||
"host": parameters.host,
|
||||
"port": parameters.port,
|
||||
"username": parameters.username,
|
||||
"password": parameters.password,
|
||||
"schema": parameters.schema,
|
||||
}
|
||||
);
|
||||
}
|
||||
default: {
|
||||
throw (new Error("database implementation not available: " + _zeitbild.conf.get().database.kind));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function get_revision(
|
||||
) : Promise<(null | string)>
|
||||
{
|
||||
return (
|
||||
get_implementation().query_select(
|
||||
{
|
||||
"source": "_meta",
|
||||
"fields": ["revision"],
|
||||
}
|
||||
)
|
||||
.then<(null | string)>(
|
||||
(rows) => Promise.resolve<(null | string)>(rows[0]["revision"])
|
||||
)
|
||||
.catch<(null | string)>(
|
||||
(reason) => {
|
||||
lib_plankton.log.warning(
|
||||
"database_get_revision_error",
|
||||
{
|
||||
"reason": String(reason),
|
||||
}
|
||||
);
|
||||
return Promise.resolve<(null | string)>(null);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function check(
|
||||
) : Promise<void>
|
||||
{
|
||||
const revision : (null | string) = await get_revision();
|
||||
lib_plankton.log.info(
|
||||
"database_check",
|
||||
{
|
||||
"revision_found": revision,
|
||||
"revisions_compatible": _compatible_revisions,
|
||||
}
|
||||
);
|
||||
if (revision === null) {
|
||||
return Promise.reject<void>(new Error("database appearently missing"));
|
||||
}
|
||||
else {
|
||||
if (! _compatible_revisions.includes(revision)) {
|
||||
return Promise.reject<void>(new Error("database revision incompatible; found: " + revision + "; required: " + String(_compatible_revisions)));
|
||||
}
|
||||
else {
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
/**
|
||||
*/
|
||||
namespace _zeitbild.frontend.helpers
|
||||
namespace _zeitbild.helpers
|
||||
{
|
||||
|
||||
/**
|
|
@ -1,26 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
||||
<script type="text/javascript" src="logic.js">
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
() => {
|
||||
_zeitbild.frontend.main()
|
||||
.then(
|
||||
() => {}
|
||||
)
|
||||
.catch(
|
||||
(error) => {console.error(error);}
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -1,333 +0,0 @@
|
|||
/**
|
||||
*/
|
||||
namespace _zeitbild.frontend.resources.backend
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
var _data : _zeitbild.frontend.type_datamodel;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function init(
|
||||
) : Promise<void>
|
||||
{
|
||||
const path : string = "data.json";
|
||||
if (_data === undefined) {
|
||||
_data = lib_plankton.call.convey(
|
||||
await lib_plankton.file.read(path),
|
||||
[
|
||||
lib_plankton.json.decode,
|
||||
(data_raw : any) => (
|
||||
({
|
||||
"users": data_raw["users"],
|
||||
"calendars": (
|
||||
data_raw["calendars"]
|
||||
.map(
|
||||
(calendar_entry_raw : any) => ({
|
||||
"id": calendar_entry_raw["id"],
|
||||
"object": (
|
||||
((calendar_object_raw) => {
|
||||
switch (calendar_object_raw["kind"]) {
|
||||
default: {
|
||||
return calendar_object_raw;
|
||||
break;
|
||||
}
|
||||
case "concrete": {
|
||||
return {
|
||||
"kind": "concrete",
|
||||
"data": {
|
||||
"name": calendar_object_raw["data"]["name"],
|
||||
"private": (
|
||||
calendar_object_raw["data"]["private"]
|
||||
??
|
||||
false
|
||||
),
|
||||
"users": calendar_object_raw["data"]["users"],
|
||||
"events": (
|
||||
calendar_object_raw["data"]["events"]
|
||||
.map(
|
||||
(event_raw : any) => ({
|
||||
"name": event_raw["name"],
|
||||
"begin": event_raw["begin"],
|
||||
"end": (
|
||||
(
|
||||
(
|
||||
event_raw["end"]
|
||||
??
|
||||
null
|
||||
)
|
||||
===
|
||||
null
|
||||
)
|
||||
?
|
||||
null
|
||||
:
|
||||
event_raw["end"]
|
||||
),
|
||||
"location": (
|
||||
event_raw["location"]
|
||||
??
|
||||
null
|
||||
),
|
||||
"description": (
|
||||
event_raw["description"]
|
||||
??
|
||||
null
|
||||
),
|
||||
})
|
||||
)
|
||||
),
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}) (calendar_entry_raw["object"])
|
||||
),
|
||||
})
|
||||
)
|
||||
),
|
||||
}) as type_datamodel
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
else {
|
||||
// do nothing
|
||||
}
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function calendar_list(
|
||||
) : Promise<
|
||||
Array<
|
||||
{
|
||||
key : type_calendar_id;
|
||||
preview : {
|
||||
name : string;
|
||||
}
|
||||
}
|
||||
>
|
||||
>
|
||||
{
|
||||
await init();
|
||||
return Promise.resolve(
|
||||
_data.calendars
|
||||
.map(
|
||||
(calendar_entry) => {
|
||||
switch (calendar_entry.object.kind) {
|
||||
case "concrete": {
|
||||
return {
|
||||
"key": calendar_entry.id,
|
||||
"preview": {
|
||||
"name": calendar_entry.object.data.name,
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
case "caldav": {
|
||||
return {
|
||||
"key": calendar_entry.id,
|
||||
"preview": {
|
||||
"name": "(imported)",
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function calendar_read(
|
||||
calendar_id : type_calendar_id
|
||||
) : Promise<type_calendar_object>
|
||||
{
|
||||
await init();
|
||||
const hits = (
|
||||
_data.calendars
|
||||
.filter(
|
||||
(calendar_entry) => (calendar_entry.id === calendar_id)
|
||||
)
|
||||
);
|
||||
if (hits.length <= 0) {
|
||||
return Promise.reject<type_calendar_object>(new Error("not found"));
|
||||
}
|
||||
else {
|
||||
return Promise.resolve<type_calendar_object>(hits[0].object);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo prevent loops
|
||||
*/
|
||||
export async function calendar_gather_events(
|
||||
calendar_ids : Array<type_calendar_id>,
|
||||
from_pit : _zeitbild.frontend.helpers.type_pit,
|
||||
to_pit : _zeitbild.frontend.helpers.type_pit
|
||||
) : Promise<
|
||||
Array<
|
||||
{
|
||||
calendar_id : type_calendar_id;
|
||||
calendar_name : string;
|
||||
event : type_event;
|
||||
}
|
||||
>
|
||||
>
|
||||
{
|
||||
lib_plankton.log.info(
|
||||
"calendar_gather_events",
|
||||
{
|
||||
"calendar_ids": calendar_ids,
|
||||
}
|
||||
);
|
||||
await init();
|
||||
let result : Array<
|
||||
{
|
||||
calendar_id : type_calendar_id;
|
||||
calendar_name : string;
|
||||
event : type_event;
|
||||
}
|
||||
> = [];
|
||||
for await (const calendar_id of calendar_ids) {
|
||||
const calendar_object : type_calendar_object = await calendar_read(
|
||||
calendar_id
|
||||
);
|
||||
if (calendar_object.data.private) {
|
||||
lib_plankton.log.info(
|
||||
"calendar_gather_events_private_calendar_blocked",
|
||||
{
|
||||
"calendar_id": calendar_id,
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
switch (calendar_object.kind) {
|
||||
case "concrete": {
|
||||
result = (
|
||||
result
|
||||
.concat(
|
||||
calendar_object.data.events
|
||||
.filter(
|
||||
(event) => _zeitbild.frontend.helpers.pit_is_between(
|
||||
_zeitbild.frontend.helpers.pit_from_datetime(event.begin),
|
||||
from_pit,
|
||||
to_pit
|
||||
)
|
||||
)
|
||||
.map(
|
||||
(event) => ({
|
||||
"calendar_id": calendar_id,
|
||||
"calendar_name": calendar_object.data.name,
|
||||
"event": event
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "caldav": {
|
||||
const url : lib_plankton.url.type_url = lib_plankton.url.decode(
|
||||
calendar_object.data.source_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 = lib_plankton.ical.ics_decode(
|
||||
http_response.body.toString(),
|
||||
{
|
||||
}
|
||||
);
|
||||
result = (
|
||||
result
|
||||
.concat(
|
||||
vcalendar.vevents
|
||||
.map(
|
||||
(vevent : lib_plankton.ical.type_vevent) => (
|
||||
(vevent.dtstart !== undefined)
|
||||
?
|
||||
{
|
||||
"name": (
|
||||
(vevent.summary !== undefined)
|
||||
?
|
||||
vevent.summary
|
||||
:
|
||||
"???"
|
||||
),
|
||||
"begin": _zeitbild.frontend.helpers.ical_dt_to_own_datetime(vevent.dtstart),
|
||||
"end": (
|
||||
(vevent.dtend !== undefined)
|
||||
?
|
||||
_zeitbild.frontend.helpers.ical_dt_to_own_datetime(vevent.dtend)
|
||||
:
|
||||
null
|
||||
),
|
||||
"location": (
|
||||
(vevent.location !== undefined)
|
||||
?
|
||||
vevent.location
|
||||
:
|
||||
null
|
||||
),
|
||||
"description": (
|
||||
(vevent.description !== undefined)
|
||||
?
|
||||
vevent.description
|
||||
:
|
||||
null
|
||||
),
|
||||
}
|
||||
:
|
||||
null
|
||||
)
|
||||
)
|
||||
.filter(
|
||||
(event) => (event !== null)
|
||||
)
|
||||
.filter(
|
||||
(event) => _zeitbild.frontend.helpers.pit_is_between(
|
||||
_zeitbild.frontend.helpers.pit_from_datetime(event.begin),
|
||||
from_pit,
|
||||
to_pit
|
||||
)
|
||||
)
|
||||
.map(
|
||||
(event) => ({
|
||||
"calendar_id": calendar_id,
|
||||
"calendar_name": calendar_object.data.name,
|
||||
"event": event,
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,218 +0,0 @@
|
|||
|
||||
|
||||
/**
|
||||
*/
|
||||
async function main(
|
||||
args_raw : Array<string>
|
||||
) : Promise<void>
|
||||
{
|
||||
const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler({
|
||||
"data_path": lib_plankton.args.class_argument.volatile({
|
||||
"indicators_long": ["data-path"],
|
||||
"indicators_short": ["d"],
|
||||
"type": lib_plankton.args.enum_type.string,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": "data.json",
|
||||
// "info": null,
|
||||
"name": "data-path",
|
||||
}),
|
||||
"timezone_shift": lib_plankton.args.class_argument.volatile({
|
||||
"indicators_long": ["timezone-shift"],
|
||||
"indicators_short": ["t"],
|
||||
"type": lib_plankton.args.enum_type.integer,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": 0,
|
||||
// "info": null,
|
||||
"name": "timezone-shift",
|
||||
}),
|
||||
"calendar_id": lib_plankton.args.class_argument.volatile({
|
||||
"indicators_long": ["calendar"],
|
||||
"indicators_short": ["c"],
|
||||
"type": lib_plankton.args.enum_type.integer,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": 1,
|
||||
// "info": null,
|
||||
"name": "calendar_id",
|
||||
}),
|
||||
"view_mode": lib_plankton.args.class_argument.volatile({
|
||||
"indicators_long": ["view-moode"],
|
||||
"indicators_short": ["m"],
|
||||
"type": lib_plankton.args.enum_type.string,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": "table",
|
||||
// "info": null,
|
||||
"name": "view_mode",
|
||||
}),
|
||||
"help": lib_plankton.args.class_argument.volatile({
|
||||
"indicators_long": ["help"],
|
||||
"indicators_short": ["h"],
|
||||
"type": lib_plankton.args.enum_type.boolean,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": false,
|
||||
// "info": null,
|
||||
"name": "help",
|
||||
}),
|
||||
});
|
||||
const args : Record<string, any> = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" "));
|
||||
|
||||
// init
|
||||
lib_plankton.log.conf_push(
|
||||
[
|
||||
// lib_plankton.log.channel_make({"kind": "file", "data": {"threshold": "debug", "path": "/tmp/kalender.log"}}),
|
||||
lib_plankton.log.channel_make({"kind": "file", "data": {"threshold": "info", "path": "/dev/stderr"}}),
|
||||
// lib_plankton.log.channel_make({"kind": "stdout", "data": {"threshold": "info"}}),
|
||||
]
|
||||
);
|
||||
|
||||
// exec
|
||||
if (args["help"]) {
|
||||
process.stdout.write(
|
||||
arg_handler.generate_help(
|
||||
{
|
||||
"programname": "kalender",
|
||||
"description": "Kalender",
|
||||
"executable": "kalender",
|
||||
}
|
||||
)
|
||||
+
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
else {
|
||||
const data : type_datamodel = lib_plankton.call.convey(
|
||||
await lib_plankton.file.read(args["data_path"]),
|
||||
[
|
||||
lib_plankton.json.decode,
|
||||
(data_raw : any) => (
|
||||
({
|
||||
"users": data_raw["users"],
|
||||
"calendars": (
|
||||
data_raw["calendars"]
|
||||
.map(
|
||||
(calendar_entry_raw : any) => ({
|
||||
"id": calendar_entry_raw["id"],
|
||||
"object": (
|
||||
((calendar_object_raw) => {
|
||||
switch (calendar_object_raw["kind"]) {
|
||||
default: {
|
||||
return calendar_object_raw;
|
||||
break;
|
||||
}
|
||||
case "concrete": {
|
||||
return {
|
||||
"kind": "concrete",
|
||||
"data": {
|
||||
"name": calendar_object_raw["data"]["name"],
|
||||
"users": calendar_object_raw["data"]["users"],
|
||||
"events": (
|
||||
calendar_object_raw["data"]["events"]
|
||||
.map(
|
||||
(event_raw : any) => ({
|
||||
"name": event_raw["name"],
|
||||
"begin": event_raw["begin"],
|
||||
"end": (
|
||||
(event_raw["end"] === null)
|
||||
?
|
||||
null
|
||||
:
|
||||
event_raw["end"]
|
||||
),
|
||||
"description": event_raw["description"],
|
||||
})
|
||||
)
|
||||
),
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}) (calendar_entry_raw["object"])
|
||||
),
|
||||
})
|
||||
)
|
||||
),
|
||||
}) as type_datamodel
|
||||
),
|
||||
]
|
||||
);
|
||||
let content : string;
|
||||
switch (args["view_mode"]) {
|
||||
default: {
|
||||
content = "";
|
||||
throw (new Error("invalid view mode"));
|
||||
break;
|
||||
}
|
||||
case "table": {
|
||||
content = await calendar_view_table_html(
|
||||
data,
|
||||
args.calendar_id,
|
||||
{
|
||||
"timezone_shift": args["timezone_shift"],
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "list": {
|
||||
content = await calendar_view_list_html(
|
||||
data,
|
||||
args.calendar_id,
|
||||
{
|
||||
"timezone_shift": args["timezone_shift"],
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
const output : string = template_coin(
|
||||
"main",
|
||||
{
|
||||
"content": content,
|
||||
}
|
||||
);
|
||||
process.stdout.write(output);
|
||||
}
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
|
||||
/*
|
||||
process.stderr.write(
|
||||
JSON.stringify(
|
||||
{
|
||||
"x1": datetime_from_date_object(
|
||||
new Date(Date.now()),
|
||||
{
|
||||
"timezone_shift": 2,
|
||||
}
|
||||
),
|
||||
"x2": datetime_from_date_object(
|
||||
new Date(Date.now()),
|
||||
{
|
||||
"timezone_shift": 0,
|
||||
}
|
||||
),
|
||||
"x3": pit_from_year_and_week(
|
||||
2024,
|
||||
37,
|
||||
{
|
||||
"timezone_shift": 0,
|
||||
}
|
||||
),
|
||||
},
|
||||
undefined,
|
||||
"\t"
|
||||
)
|
||||
+
|
||||
"\n"
|
||||
);
|
||||
*/
|
||||
(
|
||||
main(process.argv.slice(2))
|
||||
.then(
|
||||
() => {}
|
||||
)
|
||||
/*
|
||||
.catch(
|
||||
(error) => {process.stderr.write(String(error) + "\n");}
|
||||
)
|
||||
*/
|
||||
);
|
|
@ -1,133 +0,0 @@
|
|||
/**
|
||||
*/
|
||||
namespace _zeitbild.frontend
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
type type_conf = {
|
||||
view_mode : string;
|
||||
calendar_ids : Array<int>;
|
||||
timezone_shift : int;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
async function render(
|
||||
conf : type_conf,
|
||||
calendar_ids : Array<type_calendar_id>
|
||||
) : Promise<void>
|
||||
{
|
||||
calendar_ids.sort();
|
||||
const target : HTMLElement = (document.querySelector("body") as HTMLBodyElement);
|
||||
switch (conf.view_mode) {
|
||||
default: {
|
||||
throw (new Error("invalid view mode"));
|
||||
break;
|
||||
}
|
||||
case "table": {
|
||||
const content : string = await _zeitbild.frontend.view.calendar_view_table_html(
|
||||
calendar_ids,
|
||||
{
|
||||
"from": {
|
||||
"year": 2024,
|
||||
"week": 35
|
||||
},
|
||||
"to": {
|
||||
"year": 2024,
|
||||
"week": 43
|
||||
},
|
||||
"timezone_shift": conf.timezone_shift,
|
||||
}
|
||||
);
|
||||
target.innerHTML = content;
|
||||
/*
|
||||
document.querySelectorAll(".tableview-sources-entry").forEach(
|
||||
(element) => {
|
||||
element.addEventListener(
|
||||
"click",
|
||||
(event) => {
|
||||
const element_ : HTMLElement = (event.target as HTMLElement);
|
||||
const calendar_id : type_calendar_id = parseInt(element_.getAttribute("rel") as string);
|
||||
const active : boolean = element_.classList.toggle("tableview-sources-entry-active");
|
||||
render(
|
||||
conf,
|
||||
lib_plankton.call.convey(
|
||||
calendar_ids,
|
||||
[
|
||||
(x : Array<int>) => (
|
||||
active
|
||||
?
|
||||
calendar_ids.concat([calendar_id])
|
||||
:
|
||||
calendar_ids.filter(y => (y !== calendar_id))
|
||||
),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
*/
|
||||
break;
|
||||
}
|
||||
case "list": {
|
||||
const content : string = await _zeitbild.frontend.view.calendar_view_list_html(
|
||||
calendar_ids,
|
||||
{
|
||||
"timezone_shift": conf.timezone_shift,
|
||||
}
|
||||
);
|
||||
target.innerHTML = content;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function main(
|
||||
) : Promise<void>
|
||||
{
|
||||
// init
|
||||
lib_plankton.log.conf_push(
|
||||
[
|
||||
lib_plankton.log.channel_make({"kind": "console", "data": {"threshold": "info"}}),
|
||||
]
|
||||
);
|
||||
|
||||
// conf
|
||||
const conf : type_conf = lib_plankton.json.decode(await lib_plankton.file.read("conf.json"));
|
||||
|
||||
// args
|
||||
|
||||
const url : URL = new URL(window.location.toString());
|
||||
const calendar_ids : Array<type_calendar_id> = (
|
||||
(url.searchParams.get("ids") !== null)
|
||||
?
|
||||
lib_plankton.call.convey(
|
||||
url.searchParams.get("ids"),
|
||||
[
|
||||
(x : string) => lib_plankton.string.split(x, ","),
|
||||
(x : Array<string>) => x.map(y => parseInt(y)),
|
||||
]
|
||||
)
|
||||
:
|
||||
(await _zeitbild.frontend.resources.backend.calendar_list()).map(x => x.key)
|
||||
);
|
||||
|
||||
// exec
|
||||
await render(
|
||||
conf,
|
||||
calendar_ids
|
||||
);
|
||||
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
|
||||
/**
|
||||
*/
|
||||
namespace _zeitbild.frontend
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
type type_role = (
|
||||
"editor"
|
||||
|
|
||||
"viewer"
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
type type_user_id = int;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
type type_user_object = {
|
||||
name : string;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_event = {
|
||||
name : string;
|
||||
begin : _zeitbild.frontend.helpers.type_datetime;
|
||||
end : (
|
||||
null
|
||||
|
|
||||
_zeitbild.frontend.helpers.type_datetime
|
||||
);
|
||||
location : (
|
||||
null
|
||||
|
|
||||
string
|
||||
);
|
||||
description : (
|
||||
null
|
||||
|
|
||||
string
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_calendar_id = int;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_calendar_object = (
|
||||
{
|
||||
kind : "concrete";
|
||||
data : {
|
||||
name : string;
|
||||
private : boolean;
|
||||
users : Array<
|
||||
{
|
||||
id : type_user_id;
|
||||
role : type_role;
|
||||
}
|
||||
>;
|
||||
events : Array<type_event>;
|
||||
};
|
||||
}
|
||||
|
|
||||
{
|
||||
kind : "caldav";
|
||||
data : {
|
||||
name : string;
|
||||
private : boolean;
|
||||
read_only : boolean;
|
||||
source_url : string;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type type_datamodel = {
|
||||
users : Array<
|
||||
{
|
||||
id : type_user_id;
|
||||
object : type_user_object;
|
||||
}
|
||||
>;
|
||||
calendars : Array<
|
||||
{
|
||||
id : type_calendar_id;
|
||||
object : type_calendar_object;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
}
|
227
source/main.ts
Normal file
227
source/main.ts
Normal file
|
@ -0,0 +1,227 @@
|
|||
|
||||
/**
|
||||
*/
|
||||
async function main(
|
||||
args_raw : Array<string>
|
||||
) : Promise<void>
|
||||
{
|
||||
// init1
|
||||
lib_plankton.log.conf_push(
|
||||
[
|
||||
lib_plankton.log.channel_make({"kind": "stdout", "data": {"threshold": "debug"}}),
|
||||
]
|
||||
);
|
||||
|
||||
// args
|
||||
const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler({
|
||||
"action": lib_plankton.args.class_argument.positional({
|
||||
"index": 0,
|
||||
"type": lib_plankton.args.enum_type.string,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": "serve",
|
||||
"name": "action",
|
||||
"info": lib_plankton.string.coin(
|
||||
"{{description}}:\n{{options}}\n\t\t",
|
||||
{
|
||||
"description": "action",
|
||||
"options": (
|
||||
[
|
||||
{
|
||||
"name": "serve",
|
||||
"description": "serve"
|
||||
},
|
||||
{
|
||||
"name": "api-doc",
|
||||
"description": "api-doc"
|
||||
},
|
||||
{
|
||||
"name": "expose-conf",
|
||||
"description": "expose-conf"
|
||||
},
|
||||
{
|
||||
"name": "help",
|
||||
"description": "help"
|
||||
},
|
||||
]
|
||||
.map(
|
||||
entry => lib_plankton.string.coin(
|
||||
"\t\t- {{name}}\n\t\t\t{{description}}\n",
|
||||
{
|
||||
"name": entry.name,
|
||||
"description": entry.description,
|
||||
}
|
||||
)
|
||||
)
|
||||
.join("")
|
||||
),
|
||||
}
|
||||
),
|
||||
}),
|
||||
"conf_path": lib_plankton.args.class_argument.volatile({
|
||||
"indicators_long": ["conf_path"],
|
||||
"indicators_short": ["c"],
|
||||
"type": lib_plankton.args.enum_type.string,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": "conf.json",
|
||||
// "info": null,
|
||||
"name": "conf-path",
|
||||
}),
|
||||
"data_path": lib_plankton.args.class_argument.volatile({
|
||||
"indicators_long": ["data-path"],
|
||||
"indicators_short": ["d"],
|
||||
"type": lib_plankton.args.enum_type.string,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": "data.json",
|
||||
// "info": null,
|
||||
"name": "data-path",
|
||||
}),
|
||||
"help": lib_plankton.args.class_argument.volatile({
|
||||
"indicators_long": ["help"],
|
||||
"indicators_short": ["h"],
|
||||
"type": lib_plankton.args.enum_type.boolean,
|
||||
"mode": lib_plankton.args.enum_mode.replace,
|
||||
"default": false,
|
||||
// "info": null,
|
||||
"name": "help",
|
||||
}),
|
||||
});
|
||||
const args : Record<string, any> = arg_handler.read(lib_plankton.args.enum_environment.cli, args_raw.join(" "));
|
||||
|
||||
// init2
|
||||
await _zeitbild.conf.load(args["conf_path"]);
|
||||
lib_plankton.log.conf_push(
|
||||
_zeitbild.conf.get().log.map(
|
||||
log_output => lib_plankton.log.channel_make(
|
||||
{
|
||||
"kind": log_output.kind,
|
||||
"data": log_output.data
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// exec
|
||||
if (args["help"]) {
|
||||
process.stdout.write(
|
||||
arg_handler.generate_help(
|
||||
{
|
||||
"programname": "zeitbild",
|
||||
"description": "zeitbild-backend",
|
||||
"executable": "zeitbild",
|
||||
}
|
||||
)
|
||||
+
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
else {
|
||||
switch (args["action"]) {
|
||||
default: {
|
||||
lib_plankton.log.error(
|
||||
"main_invalid_action",
|
||||
{
|
||||
"action": args["action"],
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "expose-conf": {
|
||||
process.stdout.write(
|
||||
JSON.stringify(
|
||||
_zeitbild.conf.get(),
|
||||
undefined,
|
||||
"\t"
|
||||
)
|
||||
+
|
||||
"\n"
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "api-doc": {
|
||||
lib_plankton.log.conf_push([]);
|
||||
const rest_subject : lib_plankton.rest.type_rest = _zeitbild.api.make();
|
||||
lib_plankton.log.conf_pop();
|
||||
process.stdout.write(
|
||||
JSON.stringify(
|
||||
lib_plankton.rest.to_oas(rest_subject),
|
||||
undefined,
|
||||
"\t"
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "serve": {
|
||||
// prepare database
|
||||
await _zeitbild.database.check();
|
||||
|
||||
await lib_plankton.session.setup(
|
||||
{
|
||||
"data_chest": (
|
||||
_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.sql_table_common.chest(
|
||||
{
|
||||
"database_implementation": _zeitbild.database.get_implementation(),
|
||||
"table_name": "sessions",
|
||||
"key_names": ["key"],
|
||||
}
|
||||
),
|
||||
[
|
||||
(core : any) => ({
|
||||
"setup": (input : any) => core.setup(undefined),
|
||||
"clear": () => core.clear(),
|
||||
"write": (key : any, value : any) => core.write([key], {"data": JSON.stringify(value)}),
|
||||
"delete": (key : any) => core.delete([key]),
|
||||
"read": (key : any) => core.read([key]).then((row : any) => JSON.parse(row["data"])),
|
||||
// "search": (term : any) => core.search(term).then(() => []),
|
||||
"search": (term : any) => Promise.reject(new Error("not implemented")),
|
||||
}),
|
||||
]
|
||||
)
|
||||
),
|
||||
"default_lifetime": _zeitbild.conf.get().session_management.lifetime,
|
||||
}
|
||||
);
|
||||
|
||||
const rest_subject : lib_plankton.rest.type_rest = _zeitbild.api.make();
|
||||
const server : lib_plankton.server.type_subject = lib_plankton.server.make(
|
||||
async (input, metadata) => {
|
||||
const http_request : lib_plankton.http.type_request = lib_plankton.http.decode_request(input.toString());
|
||||
const http_response : lib_plankton.http.type_response = await lib_plankton.rest.call(
|
||||
rest_subject,
|
||||
http_request,
|
||||
{
|
||||
"checklevel_restriction": lib_plankton.api.enum_checklevel.hard,
|
||||
// "checklevel_input": lib_plankton.api.enum_checklevel.soft,
|
||||
// "checklevel_output": lib_plankton.api.enum_checklevel.soft,
|
||||
}
|
||||
);
|
||||
const output : string = lib_plankton.http.encode_response(http_response);
|
||||
return output;
|
||||
},
|
||||
{
|
||||
"host": _zeitbild.conf.get().server.host,
|
||||
"port": _zeitbild.conf.get().server.port,
|
||||
// DANGER! DANGER!
|
||||
"threshold": 0.125,
|
||||
}
|
||||
);
|
||||
|
||||
lib_plankton.server.start(server);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve<void>(undefined);
|
||||
}
|
||||
|
||||
(
|
||||
main(process.argv.slice(2))
|
||||
.then(
|
||||
() => {}
|
||||
)
|
||||
.catch(
|
||||
(error) => {process.stderr.write(String(error) + "\n");}
|
||||
)
|
||||
);
|
254
source/repositories/calendar.ts
Normal file
254
source/repositories/calendar.ts
Normal file
|
@ -0,0 +1,254 @@
|
|||
|
||||
namespace _zeitbild.repository.calendar
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
var _core_store : (
|
||||
null
|
||||
|
|
||||
lib_plankton.storage.type_core_store<
|
||||
_zeitbild.type.calendar_id,
|
||||
Record<string, any>,
|
||||
{},
|
||||
lib_plankton.storage.type_sql_table_autokey_search_term,
|
||||
Record<string, any>
|
||||
>
|
||||
) = null;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
var _event_store : (
|
||||
null
|
||||
|
|
||||
lib_plankton.storage.type_core_store<
|
||||
_zeitbild.type.event_id,
|
||||
Record<string, any>,
|
||||
{},
|
||||
lib_plankton.storage.type_sql_table_autokey_search_term,
|
||||
Record<string, any>
|
||||
>
|
||||
) = null;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function get_core_store(
|
||||
) : lib_plankton.storage.type_core_store<
|
||||
_zeitbild.type.calendar_id,
|
||||
Record<string, any>,
|
||||
{},
|
||||
lib_plankton.storage.type_sql_table_autokey_search_term,
|
||||
Record<string, any>
|
||||
>
|
||||
{
|
||||
if (_core_store === null) {
|
||||
_core_store = lib_plankton.storage.sql_table_autokey_core_store(
|
||||
{
|
||||
"database_implementation": _zeitbild.database.get_implementation(),
|
||||
"table_name": "calendars",
|
||||
"key_name": "id",
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
// do nothing
|
||||
}
|
||||
return _core_store;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function get_event_store(
|
||||
) : lib_plankton.storage.type_core_store<
|
||||
_zeitbild.type.event_id,
|
||||
Record<string, any>,
|
||||
{},
|
||||
lib_plankton.storage.type_sql_table_autokey_search_term,
|
||||
Record<string, any>
|
||||
>
|
||||
{
|
||||
if (_event_store === null) {
|
||||
_event_store = lib_plankton.storage.sql_table_autokey_core_store(
|
||||
{
|
||||
"database_implementation": _zeitbild.database.get_implementation(),
|
||||
"table_name": "events",
|
||||
"key_name": "id",
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
// do nothing
|
||||
}
|
||||
return _event_store;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo use events table
|
||||
*/
|
||||
function encode(
|
||||
object : _zeitbild.type.calendar_object
|
||||
) : Record<string, any>
|
||||
{
|
||||
switch (object.kind) {
|
||||
/*
|
||||
case "concrete": {
|
||||
const data_raw : any = lib_plankton.json.encode(object.data);
|
||||
data_raw["users"]
|
||||
return {
|
||||
"name": object.name,
|
||||
"private": object.private,
|
||||
"kind": object.kind,
|
||||
"data": {
|
||||
"users": data_raw["users"],
|
||||
"events": [] // TODO
|
||||
},
|
||||
};
|
||||
}
|
||||
*/
|
||||
default: {
|
||||
return {
|
||||
"name": object.name,
|
||||
"private": object.private,
|
||||
"kind": object.kind,
|
||||
"data": lib_plankton.json.encode(object.data),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function decode(
|
||||
row : Record<string, any>
|
||||
) : _zeitbild.type.calendar_object
|
||||
{
|
||||
return {
|
||||
"name": row["name"],
|
||||
"private": row["private"],
|
||||
"kind": row["kind"],
|
||||
"data": lib_plankton.json.decode(row["data"]),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function dump(
|
||||
) : Promise<
|
||||
Array<
|
||||
{
|
||||
id : _zeitbild.type.calendar_id;
|
||||
object : _zeitbild.type.calendar_object;
|
||||
}
|
||||
>
|
||||
>
|
||||
{
|
||||
return (
|
||||
(await get_core_store().search(null))
|
||||
.map(
|
||||
({"key": key, "preview": preview}) => ({
|
||||
"id": key,
|
||||
"object": (preview as _zeitbild.type.calendar_object),
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo optimize
|
||||
*/
|
||||
export async function list(
|
||||
search_term : (null | string)
|
||||
) : Promise<
|
||||
Array<
|
||||
{
|
||||
id : _zeitbild.type.calendar_id;
|
||||
preview : {
|
||||
name : string;
|
||||
};
|
||||
}
|
||||
>
|
||||
>
|
||||
{
|
||||
return (
|
||||
(await get_core_store().search(null))
|
||||
.filter(
|
||||
({"key": key, "preview": preview}) => (
|
||||
(
|
||||
(search_term === null)
|
||||
||
|
||||
(search_term.length <= 1)
|
||||
)
|
||||
? true
|
||||
: (
|
||||
preview["name"].toLowerCase().includes(search_term.toLowerCase())
|
||||
)
|
||||
)
|
||||
)
|
||||
.map(
|
||||
({"key": key, "preview": preview}) => ({
|
||||
"id": key,
|
||||
"preview": {
|
||||
"name": preview["name"],
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function read(
|
||||
id : _zeitbild.type.calendar_id
|
||||
) : Promise<_zeitbild.type.calendar_object>
|
||||
{
|
||||
const row : Record<string, any> = await get_core_store().read(id);
|
||||
|
||||
return decode(row);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function create(
|
||||
value : _zeitbild.type.calendar_object
|
||||
) : Promise<_zeitbild.type.calendar_id>
|
||||
{
|
||||
const row : Record<string, any> = encode(value);
|
||||
const id : _zeitbild.type.calendar_id = await get_core_store().create(row);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function update(
|
||||
id : _zeitbild.type.calendar_id,
|
||||
value : _zeitbild.type.calendar_object
|
||||
) : Promise<void>
|
||||
{
|
||||
const row : Record<string, any> = encode(value);
|
||||
|
||||
await get_core_store().update(id, row);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function delete_(
|
||||
id : _zeitbild.type.calendar_id
|
||||
) : Promise<void>
|
||||
{
|
||||
await get_core_store().delete(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
206
source/services/calendar.ts
Normal file
206
source/services/calendar.ts
Normal file
|
@ -0,0 +1,206 @@
|
|||
|
||||
namespace _zeitbild.service.calendar
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function list(
|
||||
search_term : (null | string)
|
||||
) : Promise<
|
||||
Array<
|
||||
{
|
||||
id : _zeitbild.type.calendar_id;
|
||||
preview : {
|
||||
name : string;
|
||||
}
|
||||
}
|
||||
>
|
||||
>
|
||||
{
|
||||
return (
|
||||
_zeitbild.repository.calendar.list(search_term)
|
||||
.then(
|
||||
x => x.map(
|
||||
(y : any) => ({
|
||||
"id": y.key,
|
||||
"preview": y.preview,
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export async function get(
|
||||
calendar_id : _zeitbild.type.calendar_id
|
||||
) : Promise<_zeitbild.type.calendar_object>
|
||||
{
|
||||
return _zeitbild.repository.calendar.read(calendar_id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @todo prevent loops
|
||||
*/
|
||||
export async function gather_events(
|
||||
calendar_ids : Array<_zeitbild.type.calendar_id>,
|
||||
from_pit : _zeitbild.helpers.type_pit,
|
||||
to_pit : _zeitbild.helpers.type_pit
|
||||
) : Promise<
|
||||
Array<
|
||||
{
|
||||
calendar_id : _zeitbild.type.calendar_id;
|
||||
calendar_name : string;
|
||||
event : _zeitbild.type.event_object;
|
||||
}
|
||||
>
|
||||
>
|
||||
{
|
||||
lib_plankton.log.info(
|
||||
"calendar_gather_events",
|
||||
{
|
||||
"calendar_ids": calendar_ids,
|
||||
}
|
||||
);
|
||||
let result : Array<
|
||||
{
|
||||
calendar_id : _zeitbild.type.calendar_id;
|
||||
calendar_name : string;
|
||||
event : _zeitbild.type.event_object;
|
||||
}
|
||||
> = [];
|
||||
for await (const calendar_id of calendar_ids) {
|
||||
const calendar_object : _zeitbild.type.calendar_object = await _zeitbild.repository.calendar.read(
|
||||
calendar_id
|
||||
);
|
||||
if (calendar_object.private) {
|
||||
lib_plankton.log.info(
|
||||
"calendar_gather_events_private_calendar_blocked",
|
||||
{
|
||||
"calendar_id": calendar_id,
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
switch (calendar_object.kind) {
|
||||
case "concrete": {
|
||||
result = (
|
||||
result
|
||||
.concat(
|
||||
calendar_object.data.events
|
||||
.filter(
|
||||
(event : _zeitbild.type.event_object) => _zeitbild.helpers.pit_is_between(
|
||||
_zeitbild.helpers.pit_from_datetime(event.begin),
|
||||
from_pit,
|
||||
to_pit
|
||||
)
|
||||
)
|
||||
.map(
|
||||
(event : _zeitbild.type.event_object) => ({
|
||||
"calendar_id": calendar_id,
|
||||
"calendar_name": calendar_object.name,
|
||||
"event": event
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "caldav": {
|
||||
const url : lib_plankton.url.type_url = lib_plankton.url.decode(
|
||||
calendar_object.data.source_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 = lib_plankton.ical.ics_decode(
|
||||
http_response.body.toString(),
|
||||
{
|
||||
}
|
||||
);
|
||||
result = (
|
||||
result
|
||||
.concat(
|
||||
vcalendar.vevents
|
||||
.map(
|
||||
(vevent : lib_plankton.ical.type_vevent) => (
|
||||
(vevent.dtstart !== undefined)
|
||||
?
|
||||
{
|
||||
"name": (
|
||||
(vevent.summary !== undefined)
|
||||
?
|
||||
vevent.summary
|
||||
:
|
||||
"???"
|
||||
),
|
||||
"begin": _zeitbild.helpers.ical_dt_to_own_datetime(vevent.dtstart),
|
||||
"end": (
|
||||
(vevent.dtend !== undefined)
|
||||
?
|
||||
_zeitbild.helpers.ical_dt_to_own_datetime(vevent.dtend)
|
||||
:
|
||||
null
|
||||
),
|
||||
"location": (
|
||||
(vevent.location !== undefined)
|
||||
?
|
||||
vevent.location
|
||||
:
|
||||
null
|
||||
),
|
||||
"description": (
|
||||
(vevent.description !== undefined)
|
||||
?
|
||||
vevent.description
|
||||
:
|
||||
null
|
||||
),
|
||||
}
|
||||
:
|
||||
null
|
||||
)
|
||||
)
|
||||
.filter(
|
||||
(event) => (event !== null)
|
||||
)
|
||||
.filter(
|
||||
(event) => _zeitbild.helpers.pit_is_between(
|
||||
_zeitbild.helpers.pit_from_datetime(event.begin),
|
||||
from_pit,
|
||||
to_pit
|
||||
)
|
||||
)
|
||||
.map(
|
||||
(event) => ({
|
||||
"calendar_id": calendar_id,
|
||||
"calendar_name": calendar_object.name,
|
||||
"event": event,
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
html {
|
||||
background-color: #111;
|
||||
color: #FFF;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.calendar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.calendar-pane-left {
|
||||
flex-basis: 12.5%;
|
||||
}
|
||||
|
||||
.calendar-pane-right {
|
||||
flex-basis: 87.5%;
|
||||
}
|
||||
|
||||
.tableview-sources {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
.tableview-sources-entry {
|
||||
margin: 8px;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tableview-sources-entry:not(.tableview-sources-entry-active) {
|
||||
filter: saturate(0);
|
||||
}
|
||||
|
||||
.calendar table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.calendar-cell {
|
||||
border: 1px solid #888;
|
||||
padding: 8px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.calendar-cell-day {
|
||||
width: 13.5%;
|
||||
}
|
||||
|
||||
.calendar-cell-week {
|
||||
width: 5.5%;
|
||||
}
|
||||
|
||||
.calendar-cell-regular {
|
||||
width: 13.5%;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.calendar-cell-today {
|
||||
outline: 4px solid #FFF;
|
||||
}
|
||||
|
||||
.calendar-day {
|
||||
font-size: 0.75em;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.calendar-events {
|
||||
margin: 0; padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.calendar-event_entry {
|
||||
margin: 4px;
|
||||
padding: 4px;
|
||||
border-radius: 2px;
|
||||
font-size: 0.75em;
|
||||
color: #FFF;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -1,3 +0,0 @@
|
|||
<li class="calendar-event_entry" style="background-color: {{color}};" title="{{title}}">
|
||||
{{name}}
|
||||
</li>
|
|
@ -1,8 +0,0 @@
|
|||
<td class="calendar-cell calendar-cell-regular{{extra_classes}}">
|
||||
<span class="calendar-day" title="{{title}}">
|
||||
{{day}}
|
||||
</span>
|
||||
<ul class="calendar-events">
|
||||
{{entries}}
|
||||
</ul>
|
||||
</td>
|
|
@ -1,6 +0,0 @@
|
|||
<tr>
|
||||
<th class="calendar-cell calendar-cell-week">
|
||||
{{week}}
|
||||
</th>
|
||||
{{cells}}
|
||||
</tr>
|
|
@ -1 +0,0 @@
|
|||
<li class="tableview-sources-entry tableview-sources-entry-active" style="background-color: {{color}}" rel="{{rel}}">{{name}}</li>
|
|
@ -1,27 +0,0 @@
|
|||
<div class="calendar">
|
||||
<div class="calendar-pane calendar-pane-left">
|
||||
<ul class="tableview-sources">
|
||||
{{sources}}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="calendar-pane calendar-pane-right">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="calendar-cell"></th>
|
||||
<th class="calendar-cell calendar-cell-day">Mo</th>
|
||||
<th class="calendar-cell calendar-cell-day">Di</th>
|
||||
<th class="calendar-cell calendar-cell-day">Mi</th>
|
||||
<th class="calendar-cell calendar-cell-day">Do</th>
|
||||
<th class="calendar-cell calendar-cell-day">Fr</th>
|
||||
<th class="calendar-cell calendar-cell-day">Sa</th>
|
||||
<th class="calendar-cell calendar-cell-day">So</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{rows}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
93
source/types.ts
Normal file
93
source/types.ts
Normal file
|
@ -0,0 +1,93 @@
|
|||
|
||||
/**
|
||||
*/
|
||||
namespace _zeitbild.type
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
type role = (
|
||||
"editor"
|
||||
|
|
||||
"viewer"
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
type user_id = int;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
type user_object = {
|
||||
name : string;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type event_id = int;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type event_object = {
|
||||
name : string;
|
||||
begin : _zeitbild.helpers.type_datetime;
|
||||
end : (
|
||||
null
|
||||
|
|
||||
_zeitbild.helpers.type_datetime
|
||||
);
|
||||
location : (
|
||||
null
|
||||
|
|
||||
string
|
||||
);
|
||||
description : (
|
||||
null
|
||||
|
|
||||
string
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type calendar_id = int;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export type calendar_object = (
|
||||
{
|
||||
name : string;
|
||||
private : boolean;
|
||||
}
|
||||
&
|
||||
(
|
||||
{
|
||||
kind : "concrete";
|
||||
data : {
|
||||
users : Array<
|
||||
{
|
||||
id : user_id;
|
||||
role : role;
|
||||
}
|
||||
>;
|
||||
events : Array<event_object>;
|
||||
};
|
||||
}
|
||||
|
|
||||
{
|
||||
kind : "caldav";
|
||||
data : {
|
||||
read_only : boolean;
|
||||
source_url : string;
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
}
|
|
@ -12,7 +12,7 @@ def main():
|
|||
"-o",
|
||||
"--output-directory",
|
||||
type = str,
|
||||
default = "/tmp/kalender",
|
||||
default = "/tmp/zeitbild",
|
||||
metavar = "<output-directory>",
|
||||
help = "output directory",
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
dir_lib := lib
|
||||
dir_source := source
|
||||
dir_temp := /tmp/kalender-temp
|
||||
dir_temp := /tmp/zeitbild-temp
|
||||
dir_build := build
|
||||
dir_tools := tools
|
||||
|
||||
|
@ -11,53 +11,42 @@ cmd_chmod := chmod
|
|||
cmd_cp := cp
|
||||
cmd_log := echo "--"
|
||||
cmd_mkdir := mkdir -p
|
||||
cmd_echo := echo
|
||||
cmd_tsc := ${dir_tools}/typescript/node_modules/.bin/tsc
|
||||
|
||||
|
||||
## rules
|
||||
|
||||
.PHONY: default
|
||||
default: index templates style logic
|
||||
default: ${dir_build}/zeitbild
|
||||
|
||||
.PHONY: index
|
||||
index: ${dir_build}/index.html
|
||||
|
||||
${dir_build}/index.html: \
|
||||
${dir_source}/index.html
|
||||
@ ${cmd_log} "index …"
|
||||
@ ${cmd_cp} -u -v $^ $@
|
||||
|
||||
.PHONY: templates
|
||||
templates: \
|
||||
$(wildcard ${dir_source}/templates/*)
|
||||
@ ${cmd_log} "templates …"
|
||||
@ ${cmd_mkdir} ${dir_build}/templates
|
||||
@ ${cmd_cp} -r -u -v ${dir_source}/templates/* ${dir_build}/templates/
|
||||
|
||||
.PHONY: style
|
||||
style: \
|
||||
$(wildcard ${dir_source}/style/*)
|
||||
@ ${cmd_log} "style …"
|
||||
@ ${cmd_mkdir} ${dir_build}
|
||||
@ ${cmd_cat} ${dir_source}/style/* > ${dir_build}/style.css
|
||||
|
||||
.PHONY: logic
|
||||
logic: ${dir_build}/logic.js
|
||||
|
||||
${dir_temp}/logic-unlinked.js: \
|
||||
${dir_temp}/zeitbild-unlinked.js: \
|
||||
${dir_lib}/plankton/plankton.d.ts \
|
||||
${dir_source}/logic/helpers.ts \
|
||||
${dir_source}/logic/types.ts \
|
||||
${dir_source}/logic/backend.ts \
|
||||
${dir_source}/logic/view.ts \
|
||||
${dir_source}/logic/main.ts
|
||||
@ ${cmd_log} "logic | compile …"
|
||||
${dir_source}/helpers.ts \
|
||||
${dir_source}/conf.ts \
|
||||
${dir_source}/database.ts \
|
||||
${dir_source}/types.ts \
|
||||
${dir_source}/repositories/calendar.ts \
|
||||
${dir_source}/services/calendar.ts \
|
||||
${dir_source}/api/base.ts \
|
||||
${dir_source}/api/actions/meta_ping.ts \
|
||||
${dir_source}/api/actions/meta_spec.ts \
|
||||
${dir_source}/api/actions/calendar_list.ts \
|
||||
${dir_source}/api/functions.ts \
|
||||
${dir_source}/main.ts
|
||||
@ ${cmd_log} "compile …"
|
||||
@ ${cmd_mkdir} $(dir $@)
|
||||
@ ${cmd_tsc} --lib dom,es2020 --strict $^ --outFile $@
|
||||
@ ${cmd_tsc} --lib es2020 --strict $^ --outFile $@
|
||||
|
||||
${dir_build}/logic.js: \
|
||||
${dir_temp}/head.js:
|
||||
@ ${cmd_mkdir} $(dir $@)
|
||||
@ ${cmd_echo} "#!/usr/bin/env node" > $@
|
||||
|
||||
${dir_build}/zeitbild: \
|
||||
${dir_temp}/head.js \
|
||||
${dir_lib}/plankton/plankton.js \
|
||||
${dir_temp}/logic-unlinked.js
|
||||
@ ${cmd_log} "logic | link …"
|
||||
${dir_temp}/zeitbild-unlinked.js
|
||||
@ ${cmd_log} "link …"
|
||||
@ ${cmd_mkdir} $(dir $@)
|
||||
@ ${cmd_cat} $^ > $@
|
||||
@ ${cmd_chmod} +x $@
|
||||
|
|
|
@ -7,22 +7,26 @@ dir=lib/plankton
|
|||
modules=""
|
||||
modules="${modules} base"
|
||||
modules="${modules} call"
|
||||
modules="${modules} log"
|
||||
modules="${modules} storage"
|
||||
modules="${modules} database"
|
||||
modules="${modules} session"
|
||||
modules="${modules} file"
|
||||
modules="${modules} string"
|
||||
modules="${modules} structures"
|
||||
modules="${modules} json"
|
||||
modules="${modules} args"
|
||||
modules="${modules} string"
|
||||
modules="${modules} color"
|
||||
modules="${modules} xml"
|
||||
modules="${modules} ical"
|
||||
modules="${modules} http"
|
||||
modules="${modules} log"
|
||||
modules="${modules} url"
|
||||
modules="${modules} http"
|
||||
modules="${modules} api"
|
||||
modules="${modules} rest"
|
||||
modules="${modules} server"
|
||||
modules="${modules} args"
|
||||
|
||||
|
||||
## exec
|
||||
|
||||
mkdir -p ${dir}
|
||||
cd ${dir}
|
||||
ptk bundle web ${modules}
|
||||
ptk bundle node ${modules}
|
||||
cd - > /dev/null
|
||||
|
|
Loading…
Add table
Reference in a new issue