Compare commits

...

5 Commits

Author SHA1 Message Date
Lennart
22a0337375 version 0.10.5 2025-11-17 19:14:17 +01:00
Lennart
21902e108a fix some error messages 2025-11-17 19:13:13 +01:00
Lennart
08f526fa5b Add startup routine to fix orphaned objects
fixes #145, related to #142
2025-11-17 19:11:30 +01:00
Lennart
ac73f3aaff addressbook_store: Commit import addressbooks to changelog 2025-11-17 18:35:10 +01:00
Lennart
9fdc8434db calendar import: log added events 2025-11-17 18:22:33 +01:00
7 changed files with 346 additions and 139 deletions

View 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"
}

View 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"
}

182
Cargo.lock generated
View File

@@ -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.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -175,7 +175,7 @@ checksum = "34921de3d57974069bad483fdfe0ec65d88c4ff892edd1ab4d8b03be0dda1b9b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -310,7 +310,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "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.1" 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 = "5136e6c5e7e7978fe23e9876fb924af2c0f84c72127ac6ac17e7c46f457d362c" checksum = "dbfe9f610fe4e99cf0cfcd03ccf8c63c28c616fe714d80475ef731f3b13dd21b"
dependencies = [ dependencies = [
"axum", "axum",
"axum-core", "axum-core",
@@ -525,15 +525,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 +586,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 +596,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 +615,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -747,7 +747,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -771,7 +771,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -782,7 +782,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -823,7 +823,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
"unicode-xid", "unicode-xid",
] ]
@@ -847,7 +847,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -1048,9 +1048,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 +1178,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -1466,9 +1466,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 +1519,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",
@@ -1957,9 +1957,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 +2081,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 +2102,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -2116,9 +2116,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 +2327,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"proc-macro2-diagnostics", "proc-macro2-diagnostics",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -2400,7 +2400,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -2526,7 +2526,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
"version_check", "version_check",
"yansi", "yansi",
] ]
@@ -2551,14 +2551,14 @@ dependencies = [
"itertools 0.14.0", "itertools 0.14.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "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 +2620,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 +2718,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -2849,9 +2849,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 +2892,7 @@ dependencies = [
"regex", "regex",
"relative-path", "relative-path",
"rustc_version", "rustc_version",
"syn 2.0.109", "syn 2.0.110",
"unicode-ident", "unicode-ident",
] ]
@@ -2904,7 +2904,7 @@ checksum = "b3a8fb4672e840a587a66fc577a5491375df51ddb88f2a2c2a792598c326fe14"
dependencies = [ dependencies = [
"quote", "quote",
"rand 0.8.5", "rand 0.8.5",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -2937,7 +2937,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rust-embed-utils", "rust-embed-utils",
"syn 2.0.109", "syn 2.0.110",
"walkdir", "walkdir",
] ]
@@ -2974,7 +2974,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical" name = "rustical"
version = "0.10.4" version = "0.10.5"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"argon2", "argon2",
@@ -3017,7 +3017,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_caldav" name = "rustical_caldav"
version = "0.10.4" version = "0.10.5"
dependencies = [ dependencies = [
"async-std", "async-std",
"async-trait", "async-trait",
@@ -3057,7 +3057,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_carddav" name = "rustical_carddav"
version = "0.10.4" version = "0.10.5"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
@@ -3089,7 +3089,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_dav" name = "rustical_dav"
version = "0.10.4" version = "0.10.5"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
@@ -3114,7 +3114,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_dav_push" name = "rustical_dav_push"
version = "0.10.4" version = "0.10.5"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
@@ -3139,7 +3139,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_frontend" name = "rustical_frontend"
version = "0.10.4" version = "0.10.5"
dependencies = [ dependencies = [
"askama", "askama",
"askama_web", "askama_web",
@@ -3175,7 +3175,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_ical" name = "rustical_ical"
version = "0.10.4" version = "0.10.5"
dependencies = [ dependencies = [
"axum", "axum",
"chrono", "chrono",
@@ -3192,7 +3192,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_oidc" name = "rustical_oidc"
version = "0.10.4" version = "0.10.5"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
@@ -3208,7 +3208,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_store" name = "rustical_store"
version = "0.10.4" version = "0.10.5"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -3241,7 +3241,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_store_sqlite" name = "rustical_store_sqlite"
version = "0.10.4" version = "0.10.5"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"chrono", "chrono",
@@ -3262,7 +3262,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_xml" name = "rustical_xml"
version = "0.10.4" version = "0.10.5"
dependencies = [ dependencies = [
"quick-xml", "quick-xml",
"thiserror 2.0.17", "thiserror 2.0.17",
@@ -3425,7 +3425,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -3493,9 +3493,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",
@@ -3512,14 +3512,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.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -3687,7 +3687,7 @@ dependencies = [
"quote", "quote",
"sqlx-core", "sqlx-core",
"sqlx-macros-core", "sqlx-macros-core",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -3710,7 +3710,7 @@ dependencies = [
"sqlx-mysql", "sqlx-mysql",
"sqlx-postgres", "sqlx-postgres",
"sqlx-sqlite", "sqlx-sqlite",
"syn 2.0.109", "syn 2.0.110",
"tokio", "tokio",
"url", "url",
] ]
@@ -3862,7 +3862,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -3884,9 +3884,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.109" 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 = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -3910,7 +3910,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -3939,7 +3939,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -3950,7 +3950,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -4044,7 +4044,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -4356,7 +4356,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -4639,7 +4639,7 @@ dependencies = [
"bumpalo", "bumpalo",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@@ -4721,7 +4721,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -4732,7 +4732,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -5013,14 +5013,14 @@ checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
[[package]] [[package]]
name = "xml_derive" name = "xml_derive"
version = "0.10.4" 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.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -5048,7 +5048,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
"synstructure", "synstructure",
] ]
@@ -5069,7 +5069,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]
[[package]] [[package]]
@@ -5089,7 +5089,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
"synstructure", "synstructure",
] ]
@@ -5129,5 +5129,5 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.109", "syn 2.0.110",
] ]

View File

@@ -2,7 +2,7 @@
members = ["crates/*"] members = ["crates/*"]
[workspace.package] [workspace.package]
version = "0.10.4" version = "0.10.5"
rust-version = "1.91" rust-version = "1.91"
edition = "2024" edition = "2024"
description = "A CalDAV server" description = "A CalDAV server"

View File

@@ -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};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct AddressObjectRow { struct AddressObjectRow {
@@ -32,6 +32,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,
@@ -90,9 +144,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 = ?
@@ -116,7 +170,7 @@ impl SqliteAddressbookStore {
async fn _insert_addressbook<'e, E: Executor<'e, Database = Sqlite>>( async fn _insert_addressbook<'e, E: Executor<'e, Database = Sqlite>>(
executor: E, executor: E,
addressbook: Addressbook, addressbook: &Addressbook,
) -> Result<(), rustical_store::Error> { ) -> Result<(), rustical_store::Error> {
sqlx::query!( sqlx::query!(
r#"INSERT INTO addressbooks (principal, id, displayname, description, push_topic) r#"INSERT INTO addressbooks (principal, id, displayname, description, push_topic)
@@ -283,9 +337,9 @@ impl SqliteAddressbookStore {
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,
addressbook_id: String, addressbook_id: &str,
object: AddressObject, object: &AddressObject,
overwrite: bool, overwrite: bool,
) -> Result<(), rustical_store::Error> { ) -> Result<(), rustical_store::Error> {
let (object_id, vcf) = (object.get_id(), object.get_vcf()); let (object_id, vcf) = (object.get_id(), object.get_vcf());
@@ -397,7 +451,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]
@@ -405,7 +459,7 @@ impl AddressbookStore for SqliteAddressbookStore {
&self, &self,
addressbook: Addressbook, addressbook: Addressbook,
) -> Result<(), rustical_store::Error> { ) -> Result<(), rustical_store::Error> {
Self::_insert_addressbook(&self.db, addressbook).await Self::_insert_addressbook(&self.db, &addressbook).await
} }
#[instrument] #[instrument]
@@ -521,14 +575,7 @@ impl AddressbookStore for SqliteAddressbookStore {
let object_id = object.get_id().to_owned(); let object_id = object.get_id().to_owned();
Self::_put_object( Self::_put_object(&mut *tx, &principal, &addressbook_id, &object, overwrite).await?;
&mut *tx,
principal.clone(),
addressbook_id.clone(),
object,
overwrite,
)
.await?;
let sync_token = log_object_operation( let sync_token = log_object_operation(
&mut tx, &mut tx,
@@ -628,7 +675,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(())
@@ -659,21 +706,44 @@ impl AddressbookStore for SqliteAddressbookStore {
return Err(Error::AlreadyExists); return Err(Error::AlreadyExists);
} }
if existing.is_none() { if existing.is_none() {
Self::_insert_addressbook(&mut *tx, addressbook.clone()).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,
addressbook.principal.clone(), &addressbook.principal,
addressbook.id.clone(), &addressbook.id,
object, &object,
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(())
} }
} }
@@ -685,7 +755,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,
} }
@@ -700,7 +770,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#"
@@ -714,6 +785,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))
} }

View File

@@ -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(())
} }

View File

@@ -69,7 +69,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));
( (