[Fix] Connections

This commit is contained in:
Alex Emmet 2026-08-30 19:18:01 +02:00
commit dd69b5bd97
No known key found for this signature in database
19 changed files with 1010 additions and 341 deletions

View file

@ -523,7 +523,7 @@ pub fn add_message(message: NewMessage<'_>) -> Result<i64, StorageError> {
let mut contact = crate::users::contact::Contact::new(external_user);
contact.set_last_message_at(send_time);
crate::util::chats_util::mod_user(storage_owner, &contact);
crate::util::chats_util::mod_user(storage_owner, &contact)?;
Ok(msg_id)
}
@ -546,8 +546,17 @@ pub fn change_message_state_by_relay_id(
return Ok(());
};
let state = MessageState::from_str(&current).upgrade(new_state).as_str();
tx.execute("UPDATE messages SET message_state = ?1 WHERE id = ?2", params![state, msg_id])?;
sync::record_event(&tx, storage_owner, EntityType::Message, msg_id, Operation::Upsert)?;
tx.execute(
"UPDATE messages SET message_state = ?1 WHERE id = ?2",
params![state, msg_id],
)?;
sync::record_event(
&tx,
storage_owner,
EntityType::Message,
msg_id,
Operation::Upsert,
)?;
tx.commit()?;
Ok(())
})
@ -581,7 +590,9 @@ pub fn record_message_receipt(
return Err(StorageError::Other("message receipt target was not found".into()));
};
if external_user != receipt_signer_id {
return Err(StorageError::Other("message receipt signer is not the chat partner".into()));
return Err(StorageError::Other(
"message receipt signer is not the chat partner".into(),
));
}
tx.execute(
"INSERT OR IGNORE INTO message_receipts (storage_owner, target_signer_id, target_message_id, receipt_signer_id, receipt_message_id, receipt_type, event_at, recorded_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
@ -592,9 +603,11 @@ pub fn record_message_receipt(
} else {
("client_received_at", "client_received_recorded_at")
};
let state = MessageState::from_str(
&tx.query_row("SELECT message_state FROM messages WHERE id = ?1", [message_id], |row| row.get::<_, String>(0))?,
)
let state = MessageState::from_str(&tx.query_row(
"SELECT message_state FROM messages WHERE id = ?1",
[message_id],
|row| row.get::<_, String>(0),
)?)
.upgrade(MessageState::from_str(receipt_type))
.as_str()
.to_string();
@ -602,7 +615,13 @@ pub fn record_message_receipt(
&format!("UPDATE messages SET {state_column} = COALESCE({state_column}, ?1), {recorded_column} = COALESCE({recorded_column}, ?2), message_state = ?3 WHERE id = ?4"),
params![event_at, recorded_at, state, message_id],
)?;
sync::record_event(&tx, storage_owner, EntityType::Message, message_id, Operation::Upsert)?;
sync::record_event(
&tx,
storage_owner,
EntityType::Message,
message_id,
Operation::Upsert,
)?;
tx.commit()?;
Ok(())
})
@ -630,7 +649,13 @@ pub fn record_destination_iota_received(
"UPDATE messages SET destination_iota_received_at = COALESCE(destination_iota_received_at, ?1), message_state = CASE WHEN message_state = 'sending' THEN 'sent' ELSE message_state END WHERE id = ?2",
params![accepted_at, message_id],
)?;
sync::record_event(&tx, storage_owner, EntityType::Message, message_id, Operation::Upsert)?;
sync::record_event(
&tx,
storage_owner,
EntityType::Message,
message_id,
Operation::Upsert,
)?;
tx.commit()?;
Ok(())
})

View file

@ -14,9 +14,8 @@ pub fn has_user(storage_owner: i64, user_id: i64) -> Result<bool, StorageError>
})
}
pub fn mod_user(storage_owner: i64, contact: &Contact) {
if let Err(e) = db::with_db(|conn| {
let tx = conn.unchecked_transaction()?;
pub fn mod_user(storage_owner: i64, contact: &Contact) -> Result<(), StorageError> {
db::with_immediate_transaction(|tx| {
tx.execute(
r#"
INSERT INTO contacts (storage_owner, user_id, user_name, last_message_at)
@ -28,37 +27,34 @@ pub fn mod_user(storage_owner: i64, contact: &Contact) {
params![
storage_owner,
contact.user_id,
contact.user_name.clone(),
contact.user_name,
contact.last_message_at,
],
)?;
sync::record_event(
&tx,
tx,
storage_owner,
EntityType::Contact,
contact.user_id,
Operation::Upsert,
)?;
tx.commit()?;
Ok(())
}) {
eprintln!("Failed to mod_user: {}", e);
}
})
}
pub fn get_users_by_ids(storage_owner: i64, ids: &[i64]) -> Vec<Contact> {
pub fn get_users_by_ids(storage_owner: i64, ids: &[i64]) -> Result<Vec<Contact>, StorageError> {
if ids.is_empty() {
return Vec::new();
return Ok(Vec::new());
}
let wanted: std::collections::HashSet<i64> = ids.iter().copied().collect();
get_users(storage_owner)
Ok(get_users(storage_owner)?
.into_iter()
.filter(|contact| wanted.contains(&contact.user_id))
.collect()
.collect())
}
pub fn get_user(storage_owner: i64, user_id: i64) -> Option<Contact> {
match db::with_db(|conn| {
pub fn get_user(storage_owner: i64, user_id: i64) -> Result<Option<Contact>, StorageError> {
db::with_db(|conn| {
match conn.query_row(
r#"
SELECT user_id, user_name, last_message_at
@ -79,17 +75,11 @@ pub fn get_user(storage_owner: i64, user_id: i64) -> Option<Contact> {
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
Err(e) => Err(e.into()),
}
}) {
Ok(opt) => opt,
Err(e) => {
eprintln!("Error querying user in get_user: {}", e);
None
}
}
})
}
pub fn get_users(storage_owner: i64) -> Vec<Contact> {
match db::with_db(|conn| {
pub fn get_users(storage_owner: i64) -> Result<Vec<Contact>, StorageError> {
db::with_db(|conn| {
let mut stmt = conn.prepare(
r#"
SELECT user_id, user_name, last_message_at
@ -112,17 +102,8 @@ pub fn get_users(storage_owner: i64) -> Vec<Contact> {
let mut out = Vec::new();
for row in rows {
match row {
Ok(contact) => out.push(contact),
Err(e) => eprintln!("Failed to read contact row: {}", e),
}
out.push(row?);
}
Ok(out)
}) {
Ok(v) => v,
Err(e) => {
eprintln!("Failed to query contacts in get_users: {}", e);
Vec::new()
}
}
})
}

View file

@ -421,9 +421,15 @@ fn run_migrations_on_connection(conn: &Connection) -> Result<(), StorageError> {
("relay_message_id", "relay_message_id TEXT"),
("authored_at", "authored_at INTEGER"),
("origin_iota_received_at", "origin_iota_received_at INTEGER"),
("destination_iota_received_at", "destination_iota_received_at INTEGER"),
(
"destination_iota_received_at",
"destination_iota_received_at INTEGER",
),
("client_received_at", "client_received_at INTEGER"),
("client_received_recorded_at", "client_received_recorded_at INTEGER"),
(
"client_received_recorded_at",
"client_received_recorded_at INTEGER",
),
("read_at", "read_at INTEGER"),
("read_recorded_at", "read_recorded_at INTEGER"),
] {
@ -489,6 +495,38 @@ fn run_migrations_on_connection(conn: &Connection) -> Result<(), StorageError> {
)?;
}
if current_version < 13 {
conn.execute_batch(
r#"
CREATE TABLE IF NOT EXISTS pending_user_operations (
user_id INTEGER PRIMARY KEY,
operation TEXT NOT NULL
CHECK (operation IN ('create', 'attach', 'release')),
username TEXT NOT NULL,
public_key TEXT,
private_key_hash TEXT,
reset_token TEXT,
registration_token TEXT,
created_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_pending_user_operations_operation
ON pending_user_operations (operation, created_at);
PRAGMA user_version = 13;
"#,
)?;
}
if current_version < 14 {
conn.execute_batch(
r#"
ALTER TABLE pending_user_operations
ADD COLUMN phase TEXT NOT NULL DEFAULT 'prepared'
CHECK (phase IN ('prepared', 'credential_written', 'remote_committed', 'local_committed'));
PRAGMA user_version = 14;
"#,
)?;
}
Ok(())
}
@ -558,7 +596,7 @@ mod tests {
run_migrations_on_connection(&conn)?;
let version: i64 = conn.pragma_query_value(None, "user_version", |row| row.get(0))?;
assert_eq!(version, 12);
assert_eq!(version, 13);
for column in ["height", "reply_to", "edited_count", "deleted_by_external"] {
let mut statement =
conn.prepare("SELECT 1 FROM pragma_table_info('messages') WHERE name = ?1")?;
@ -577,7 +615,7 @@ mod tests {
run_migrations_on_connection(&conn)?;
run_migrations_on_connection(&conn)?;
let version: i64 = conn.pragma_query_value(None, "user_version", |row| row.get(0))?;
assert_eq!(version, 12);
assert_eq!(version, 13);
for table in [
"sync_heads",
"sync_events",
@ -587,6 +625,7 @@ mod tests {
"pending_relays",
"relay_inbox",
"synced_settings",
"pending_user_operations",
] {
let exists: i64 = conn.query_row(
"SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = ?1",
@ -611,8 +650,17 @@ mod tests {
run_migrations_on_connection(&conn)?;
let version: i64 = conn.pragma_query_value(None, "user_version", |row| row.get(0))?;
assert_eq!(version, 12);
for column in ["id", "user_id", "scope_type", "scope_key", "name", "payload", "revision", "deleted"] {
assert_eq!(version, 13);
for column in [
"id",
"user_id",
"scope_type",
"scope_key",
"name",
"payload",
"revision",
"deleted",
] {
let mut statement =
conn.prepare("SELECT 1 FROM pragma_table_info('synced_settings') WHERE name = ?1")?;
assert!(statement.exists([column])?);

View file

@ -291,30 +291,15 @@ mod tests {
)
.unwrap();
let transaction = connection.unchecked_transaction().unwrap();
let message = super::record_event(
&transaction,
1,
EntityType::Message,
10,
Operation::Upsert,
)
.unwrap();
let setting = super::record_event(
&transaction,
1,
EntityType::Setting,
11,
Operation::Upsert,
)
.unwrap();
let contact = super::record_event(
&transaction,
1,
EntityType::Contact,
12,
Operation::Upsert,
)
.unwrap();
let message =
super::record_event(&transaction, 1, EntityType::Message, 10, Operation::Upsert)
.unwrap();
let setting =
super::record_event(&transaction, 1, EntityType::Setting, 11, Operation::Upsert)
.unwrap();
let contact =
super::record_event(&transaction, 1, EntityType::Contact, 12, Operation::Upsert)
.unwrap();
transaction.commit().unwrap();
assert_eq!((message, setting, contact), (1, 2, 3));

View file

@ -1,7 +1,7 @@
use crate::storage_error::StorageError;
use crate::util::db;
use crate::util::sync::{self, EntityType, Operation};
use rusqlite::{params, Connection, OptionalExtension, Row, Transaction};
use rusqlite::{Connection, OptionalExtension, Row, Transaction, params};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SettingScope {
@ -401,8 +401,8 @@ pub(crate) fn delete_scope_in_tx(
#[cfg(test)]
mod tests {
use super::{
delete_in_tx, get_from_connection, is_valid_name, list_by_ids_from_connection,
list_from_connection, set_in_tx, SettingScope,
SettingScope, delete_in_tx, get_from_connection, is_valid_name,
list_by_ids_from_connection, list_from_connection, set_in_tx,
};
use rusqlite::Connection;
@ -618,9 +618,11 @@ mod tests {
assert!(deleted.changed);
assert_eq!(journal_operation, "delete");
assert!(list_from_connection(&connection, 1).unwrap().is_empty());
assert!(list_by_ids_from_connection(&connection, 1, &[stored.id])
.unwrap()
.is_empty());
assert!(
list_by_ids_from_connection(&connection, 1, &[stored.id])
.unwrap()
.is_empty()
);
}
#[test]