Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8460c7a994 | |||
|
|
ec3f5e6a6e |
12 changed files with 379 additions and 202 deletions
142
Cargo.lock
generated
142
Cargo.lock
generated
|
|
@ -51,7 +51,7 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rand 0.10.2",
|
"rand 0.10.2",
|
||||||
"sha1 0.11.0",
|
"sha1",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
|
@ -224,13 +224,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aes"
|
name = "aes"
|
||||||
version = "0.8.4"
|
version = "0.9.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
checksum = "35f0f96ce78e38c3dc6d8948aa8163d06385be74000f3c7a95bf1eef35d3ea32"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cipher 0.5.2",
|
||||||
"cipher",
|
"cpubits",
|
||||||
"cpufeatures 0.2.17",
|
"cpufeatures 0.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -337,15 +337,6 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "arbitrary"
|
|
||||||
version = "1.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
|
||||||
dependencies = [
|
|
||||||
"derive_arbitrary",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
version = "1.9.2"
|
version = "1.9.2"
|
||||||
|
|
@ -541,6 +532,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
|
checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hybrid-array",
|
"hybrid-array",
|
||||||
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -652,7 +644,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
|
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cipher",
|
"cipher 0.4.4",
|
||||||
"cpufeatures 0.2.17",
|
"cpufeatures 0.2.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -675,7 +667,7 @@ checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aead",
|
"aead",
|
||||||
"chacha20 0.9.1",
|
"chacha20 0.9.1",
|
||||||
"cipher",
|
"cipher 0.4.4",
|
||||||
"poly1305",
|
"poly1305",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
@ -700,10 +692,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-common 0.1.7",
|
"crypto-common 0.1.7",
|
||||||
"inout",
|
"inout 0.1.4",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common 0.2.2",
|
||||||
|
"inout 0.2.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.6.6"
|
version = "4.6.6"
|
||||||
|
|
@ -817,9 +819,9 @@ checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "constant_time_eq"
|
name = "constant_time_eq"
|
||||||
version = "0.3.1"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
|
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
|
|
@ -867,6 +869,12 @@ 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 = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpubits"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
|
|
@ -885,21 +893,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crc"
|
|
||||||
version = "3.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
|
|
||||||
dependencies = [
|
|
||||||
"crc-catalog",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crc-catalog"
|
|
||||||
version = "2.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
|
@ -1139,17 +1132,6 @@ version = "0.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_arbitrary"
|
|
||||||
version = "1.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.119",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "2.1.1"
|
version = "2.1.1"
|
||||||
|
|
@ -1194,6 +1176,7 @@ dependencies = [
|
||||||
"const-oid 0.10.2",
|
"const-oid 0.10.2",
|
||||||
"crypto-common 0.2.2",
|
"crypto-common 0.2.2",
|
||||||
"ctutils",
|
"ctutils",
|
||||||
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1722,16 +1705,7 @@ version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018"
|
checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hmac 0.13.0",
|
"hmac",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hmac"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
|
||||||
dependencies = [
|
|
||||||
"digest 0.10.7",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2051,6 +2025,15 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inout"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
|
||||||
|
dependencies = [
|
||||||
|
"hybrid-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instability"
|
name = "instability"
|
||||||
version = "0.3.13"
|
version = "0.3.13"
|
||||||
|
|
@ -2579,12 +2562,11 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lzma-rust2"
|
name = "lzma-rust2"
|
||||||
version = "0.13.0"
|
version = "0.16.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c60a23ffb90d527e23192f1246b14746e2f7f071cb84476dd879071696c18a4a"
|
checksum = "ca93e534d1142d1d0dcca6d25fe302508a5dfb40b302802904577725ea0b695b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc",
|
"sha2 0.11.0",
|
||||||
"sha2 0.10.9",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3128,12 +3110,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pbkdf2"
|
name = "pbkdf2"
|
||||||
version = "0.12.2"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"digest 0.10.7",
|
"digest 0.11.3",
|
||||||
"hmac 0.12.1",
|
"hmac",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -4084,17 +4066,6 @@ dependencies = [
|
||||||
"unsafe-libyaml",
|
"unsafe-libyaml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sha1"
|
|
||||||
version = "0.10.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cpufeatures 0.2.17",
|
|
||||||
"digest 0.10.7",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
|
@ -4433,7 +4404,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.4.3",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
|
|
@ -4562,6 +4533,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134"
|
checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"num-conv",
|
"num-conv",
|
||||||
"num_threads",
|
"num_threads",
|
||||||
|
|
@ -4759,6 +4731,12 @@ version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typed-path"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.20.1"
|
version = "1.20.1"
|
||||||
|
|
@ -5553,26 +5531,26 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zip"
|
name = "zip"
|
||||||
version = "6.0.0"
|
version = "8.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb2a05c7c36fde6c09b08576c9f7fb4cda705990f73b58fe011abf7dfb24168b"
|
checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"arbitrary",
|
|
||||||
"bzip2",
|
"bzip2",
|
||||||
"constant_time_eq",
|
"constant_time_eq",
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"deflate64",
|
"deflate64",
|
||||||
"flate2",
|
"flate2",
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.4.3",
|
||||||
"hmac 0.12.1",
|
"hmac",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"lzma-rust2",
|
"lzma-rust2",
|
||||||
"memchr",
|
"memchr",
|
||||||
"pbkdf2",
|
"pbkdf2",
|
||||||
"ppmd-rust",
|
"ppmd-rust",
|
||||||
"sha1 0.10.7",
|
"sha1",
|
||||||
"time",
|
"time",
|
||||||
|
"typed-path",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
"zopfli",
|
"zopfli",
|
||||||
"zstd",
|
"zstd",
|
||||||
|
|
|
||||||
|
|
@ -226,11 +226,6 @@ impl ClientConnection {
|
||||||
// Direct messages //
|
// Direct messages //
|
||||||
// ************************************************ //
|
// ************************************************ //
|
||||||
|
|
||||||
if cv.is_type(CommunicationType::MessageState) {
|
|
||||||
message_handlers::handle_message_state(&cv);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if cv.is_type(CommunicationType::MessageEdit) {
|
if cv.is_type(CommunicationType::MessageEdit) {
|
||||||
self.send_message(&message_handlers::handle_message_edit(&cv))
|
self.send_message(&message_handlers::handle_message_edit(&cv))
|
||||||
.await;
|
.await;
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,6 @@ fn add_conversation_for_user(
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
contact.user_name = Some(name.to_string());
|
contact.user_name = Some(name.to_string());
|
||||||
}
|
}
|
||||||
contact.set_last_message_at(now_millis_i64());
|
|
||||||
mod_user(user_id, &contact)
|
mod_user(user_id, &contact)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,7 +227,11 @@ pub fn apply_verified_relay_content(
|
||||||
reply_to,
|
reply_to,
|
||||||
origin_iota_received_at: sent_by_self.then_some(accepted_at),
|
origin_iota_received_at: sent_by_self.then_some(accepted_at),
|
||||||
destination_iota_received_at: (!sent_by_self).then_some(accepted_at),
|
destination_iota_received_at: (!sent_by_self).then_some(accepted_at),
|
||||||
initial_state: MessageState::Sent,
|
initial_state: if sent_by_self {
|
||||||
|
MessageState::Sending
|
||||||
|
} else {
|
||||||
|
MessageState::Sent
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.map_err(|error| error.to_string())?;
|
.map_err(|error| error.to_string())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -430,6 +433,12 @@ fn stored_message_fields(
|
||||||
DataValue::Str(relay_message_id.clone()),
|
DataValue::Str(relay_message_id.clone()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if let Some(relay_signer_id) = message.relay_signer_id {
|
||||||
|
fields.push((
|
||||||
|
DataType::SenderId,
|
||||||
|
DataValue::SignedNumber(relay_signer_id.into()),
|
||||||
|
));
|
||||||
|
}
|
||||||
for (data_type, timestamp) in [
|
for (data_type, timestamp) in [
|
||||||
(DataType::AuthoredAt, message.authored_at),
|
(DataType::AuthoredAt, message.authored_at),
|
||||||
(
|
(
|
||||||
|
|
@ -452,6 +461,15 @@ fn stored_message_fields(
|
||||||
fields.push((data_type, DataValue::SignedNumber(timestamp.into())));
|
fields.push((data_type, DataValue::SignedNumber(timestamp.into())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(failed_at) = message.delivery_failed_at {
|
||||||
|
fields.push((
|
||||||
|
DataType::UpdatedAt,
|
||||||
|
DataValue::SignedNumber(failed_at.into()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(failure) = &message.delivery_failure {
|
||||||
|
fields.push((DataType::ErrorType, DataValue::Str(failure.clone())));
|
||||||
|
}
|
||||||
if message.edited {
|
if message.edited {
|
||||||
fields.push((DataType::Edited, DataValue::Bool(true)));
|
fields.push((DataType::Edited, DataValue::Bool(true)));
|
||||||
}
|
}
|
||||||
|
|
@ -644,6 +662,12 @@ fn contact_value(
|
||||||
if let Some(name) = &contact.user_name {
|
if let Some(name) = &contact.user_name {
|
||||||
fields.push((DataType::Username, DataValue::Str(name.clone())));
|
fields.push((DataType::Username, DataValue::Str(name.clone())));
|
||||||
}
|
}
|
||||||
|
if contact.created_at > 0 {
|
||||||
|
fields.push((
|
||||||
|
DataType::CreatedAt,
|
||||||
|
DataValue::SignedNumber(contact.created_at.into()),
|
||||||
|
));
|
||||||
|
}
|
||||||
if let Some(last_message_at) = contact.last_message_at {
|
if let Some(last_message_at) = contact.last_message_at {
|
||||||
fields.push((
|
fields.push((
|
||||||
DataType::LastMessageAt,
|
DataType::LastMessageAt,
|
||||||
|
|
@ -918,26 +942,6 @@ pub fn handle_client_state_ack(cv: &CommunicationValue) -> CommunicationValue {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_message_state(cv: &CommunicationValue) {
|
|
||||||
let sender_id = match required_sender_id(cv) {
|
|
||||||
Ok(sender_id) => sender_id,
|
|
||||||
Err(_) => return,
|
|
||||||
};
|
|
||||||
let receiver_id = match data_i64(cv, DataType::ChatPartnerId) {
|
|
||||||
Some(id) if id > 0 => id,
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
let timestamp_i64 = data_i64(cv, DataType::SendTime).unwrap_or_else(now_millis_i64);
|
|
||||||
|
|
||||||
let _ = chat_files::change_message_state(
|
|
||||||
timestamp_i64,
|
|
||||||
receiver_id,
|
|
||||||
sender_id,
|
|
||||||
MessageState::from_str(cv.get_data(DataType::MessageState).as_str().unwrap_or("")),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_messages_get(cv: &CommunicationValue) -> CommunicationValue {
|
pub fn handle_messages_get(cv: &CommunicationValue) -> CommunicationValue {
|
||||||
let my_id = match cv.require_sender() {
|
let my_id = match cv.require_sender() {
|
||||||
Ok(my_id) => my_id,
|
Ok(my_id) => my_id,
|
||||||
|
|
@ -1026,6 +1030,12 @@ pub fn handle_get_chats(cv: &CommunicationValue) -> CommunicationValue {
|
||||||
if let Some(name) = user.user_name {
|
if let Some(name) = user.user_name {
|
||||||
container.push((DataType::Username, DataValue::Str(name)));
|
container.push((DataType::Username, DataValue::Str(name)));
|
||||||
}
|
}
|
||||||
|
if user.created_at > 0 {
|
||||||
|
container.push((
|
||||||
|
DataType::CreatedAt,
|
||||||
|
DataValue::SignedNumber(user.created_at.into()),
|
||||||
|
));
|
||||||
|
}
|
||||||
if let Some(ts) = user.last_message_at {
|
if let Some(ts) = user.last_message_at {
|
||||||
container.push((DataType::LastMessageAt, DataValue::SignedNumber(ts as i128)));
|
container.push((DataType::LastMessageAt, DataValue::SignedNumber(ts as i128)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,6 @@ edition = "2024"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
zip = "6"
|
zip = "8"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
iota-paths = { path = "../iota-paths" }
|
iota-paths = { path = "../iota-paths" }
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,32 @@
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Contact {
|
pub struct Contact {
|
||||||
pub user_id: i64,
|
pub user_id: i64,
|
||||||
pub user_name: Option<String>,
|
pub user_name: Option<String>,
|
||||||
|
pub created_at: i64,
|
||||||
pub last_message_at: Option<i64>,
|
pub last_message_at: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Contact {
|
impl Default for Contact {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let now = SystemTime::now()
|
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_millis() as i64;
|
|
||||||
Contact {
|
Contact {
|
||||||
user_id: 0,
|
user_id: 0,
|
||||||
user_name: None,
|
user_name: None,
|
||||||
last_message_at: Some(now),
|
created_at: 0,
|
||||||
|
last_message_at: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Contact {
|
impl Contact {
|
||||||
pub fn new(user_id: i64) -> Self {
|
pub fn new(user_id: i64) -> Self {
|
||||||
|
let created_at = std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.as_millis() as i64;
|
||||||
Contact {
|
Contact {
|
||||||
user_id: user_id,
|
user_id: user_id,
|
||||||
user_name: None,
|
user_name: None,
|
||||||
|
created_at,
|
||||||
last_message_at: None,
|
last_message_at: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,29 @@ impl UserProfile {
|
||||||
public_key: String,
|
public_key: String,
|
||||||
private_key_hash: String,
|
private_key_hash: String,
|
||||||
reset_token: String,
|
reset_token: String,
|
||||||
|
) -> Self {
|
||||||
|
Self::new_with_created_at(
|
||||||
|
user_id,
|
||||||
|
username,
|
||||||
|
display_name,
|
||||||
|
public_key,
|
||||||
|
private_key_hash,
|
||||||
|
reset_token,
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.as_millis() as i64,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_created_at(
|
||||||
|
user_id: i64,
|
||||||
|
username: String,
|
||||||
|
display_name: Option<String>,
|
||||||
|
public_key: String,
|
||||||
|
private_key_hash: String,
|
||||||
|
reset_token: String,
|
||||||
|
created_at: i64,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
user_id,
|
user_id,
|
||||||
|
|
@ -34,10 +57,7 @@ impl UserProfile {
|
||||||
display_name,
|
display_name,
|
||||||
public_key,
|
public_key,
|
||||||
private_key_hash,
|
private_key_hash,
|
||||||
created_at: SystemTime::now()
|
created_at,
|
||||||
.duration_since(UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_millis() as i64,
|
|
||||||
reset_token,
|
reset_token,
|
||||||
trusted_apps: std::collections::HashMap::new(),
|
trusted_apps: std::collections::HashMap::new(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ pub struct StoredMessage {
|
||||||
pub client_received_recorded_at: Option<i64>,
|
pub client_received_recorded_at: Option<i64>,
|
||||||
pub read_at: Option<i64>,
|
pub read_at: Option<i64>,
|
||||||
pub read_recorded_at: Option<i64>,
|
pub read_recorded_at: Option<i64>,
|
||||||
|
pub delivery_failed_at: Option<i64>,
|
||||||
|
pub delivery_failure: Option<String>,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub edited: bool,
|
pub edited: bool,
|
||||||
pub sent_by_self: bool,
|
pub sent_by_self: bool,
|
||||||
|
|
@ -483,7 +485,7 @@ pub fn add_message(message: NewMessage<'_>) -> Result<i64, StorageError> {
|
||||||
destination_iota_received_at,
|
destination_iota_received_at,
|
||||||
initial_state,
|
initial_state,
|
||||||
} = message;
|
} = message;
|
||||||
let msg_id = db::with_db(|conn| {
|
db::with_db(|conn| {
|
||||||
let tx = conn.unchecked_transaction()?;
|
let tx = conn.unchecked_transaction()?;
|
||||||
tx.execute(
|
tx.execute(
|
||||||
r#"
|
r#"
|
||||||
|
|
@ -517,14 +519,16 @@ pub fn add_message(message: NewMessage<'_>) -> Result<i64, StorageError> {
|
||||||
msg_id,
|
msg_id,
|
||||||
Operation::Upsert,
|
Operation::Upsert,
|
||||||
)?;
|
)?;
|
||||||
|
let mut contact = crate::users::contact::Contact::new(external_user);
|
||||||
|
contact.set_last_message_at(
|
||||||
|
destination_iota_received_at
|
||||||
|
.or(origin_iota_received_at)
|
||||||
|
.unwrap_or(authored_at),
|
||||||
|
);
|
||||||
|
crate::util::chats_util::upsert_contact(&tx, storage_owner, &contact)?;
|
||||||
tx.commit()?;
|
tx.commit()?;
|
||||||
Ok(msg_id)
|
Ok(msg_id)
|
||||||
})?;
|
})
|
||||||
|
|
||||||
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)?;
|
|
||||||
Ok(msg_id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_message_state_by_relay_id(
|
pub fn change_message_state_by_relay_id(
|
||||||
|
|
@ -579,11 +583,11 @@ pub fn record_message_receipt(
|
||||||
};
|
};
|
||||||
db::with_db(|conn| {
|
db::with_db(|conn| {
|
||||||
let tx = conn.unchecked_transaction()?;
|
let tx = conn.unchecked_transaction()?;
|
||||||
let Some((message_id, external_user)) = tx
|
let Some((message_id, external_user, authored_at)) = tx
|
||||||
.query_row(
|
.query_row(
|
||||||
"SELECT id, external_user FROM messages WHERE storage_owner = ?1 AND relay_signer_id = ?2 AND relay_message_id = ?3",
|
"SELECT id, external_user, authored_at FROM messages WHERE storage_owner = ?1 AND relay_signer_id = ?2 AND relay_message_id = ?3",
|
||||||
params![storage_owner, target_signer_id, target_message_id],
|
params![storage_owner, target_signer_id, target_message_id],
|
||||||
|row| Ok((row.get::<_, i64>(0)?, row.get::<_, i64>(1)?)),
|
|row| Ok((row.get::<_, i64>(0)?, row.get::<_, i64>(1)?, row.get::<_, Option<i64>>(2)?)),
|
||||||
)
|
)
|
||||||
.optional()?
|
.optional()?
|
||||||
else {
|
else {
|
||||||
|
|
@ -594,6 +598,13 @@ pub fn record_message_receipt(
|
||||||
"message receipt signer is not the chat partner".into(),
|
"message receipt signer is not the chat partner".into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if event_at > recorded_at.saturating_add(5 * 60 * 1000)
|
||||||
|
|| authored_at.is_some_and(|authored_at| event_at < authored_at)
|
||||||
|
{
|
||||||
|
return Err(StorageError::Other(
|
||||||
|
"message receipt event time is outside the accepted clock range".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
tx.execute(
|
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)",
|
"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)",
|
||||||
params![storage_owner, target_signer_id, target_message_id, receipt_signer_id, receipt_message_id, receipt_type, event_at, recorded_at],
|
params![storage_owner, target_signer_id, target_message_id, receipt_signer_id, receipt_message_id, receipt_type, event_at, recorded_at],
|
||||||
|
|
@ -646,7 +657,7 @@ pub fn record_destination_iota_received(
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"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",
|
"UPDATE messages SET destination_iota_received_at = COALESCE(destination_iota_received_at, ?1), delivery_failed_at = NULL, delivery_failure = NULL, message_state = CASE WHEN message_state = 'sending' THEN 'sent' ELSE message_state END WHERE id = ?2",
|
||||||
params![accepted_at, message_id],
|
params![accepted_at, message_id],
|
||||||
)?;
|
)?;
|
||||||
sync::record_event(
|
sync::record_event(
|
||||||
|
|
@ -661,6 +672,41 @@ pub fn record_destination_iota_received(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn record_delivery_failure(
|
||||||
|
storage_owner: i64,
|
||||||
|
relay_signer_id: i64,
|
||||||
|
relay_message_id: &str,
|
||||||
|
failure: &str,
|
||||||
|
failed_at: i64,
|
||||||
|
) -> Result<(), StorageError> {
|
||||||
|
db::with_db(|conn| {
|
||||||
|
let tx = conn.unchecked_transaction()?;
|
||||||
|
let Some(message_id) = tx
|
||||||
|
.query_row(
|
||||||
|
"SELECT id FROM messages WHERE storage_owner = ?1 AND relay_signer_id = ?2 AND relay_message_id = ?3",
|
||||||
|
params![storage_owner, relay_signer_id, relay_message_id],
|
||||||
|
|row| row.get::<_, i64>(0),
|
||||||
|
)
|
||||||
|
.optional()?
|
||||||
|
else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
tx.execute(
|
||||||
|
"UPDATE messages SET delivery_failed_at = ?1, delivery_failure = ?2 WHERE id = ?3 AND destination_iota_received_at IS NULL",
|
||||||
|
params![failed_at, failure, message_id],
|
||||||
|
)?;
|
||||||
|
sync::record_event(
|
||||||
|
&tx,
|
||||||
|
storage_owner,
|
||||||
|
EntityType::Message,
|
||||||
|
message_id,
|
||||||
|
Operation::Upsert,
|
||||||
|
)?;
|
||||||
|
tx.commit()?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn change_message_state(
|
pub fn change_message_state(
|
||||||
timestamp: i64,
|
timestamp: i64,
|
||||||
storage_owner: i64,
|
storage_owner: i64,
|
||||||
|
|
@ -783,11 +829,11 @@ pub fn get_messages(
|
||||||
SELECT id, relay_signer_id, relay_message_id, message_time, authored_at,
|
SELECT id, relay_signer_id, relay_message_id, message_time, authored_at,
|
||||||
origin_iota_received_at, destination_iota_received_at,
|
origin_iota_received_at, destination_iota_received_at,
|
||||||
client_received_at, client_received_recorded_at, read_at,
|
client_received_at, client_received_recorded_at, read_at,
|
||||||
read_recorded_at, content, sent_by_self, message_state, height,
|
read_recorded_at, delivery_failed_at, delivery_failure, content, sent_by_self, message_state, height,
|
||||||
reply_to, edited_count
|
reply_to, edited_count
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE storage_owner = ?1 AND external_user = ?2 AND deleted_by_external = 0
|
WHERE storage_owner = ?1 AND external_user = ?2 AND deleted_by_external = 0
|
||||||
ORDER BY message_time DESC, id DESC
|
ORDER BY COALESCE(destination_iota_received_at, origin_iota_received_at, authored_at, id) DESC, id DESC
|
||||||
LIMIT ?3 OFFSET ?4
|
LIMIT ?3 OFFSET ?4
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
@ -808,12 +854,14 @@ pub fn get_messages(
|
||||||
client_received_recorded_at: row.get(8)?,
|
client_received_recorded_at: row.get(8)?,
|
||||||
read_at: row.get(9)?,
|
read_at: row.get(9)?,
|
||||||
read_recorded_at: row.get(10)?,
|
read_recorded_at: row.get(10)?,
|
||||||
content: row.get(11)?,
|
delivery_failed_at: row.get(11)?,
|
||||||
sent_by_self: row.get::<_, i64>(12)? != 0,
|
delivery_failure: row.get(12)?,
|
||||||
message_state: row.get(13)?,
|
content: row.get(13)?,
|
||||||
height: row.get(14).unwrap_or(0),
|
sent_by_self: row.get::<_, i64>(14)? != 0,
|
||||||
reply_to: row.get(15).ok().flatten(),
|
message_state: row.get(15)?,
|
||||||
edited: row.get::<_, i64>(16).unwrap_or(0) > 0,
|
height: row.get(16).unwrap_or(0),
|
||||||
|
reply_to: row.get(17).ok().flatten(),
|
||||||
|
edited: row.get::<_, i64>(18).unwrap_or(0) > 0,
|
||||||
reactions: Vec::new(),
|
reactions: Vec::new(),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
@ -854,7 +902,7 @@ pub fn get_message(
|
||||||
SELECT id, relay_signer_id, relay_message_id, message_time, authored_at,
|
SELECT id, relay_signer_id, relay_message_id, message_time, authored_at,
|
||||||
origin_iota_received_at, destination_iota_received_at,
|
origin_iota_received_at, destination_iota_received_at,
|
||||||
client_received_at, client_received_recorded_at, read_at,
|
client_received_at, client_received_recorded_at, read_at,
|
||||||
read_recorded_at, content, sent_by_self, message_state, height,
|
read_recorded_at, delivery_failed_at, delivery_failure, content, sent_by_self, message_state, height,
|
||||||
reply_to, edited_count, external_user
|
reply_to, edited_count, external_user
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE storage_owner = ?1
|
WHERE storage_owner = ?1
|
||||||
|
|
@ -878,13 +926,15 @@ pub fn get_message(
|
||||||
client_received_recorded_at: row.get(8)?,
|
client_received_recorded_at: row.get(8)?,
|
||||||
read_at: row.get(9)?,
|
read_at: row.get(9)?,
|
||||||
read_recorded_at: row.get(10)?,
|
read_recorded_at: row.get(10)?,
|
||||||
content: row.get(11)?,
|
delivery_failed_at: row.get(11)?,
|
||||||
sent_by_self: row.get::<_, i64>(12)? != 0,
|
delivery_failure: row.get(12)?,
|
||||||
message_state: row.get(13)?,
|
content: row.get(13)?,
|
||||||
height: row.get(14).unwrap_or(0),
|
sent_by_self: row.get::<_, i64>(14)? != 0,
|
||||||
reply_to: row.get(15).ok().flatten(),
|
message_state: row.get(15)?,
|
||||||
edited: row.get::<_, i64>(16).unwrap_or(0) > 0,
|
height: row.get(16).unwrap_or(0),
|
||||||
external_user: row.get(17)?,
|
reply_to: row.get(17).ok().flatten(),
|
||||||
|
edited: row.get::<_, i64>(18).unwrap_or(0) > 0,
|
||||||
|
external_user: row.get(19)?,
|
||||||
reactions: Vec::new(),
|
reactions: Vec::new(),
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
@ -928,14 +978,16 @@ pub fn get_message_with_offset(
|
||||||
AND external_user = ?2
|
AND external_user = ?2
|
||||||
AND deleted_by_external = 0
|
AND deleted_by_external = 0
|
||||||
AND (
|
AND (
|
||||||
message_time > ?3
|
COALESCE(destination_iota_received_at, origin_iota_received_at, authored_at, id) >
|
||||||
OR (message_time = ?3 AND id > ?4)
|
COALESCE(?3, ?4)
|
||||||
|
OR (COALESCE(destination_iota_received_at, origin_iota_received_at, authored_at, id) =
|
||||||
|
COALESCE(?3, ?4) AND id > ?4)
|
||||||
)
|
)
|
||||||
"#,
|
"#,
|
||||||
params![
|
params![
|
||||||
storage_owner,
|
storage_owner,
|
||||||
external_user,
|
external_user,
|
||||||
message.message_time,
|
message.destination_iota_received_at.or(message.origin_iota_received_at).or(message.authored_at),
|
||||||
message.id
|
message.id
|
||||||
],
|
],
|
||||||
|row| row.get(0),
|
|row| row.get(0),
|
||||||
|
|
@ -953,9 +1005,9 @@ pub fn get_messages_by_ids(storage_owner: i64, ids: &[i64]) -> Vec<StoredMessage
|
||||||
// A journal id uniquely identifies a row. Load all messages for this owner and retain only
|
// A journal id uniquely identifies a row. Load all messages for this owner and retain only
|
||||||
// those ids; this keeps reaction hydration identical to normal message loading.
|
// those ids; this keeps reaction hydration identical to normal message loading.
|
||||||
match db::with_db(|conn| {
|
match db::with_db(|conn| {
|
||||||
let mut stmt = conn.prepare("SELECT id, relay_signer_id, relay_message_id, message_time, authored_at, origin_iota_received_at, destination_iota_received_at, client_received_at, client_received_recorded_at, read_at, read_recorded_at, content, sent_by_self, message_state, height, reply_to, edited_count, external_user FROM messages WHERE storage_owner = ?1 AND deleted_by_external = 0")?;
|
let mut stmt = conn.prepare("SELECT id, relay_signer_id, relay_message_id, message_time, authored_at, origin_iota_received_at, destination_iota_received_at, client_received_at, client_received_recorded_at, read_at, read_recorded_at, delivery_failed_at, delivery_failure, content, sent_by_self, message_state, height, reply_to, edited_count, external_user FROM messages WHERE storage_owner = ?1 AND deleted_by_external = 0")?;
|
||||||
let rows = stmt.query_map([storage_owner], |row| {
|
let rows = stmt.query_map([storage_owner], |row| {
|
||||||
let external_user: i64 = row.get(17)?;
|
let external_user: i64 = row.get(19)?;
|
||||||
Ok(StoredMessage {
|
Ok(StoredMessage {
|
||||||
id: row.get(0)?,
|
id: row.get(0)?,
|
||||||
external_user,
|
external_user,
|
||||||
|
|
@ -969,12 +1021,14 @@ pub fn get_messages_by_ids(storage_owner: i64, ids: &[i64]) -> Vec<StoredMessage
|
||||||
client_received_recorded_at: row.get(8)?,
|
client_received_recorded_at: row.get(8)?,
|
||||||
read_at: row.get(9)?,
|
read_at: row.get(9)?,
|
||||||
read_recorded_at: row.get(10)?,
|
read_recorded_at: row.get(10)?,
|
||||||
content: row.get(11)?,
|
delivery_failed_at: row.get(11)?,
|
||||||
sent_by_self: row.get::<_, i64>(12)? != 0,
|
delivery_failure: row.get(12)?,
|
||||||
message_state: row.get(13)?,
|
content: row.get(13)?,
|
||||||
height: row.get(14).unwrap_or(0),
|
sent_by_self: row.get::<_, i64>(14)? != 0,
|
||||||
reply_to: row.get(15).ok().flatten(),
|
message_state: row.get(15)?,
|
||||||
edited: row.get::<_, i64>(16).unwrap_or(0) > 0,
|
height: row.get(16).unwrap_or(0),
|
||||||
|
reply_to: row.get(17).ok().flatten(),
|
||||||
|
edited: row.get::<_, i64>(18).unwrap_or(0) > 0,
|
||||||
reactions: Vec::new(),
|
reactions: Vec::new(),
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,42 @@ use crate::util::db;
|
||||||
use crate::util::sync::{self, EntityType, Operation};
|
use crate::util::sync::{self, EntityType, Operation};
|
||||||
use rusqlite::params;
|
use rusqlite::params;
|
||||||
|
|
||||||
|
pub(crate) fn upsert_contact(
|
||||||
|
tx: &rusqlite::Transaction<'_>,
|
||||||
|
storage_owner: i64,
|
||||||
|
contact: &Contact,
|
||||||
|
) -> Result<(), StorageError> {
|
||||||
|
tx.execute(
|
||||||
|
r#"
|
||||||
|
INSERT INTO contacts (storage_owner, user_id, user_name, created_at, last_message_at)
|
||||||
|
VALUES (?1, ?2, ?3, ?4, ?5)
|
||||||
|
ON CONFLICT(storage_owner, user_id) DO UPDATE SET
|
||||||
|
user_name = COALESCE(excluded.user_name, contacts.user_name),
|
||||||
|
created_at = MIN(contacts.created_at, excluded.created_at),
|
||||||
|
last_message_at = CASE
|
||||||
|
WHEN excluded.last_message_at IS NULL THEN contacts.last_message_at
|
||||||
|
WHEN contacts.last_message_at IS NULL THEN excluded.last_message_at
|
||||||
|
ELSE MAX(contacts.last_message_at, excluded.last_message_at)
|
||||||
|
END
|
||||||
|
"#,
|
||||||
|
params![
|
||||||
|
storage_owner,
|
||||||
|
contact.user_id,
|
||||||
|
contact.user_name,
|
||||||
|
contact.created_at,
|
||||||
|
contact.last_message_at,
|
||||||
|
],
|
||||||
|
)?;
|
||||||
|
sync::record_event(
|
||||||
|
tx,
|
||||||
|
storage_owner,
|
||||||
|
EntityType::Contact,
|
||||||
|
contact.user_id,
|
||||||
|
Operation::Upsert,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_user(storage_owner: i64, user_id: i64) -> Result<bool, StorageError> {
|
pub fn has_user(storage_owner: i64, user_id: i64) -> Result<bool, StorageError> {
|
||||||
db::with_db(|conn| {
|
db::with_db(|conn| {
|
||||||
Ok(conn.query_row(
|
Ok(conn.query_row(
|
||||||
|
|
@ -15,31 +51,7 @@ pub fn has_user(storage_owner: i64, user_id: i64) -> Result<bool, StorageError>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mod_user(storage_owner: i64, contact: &Contact) -> Result<(), StorageError> {
|
pub fn mod_user(storage_owner: i64, contact: &Contact) -> Result<(), StorageError> {
|
||||||
db::with_immediate_transaction(|tx| {
|
db::with_immediate_transaction(|tx| upsert_contact(tx, storage_owner, contact))
|
||||||
tx.execute(
|
|
||||||
r#"
|
|
||||||
INSERT INTO contacts (storage_owner, user_id, user_name, last_message_at)
|
|
||||||
VALUES (?1, ?2, ?3, ?4)
|
|
||||||
ON CONFLICT(storage_owner, user_id) DO UPDATE SET
|
|
||||||
user_name = excluded.user_name,
|
|
||||||
last_message_at = excluded.last_message_at
|
|
||||||
"#,
|
|
||||||
params![
|
|
||||||
storage_owner,
|
|
||||||
contact.user_id,
|
|
||||||
contact.user_name,
|
|
||||||
contact.last_message_at,
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
sync::record_event(
|
|
||||||
tx,
|
|
||||||
storage_owner,
|
|
||||||
EntityType::Contact,
|
|
||||||
contact.user_id,
|
|
||||||
Operation::Upsert,
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_users_by_ids(storage_owner: i64, ids: &[i64]) -> Result<Vec<Contact>, StorageError> {
|
pub fn get_users_by_ids(storage_owner: i64, ids: &[i64]) -> Result<Vec<Contact>, StorageError> {
|
||||||
|
|
@ -57,7 +69,7 @@ pub fn get_user(storage_owner: i64, user_id: i64) -> Result<Option<Contact>, Sto
|
||||||
db::with_db(|conn| {
|
db::with_db(|conn| {
|
||||||
match conn.query_row(
|
match conn.query_row(
|
||||||
r#"
|
r#"
|
||||||
SELECT user_id, user_name, last_message_at
|
SELECT user_id, user_name, created_at, last_message_at
|
||||||
FROM contacts
|
FROM contacts
|
||||||
WHERE storage_owner = ?1 AND user_id = ?2
|
WHERE storage_owner = ?1 AND user_id = ?2
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
|
|
@ -67,7 +79,8 @@ pub fn get_user(storage_owner: i64, user_id: i64) -> Result<Option<Contact>, Sto
|
||||||
Ok(Contact {
|
Ok(Contact {
|
||||||
user_id: r.get(0)?,
|
user_id: r.get(0)?,
|
||||||
user_name: r.get(1)?,
|
user_name: r.get(1)?,
|
||||||
last_message_at: r.get(2)?,
|
created_at: r.get(2)?,
|
||||||
|
last_message_at: r.get(3)?,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
|
@ -82,7 +95,7 @@ pub fn get_users(storage_owner: i64) -> Result<Vec<Contact>, StorageError> {
|
||||||
db::with_db(|conn| {
|
db::with_db(|conn| {
|
||||||
let mut stmt = conn.prepare(
|
let mut stmt = conn.prepare(
|
||||||
r#"
|
r#"
|
||||||
SELECT user_id, user_name, last_message_at
|
SELECT user_id, user_name, created_at, last_message_at
|
||||||
FROM contacts
|
FROM contacts
|
||||||
WHERE storage_owner = ?1
|
WHERE storage_owner = ?1
|
||||||
ORDER BY
|
ORDER BY
|
||||||
|
|
@ -96,7 +109,8 @@ pub fn get_users(storage_owner: i64) -> Result<Vec<Contact>, StorageError> {
|
||||||
Ok(Contact {
|
Ok(Contact {
|
||||||
user_id: r.get(0)?,
|
user_id: r.get(0)?,
|
||||||
user_name: r.get(1)?,
|
user_name: r.get(1)?,
|
||||||
last_message_at: r.get(2)?,
|
created_at: r.get(2)?,
|
||||||
|
last_message_at: r.get(3)?,
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,7 @@ fn run_migrations_on_connection(conn: &Connection) -> Result<(), StorageError> {
|
||||||
storage_owner INTEGER NOT NULL,
|
storage_owner INTEGER NOT NULL,
|
||||||
user_id INTEGER NOT NULL,
|
user_id INTEGER NOT NULL,
|
||||||
user_name TEXT,
|
user_name TEXT,
|
||||||
|
created_at INTEGER NOT NULL,
|
||||||
last_message_at INTEGER,
|
last_message_at INTEGER,
|
||||||
UNIQUE(storage_owner, user_id)
|
UNIQUE(storage_owner, user_id)
|
||||||
);
|
);
|
||||||
|
|
@ -527,6 +528,57 @@ fn run_migrations_on_connection(conn: &Connection) -> Result<(), StorageError> {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if current_version < 15 {
|
||||||
|
let messages_exist: bool = conn.query_row(
|
||||||
|
"SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'messages')",
|
||||||
|
[],
|
||||||
|
|row| row.get(0),
|
||||||
|
)?;
|
||||||
|
if messages_exist {
|
||||||
|
conn.execute_batch(
|
||||||
|
r#"
|
||||||
|
DROP INDEX IF EXISTS idx_messages_history;
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_messages_history_accepted
|
||||||
|
ON messages (
|
||||||
|
storage_owner,
|
||||||
|
external_user,
|
||||||
|
deleted_by_external,
|
||||||
|
destination_iota_received_at DESC,
|
||||||
|
origin_iota_received_at DESC,
|
||||||
|
authored_at DESC,
|
||||||
|
id DESC
|
||||||
|
);
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
conn.pragma_update(None, "user_version", 15)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if current_version < 16 {
|
||||||
|
let messages_exist: bool = conn.query_row(
|
||||||
|
"SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'messages')",
|
||||||
|
[],
|
||||||
|
|row| row.get(0),
|
||||||
|
)?;
|
||||||
|
if messages_exist {
|
||||||
|
add_column_if_missing(conn, "delivery_failed_at", "delivery_failed_at INTEGER")?;
|
||||||
|
add_column_if_missing(conn, "delivery_failure", "delivery_failure TEXT")?;
|
||||||
|
}
|
||||||
|
let contacts_exist: bool = conn.query_row(
|
||||||
|
"SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'contacts')",
|
||||||
|
[],
|
||||||
|
|row| row.get(0),
|
||||||
|
)?;
|
||||||
|
if contacts_exist {
|
||||||
|
add_table_column_if_missing(conn, "contacts", "created_at", "created_at INTEGER")?;
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE contacts SET created_at = COALESCE(created_at, last_message_at, 0)",
|
||||||
|
[],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
conn.pragma_update(None, "user_version", 16)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -596,7 +648,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))?;
|
let version: i64 = conn.pragma_query_value(None, "user_version", |row| row.get(0))?;
|
||||||
assert_eq!(version, 13);
|
assert_eq!(version, 16);
|
||||||
for column in ["height", "reply_to", "edited_count", "deleted_by_external"] {
|
for column in ["height", "reply_to", "edited_count", "deleted_by_external"] {
|
||||||
let mut statement =
|
let mut statement =
|
||||||
conn.prepare("SELECT 1 FROM pragma_table_info('messages') WHERE name = ?1")?;
|
conn.prepare("SELECT 1 FROM pragma_table_info('messages') WHERE name = ?1")?;
|
||||||
|
|
@ -615,7 +667,7 @@ mod tests {
|
||||||
run_migrations_on_connection(&conn)?;
|
run_migrations_on_connection(&conn)?;
|
||||||
run_migrations_on_connection(&conn)?;
|
run_migrations_on_connection(&conn)?;
|
||||||
let version: i64 = conn.pragma_query_value(None, "user_version", |row| row.get(0))?;
|
let version: i64 = conn.pragma_query_value(None, "user_version", |row| row.get(0))?;
|
||||||
assert_eq!(version, 13);
|
assert_eq!(version, 16);
|
||||||
for table in [
|
for table in [
|
||||||
"sync_heads",
|
"sync_heads",
|
||||||
"sync_events",
|
"sync_events",
|
||||||
|
|
@ -650,7 +702,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))?;
|
let version: i64 = conn.pragma_query_value(None, "user_version", |row| row.get(0))?;
|
||||||
assert_eq!(version, 13);
|
assert_eq!(version, 16);
|
||||||
for column in [
|
for column in [
|
||||||
"id",
|
"id",
|
||||||
"user_id",
|
"user_id",
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ tokio = { version = "1.50.0", features = ["full"] }
|
||||||
sysinfo = "0.38.0"
|
sysinfo = "0.38.0"
|
||||||
uuid = { version = "*", features = ["v4"] }
|
uuid = { version = "*", features = ["v4"] }
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
zip = "6.0.0"
|
zip = "8.0.0"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
hex = "*"
|
hex = "*"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,21 @@ const IOTA_KEYRING_PATH: &str = "iota.mk";
|
||||||
static IDENTITY_PATH: std::sync::OnceLock<PathBuf> = std::sync::OnceLock::new();
|
static IDENTITY_PATH: std::sync::OnceLock<PathBuf> = std::sync::OnceLock::new();
|
||||||
static OMIKRON_PUBLIC_KEY_PATH: std::sync::OnceLock<PathBuf> = std::sync::OnceLock::new();
|
static OMIKRON_PUBLIC_KEY_PATH: std::sync::OnceLock<PathBuf> = std::sync::OnceLock::new();
|
||||||
|
|
||||||
|
fn record_origin_delivery_failure(signer_id: u64, relay_message_id: &str, failure: &str) {
|
||||||
|
let Ok(storage_owner) = i64::try_from(signer_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Err(error) = chat_files::record_delivery_failure(
|
||||||
|
storage_owner,
|
||||||
|
storage_owner,
|
||||||
|
relay_message_id,
|
||||||
|
failure,
|
||||||
|
now_millis_i64(),
|
||||||
|
) {
|
||||||
|
log!("Relay delivery failure storage failed: {error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keeps identity and pinned Omikron key files independent from the process
|
* Keeps identity and pinned Omikron key files independent from the process
|
||||||
* working directory, so restarts use the same trusted material.
|
* working directory, so restarts use the same trusted material.
|
||||||
|
|
@ -1136,6 +1151,11 @@ impl OmikronConnection {
|
||||||
Ok(destination_iota) => destination_iota,
|
Ok(destination_iota) => destination_iota,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
log!("Relay origin route lookup failed: {}", error);
|
log!("Relay origin route lookup failed: {}", error);
|
||||||
|
record_origin_delivery_failure(
|
||||||
|
verified.context.signer_id,
|
||||||
|
&verified.context.message_id,
|
||||||
|
"destination_iota_not_found",
|
||||||
|
);
|
||||||
self.send_relay_response(frame.id(), CommunicationType::ErrorNoIota)
|
self.send_relay_response(frame.id(), CommunicationType::ErrorNoIota)
|
||||||
.await;
|
.await;
|
||||||
return;
|
return;
|
||||||
|
|
@ -1145,6 +1165,11 @@ impl OmikronConnection {
|
||||||
Ok(value) => value,
|
Ok(value) => value,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
log!("Relay origin forwarding validation failed: {}", error);
|
log!("Relay origin forwarding validation failed: {}", error);
|
||||||
|
record_origin_delivery_failure(
|
||||||
|
verified.context.signer_id,
|
||||||
|
&verified.context.message_id,
|
||||||
|
"forwarding_validation_failed",
|
||||||
|
);
|
||||||
self.send_relay_response(frame.id(), CommunicationType::ErrorInvalidData)
|
self.send_relay_response(frame.id(), CommunicationType::ErrorInvalidData)
|
||||||
.await;
|
.await;
|
||||||
return;
|
return;
|
||||||
|
|
@ -1154,6 +1179,11 @@ impl OmikronConnection {
|
||||||
Ok(bytes) => bytes,
|
Ok(bytes) => bytes,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
log!("Relay origin retry could not be serialized: {}", error);
|
log!("Relay origin retry could not be serialized: {}", error);
|
||||||
|
record_origin_delivery_failure(
|
||||||
|
verified.context.signer_id,
|
||||||
|
&verified.context.message_id,
|
||||||
|
"serialization_failed",
|
||||||
|
);
|
||||||
self.send_relay_response(frame.id(), CommunicationType::ErrorInternal)
|
self.send_relay_response(frame.id(), CommunicationType::ErrorInternal)
|
||||||
.await;
|
.await;
|
||||||
return;
|
return;
|
||||||
|
|
@ -1167,6 +1197,11 @@ impl OmikronConnection {
|
||||||
&type_map_version,
|
&type_map_version,
|
||||||
) {
|
) {
|
||||||
log!("Relay origin retry queue failed: {}", error);
|
log!("Relay origin retry queue failed: {}", error);
|
||||||
|
record_origin_delivery_failure(
|
||||||
|
verified.context.signer_id,
|
||||||
|
&verified.context.message_id,
|
||||||
|
"queue_failed",
|
||||||
|
);
|
||||||
self.send_relay_response(frame.id(), CommunicationType::ErrorInternal)
|
self.send_relay_response(frame.id(), CommunicationType::ErrorInternal)
|
||||||
.await;
|
.await;
|
||||||
return;
|
return;
|
||||||
|
|
@ -1242,6 +1277,11 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
log!("Relay origin route returned {}", response.get_type());
|
log!("Relay origin route returned {}", response.get_type());
|
||||||
|
record_origin_delivery_failure(
|
||||||
|
verified.context.signer_id,
|
||||||
|
&verified.context.message_id,
|
||||||
|
"destination_rejected",
|
||||||
|
);
|
||||||
self.send_relay_response(
|
self.send_relay_response(
|
||||||
frame.id(),
|
frame.id(),
|
||||||
response
|
response
|
||||||
|
|
@ -1252,6 +1292,11 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
log!("Relay origin forwarding failed: {}", error);
|
log!("Relay origin forwarding failed: {}", error);
|
||||||
|
record_origin_delivery_failure(
|
||||||
|
verified.context.signer_id,
|
||||||
|
&verified.context.message_id,
|
||||||
|
"destination_unreachable",
|
||||||
|
);
|
||||||
self.send_relay_response(frame.id(), CommunicationType::ErrorInternal)
|
self.send_relay_response(frame.id(), CommunicationType::ErrorInternal)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
@ -1521,7 +1566,6 @@ impl OmikronConnection {
|
||||||
dispatch!(DeleteApp, handle_delete_app);
|
dispatch!(DeleteApp, handle_delete_app);
|
||||||
dispatch!(ClientConnected, handle_client_connected);
|
dispatch!(ClientConnected, handle_client_connected);
|
||||||
dispatch!(ClientStateAck, handle_client_state_ack);
|
dispatch!(ClientStateAck, handle_client_state_ack);
|
||||||
dispatch!(MessageState, handle_message_state);
|
|
||||||
dispatch!(MessageEdit, handle_message_edit);
|
dispatch!(MessageEdit, handle_message_edit);
|
||||||
dispatch!(MessageEditLive, handle_message_edit_live);
|
dispatch!(MessageEditLive, handle_message_edit_live);
|
||||||
dispatch!(MessageReactionAdd, handle_message_reaction_add);
|
dispatch!(MessageReactionAdd, handle_message_reaction_add);
|
||||||
|
|
@ -1771,10 +1815,6 @@ impl OmikronConnection {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_message_state(self: Arc<Self>, cv: &CommunicationValue) {
|
|
||||||
message_handlers::handle_message_state(cv);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mutation_live_message(
|
fn mutation_live_message(
|
||||||
ty: CommunicationType,
|
ty: CommunicationType,
|
||||||
request: &CommunicationValue,
|
request: &CommunicationValue,
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ fn sign_lifecycle_payload(
|
||||||
async fn inspect_credential_account(
|
async fn inspect_credential_account(
|
||||||
connection: &dyn OmikronClient,
|
connection: &dyn OmikronClient,
|
||||||
credential: &TuCredential,
|
credential: &TuCredential,
|
||||||
) -> Result<(String, String), LifecycleUserError> {
|
) -> Result<(String, String, i64), LifecycleUserError> {
|
||||||
if credential.omega_host != omega_discovery::omega_host() {
|
if credential.omega_host != omega_discovery::omega_host() {
|
||||||
return Err(LifecycleUserError::OmegaHostMismatch);
|
return Err(LifecycleUserError::OmegaHostMismatch);
|
||||||
}
|
}
|
||||||
|
|
@ -108,10 +108,16 @@ async fn inspect_credential_account(
|
||||||
.as_str()
|
.as_str()
|
||||||
.map(str::to_owned)
|
.map(str::to_owned)
|
||||||
.ok_or(LifecycleUserError::RemoteRejected)?;
|
.ok_or(LifecycleUserError::RemoteRejected)?;
|
||||||
|
let created_at = response
|
||||||
|
.get_data(DataType::CreatedAt)
|
||||||
|
.as_signed_number()
|
||||||
|
.and_then(|value| i64::try_from(value).ok())
|
||||||
|
.filter(|value| *value > 0)
|
||||||
|
.ok_or(LifecycleUserError::RemoteRejected)?;
|
||||||
if public_key != public_key_bundle_to_base64(&credential.public_key_bundle()) {
|
if public_key != public_key_bundle_to_base64(&credential.public_key_bundle()) {
|
||||||
return Err(LifecycleUserError::RemoteRejected);
|
return Err(LifecycleUserError::RemoteRejected);
|
||||||
}
|
}
|
||||||
Ok((username, public_key))
|
Ok((username, public_key, created_at))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn credential_proof(
|
async fn credential_proof(
|
||||||
|
|
@ -168,17 +174,17 @@ pub async fn attach_user_from_tu(
|
||||||
) -> Result<UserProfile, LifecycleUserError> {
|
) -> Result<UserProfile, LifecycleUserError> {
|
||||||
let credential = TuCredential::parse(contents)
|
let credential = TuCredential::parse(contents)
|
||||||
.map_err(|error| LifecycleUserError::InvalidCredential(error.to_string()))?;
|
.map_err(|error| LifecycleUserError::InvalidCredential(error.to_string()))?;
|
||||||
let (username, public_key) = inspect_credential_account(connection, &credential).await?;
|
let (username, public_key, created_at) =
|
||||||
let profile = UserProfile::new(
|
inspect_credential_account(connection, &credential).await?;
|
||||||
|
let profile = UserProfile::new_with_created_at(
|
||||||
credential.user_id,
|
credential.user_id,
|
||||||
username,
|
username,
|
||||||
None,
|
None,
|
||||||
public_key,
|
public_key,
|
||||||
hex_hash(contents),
|
hex_hash(contents),
|
||||||
String::new(),
|
String::new(),
|
||||||
|
created_at,
|
||||||
);
|
);
|
||||||
write_user_credential(&profile.username, &credential.to_canonical_string())
|
|
||||||
.map_err(|error| LifecycleUserError::LocalPersistence(error.to_string()))?;
|
|
||||||
pending_operations::upsert(&PendingUserOperation {
|
pending_operations::upsert(&PendingUserOperation {
|
||||||
user_id: profile.user_id,
|
user_id: profile.user_id,
|
||||||
operation: PendingUserOperationKind::Attach,
|
operation: PendingUserOperationKind::Attach,
|
||||||
|
|
@ -191,6 +197,13 @@ pub async fn attach_user_from_tu(
|
||||||
created_at: now_millis(),
|
created_at: now_millis(),
|
||||||
})
|
})
|
||||||
.map_err(|error| LifecycleUserError::LocalPersistence(error.to_string()))?;
|
.map_err(|error| LifecycleUserError::LocalPersistence(error.to_string()))?;
|
||||||
|
write_user_credential(&profile.username, &credential.to_canonical_string())
|
||||||
|
.map_err(|error| LifecycleUserError::LocalPersistence(error.to_string()))?;
|
||||||
|
pending_operations::update_phase(
|
||||||
|
profile.user_id,
|
||||||
|
PendingUserOperationPhase::CredentialWritten,
|
||||||
|
)
|
||||||
|
.map_err(|error| LifecycleUserError::LocalPersistence(error.to_string()))?;
|
||||||
if let Err(error) = credential_proof(
|
if let Err(error) = credential_proof(
|
||||||
connection,
|
connection,
|
||||||
&credential,
|
&credential,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue