mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-19 02:39:22 +00:00
Merge branch 'main' into feature/birthday-calendar
This commit is contained in:
@@ -9,7 +9,7 @@ use rustical_store::{
|
||||
};
|
||||
use sqlx::{Acquire, Executor, Sqlite, SqlitePool, Transaction};
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use tracing::{error, instrument};
|
||||
use tracing::{error, instrument, warn};
|
||||
|
||||
pub mod birthday_calendar;
|
||||
|
||||
@@ -34,6 +34,60 @@ pub struct 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>>(
|
||||
executor: E,
|
||||
principal: &str,
|
||||
@@ -92,9 +146,9 @@ impl SqliteAddressbookStore {
|
||||
|
||||
async fn _update_addressbook<'e, E: Executor<'e, Database = Sqlite>>(
|
||||
executor: E,
|
||||
principal: String,
|
||||
id: String,
|
||||
addressbook: Addressbook,
|
||||
principal: &str,
|
||||
id: &str,
|
||||
addressbook: &Addressbook,
|
||||
) -> Result<(), rustical_store::Error> {
|
||||
let result = sqlx::query!(
|
||||
r#"UPDATE addressbooks SET principal = ?, id = ?, displayname = ?, description = ?, push_topic = ?
|
||||
@@ -399,7 +453,7 @@ impl AddressbookStore for SqliteAddressbookStore {
|
||||
id: String,
|
||||
addressbook: Addressbook,
|
||||
) -> Result<(), rustical_store::Error> {
|
||||
Self::_update_addressbook(&self.db, principal, id, addressbook).await
|
||||
Self::_update_addressbook(&self.db, &principal, &id, &addressbook).await
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
@@ -631,7 +685,7 @@ impl AddressbookStore for SqliteAddressbookStore {
|
||||
.await?
|
||||
.push_topic,
|
||||
}) {
|
||||
error!("Push notification about deleted addressbook failed: {err}");
|
||||
error!("Push notification about restored addressbook object failed: {err}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -665,6 +719,7 @@ impl AddressbookStore for SqliteAddressbookStore {
|
||||
Self::_insert_addressbook(&mut *tx, &addressbook).await?;
|
||||
}
|
||||
|
||||
let mut sync_token = None;
|
||||
for object in objects {
|
||||
Self::_put_object(
|
||||
&mut *tx,
|
||||
@@ -674,9 +729,31 @@ impl AddressbookStore for SqliteAddressbookStore {
|
||||
false,
|
||||
)
|
||||
.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)?;
|
||||
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(())
|
||||
}
|
||||
}
|
||||
@@ -688,7 +765,7 @@ async fn log_object_operation(
|
||||
addressbook_id: &str,
|
||||
object_id: &str,
|
||||
operation: ChangeOperation,
|
||||
) -> Result<String, sqlx::Error> {
|
||||
) -> Result<String, Error> {
|
||||
struct Synctoken {
|
||||
synctoken: i64,
|
||||
}
|
||||
@@ -703,7 +780,8 @@ async fn log_object_operation(
|
||||
addressbook_id
|
||||
)
|
||||
.fetch_one(&mut **tx)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(crate::Error::from)?;
|
||||
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -717,6 +795,7 @@ async fn log_object_operation(
|
||||
operation
|
||||
)
|
||||
.execute(&mut **tx)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(crate::Error::from)?;
|
||||
Ok(format_synctoken(synctoken))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user