mirror of
https://github.com/lennart-k/rustical.git
synced 2026-01-31 08:58:21 +00:00
Compare commits
19 Commits
4adf1818d4
...
b9c2a4cc27
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9c2a4cc27 | ||
|
|
c91205558e | ||
|
|
cd9e3ed8d6 | ||
|
|
200d5e7170 | ||
|
|
5cb538d3fb | ||
|
|
d9da123ff4 | ||
|
|
eba2f0da9f | ||
|
|
291bd967da | ||
|
|
002814a564 | ||
|
|
ba13aaa703 | ||
|
|
7a02bfeffc | ||
|
|
1b69148d6f | ||
|
|
f4de80c6b9 | ||
|
|
7a1ec3e351 | ||
|
|
eb7bdd0018 | ||
|
|
8e583e24cb | ||
|
|
5e5017a185 | ||
|
|
3c87191f69 | ||
|
|
d1947a159b |
2
.github/workflows/docker-publish.yml
vendored
2
.github/workflows/docker-publish.yml
vendored
@@ -2,7 +2,7 @@ name: Docker
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["main", "dev"]
|
branches: ["main"]
|
||||||
release:
|
release:
|
||||||
types: ["published"]
|
types: ["published"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"db_name": "SQLite",
|
|
||||||
"query": "DELETE FROM calendarobjectchangelog WHERE (principal, cal_id, object_id) = (?, ?, ?);",
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"parameters": {
|
|
||||||
"Right": 3
|
|
||||||
},
|
|
||||||
"nullable": []
|
|
||||||
},
|
|
||||||
"hash": "146e23ae4e0eaae4d65ac7563c67d4f295ccc2534dcc4b3bd710de773ed137f9"
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"db_name": "SQLite",
|
|
||||||
"query": "UPDATE calendarobjects SET ics = ? WHERE (principal, cal_id, id) = (?, ?, ?);",
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"parameters": {
|
|
||||||
"Right": 4
|
|
||||||
},
|
|
||||||
"nullable": []
|
|
||||||
},
|
|
||||||
"hash": "354decac84758c88280f60fbf0f93dddc6c7ff92ac7b8ba44049d31df3c680e3"
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
"db_name": "SQLite",
|
|
||||||
"query": "SELECT principal, cal_id, id, ics FROM calendarobjects WHERE ics LIKE '%VERSION:4.0%';",
|
|
||||||
"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": "ics",
|
|
||||||
"ordinal": 3,
|
|
||||||
"type_info": "Text"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Right": 0
|
|
||||||
},
|
|
||||||
"nullable": [
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hash": "bdaa4bee8b01d0e3773e34672ed4805d1e71d24888f2227045afd90bf080fc23"
|
|
||||||
}
|
|
||||||
30
Cargo.lock
generated
30
Cargo.lock
generated
@@ -2870,9 +2870,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.39.0"
|
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 = "f2e3bf4aa9d243beeb01a7b3bc30b77cfe2c44e24ec02d751a7104a53c2c49a1"
|
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@@ -3317,7 +3317,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical"
|
name = "rustical"
|
||||||
version = "0.11.17"
|
version = "0.11.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argon2",
|
"argon2",
|
||||||
@@ -3328,7 +3328,6 @@ dependencies = [
|
|||||||
"figment",
|
"figment",
|
||||||
"headers",
|
"headers",
|
||||||
"http",
|
"http",
|
||||||
"ical",
|
|
||||||
"insta",
|
"insta",
|
||||||
"opentelemetry",
|
"opentelemetry",
|
||||||
"opentelemetry-otlp",
|
"opentelemetry-otlp",
|
||||||
@@ -3363,7 +3362,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_caldav"
|
name = "rustical_caldav"
|
||||||
version = "0.11.17"
|
version = "0.11.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3405,7 +3404,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_carddav"
|
name = "rustical_carddav"
|
||||||
version = "0.11.17"
|
version = "0.11.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3439,7 +3438,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_dav"
|
name = "rustical_dav"
|
||||||
version = "0.11.17"
|
version = "0.11.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3465,7 +3464,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_dav_push"
|
name = "rustical_dav_push"
|
||||||
version = "0.11.17"
|
version = "0.11.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3490,7 +3489,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_frontend"
|
name = "rustical_frontend"
|
||||||
version = "0.11.17"
|
version = "0.11.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"askama",
|
"askama",
|
||||||
"askama_web",
|
"askama_web",
|
||||||
@@ -3526,7 +3525,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_ical"
|
name = "rustical_ical"
|
||||||
version = "0.11.17"
|
version = "0.11.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -3545,7 +3544,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_oidc"
|
name = "rustical_oidc"
|
||||||
version = "0.11.17"
|
version = "0.11.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3561,7 +3560,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_store"
|
name = "rustical_store"
|
||||||
version = "0.11.17"
|
version = "0.11.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3594,7 +3593,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_store_sqlite"
|
name = "rustical_store_sqlite"
|
||||||
version = "0.11.17"
|
version = "0.11.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -3604,7 +3603,6 @@ dependencies = [
|
|||||||
"password-auth",
|
"password-auth",
|
||||||
"password-hash",
|
"password-hash",
|
||||||
"pbkdf2",
|
"pbkdf2",
|
||||||
"regex",
|
|
||||||
"rstest",
|
"rstest",
|
||||||
"rustical_ical",
|
"rustical_ical",
|
||||||
"rustical_store",
|
"rustical_store",
|
||||||
@@ -3619,7 +3617,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_xml"
|
name = "rustical_xml"
|
||||||
version = "0.11.17"
|
version = "0.11.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
@@ -5441,7 +5439,7 @@ checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xml_derive"
|
name = "xml_derive"
|
||||||
version = "0.11.17"
|
version = "0.11.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling 0.23.0",
|
"darling 0.23.0",
|
||||||
"heck",
|
"heck",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
members = ["crates/*"]
|
members = ["crates/*"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.11.17"
|
version = "0.11.10"
|
||||||
rust-version = "1.92"
|
rust-version = "1.92"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "A CalDAV server"
|
description = "A CalDAV server"
|
||||||
@@ -73,7 +73,7 @@ tokio = { version = "1.48", features = [
|
|||||||
url = "2.5"
|
url = "2.5"
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
quick-xml = { version = "0.39" }
|
quick-xml = { version = "0.38" }
|
||||||
rust-embed = "8.9"
|
rust-embed = "8.9"
|
||||||
tower-sessions = "0.14"
|
tower-sessions = "0.14"
|
||||||
futures-core = "0.3"
|
futures-core = "0.3"
|
||||||
@@ -160,7 +160,6 @@ 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
|
||||||
ical.workspace = true
|
|
||||||
toml.workspace = true
|
toml.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ COPY --from=planner /rustical/recipe.json recipe.json
|
|||||||
RUN cargo chef cook --release --target "$(cat /tmp/rust_target)"
|
RUN cargo chef cook --release --target "$(cat /tmp/rust_target)"
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN cargo install --locked --target "$(cat /tmp/rust_target)" --path .
|
RUN cargo install --target "$(cat /tmp/rust_target)" --path .
|
||||||
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
COPY --from=builder /usr/local/cargo/bin/rustical /usr/local/bin/rustical
|
COPY --from=builder /usr/local/cargo/bin/rustical /usr/local/bin/rustical
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ impl<PN: XmlDeserialize> XmlDeserialize for PropElement<PN> {
|
|||||||
// start of a child element
|
// start of a child element
|
||||||
Event::Start(start) | Event::Empty(start) => {
|
Event::Start(start) | Event::Empty(start) => {
|
||||||
let empty = matches!(event, Event::Empty(_));
|
let empty = matches!(event, Event::Empty(_));
|
||||||
let (ns, name) = reader.resolver().resolve_element(start.name());
|
let (ns, name) = reader.resolve_element(start.name());
|
||||||
let ns = match ns {
|
let ns = match ns {
|
||||||
ResolveResult::Bound(ns) => Some(NamespaceOwned::from(ns)),
|
ResolveResult::Bound(ns) => Some(NamespaceOwned::from(ns)),
|
||||||
ResolveResult::Unknown(_ns) => todo!("handle error"),
|
ResolveResult::Unknown(_ns) => todo!("handle error"),
|
||||||
|
|||||||
@@ -37,4 +37,3 @@ pbkdf2.workspace = true
|
|||||||
rustical_ical.workspace = true
|
rustical_ical.workspace = true
|
||||||
rstest = { workspace = true, optional = true }
|
rstest = { workspace = true, optional = true }
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
regex.workspace = true
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use async_trait::async_trait;
|
|||||||
use chrono::TimeDelta;
|
use chrono::TimeDelta;
|
||||||
use derive_more::derive::Constructor;
|
use derive_more::derive::Constructor;
|
||||||
use ical::types::CalDateTime;
|
use ical::types::CalDateTime;
|
||||||
use regex::Regex;
|
|
||||||
use rustical_ical::{CalendarObject, CalendarObjectType};
|
use rustical_ical::{CalendarObject, CalendarObjectType};
|
||||||
use rustical_store::calendar_store::CalendarQuery;
|
use rustical_store::calendar_store::CalendarQuery;
|
||||||
use rustical_store::synctoken::format_synctoken;
|
use rustical_store::synctoken::format_synctoken;
|
||||||
@@ -146,83 +145,6 @@ impl SqliteCalendarStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// In the past exports generated objects with invalid VERSION:4.0
|
|
||||||
/// This repair sets them to VERSION:2.0
|
|
||||||
#[allow(clippy::missing_panics_doc)]
|
|
||||||
pub async fn repair_invalid_version_4_0(&self) -> Result<(), Error> {
|
|
||||||
struct Row {
|
|
||||||
principal: String,
|
|
||||||
cal_id: String,
|
|
||||||
id: String,
|
|
||||||
ics: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut tx = self
|
|
||||||
.db
|
|
||||||
.begin_with(BEGIN_IMMEDIATE)
|
|
||||||
.await
|
|
||||||
.map_err(crate::Error::from)?;
|
|
||||||
|
|
||||||
#[allow(clippy::missing_panics_doc)]
|
|
||||||
let version_pattern = Regex::new(r"(?mi)^VERSION:4.0").unwrap();
|
|
||||||
|
|
||||||
let repairs: Vec<Row> = sqlx::query_as!(
|
|
||||||
Row,
|
|
||||||
r#"SELECT principal, cal_id, id, ics FROM calendarobjects WHERE ics LIKE '%VERSION:4.0%';"#
|
|
||||||
)
|
|
||||||
.fetch_all(&mut *tx)
|
|
||||||
.await
|
|
||||||
.map_err(crate::Error::from)?
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|mut row| {
|
|
||||||
version_pattern.find(&row.ics)?;
|
|
||||||
let new_ics = version_pattern.replace(&row.ics, "VERSION:2.0");
|
|
||||||
// Safeguard that we really only changed the version
|
|
||||||
assert_eq!(row.ics.len(), new_ics.len());
|
|
||||||
row.ics = new_ics.to_string();
|
|
||||||
Some(row)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if repairs.is_empty() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
warn!(
|
|
||||||
"Found {} calendar objects with invalid VERSION:4.0. Repairing by setting to VERSION:2.0",
|
|
||||||
repairs.len()
|
|
||||||
);
|
|
||||||
|
|
||||||
for repair in &repairs {
|
|
||||||
// calendarobjectchangelog is used by sync-collection to fetch changes
|
|
||||||
// By deleting entries we will later regenerate new entries such that clients will notice
|
|
||||||
// the objects have changed
|
|
||||||
warn!(
|
|
||||||
"Repairing VERSION for {}/{}/{}.ics",
|
|
||||||
repair.principal, repair.cal_id, repair.id
|
|
||||||
);
|
|
||||||
sqlx::query!(
|
|
||||||
"DELETE FROM calendarobjectchangelog WHERE (principal, cal_id, object_id) = (?, ?, ?);",
|
|
||||||
repair.principal, repair.cal_id, repair.id
|
|
||||||
).execute(&mut *tx).await
|
|
||||||
.map_err(crate::Error::from)?;
|
|
||||||
|
|
||||||
sqlx::query!(
|
|
||||||
"UPDATE calendarobjects SET ics = ? WHERE (principal, cal_id, id) = (?, ?, ?);",
|
|
||||||
repair.ics,
|
|
||||||
repair.principal,
|
|
||||||
repair.cal_id,
|
|
||||||
repair.id
|
|
||||||
)
|
|
||||||
.execute(&mut *tx)
|
|
||||||
.await
|
|
||||||
.map_err(crate::Error::from)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.commit().await.map_err(crate::Error::from)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit "orphaned" objects to the changelog table
|
// Commit "orphaned" objects to the changelog table
|
||||||
pub async fn repair_orphans(&self) -> Result<(), Error> {
|
pub async fn repair_orphans(&self) -> Result<(), Error> {
|
||||||
struct Row {
|
struct Row {
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ impl NamedStruct {
|
|||||||
#(#builder_field_inits),*
|
#(#builder_field_inits),*
|
||||||
};
|
};
|
||||||
|
|
||||||
let (ns, name) = reader.resolver().resolve_element(start.name());
|
let (ns, name) = reader.resolve_element(start.name());
|
||||||
#(#tagname_field_branches);*
|
#(#tagname_field_branches);*
|
||||||
#(#namespace_field_branches);*
|
#(#namespace_field_branches);*
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ impl NamedStruct {
|
|||||||
// start of a child element
|
// start of a child element
|
||||||
Event::Start(start) | Event::Empty(start) => {
|
Event::Start(start) | Event::Empty(start) => {
|
||||||
let empty = matches!(event, Event::Empty(_));
|
let empty = matches!(event, Event::Empty(_));
|
||||||
let (ns, name) = reader.resolver().resolve_element(start.name());
|
let (ns, name) = reader.resolve_element(start.name());
|
||||||
match (ns, name.as_ref()) {
|
match (ns, name.as_ref()) {
|
||||||
#(#named_field_branches),*
|
#(#named_field_branches),*
|
||||||
#(#untagged_field_branches),*
|
#(#untagged_field_branches),*
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ impl<T: XmlRootTag + XmlDeserialize> XmlDocument for T {
|
|||||||
match event {
|
match event {
|
||||||
Event::Decl(_) | Event::Comment(_) => { /* ignore this */ }
|
Event::Decl(_) | Event::Comment(_) => { /* ignore this */ }
|
||||||
Event::Start(start) | Event::Empty(start) => {
|
Event::Start(start) | Event::Empty(start) => {
|
||||||
let (ns, name) = reader.resolver().resolve_element(start.name());
|
let (ns, name) = reader.resolve_element(start.name());
|
||||||
let matches = match (Self::root_ns(), &ns, name) {
|
let matches = match (Self::root_ns(), &ns, name) {
|
||||||
// Wrong tag
|
// Wrong tag
|
||||||
(_, _, name) if name.as_ref() != Self::root_tag().as_bytes() => false,
|
(_, _, name) if name.as_ref() != Self::root_tag().as_bytes() => false,
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ pub fn cmd_gen_config(_args: GenConfigArgs) -> anyhow::Result<()> {
|
|||||||
http: HttpConfig::default(),
|
http: HttpConfig::default(),
|
||||||
data_store: DataStoreConfig::Sqlite(SqliteDataStoreConfig {
|
data_store: DataStoreConfig::Sqlite(SqliteDataStoreConfig {
|
||||||
db_url: "/var/lib/rustical/db.sqlite3".to_owned(),
|
db_url: "/var/lib/rustical/db.sqlite3".to_owned(),
|
||||||
run_repairs: true,
|
|
||||||
}),
|
}),
|
||||||
tracing: TracingConfig::default(),
|
tracing: TracingConfig::default(),
|
||||||
frontend: FrontendConfig {
|
frontend: FrontendConfig {
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ impl Default for HttpConfig {
|
|||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct SqliteDataStoreConfig {
|
pub struct SqliteDataStoreConfig {
|
||||||
pub db_url: String,
|
pub db_url: String,
|
||||||
#[serde(default = "default_true")]
|
|
||||||
pub run_repairs: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
|||||||
26
src/main.rs
26
src/main.rs
@@ -25,7 +25,7 @@ use std::sync::Arc;
|
|||||||
use tokio::sync::mpsc::Receiver;
|
use tokio::sync::mpsc::Receiver;
|
||||||
use tower::Layer;
|
use tower::Layer;
|
||||||
use tower_http::normalize_path::NormalizePathLayer;
|
use tower_http::normalize_path::NormalizePathLayer;
|
||||||
use tracing::{info, warn};
|
use tracing::info;
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod commands;
|
mod commands;
|
||||||
@@ -34,9 +34,6 @@ mod config;
|
|||||||
pub mod integration_tests;
|
pub mod integration_tests;
|
||||||
mod setup_tracing;
|
mod setup_tracing;
|
||||||
|
|
||||||
mod migration_0_12;
|
|
||||||
use migration_0_12::{validate_address_objects_0_12, validate_calendar_objects_0_12};
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
struct Args {
|
struct Args {
|
||||||
@@ -70,22 +67,15 @@ async fn get_data_stores(
|
|||||||
Receiver<CollectionOperation>,
|
Receiver<CollectionOperation>,
|
||||||
)> {
|
)> {
|
||||||
Ok(match &config {
|
Ok(match &config {
|
||||||
DataStoreConfig::Sqlite(SqliteDataStoreConfig {
|
DataStoreConfig::Sqlite(SqliteDataStoreConfig { db_url }) => {
|
||||||
db_url,
|
|
||||||
run_repairs,
|
|
||||||
}) => {
|
|
||||||
let db = create_db_pool(db_url, migrate).await?;
|
let db = create_db_pool(db_url, migrate).await?;
|
||||||
// Channel to watch for changes (for DAV Push)
|
// Channel to watch for changes (for DAV Push)
|
||||||
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));
|
||||||
if *run_repairs {
|
cal_store.repair_orphans().await?;
|
||||||
info!("Running repair tasks");
|
|
||||||
addressbook_store.repair_orphans().await?;
|
|
||||||
cal_store.repair_invalid_version_4_0().await?;
|
|
||||||
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));
|
||||||
(
|
(
|
||||||
@@ -125,14 +115,6 @@ async fn main() -> Result<()> {
|
|||||||
let (addr_store, cal_store, subscription_store, principal_store, update_recv) =
|
let (addr_store, cal_store, subscription_store, principal_store, update_recv) =
|
||||||
get_data_stores(!args.no_migrations, &config.data_store).await?;
|
get_data_stores(!args.no_migrations, &config.data_store).await?;
|
||||||
|
|
||||||
warn!(
|
|
||||||
"Validating calendar data against the next-version ical parser.
|
|
||||||
In the next major release these will be rejected and cause errors.
|
|
||||||
If any errors occur, please open an issue so they can be fixed before the next major release."
|
|
||||||
);
|
|
||||||
validate_calendar_objects_0_12(principal_store.as_ref(), cal_store.as_ref()).await?;
|
|
||||||
validate_address_objects_0_12(principal_store.as_ref(), addr_store.as_ref()).await?;
|
|
||||||
|
|
||||||
let mut tasks = vec![];
|
let mut tasks = vec![];
|
||||||
|
|
||||||
if config.dav_push.enabled {
|
if config.dav_push.enabled {
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
use ical::parser::{ical::IcalObjectParser, vcard::VcardParser};
|
|
||||||
use rustical_store::{AddressbookStore, CalendarStore, auth::AuthenticationProvider};
|
|
||||||
use tracing::{error, info};
|
|
||||||
|
|
||||||
pub async fn validate_calendar_objects_0_12(
|
|
||||||
principal_store: &impl AuthenticationProvider,
|
|
||||||
cal_store: &impl CalendarStore,
|
|
||||||
) -> Result<(), rustical_store::Error> {
|
|
||||||
let mut success = true;
|
|
||||||
for principal in principal_store.get_principals().await? {
|
|
||||||
for calendar in cal_store.get_calendars(&principal.id).await? {
|
|
||||||
for (object_id, object) in cal_store
|
|
||||||
.get_objects(&calendar.principal, &calendar.id)
|
|
||||||
.await?
|
|
||||||
{
|
|
||||||
if let Err(err) =
|
|
||||||
IcalObjectParser::from_slice(object.get_ics().as_bytes()).expect_one()
|
|
||||||
{
|
|
||||||
success = false;
|
|
||||||
error!(
|
|
||||||
"An error occured parsing a calendar object: principal={principal}, calendar={calendar}, object_id={object_id}: {err}",
|
|
||||||
principal = principal.id,
|
|
||||||
calendar = calendar.id,
|
|
||||||
);
|
|
||||||
println!("{}", object.get_ics());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if success {
|
|
||||||
info!("Your calendar data seems to be valid in the next major version.");
|
|
||||||
} else {
|
|
||||||
error!(
|
|
||||||
"Not all calendar objects will be successfully parsed in the next major version (v0.12).
|
|
||||||
This will not cause issues in this version, but please comment under the tracking issue on GitHub:
|
|
||||||
https://github.com/lennart-k/rustical/issues/165"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn validate_address_objects_0_12(
|
|
||||||
principal_store: &impl AuthenticationProvider,
|
|
||||||
addr_store: &impl AddressbookStore,
|
|
||||||
) -> Result<(), rustical_store::Error> {
|
|
||||||
let mut success = true;
|
|
||||||
for principal in principal_store.get_principals().await? {
|
|
||||||
for addressbook in addr_store.get_addressbooks(&principal.id).await? {
|
|
||||||
for (object_id, object) in addr_store
|
|
||||||
.get_objects(&addressbook.principal, &addressbook.id)
|
|
||||||
.await?
|
|
||||||
{
|
|
||||||
if let Err(err) = VcardParser::from_slice(object.get_vcf().as_bytes()).expect_one()
|
|
||||||
{
|
|
||||||
success = false;
|
|
||||||
error!(
|
|
||||||
"An error occured parsing an address object: principal={principal}, addressbook={addressbook}, object_id={object_id}: {err}",
|
|
||||||
principal = principal.id,
|
|
||||||
addressbook = addressbook.id,
|
|
||||||
);
|
|
||||||
println!("{}", object.get_vcf());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if success {
|
|
||||||
info!("Your addressbook data seems to be valid in the next major version.");
|
|
||||||
} else {
|
|
||||||
error!(
|
|
||||||
"Not all address objects will be successfully parsed in the next major version (v0.12).
|
|
||||||
This will not cause issues in this version, but please comment under the tracking issue on GitHub:
|
|
||||||
https://github.com/lennart-k/rustical/issues/165"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user