mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 10:02:18 +00:00
Compare commits
20 Commits
873b40ad10
...
d5c1ddc590
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5c1ddc590 | ||
|
|
a79e1901b8 | ||
|
|
f29c8fa925 | ||
|
|
54f1ee0788 | ||
|
|
96f221f721 | ||
|
|
ba3b64a9c4 | ||
|
|
22a0337375 | ||
|
|
21902e108a | ||
|
|
08f526fa5b | ||
|
|
ac73f3aaff | ||
|
|
9fdc8434db | ||
|
|
85f3d89235 | ||
|
|
092604694a | ||
|
|
8ef24668ba | ||
|
|
416658d069 | ||
|
|
80eae5db9e | ||
|
|
66f541f1c7 | ||
|
|
ea7196501e | ||
|
|
33d14a9ba0 | ||
|
|
d843909084 |
38
.sqlx/query-053c17f3b54ae3e153137926115486eb19a801bd73a74230bcf72a9a7254824a.json
generated
Normal file
38
.sqlx/query-053c17f3b54ae3e153137926115486eb19a801bd73a74230bcf72a9a7254824a.json
generated
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "\n SELECT principal, cal_id, id, (deleted_at IS NOT NULL) AS \"deleted: bool\"\n FROM calendarobjects\n WHERE (principal, cal_id, id) NOT IN (\n SELECT DISTINCT principal, cal_id, object_id FROM calendarobjectchangelog\n )\n ;\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "principal",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cal_id",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "deleted: bool",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Integer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 0
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "053c17f3b54ae3e153137926115486eb19a801bd73a74230bcf72a9a7254824a"
|
||||||
|
}
|
||||||
38
.sqlx/query-c138b1143ac04af4930266ffae0990e82005911c11a683ad565e92335e085f4d.json
generated
Normal file
38
.sqlx/query-c138b1143ac04af4930266ffae0990e82005911c11a683ad565e92335e085f4d.json
generated
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "\n SELECT principal, addressbook_id, id, (deleted_at IS NOT NULL) AS \"deleted: bool\"\n FROM addressobjects\n WHERE (principal, addressbook_id, id) NOT IN (\n SELECT DISTINCT principal, addressbook_id, object_id FROM addressobjectchangelog\n )\n ;\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "principal",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "addressbook_id",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "deleted: bool",
|
||||||
|
"ordinal": 3,
|
||||||
|
"type_info": "Integer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 0
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "c138b1143ac04af4930266ffae0990e82005911c11a683ad565e92335e085f4d"
|
||||||
|
}
|
||||||
275
Cargo.lock
generated
275
Cargo.lock
generated
@@ -73,22 +73,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-query"
|
name = "anstyle-query"
|
||||||
version = "1.1.4"
|
version = "1.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-wincon"
|
name = "anstyle-wincon"
|
||||||
version = "3.0.10"
|
version = "3.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"once_cell_polyfill",
|
"once_cell_polyfill",
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -139,7 +139,7 @@ dependencies = [
|
|||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -175,7 +175,7 @@ checksum = "34921de3d57974069bad483fdfe0ec65d88c4ff892edd1ab4d8b03be0dda1b9b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -310,7 +310,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -345,9 +345,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.8.6"
|
version = "0.8.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871"
|
checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum-core",
|
"axum-core",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -397,9 +397,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-extra"
|
name = "axum-extra"
|
||||||
version = "0.12.0"
|
version = "0.12.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "460c45604cb25834835e3b4d3468510322852783dac36261d642424d75562ff3"
|
checksum = "dbfe9f610fe4e99cf0cfcd03ccf8c63c28c616fe714d80475ef731f3b13dd21b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"axum-core",
|
"axum-core",
|
||||||
@@ -505,6 +505,17 @@ dependencies = [
|
|||||||
"piper",
|
"piper",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.19.0"
|
version = "3.19.0"
|
||||||
@@ -525,15 +536,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.10.1"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.44"
|
version = "1.2.46"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3"
|
checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"find-msvc-tools",
|
"find-msvc-tools",
|
||||||
"shlex",
|
"shlex",
|
||||||
@@ -586,9 +597,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.51"
|
version = "4.5.52"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
|
checksum = "aa8120877db0e5c011242f96806ce3c94e0737ab8108532a76a3300a01db2ab8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -596,9 +607,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.51"
|
version = "4.5.52"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
|
checksum = "02576b399397b659c26064fbc92a75fede9d18ffd5f80ca1cd74ddab167016e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -615,7 +626,7 @@ dependencies = [
|
|||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -639,6 +650,18 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console"
|
||||||
|
version = "0.15.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
|
||||||
|
dependencies = [
|
||||||
|
"encode_unicode",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
@@ -747,7 +770,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -771,7 +794,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
"strsim",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -782,7 +805,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -823,7 +846,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -847,7 +870,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -947,6 +970,12 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.35"
|
version = "0.8.35"
|
||||||
@@ -1048,9 +1077,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
|
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flume"
|
name = "flume"
|
||||||
@@ -1178,7 +1207,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1466,9 +1495,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.7.0"
|
version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
|
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -1519,9 +1548,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-util"
|
name = "hyper-util"
|
||||||
version = "0.1.17"
|
version = "0.1.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8"
|
checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -1568,7 +1597,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "ical"
|
name = "ical"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
source = "git+https://github.com/lennart-k/ical-rs#38e4201d5f653b07c9800cccec93996f542267b4"
|
source = "git+https://github.com/lennart-k/ical-rs#474caf58acbc8ebefd90e6b848741d7ed5136d65"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-tz",
|
"chrono-tz",
|
||||||
@@ -1724,9 +1753,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iri-string"
|
name = "iri-string"
|
||||||
version = "0.7.8"
|
version = "0.7.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
|
checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -1957,9 +1986,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-bigint-dig"
|
name = "num-bigint-dig"
|
||||||
version = "0.8.5"
|
version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "82c79c15c05d4bf82b6f5ef163104cc81a760d8e874d38ac50ab67c8877b647b"
|
checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libm",
|
"libm",
|
||||||
@@ -2081,9 +2110,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl"
|
name = "openssl"
|
||||||
version = "0.10.74"
|
version = "0.10.75"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654"
|
checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -2102,7 +2131,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2116,9 +2145,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.110"
|
version = "0.9.111"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2"
|
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -2327,7 +2356,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"proc-macro2-diagnostics",
|
"proc-macro2-diagnostics",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2400,7 +2429,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2526,7 +2555,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
"version_check",
|
"version_check",
|
||||||
"yansi",
|
"yansi",
|
||||||
]
|
]
|
||||||
@@ -2551,14 +2580,14 @@ dependencies = [
|
|||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.38.3"
|
version = "0.38.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89"
|
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@@ -2620,9 +2649,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.41"
|
version = "1.0.42"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@@ -2718,7 +2747,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2849,9 +2878,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa"
|
name = "rsa"
|
||||||
version = "0.9.8"
|
version = "0.9.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b"
|
checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-oid",
|
"const-oid",
|
||||||
"digest",
|
"digest",
|
||||||
@@ -2892,7 +2921,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"relative-path",
|
"relative-path",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2904,7 +2933,7 @@ checksum = "b3a8fb4672e840a587a66fc577a5491375df51ddb88f2a2c2a792598c326fe14"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2919,9 +2948,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed"
|
name = "rust-embed"
|
||||||
version = "8.8.0"
|
version = "8.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb44e1917075637ee8c7bcb865cf8830e3a92b5b1189e44e3a0ab5a0d5be314b"
|
checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rust-embed-impl",
|
"rust-embed-impl",
|
||||||
"rust-embed-utils",
|
"rust-embed-utils",
|
||||||
@@ -2930,22 +2959,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed-impl"
|
name = "rust-embed-impl"
|
||||||
version = "8.8.0"
|
version = "8.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "382499b49db77a7c19abd2a574f85ada7e9dbe125d5d1160fa5cad7c4cf71fc9"
|
checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rust-embed-utils",
|
"rust-embed-utils",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed-utils"
|
name = "rust-embed-utils"
|
||||||
version = "8.8.0"
|
version = "8.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "21fcbee55c2458836bcdbfffb6ec9ba74bbc23ca7aa6816015a3dd2c4d8fc185"
|
checksum = "60b161f275cb337fe0a44d924a5f4df0ed69c2c39519858f931ce61c779d3475"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sha2",
|
"sha2",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
@@ -2974,7 +3003,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical"
|
name = "rustical"
|
||||||
version = "0.10.1"
|
version = "0.10.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argon2",
|
"argon2",
|
||||||
@@ -3017,7 +3046,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_caldav"
|
name = "rustical_caldav"
|
||||||
version = "0.10.1"
|
version = "0.10.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3043,6 +3072,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
"similar-asserts",
|
||||||
"strum",
|
"strum",
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
@@ -3057,7 +3087,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_carddav"
|
name = "rustical_carddav"
|
||||||
version = "0.10.1"
|
version = "0.10.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3089,7 +3119,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_dav"
|
name = "rustical_dav"
|
||||||
version = "0.10.1"
|
version = "0.10.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3114,7 +3144,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_dav_push"
|
name = "rustical_dav_push"
|
||||||
version = "0.10.1"
|
version = "0.10.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3139,7 +3169,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_frontend"
|
name = "rustical_frontend"
|
||||||
version = "0.10.1"
|
version = "0.10.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"askama",
|
"askama",
|
||||||
"askama_web",
|
"askama_web",
|
||||||
@@ -3175,7 +3205,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_ical"
|
name = "rustical_ical"
|
||||||
version = "0.10.1"
|
version = "0.10.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -3192,7 +3222,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_oidc"
|
name = "rustical_oidc"
|
||||||
version = "0.10.1"
|
version = "0.10.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3208,7 +3238,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_store"
|
name = "rustical_store"
|
||||||
version = "0.10.1"
|
version = "0.10.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3230,6 +3260,7 @@ dependencies = [
|
|||||||
"rustical_store_sqlite",
|
"rustical_store_sqlite",
|
||||||
"rustical_xml",
|
"rustical_xml",
|
||||||
"serde",
|
"serde",
|
||||||
|
"sha2",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower",
|
||||||
@@ -3240,7 +3271,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_store_sqlite"
|
name = "rustical_store_sqlite"
|
||||||
version = "0.10.1"
|
version = "0.10.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -3262,7 +3293,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_xml"
|
name = "rustical_xml"
|
||||||
version = "0.10.1"
|
version = "0.10.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
@@ -3284,9 +3315,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.34"
|
version = "0.23.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a9586e9ee2b4f8fab52a0048ca7334d7024eef48e2cb9407e3497bb7cab7fa7"
|
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"ring",
|
"ring",
|
||||||
@@ -3352,9 +3383,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schemars"
|
name = "schemars"
|
||||||
version = "1.0.4"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0"
|
checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"ref-cast",
|
"ref-cast",
|
||||||
@@ -3425,7 +3456,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3493,9 +3524,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with"
|
name = "serde_with"
|
||||||
version = "3.15.1"
|
version = "3.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa66c845eee442168b2c8134fec70ac50dc20e760769c8ba0ad1319ca1959b04"
|
checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -3503,7 +3534,7 @@ dependencies = [
|
|||||||
"indexmap 1.9.3",
|
"indexmap 1.9.3",
|
||||||
"indexmap 2.12.0",
|
"indexmap 2.12.0",
|
||||||
"schemars 0.9.0",
|
"schemars 0.9.0",
|
||||||
"schemars 1.0.4",
|
"schemars 1.1.0",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with_macros",
|
"serde_with_macros",
|
||||||
@@ -3512,14 +3543,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with_macros"
|
name = "serde_with_macros"
|
||||||
version = "3.15.1"
|
version = "3.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b91a903660542fced4e99881aa481bdbaec1634568ee02e0b8bd57c64cb38955"
|
checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3578,6 +3609,26 @@ dependencies = [
|
|||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "similar"
|
||||||
|
version = "2.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
||||||
|
dependencies = [
|
||||||
|
"bstr",
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "similar-asserts"
|
||||||
|
version = "1.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a"
|
||||||
|
dependencies = [
|
||||||
|
"console",
|
||||||
|
"similar",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -3687,7 +3738,7 @@ dependencies = [
|
|||||||
"quote",
|
"quote",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"sqlx-macros-core",
|
"sqlx-macros-core",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3710,7 +3761,7 @@ dependencies = [
|
|||||||
"sqlx-mysql",
|
"sqlx-mysql",
|
||||||
"sqlx-postgres",
|
"sqlx-postgres",
|
||||||
"sqlx-sqlite",
|
"sqlx-sqlite",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
@@ -3862,7 +3913,7 @@ dependencies = [
|
|||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3884,9 +3935,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.108"
|
version = "2.0.110"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
|
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -3910,7 +3961,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3939,7 +3990,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3950,7 +4001,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4044,7 +4095,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4070,9 +4121,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.16"
|
version = "0.7.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
|
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -4356,7 +4407,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4471,6 +4522,12 @@ version = "0.1.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
|
checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
@@ -4639,7 +4696,7 @@ dependencies = [
|
|||||||
"bumpalo",
|
"bumpalo",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4674,9 +4731,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "1.0.3"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8"
|
checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
@@ -4721,7 +4778,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4732,7 +4789,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5013,14 +5070,14 @@ checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xml_derive"
|
name = "xml_derive"
|
||||||
version = "0.1.0"
|
version = "0.10.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"heck",
|
"heck",
|
||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5048,7 +5105,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -5069,7 +5126,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5089,7 +5146,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -5129,5 +5186,5 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.108",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|||||||
48
Cargo.toml
48
Cargo.toml
@@ -2,7 +2,8 @@
|
|||||||
members = ["crates/*"]
|
members = ["crates/*"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.10.1"
|
version = "0.10.5"
|
||||||
|
rust-version = "1.91"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "A CalDAV server"
|
description = "A CalDAV server"
|
||||||
documentation = "https://lennart-k.github.io/rustical/"
|
documentation = "https://lennart-k.github.io/rustical/"
|
||||||
@@ -12,6 +13,7 @@ license = "AGPL-3.0-or-later"
|
|||||||
[package]
|
[package]
|
||||||
name = "rustical"
|
name = "rustical"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
@@ -35,6 +37,17 @@ opentelemetry = [
|
|||||||
debug = 0
|
debug = 0
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
rustical_dav = { path = "./crates/dav/" }
|
||||||
|
rustical_dav_push = { path = "./crates/dav_push/" }
|
||||||
|
rustical_store = { path = "./crates/store/" }
|
||||||
|
rustical_store_sqlite = { path = "./crates/store_sqlite/" }
|
||||||
|
rustical_caldav = { path = "./crates/caldav/" }
|
||||||
|
rustical_carddav = { path = "./crates/carddav/" }
|
||||||
|
rustical_frontend = { path = "./crates/frontend/" }
|
||||||
|
rustical_xml = { path = "./crates/xml/" }
|
||||||
|
rustical_oidc = { path = "./crates/oidc/" }
|
||||||
|
rustical_ical = { path = "./crates/ical/" }
|
||||||
|
|
||||||
matchit = "0.9"
|
matchit = "0.9"
|
||||||
uuid = { version = "1.11", features = ["v4", "fast-rng"] }
|
uuid = { version = "1.11", features = ["v4", "fast-rng"] }
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
@@ -108,16 +121,6 @@ tower-http = { version = "0.6", features = [
|
|||||||
"catch-panic",
|
"catch-panic",
|
||||||
] }
|
] }
|
||||||
percent-encoding = "2.3"
|
percent-encoding = "2.3"
|
||||||
rustical_dav = { path = "./crates/dav/" }
|
|
||||||
rustical_dav_push = { path = "./crates/dav_push/" }
|
|
||||||
rustical_store = { path = "./crates/store/" }
|
|
||||||
rustical_store_sqlite = { path = "./crates/store_sqlite/" }
|
|
||||||
rustical_caldav = { path = "./crates/caldav/" }
|
|
||||||
rustical_carddav = { path = "./crates/carddav/" }
|
|
||||||
rustical_frontend = { path = "./crates/frontend/" }
|
|
||||||
rustical_xml = { path = "./crates/xml/" }
|
|
||||||
rustical_oidc = { path = "./crates/oidc/" }
|
|
||||||
rustical_ical = { path = "./crates/ical/" }
|
|
||||||
chrono-tz = "0.10"
|
chrono-tz = "0.10"
|
||||||
chrono-humanize = "0.2"
|
chrono-humanize = "0.2"
|
||||||
rand = "0.9"
|
rand = "0.9"
|
||||||
@@ -145,21 +148,22 @@ ece = { version = "2.3", default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
openssl = { version = "0.10", features = ["vendored"] }
|
openssl = { version = "0.10", features = ["vendored"] }
|
||||||
async-std = { version = "1.13", features = ["attributes"] }
|
async-std = { version = "1.13", features = ["attributes"] }
|
||||||
|
similar-asserts = "1.7"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustical_store = { workspace = true }
|
rustical_store.workspace = true
|
||||||
rustical_store_sqlite = { workspace = true }
|
rustical_store_sqlite.workspace = true
|
||||||
rustical_caldav = { workspace = true }
|
rustical_caldav.workspace = true
|
||||||
rustical_carddav.workspace = true
|
rustical_carddav.workspace = true
|
||||||
rustical_frontend = { workspace = true }
|
rustical_frontend.workspace = true
|
||||||
toml = { workspace = true }
|
toml.workspace = true
|
||||||
serde = { workspace = true }
|
serde.workspace = true
|
||||||
tokio = { workspace = true }
|
tokio.workspace = true
|
||||||
tracing = { workspace = true }
|
tracing.workspace = true
|
||||||
anyhow = { workspace = true }
|
anyhow.workspace = true
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
sqlx = { workspace = true }
|
sqlx.workspace = true
|
||||||
async-trait = { workspace = true }
|
async-trait.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
axum.workspace = true
|
axum.workspace = true
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustical_caldav"
|
name = "rustical_caldav"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
@@ -17,21 +18,21 @@ serde_json.workspace = true
|
|||||||
axum.workspace = true
|
axum.workspace = true
|
||||||
axum-extra.workspace = true
|
axum-extra.workspace = true
|
||||||
tower.workspace = true
|
tower.workspace = true
|
||||||
async-trait = { workspace = true }
|
async-trait.workspace = true
|
||||||
thiserror = { workspace = true }
|
thiserror.workspace = true
|
||||||
quick-xml = { workspace = true }
|
quick-xml.workspace = true
|
||||||
tracing = { workspace = true }
|
tracing.workspace = true
|
||||||
futures-util = { workspace = true }
|
futures-util.workspace = true
|
||||||
derive_more = { workspace = true }
|
derive_more.workspace = true
|
||||||
base64 = { workspace = true }
|
base64.workspace = true
|
||||||
serde = { workspace = true }
|
serde.workspace = true
|
||||||
tokio = { workspace = true }
|
tokio.workspace = true
|
||||||
url = { workspace = true }
|
url.workspace = true
|
||||||
rustical_dav = { workspace = true }
|
rustical_dav.workspace = true
|
||||||
rustical_store = { workspace = true }
|
rustical_store.workspace = true
|
||||||
chrono = { workspace = true }
|
chrono.workspace = true
|
||||||
chrono-tz = { workspace = true }
|
chrono-tz.workspace = true
|
||||||
sha2 = { workspace = true }
|
sha2.workspace = true
|
||||||
ical.workspace = true
|
ical.workspace = true
|
||||||
percent-encoding.workspace = true
|
percent-encoding.workspace = true
|
||||||
rustical_xml.workspace = true
|
rustical_xml.workspace = true
|
||||||
@@ -44,3 +45,4 @@ tower-http.workspace = true
|
|||||||
strum.workspace = true
|
strum.workspace = true
|
||||||
strum_macros.workspace = true
|
strum_macros.workspace = true
|
||||||
vtimezones-rs.workspace = true
|
vtimezones-rs.workspace = true
|
||||||
|
similar-asserts.workspace = true
|
||||||
|
|||||||
@@ -26,16 +26,18 @@ pub async fn get_objects_calendar_multiget<C: CalendarStore>(
|
|||||||
let mut not_found = vec![];
|
let mut not_found = vec![];
|
||||||
|
|
||||||
for href in &cal_query.href {
|
for href in &cal_query.href {
|
||||||
if let Some(filename) = href.strip_prefix(path) {
|
if let Ok(href) = percent_encoding::percent_decode_str(href).decode_utf8()
|
||||||
|
&& let Some(filename) = href.strip_prefix(path)
|
||||||
|
{
|
||||||
let filename = filename.trim_start_matches('/');
|
let filename = filename.trim_start_matches('/');
|
||||||
if let Some(object_id) = filename.strip_suffix(".ics") {
|
if let Some(object_id) = filename.strip_suffix(".ics") {
|
||||||
match store.get_object(principal, cal_id, object_id, false).await {
|
match store.get_object(principal, cal_id, object_id, false).await {
|
||||||
Ok(object) => result.push(object),
|
Ok(object) => result.push(object),
|
||||||
Err(rustical_store::Error::NotFound) => not_found.push(href.to_owned()),
|
Err(rustical_store::Error::NotFound) => not_found.push(href.to_string()),
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
not_found.push(href.to_owned());
|
not_found.push(href.to_string());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
not_found.push(href.to_owned());
|
not_found.push(href.to_owned());
|
||||||
|
|||||||
@@ -211,6 +211,9 @@ END:VCALENDAR
|
|||||||
<privilege>
|
<privilege>
|
||||||
<read/>
|
<read/>
|
||||||
</privilege>
|
</privilege>
|
||||||
|
<privilege>
|
||||||
|
<write-properties/>
|
||||||
|
</privilege>
|
||||||
<privilege>
|
<privilege>
|
||||||
<read-acl/>
|
<read-acl/>
|
||||||
</privilege>
|
</privilege>
|
||||||
|
|||||||
@@ -39,9 +39,7 @@ async fn test_propfind() {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.trim()
|
.trim()
|
||||||
.replace("\r\n", "\n");
|
.replace("\r\n", "\n");
|
||||||
println!("{output}");
|
similar_asserts::assert_eq!(expected_output, output);
|
||||||
println!("{}, {} \n\n\n", output.len(), expected_output.len());
|
|
||||||
assert_eq!(output, expected_output);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustical_carddav"
|
name = "rustical_carddav"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
@@ -11,19 +12,19 @@ publish = false
|
|||||||
axum.workspace = true
|
axum.workspace = true
|
||||||
axum-extra.workspace = true
|
axum-extra.workspace = true
|
||||||
tower.workspace = true
|
tower.workspace = true
|
||||||
async-trait = { workspace = true }
|
async-trait.workspace = true
|
||||||
thiserror = { workspace = true }
|
thiserror.workspace = true
|
||||||
quick-xml = { workspace = true }
|
quick-xml.workspace = true
|
||||||
tracing = { workspace = true }
|
tracing.workspace = true
|
||||||
futures-util = { workspace = true }
|
futures-util.workspace = true
|
||||||
derive_more = { workspace = true }
|
derive_more.workspace = true
|
||||||
base64 = { workspace = true }
|
base64.workspace = true
|
||||||
serde = { workspace = true }
|
serde.workspace = true
|
||||||
tokio = { workspace = true }
|
tokio.workspace = true
|
||||||
url = { workspace = true }
|
url.workspace = true
|
||||||
rustical_dav = { workspace = true }
|
rustical_dav.workspace = true
|
||||||
rustical_store = { workspace = true }
|
rustical_store.workspace = true
|
||||||
chrono = { workspace = true }
|
chrono.workspace = true
|
||||||
rustical_xml.workspace = true
|
rustical_xml.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
rustical_dav_push.workspace = true
|
rustical_dav_push.workspace = true
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ pub async fn get_objects_addressbook_multiget<AS: AddressbookStore>(
|
|||||||
let mut not_found = vec![];
|
let mut not_found = vec![];
|
||||||
|
|
||||||
for href in &addressbook_multiget.href {
|
for href in &addressbook_multiget.href {
|
||||||
if let Some(filename) = href.strip_prefix(path) {
|
if let Ok(href) = percent_encoding::percent_decode_str(href).decode_utf8()
|
||||||
|
&& let Some(filename) = href.strip_prefix(path)
|
||||||
|
{
|
||||||
let filename = filename.trim_start_matches('/');
|
let filename = filename.trim_start_matches('/');
|
||||||
if let Some(object_id) = filename.strip_suffix(".vcf") {
|
if let Some(object_id) = filename.strip_suffix(".vcf") {
|
||||||
match store
|
match store
|
||||||
@@ -42,11 +44,11 @@ pub async fn get_objects_addressbook_multiget<AS: AddressbookStore>(
|
|||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(object) => result.push(object),
|
Ok(object) => result.push(object),
|
||||||
Err(rustical_store::Error::NotFound) => not_found.push(href.to_owned()),
|
Err(rustical_store::Error::NotFound) => not_found.push(href.to_string()),
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
not_found.push(href.to_owned());
|
not_found.push(href.to_string());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
not_found.push(href.to_owned());
|
not_found.push(href.to_owned());
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustical_dav"
|
name = "rustical_dav"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustical_dav_push"
|
name = "rustical_dav_push"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
@@ -9,15 +10,15 @@ publish = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustical_xml.workspace = true
|
rustical_xml.workspace = true
|
||||||
async-trait = { workspace = true }
|
async-trait.workspace = true
|
||||||
futures-util = { workspace = true }
|
futures-util.workspace = true
|
||||||
quick-xml = { workspace = true }
|
quick-xml.workspace = true
|
||||||
serde = { workspace = true }
|
serde.workspace = true
|
||||||
thiserror = { workspace = true }
|
thiserror.workspace = true
|
||||||
itertools = { workspace = true }
|
itertools.workspace = true
|
||||||
log = { workspace = true }
|
log.workspace = true
|
||||||
derive_more = { workspace = true }
|
derive_more.workspace = true
|
||||||
tracing = { workspace = true }
|
tracing.workspace = true
|
||||||
reqwest.workspace = true
|
reqwest.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
rustical_dav.workspace = true
|
rustical_dav.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustical_frontend"
|
name = "rustical_frontend"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { html, LitElement } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { Ref, createRef, ref } from 'lit/directives/ref.js';
|
import { Ref, createRef, ref } from 'lit/directives/ref.js';
|
||||||
import { escapeXml } from ".";
|
import { escapeXml } from ".";
|
||||||
|
import { getTimezones } from "./timezones.ts";
|
||||||
|
|
||||||
@customElement("create-calendar-form")
|
@customElement("create-calendar-form")
|
||||||
export class CreateCalendarForm extends LitElement {
|
export class CreateCalendarForm extends LitElement {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { i, x } from "./lit-DkXrt_Iv.mjs";
|
|||||||
import { n as n$1, t } from "./property-B8WoKf1Y.mjs";
|
import { n as n$1, t } from "./property-B8WoKf1Y.mjs";
|
||||||
import { e, n } from "./ref-BwbQvJBB.mjs";
|
import { e, n } from "./ref-BwbQvJBB.mjs";
|
||||||
import { e as escapeXml } from "./index-_IB1wMbZ.mjs";
|
import { e as escapeXml } from "./index-_IB1wMbZ.mjs";
|
||||||
|
import { g as getTimezones } from "./timezones-B0vBBzCP.mjs";
|
||||||
var __defProp = Object.defineProperty;
|
var __defProp = Object.defineProperty;
|
||||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||||
var __decorateClass = (decorators, target, key, kind) => {
|
var __decorateClass = (decorators, target, key, kind) => {
|
||||||
|
|||||||
@@ -2,18 +2,7 @@ import { i, x } from "./lit-DkXrt_Iv.mjs";
|
|||||||
import { n as n$1, t } from "./property-B8WoKf1Y.mjs";
|
import { n as n$1, t } from "./property-B8WoKf1Y.mjs";
|
||||||
import { e, n } from "./ref-BwbQvJBB.mjs";
|
import { e, n } from "./ref-BwbQvJBB.mjs";
|
||||||
import { e as escapeXml } from "./index-_IB1wMbZ.mjs";
|
import { e as escapeXml } from "./index-_IB1wMbZ.mjs";
|
||||||
let timezonesPromise = null;
|
import { g as getTimezones } from "./timezones-B0vBBzCP.mjs";
|
||||||
async function getTimezones() {
|
|
||||||
timezonesPromise ||= new Promise(async (resolve, reject) => {
|
|
||||||
try {
|
|
||||||
let response = await fetch("/frontend/_timezones.json");
|
|
||||||
resolve(await response.json());
|
|
||||||
} catch (e2) {
|
|
||||||
reject(e2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return await timezonesPromise;
|
|
||||||
}
|
|
||||||
var __defProp = Object.defineProperty;
|
var __defProp = Object.defineProperty;
|
||||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||||
var __decorateClass = (decorators, target, key, kind) => {
|
var __decorateClass = (decorators, target, key, kind) => {
|
||||||
|
|||||||
15
crates/frontend/public/assets/js/timezones-B0vBBzCP.mjs
Normal file
15
crates/frontend/public/assets/js/timezones-B0vBBzCP.mjs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
let timezonesPromise = null;
|
||||||
|
async function getTimezones() {
|
||||||
|
timezonesPromise ||= new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
let response = await fetch("/frontend/_timezones.json");
|
||||||
|
resolve(await response.json());
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return await timezonesPromise;
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
getTimezones as g
|
||||||
|
};
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustical_ical"
|
name = "rustical_ical"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use axum::{http::StatusCode, response::IntoResponse};
|
|||||||
|
|
||||||
use crate::CalDateTimeError;
|
use crate::CalDateTimeError;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Invalid ics/vcf input: {0}")]
|
#[error("Invalid ics/vcf input: {0}")]
|
||||||
InvalidData(String),
|
InvalidData(String),
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const LOCAL_DATE_TIME: &str = "%Y%m%dT%H%M%S";
|
|||||||
const UTC_DATE_TIME: &str = "%Y%m%dT%H%M%SZ";
|
const UTC_DATE_TIME: &str = "%Y%m%dT%H%M%SZ";
|
||||||
pub const LOCAL_DATE: &str = "%Y%m%d";
|
pub const LOCAL_DATE: &str = "%Y%m%d";
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||||
pub enum CalDateTimeError {
|
pub enum CalDateTimeError {
|
||||||
#[error(
|
#[error(
|
||||||
"Timezone has X-LIC-LOCATION property to specify a timezone from the Olson database, however its value {0} is invalid"
|
"Timezone has X-LIC-LOCATION property to specify a timezone from the Olson database, however its value {0} is invalid"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustical_oidc"
|
name = "rustical_oidc"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustical_store"
|
name = "rustical_store"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
@@ -8,15 +9,16 @@ license.workspace = true
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow.workspace = true
|
||||||
async-trait = { workspace = true }
|
async-trait.workspace = true
|
||||||
serde = { workspace = true }
|
serde.workspace = true
|
||||||
ical = { workspace = true }
|
sha2.workspace = true
|
||||||
chrono = { workspace = true }
|
ical.workspace = true
|
||||||
regex = { workspace = true }
|
chrono.workspace = true
|
||||||
thiserror = { workspace = true }
|
regex.workspace = true
|
||||||
tracing = { workspace = true }
|
thiserror.workspace = true
|
||||||
chrono-tz = { workspace = true }
|
tracing.workspace = true
|
||||||
|
chrono-tz.workspace = true
|
||||||
derive_more = { workspace = true, features = ["as_ref"] }
|
derive_more = { workspace = true, features = ["as_ref"] }
|
||||||
rustical_xml.workspace = true
|
rustical_xml.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
@@ -33,7 +35,7 @@ tower-sessions.workspace = true
|
|||||||
vtimezones-rs.workspace = true
|
vtimezones-rs.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rstest = { workspace = true }
|
rstest.workspace = true
|
||||||
rstest_reuse = { workspace = true }
|
rstest_reuse.workspace = true
|
||||||
rustical_store_sqlite.workspace = true
|
rustical_store_sqlite.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::synctoken::format_synctoken;
|
|||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
|
||||||
pub struct Addressbook {
|
pub struct Addressbook {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub principal: String,
|
pub principal: String,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use rustical_ical::CalendarObjectType;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct CalendarMetadata {
|
pub struct CalendarMetadata {
|
||||||
// Attributes that may be outsourced
|
// Attributes that may be outsourced
|
||||||
pub displayname: Option<String>,
|
pub displayname: Option<String>,
|
||||||
@@ -13,7 +13,7 @@ pub struct CalendarMetadata {
|
|||||||
pub color: Option<String>,
|
pub color: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct Calendar {
|
pub struct Calendar {
|
||||||
// Attributes that may be outsourced
|
// Attributes that may be outsourced
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ impl Error {
|
|||||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn is_not_found(&self) -> bool {
|
||||||
|
matches!(self, Self::NotFound)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoResponse for Error {
|
impl IntoResponse for Error {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustical_store_sqlite"
|
name = "rustical_store_sqlite"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
@@ -15,12 +16,12 @@ rstest.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
rustical_store = { workspace = true }
|
rustical_store.workspace = true
|
||||||
async-trait = { workspace = true }
|
async-trait.workspace = true
|
||||||
serde = { workspace = true }
|
serde.workspace = true
|
||||||
sqlx = { workspace = true }
|
sqlx.workspace = true
|
||||||
thiserror = { workspace = true }
|
thiserror.workspace = true
|
||||||
tracing = { workspace = true }
|
tracing.workspace = true
|
||||||
derive_more.workspace = true
|
derive_more.workspace = true
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
password-auth.workspace = true
|
password-auth.workspace = true
|
||||||
|
|||||||
@@ -118,15 +118,16 @@ impl SqliteAddressbookStore {
|
|||||||
#[instrument]
|
#[instrument]
|
||||||
pub async fn _insert_birthday_calendar<'e, E: Executor<'e, Database = Sqlite>>(
|
pub async fn _insert_birthday_calendar<'e, E: Executor<'e, Database = Sqlite>>(
|
||||||
executor: E,
|
executor: E,
|
||||||
addressbook: Addressbook,
|
addressbook: &Addressbook,
|
||||||
) -> Result<(), rustical_store::Error> {
|
) -> Result<(), rustical_store::Error> {
|
||||||
let birthday_name = addressbook
|
let birthday_name = addressbook
|
||||||
.displayname
|
.displayname
|
||||||
|
.as_ref()
|
||||||
.map(|name| format!("{name} birthdays"));
|
.map(|name| format!("{name} birthdays"));
|
||||||
let birthday_push_topic = {
|
let birthday_push_topic = {
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
hasher.update("birthdays");
|
hasher.update("birthdays");
|
||||||
hasher.update(addressbook.push_topic);
|
hasher.update(&addressbook.push_topic);
|
||||||
format!("{:x}", hasher.finalize())
|
format!("{:x}", hasher.finalize())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use rustical_store::{
|
|||||||
};
|
};
|
||||||
use sqlx::{Acquire, Executor, Sqlite, SqlitePool, Transaction};
|
use sqlx::{Acquire, Executor, Sqlite, SqlitePool, Transaction};
|
||||||
use tokio::sync::mpsc::Sender;
|
use tokio::sync::mpsc::Sender;
|
||||||
use tracing::{error, instrument};
|
use tracing::{error, instrument, warn};
|
||||||
|
|
||||||
pub mod birthday_calendar;
|
pub mod birthday_calendar;
|
||||||
|
|
||||||
@@ -34,6 +34,60 @@ pub struct SqliteAddressbookStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SqliteAddressbookStore {
|
impl SqliteAddressbookStore {
|
||||||
|
// Commit "orphaned" objects to the changelog table
|
||||||
|
pub async fn repair_orphans(&self) -> Result<(), Error> {
|
||||||
|
struct Row {
|
||||||
|
principal: String,
|
||||||
|
addressbook_id: String,
|
||||||
|
id: String,
|
||||||
|
deleted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tx = self
|
||||||
|
.db
|
||||||
|
.begin_with(BEGIN_IMMEDIATE)
|
||||||
|
.await
|
||||||
|
.map_err(crate::Error::from)?;
|
||||||
|
|
||||||
|
let rows = sqlx::query_as!(
|
||||||
|
Row,
|
||||||
|
r#"
|
||||||
|
SELECT principal, addressbook_id, id, (deleted_at IS NOT NULL) AS "deleted: bool"
|
||||||
|
FROM addressobjects
|
||||||
|
WHERE (principal, addressbook_id, id) NOT IN (
|
||||||
|
SELECT DISTINCT principal, addressbook_id, object_id FROM addressobjectchangelog
|
||||||
|
)
|
||||||
|
;
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.fetch_all(&mut *tx)
|
||||||
|
.await
|
||||||
|
.map_err(crate::Error::from)?;
|
||||||
|
|
||||||
|
for row in rows {
|
||||||
|
let operation = if row.deleted {
|
||||||
|
ChangeOperation::Delete
|
||||||
|
} else {
|
||||||
|
ChangeOperation::Add
|
||||||
|
};
|
||||||
|
warn!(
|
||||||
|
"Commiting orphaned addressbook object ({},{},{}), deleted={}",
|
||||||
|
&row.principal, &row.addressbook_id, &row.id, &row.deleted
|
||||||
|
);
|
||||||
|
log_object_operation(
|
||||||
|
&mut tx,
|
||||||
|
&row.principal,
|
||||||
|
&row.addressbook_id,
|
||||||
|
&row.id,
|
||||||
|
operation,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
tx.commit().await.map_err(crate::Error::from)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn _get_addressbook<'e, E: Executor<'e, Database = Sqlite>>(
|
async fn _get_addressbook<'e, E: Executor<'e, Database = Sqlite>>(
|
||||||
executor: E,
|
executor: E,
|
||||||
principal: &str,
|
principal: &str,
|
||||||
@@ -92,9 +146,9 @@ impl SqliteAddressbookStore {
|
|||||||
|
|
||||||
async fn _update_addressbook<'e, E: Executor<'e, Database = Sqlite>>(
|
async fn _update_addressbook<'e, E: Executor<'e, Database = Sqlite>>(
|
||||||
executor: E,
|
executor: E,
|
||||||
principal: String,
|
principal: &str,
|
||||||
id: String,
|
id: &str,
|
||||||
addressbook: Addressbook,
|
addressbook: &Addressbook,
|
||||||
) -> Result<(), rustical_store::Error> {
|
) -> Result<(), rustical_store::Error> {
|
||||||
let result = sqlx::query!(
|
let result = sqlx::query!(
|
||||||
r#"UPDATE addressbooks SET principal = ?, id = ?, displayname = ?, description = ?, push_topic = ?
|
r#"UPDATE addressbooks SET principal = ?, id = ?, displayname = ?, description = ?, push_topic = ?
|
||||||
@@ -399,7 +453,7 @@ impl AddressbookStore for SqliteAddressbookStore {
|
|||||||
id: String,
|
id: String,
|
||||||
addressbook: Addressbook,
|
addressbook: Addressbook,
|
||||||
) -> Result<(), rustical_store::Error> {
|
) -> Result<(), rustical_store::Error> {
|
||||||
Self::_update_addressbook(&self.db, principal, id, addressbook).await
|
Self::_update_addressbook(&self.db, &principal, &id, &addressbook).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument]
|
#[instrument]
|
||||||
@@ -413,7 +467,7 @@ impl AddressbookStore for SqliteAddressbookStore {
|
|||||||
.await
|
.await
|
||||||
.map_err(crate::Error::from)?;
|
.map_err(crate::Error::from)?;
|
||||||
Self::_insert_addressbook(&mut *tx, &addressbook).await?;
|
Self::_insert_addressbook(&mut *tx, &addressbook).await?;
|
||||||
Self::_insert_birthday_calendar(&mut *tx, addressbook).await?;
|
Self::_insert_birthday_calendar(&mut *tx, &addressbook).await?;
|
||||||
tx.commit().await.map_err(crate::Error::from)?;
|
tx.commit().await.map_err(crate::Error::from)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -631,7 +685,7 @@ impl AddressbookStore for SqliteAddressbookStore {
|
|||||||
.await?
|
.await?
|
||||||
.push_topic,
|
.push_topic,
|
||||||
}) {
|
}) {
|
||||||
error!("Push notification about deleted addressbook failed: {err}");
|
error!("Push notification about restored addressbook object failed: {err}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -665,6 +719,7 @@ impl AddressbookStore for SqliteAddressbookStore {
|
|||||||
Self::_insert_addressbook(&mut *tx, &addressbook).await?;
|
Self::_insert_addressbook(&mut *tx, &addressbook).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut sync_token = None;
|
||||||
for object in objects {
|
for object in objects {
|
||||||
Self::_put_object(
|
Self::_put_object(
|
||||||
&mut *tx,
|
&mut *tx,
|
||||||
@@ -674,9 +729,31 @@ impl AddressbookStore for SqliteAddressbookStore {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
sync_token = Some(
|
||||||
|
log_object_operation(
|
||||||
|
&mut tx,
|
||||||
|
&addressbook.principal,
|
||||||
|
&addressbook.id,
|
||||||
|
object.get_id(),
|
||||||
|
ChangeOperation::Add,
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.commit().await.map_err(crate::Error::from)?;
|
tx.commit().await.map_err(crate::Error::from)?;
|
||||||
|
if let Some(sync_token) = sync_token
|
||||||
|
&& let Err(err) = self.sender.try_send(CollectionOperation {
|
||||||
|
data: CollectionOperationInfo::Content { sync_token },
|
||||||
|
topic: self
|
||||||
|
.get_addressbook(&addressbook.principal, &addressbook.id, true)
|
||||||
|
.await?
|
||||||
|
.push_topic,
|
||||||
|
})
|
||||||
|
{
|
||||||
|
error!("Push notification about imported addressbook failed: {err}");
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -688,7 +765,7 @@ async fn log_object_operation(
|
|||||||
addressbook_id: &str,
|
addressbook_id: &str,
|
||||||
object_id: &str,
|
object_id: &str,
|
||||||
operation: ChangeOperation,
|
operation: ChangeOperation,
|
||||||
) -> Result<String, sqlx::Error> {
|
) -> Result<String, Error> {
|
||||||
struct Synctoken {
|
struct Synctoken {
|
||||||
synctoken: i64,
|
synctoken: i64,
|
||||||
}
|
}
|
||||||
@@ -703,7 +780,8 @@ async fn log_object_operation(
|
|||||||
addressbook_id
|
addressbook_id
|
||||||
)
|
)
|
||||||
.fetch_one(&mut **tx)
|
.fetch_one(&mut **tx)
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(crate::Error::from)?;
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
@@ -717,6 +795,7 @@ async fn log_object_operation(
|
|||||||
operation
|
operation
|
||||||
)
|
)
|
||||||
.execute(&mut **tx)
|
.execute(&mut **tx)
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(crate::Error::from)?;
|
||||||
Ok(format_synctoken(synctoken))
|
Ok(format_synctoken(synctoken))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use rustical_store::{CollectionOperation, CollectionOperationInfo};
|
|||||||
use sqlx::types::chrono::NaiveDateTime;
|
use sqlx::types::chrono::NaiveDateTime;
|
||||||
use sqlx::{Acquire, Executor, Sqlite, SqlitePool, Transaction};
|
use sqlx::{Acquire, Executor, Sqlite, SqlitePool, Transaction};
|
||||||
use tokio::sync::mpsc::Sender;
|
use tokio::sync::mpsc::Sender;
|
||||||
use tracing::{error, instrument};
|
use tracing::{error, instrument, warn};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct CalendarObjectRow {
|
struct CalendarObjectRow {
|
||||||
@@ -94,6 +94,53 @@ pub struct SqliteCalendarStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SqliteCalendarStore {
|
impl SqliteCalendarStore {
|
||||||
|
// Commit "orphaned" objects to the changelog table
|
||||||
|
pub async fn repair_orphans(&self) -> Result<(), Error> {
|
||||||
|
struct Row {
|
||||||
|
principal: String,
|
||||||
|
cal_id: String,
|
||||||
|
id: String,
|
||||||
|
deleted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tx = self
|
||||||
|
.db
|
||||||
|
.begin_with(BEGIN_IMMEDIATE)
|
||||||
|
.await
|
||||||
|
.map_err(crate::Error::from)?;
|
||||||
|
|
||||||
|
let rows = sqlx::query_as!(
|
||||||
|
Row,
|
||||||
|
r#"
|
||||||
|
SELECT principal, cal_id, id, (deleted_at IS NOT NULL) AS "deleted: bool"
|
||||||
|
FROM calendarobjects
|
||||||
|
WHERE (principal, cal_id, id) NOT IN (
|
||||||
|
SELECT DISTINCT principal, cal_id, object_id FROM calendarobjectchangelog
|
||||||
|
)
|
||||||
|
;
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.fetch_all(&mut *tx)
|
||||||
|
.await
|
||||||
|
.map_err(crate::Error::from)?;
|
||||||
|
|
||||||
|
for row in rows {
|
||||||
|
let operation = if row.deleted {
|
||||||
|
ChangeOperation::Delete
|
||||||
|
} else {
|
||||||
|
ChangeOperation::Add
|
||||||
|
};
|
||||||
|
warn!(
|
||||||
|
"Commiting orphaned calendar object ({},{},{}), deleted={}",
|
||||||
|
&row.principal, &row.cal_id, &row.id, &row.deleted
|
||||||
|
);
|
||||||
|
log_object_operation(&mut tx, &row.principal, &row.cal_id, &row.id, operation).await?;
|
||||||
|
}
|
||||||
|
tx.commit().await.map_err(crate::Error::from)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn _get_calendar<'e, E: Executor<'e, Database = Sqlite>>(
|
async fn _get_calendar<'e, E: Executor<'e, Database = Sqlite>>(
|
||||||
executor: E,
|
executor: E,
|
||||||
principal: &str,
|
principal: &str,
|
||||||
@@ -351,9 +398,9 @@ impl SqliteCalendarStore {
|
|||||||
#[instrument]
|
#[instrument]
|
||||||
async fn _put_object<'e, E: Executor<'e, Database = Sqlite>>(
|
async fn _put_object<'e, E: Executor<'e, Database = Sqlite>>(
|
||||||
executor: E,
|
executor: E,
|
||||||
principal: String,
|
principal: &str,
|
||||||
cal_id: String,
|
cal_id: &str,
|
||||||
object: CalendarObject,
|
object: &CalendarObject,
|
||||||
overwrite: bool,
|
overwrite: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let (object_id, uid, ics) = (object.get_id(), object.get_uid(), object.get_ics());
|
let (object_id, uid, ics) = (object.get_id(), object.get_uid(), object.get_ics());
|
||||||
@@ -600,18 +647,35 @@ impl CalendarStore for SqliteCalendarStore {
|
|||||||
Self::_insert_calendar(&mut *tx, calendar.clone()).await?;
|
Self::_insert_calendar(&mut *tx, calendar.clone()).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut sync_token = None;
|
||||||
for object in objects {
|
for object in objects {
|
||||||
Self::_put_object(
|
Self::_put_object(&mut *tx, &calendar.principal, &calendar.id, &object, false).await?;
|
||||||
&mut *tx,
|
|
||||||
calendar.principal.clone(),
|
sync_token = Some(
|
||||||
calendar.id.clone(),
|
log_object_operation(
|
||||||
object,
|
&mut tx,
|
||||||
false,
|
&calendar.principal,
|
||||||
)
|
&calendar.id,
|
||||||
.await?;
|
object.get_id(),
|
||||||
|
ChangeOperation::Add,
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.commit().await.map_err(crate::Error::from)?;
|
tx.commit().await.map_err(crate::Error::from)?;
|
||||||
|
|
||||||
|
if let Some(sync_token) = sync_token
|
||||||
|
&& let Err(err) = self.sender.try_send(CollectionOperation {
|
||||||
|
data: CollectionOperationInfo::Content { sync_token },
|
||||||
|
topic: self
|
||||||
|
.get_calendar(&calendar.principal, &calendar.id, true)
|
||||||
|
.await?
|
||||||
|
.push_topic,
|
||||||
|
})
|
||||||
|
{
|
||||||
|
error!("Push notification about imported calendar failed: {err}");
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,14 +753,7 @@ impl CalendarStore for SqliteCalendarStore {
|
|||||||
return Err(Error::ReadOnly);
|
return Err(Error::ReadOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::_put_object(
|
Self::_put_object(&mut *tx, &principal, &cal_id, &object, overwrite).await?;
|
||||||
&mut *tx,
|
|
||||||
principal.clone(),
|
|
||||||
cal_id.clone(),
|
|
||||||
object,
|
|
||||||
overwrite,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let sync_token = log_object_operation(
|
let sync_token = log_object_operation(
|
||||||
&mut tx,
|
&mut tx,
|
||||||
@@ -774,7 +831,7 @@ impl CalendarStore for SqliteCalendarStore {
|
|||||||
data: CollectionOperationInfo::Content { sync_token },
|
data: CollectionOperationInfo::Content { sync_token },
|
||||||
topic: self.get_calendar(principal, cal_id, true).await?.push_topic,
|
topic: self.get_calendar(principal, cal_id, true).await?.push_topic,
|
||||||
}) {
|
}) {
|
||||||
error!("Push notification about deleted calendar failed: {err}");
|
error!("Push notification about restored calendar object failed: {err}");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -795,6 +852,7 @@ impl CalendarStore for SqliteCalendarStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Logs an operation to the events
|
// Logs an operation to the events
|
||||||
|
// TODO: Log multiple updates
|
||||||
async fn log_object_operation(
|
async fn log_object_operation(
|
||||||
tx: &mut Transaction<'_, Sqlite>,
|
tx: &mut Transaction<'_, Sqlite>,
|
||||||
principal: &str,
|
principal: &str,
|
||||||
|
|||||||
81
crates/store_sqlite/src/tests/addressbook_store.rs
Normal file
81
crates/store_sqlite/src/tests/addressbook_store.rs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{addressbook_store::SqliteAddressbookStore, tests::get_test_addressbook_store};
|
||||||
|
use rstest::rstest;
|
||||||
|
use rustical_store::{Addressbook, AddressbookStore};
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_addressbook_store(
|
||||||
|
#[from(get_test_addressbook_store)]
|
||||||
|
#[future]
|
||||||
|
addr_store: SqliteAddressbookStore,
|
||||||
|
) {
|
||||||
|
let addr_store = addr_store.await;
|
||||||
|
|
||||||
|
let cal = Addressbook {
|
||||||
|
id: "addr".to_string(),
|
||||||
|
principal: "fake-user".to_string(),
|
||||||
|
displayname: None,
|
||||||
|
description: None,
|
||||||
|
deleted_at: None,
|
||||||
|
synctoken: 0,
|
||||||
|
push_topic: "alskdj".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
addr_store.insert_addressbook(cal).await.is_err(),
|
||||||
|
"This should fail due to the user not existing "
|
||||||
|
);
|
||||||
|
|
||||||
|
let addr = Addressbook {
|
||||||
|
id: "addr".to_string(),
|
||||||
|
principal: "user".to_string(),
|
||||||
|
displayname: None,
|
||||||
|
description: None,
|
||||||
|
deleted_at: None,
|
||||||
|
synctoken: 0,
|
||||||
|
push_topic: "alskdj".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
addr_store.insert_addressbook(addr.clone()).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
addr_store
|
||||||
|
.get_addressbook("user", "addr", false)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
addr
|
||||||
|
);
|
||||||
|
|
||||||
|
addr_store
|
||||||
|
.delete_addressbook("user", "addr", true)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let Err(err) = addr_store.get_addressbook("user", "addr", false).await else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
assert!(err.is_not_found());
|
||||||
|
|
||||||
|
addr_store
|
||||||
|
.get_addressbook("user", "addr", true)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
addr_store
|
||||||
|
.restore_addressbook("user", "addr")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
addr_store
|
||||||
|
.delete_addressbook("user", "addr", false)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let Err(err) = addr_store.get_addressbook("user", "addr", true).await else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
assert!(err.is_not_found());
|
||||||
|
}
|
||||||
|
}
|
||||||
76
crates/store_sqlite/src/tests/calendar_store.rs
Normal file
76
crates/store_sqlite/src/tests/calendar_store.rs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{calendar_store::SqliteCalendarStore, tests::get_test_calendar_store};
|
||||||
|
use rstest::rstest;
|
||||||
|
use rustical_store::{Calendar, CalendarMetadata, CalendarStore};
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_calendar_store(
|
||||||
|
#[from(get_test_calendar_store)]
|
||||||
|
#[future]
|
||||||
|
cal_store: SqliteCalendarStore,
|
||||||
|
) {
|
||||||
|
let cal_store = cal_store.await;
|
||||||
|
|
||||||
|
let cal = Calendar {
|
||||||
|
principal: "fake-user".to_string(),
|
||||||
|
timezone_id: None,
|
||||||
|
deleted_at: None,
|
||||||
|
meta: CalendarMetadata::default(),
|
||||||
|
id: "cal".to_string(),
|
||||||
|
synctoken: 0,
|
||||||
|
subscription_url: None,
|
||||||
|
push_topic: "alskdj".to_string(),
|
||||||
|
components: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
cal_store.insert_calendar(cal).await.is_err(),
|
||||||
|
"This should fail due to the user not existing "
|
||||||
|
);
|
||||||
|
|
||||||
|
let cal = Calendar {
|
||||||
|
principal: "user".to_string(),
|
||||||
|
timezone_id: None,
|
||||||
|
deleted_at: None,
|
||||||
|
meta: CalendarMetadata::default(),
|
||||||
|
id: "cal".to_string(),
|
||||||
|
synctoken: 0,
|
||||||
|
subscription_url: None,
|
||||||
|
push_topic: "alskdj".to_string(),
|
||||||
|
components: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
cal_store.insert_calendar(cal.clone()).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
cal_store.get_calendar("user", "cal", false).await.unwrap(),
|
||||||
|
cal
|
||||||
|
);
|
||||||
|
|
||||||
|
cal_store
|
||||||
|
.delete_calendar("user", "cal", true)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let Err(err) = cal_store.get_calendar("user", "cal", false).await else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
assert!(err.is_not_found());
|
||||||
|
|
||||||
|
cal_store.get_calendar("user", "cal", true).await.unwrap();
|
||||||
|
|
||||||
|
cal_store.restore_calendar("user", "cal").await.unwrap();
|
||||||
|
|
||||||
|
cal_store
|
||||||
|
.delete_calendar("user", "cal", false)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let Err(err) = cal_store.get_calendar("user", "cal", true).await else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
assert!(err.is_not_found());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,9 @@ use tokio::sync::OnceCell;
|
|||||||
|
|
||||||
static DB: OnceCell<SqlitePool> = OnceCell::const_new();
|
static DB: OnceCell<SqlitePool> = OnceCell::const_new();
|
||||||
|
|
||||||
|
mod addressbook_store;
|
||||||
|
mod calendar_store;
|
||||||
|
|
||||||
async fn get_test_db() -> SqlitePool {
|
async fn get_test_db() -> SqlitePool {
|
||||||
DB.get_or_init(async || {
|
DB.get_or_init(async || {
|
||||||
let db = SqlitePool::connect("sqlite::memory:").await.unwrap();
|
let db = SqlitePool::connect("sqlite::memory:").await.unwrap();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustical_xml"
|
name = "rustical_xml"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
description.workspace = true
|
description.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "xml_derive"
|
name = "xml_derive"
|
||||||
version = "0.1.0"
|
version.workspace = true
|
||||||
edition = "2024"
|
rust-version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|||||||
1
docs/googlec55e08580a46745c.html
Normal file
1
docs/googlec55e08580a46745c.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
google-site-verification: googlec55e08580a46745c.html
|
||||||
@@ -178,7 +178,9 @@ pub fn make_app<
|
|||||||
tracing::debug!("unauthorized");
|
tracing::debug!("unauthorized");
|
||||||
}
|
}
|
||||||
StatusCode::NOT_FOUND => {
|
StatusCode::NOT_FOUND => {
|
||||||
tracing::warn!("client error");
|
// Clients like GNOME Calendar will try to reach /remote.php/webdav
|
||||||
|
// quite often clogging up the logs
|
||||||
|
tracing::info!("client error");
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
tracing::error!("client error");
|
tracing::error!("client error");
|
||||||
|
|||||||
@@ -71,7 +71,9 @@ async fn get_data_stores(
|
|||||||
let (send, recv) = tokio::sync::mpsc::channel(1000);
|
let (send, recv) = tokio::sync::mpsc::channel(1000);
|
||||||
|
|
||||||
let addressbook_store = Arc::new(SqliteAddressbookStore::new(db.clone(), send.clone()));
|
let addressbook_store = Arc::new(SqliteAddressbookStore::new(db.clone(), send.clone()));
|
||||||
|
addressbook_store.repair_orphans().await?;
|
||||||
let cal_store = Arc::new(SqliteCalendarStore::new(db.clone(), send));
|
let cal_store = Arc::new(SqliteCalendarStore::new(db.clone(), send));
|
||||||
|
cal_store.repair_orphans().await?;
|
||||||
let subscription_store = Arc::new(SqliteStore::new(db.clone()));
|
let subscription_store = Arc::new(SqliteStore::new(db.clone()));
|
||||||
let principal_store = Arc::new(SqlitePrincipalStore::new(db));
|
let principal_store = Arc::new(SqlitePrincipalStore::new(db));
|
||||||
(
|
(
|
||||||
|
|||||||
Reference in New Issue
Block a user