Browse Source

Refactor Sends table

pull/7363/head
Timshel 1 month ago
parent
commit
0036471e9f
  1. 38
      migrations/mysql/2026-06-18-120000_add_sends_emails/up.sql
  2. 7
      migrations/postgresql/2026-06-18-120000_add_sends_emails/up.sql
  3. 2
      migrations/sqlite/2026-06-18-120000_add_sends_emails/down.sql
  4. 41
      migrations/sqlite/2026-06-18-120000_add_sends_emails/up.sql
  5. 6
      src/api/core/sends.rs
  6. 3
      src/api/notifications.rs
  7. 4
      src/api/push.rs
  8. 47
      src/db/models/send.rs
  9. 6
      src/db/schema.rs

38
migrations/mysql/2026-06-18-120000_add_sends_emails/up.sql

@ -11,3 +11,41 @@ CREATE TABLE sends_otp (
PRIMARY KEY(send_uuid, email)
);
DELETE FROM sends where user_uuid IS NULL;
UPDATE sends SET hide_email = false WHERE hide_email IS NULL;
SELECT if (
EXISTS(
SELECT CONSTRAINT_NAME FROM information_schema.table_constraints
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'sends'
AND CONSTRAINT_TYPE = 'FOREIGN KEY'
AND CONSTRAINT_NAME = 'sends_ibfk_2'
)
,'ALTER TABLE sends DROP FOREIGN KEY `sends_ibfk_2`'
,'SELECT "info: FK sends_ibfk_2 does not exist."'
) INTO @drop_stmt;
PREPARE drop_stmt FROM @drop_stmt;
EXECUTE drop_stmt;
SELECT if (
EXISTS(
SELECT CONSTRAINT_NAME FROM information_schema.table_constraints
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'sends'
AND CONSTRAINT_TYPE = 'FOREIGN KEY'
AND CONSTRAINT_NAME = '2'
)
,'ALTER TABLE sends DROP FOREIGN KEY `2`'
,'SELECT "info: FK sends 2 does not exist."'
) INTO @drop_stmt;
PREPARE drop_stmt FROM @drop_stmt;
EXECUTE drop_stmt;
DEALLOCATE PREPARE drop_stmt;
ALTER TABLE sends DROP COLUMN organization_uuid;
ALTER TABLE sends MODIFY user_uuid CHAR(36) NOT NULL;
ALTER TABLE sends MODIFY hide_email BOOLEAN NOT NULL;

7
migrations/postgresql/2026-06-18-120000_add_sends_emails/up.sql

@ -11,3 +11,10 @@ CREATE TABLE sends_otp (
PRIMARY KEY(send_uuid, email)
);
DELETE FROM sends where user_uuid IS NULL;
UPDATE sends SET hide_email = false WHERE hide_email IS NULL;
ALTER TABLE sends DROP COLUMN organization_uuid;
ALTER TABLE sends ALTER COLUMN user_uuid SET NOT NULL;
ALTER TABLE sends ALTER COLUMN hide_email SET NOT NULL;

2
migrations/sqlite/2026-06-18-120000_add_sends_emails/down.sql

@ -1,2 +0,0 @@
ALTER TABLE sends DROP COLUMN emails;
DROP TABLE sends_otp;

41
migrations/sqlite/2026-06-18-120000_add_sends_emails/up.sql

@ -1,4 +1,43 @@
ALTER TABLE sends ADD COLUMN emails TEXT;
ALTER TABLE sends RENAME TO sends_old;
CREATE TABLE sends (
uuid TEXT NOT NULL PRIMARY KEY,
user_uuid TEXT NOT NULL REFERENCES users (uuid),
name TEXT NOT NULL,
notes TEXT,
atype INTEGER NOT NULL,
data TEXT NOT NULL,
akey TEXT NOT NULL,
password_hash BLOB,
password_salt BLOB,
password_iter INTEGER,
emails TEXT,
max_access_count INTEGER,
access_count INTEGER NOT NULL,
creation_date DATETIME NOT NULL,
revision_date DATETIME NOT NULL,
expiration_date DATETIME,
deletion_date DATETIME NOT NULL,
disabled BOOLEAN NOT NULL,
hide_email BOOLEAN NOT NULL
);
INSERT INTO sends(
uuid, user_uuid, name, notes, atype, data, akey, password_hash, password_salt, password_iter,
max_access_count, access_count, creation_date, revision_date, expiration_date, deletion_date,
disabled, hide_email
) SELECT uuid, user_uuid, name, notes, atype, data, akey, password_hash, password_salt, password_iter,
max_access_count, access_count, creation_date, revision_date, expiration_date, deletion_date,
disabled,
CASE WHEN hide_email IS NOT NULL THEN hide_email ELSE false END
FROM sends_old WHERE user_uuid IS NOT NULL;
DROP TABLE sends_old;
CREATE TABLE sends_otp (
send_uuid TEXT NOT NULL REFERENCES sends(uuid) ON DELETE CASCADE ON UPDATE CASCADE,

6
src/api/core/sends.rs

@ -172,7 +172,7 @@ fn create_send(data: SendData, user_id: UserId) -> ApiResult<Send> {
data.expiration_date.map(|d| d.naive_utc()),
data.deletion_date.naive_utc(),
data.disabled,
data.hide_email,
data.hide_email.unwrap_or(false),
);
send.set_password(data.password.as_deref());
@ -665,7 +665,7 @@ pub async fn update_send_from_data(
nt: &Notify<'_>,
ut: UpdateType,
) -> EmptyResult {
if send.user_uuid.as_ref() != Some(&headers.user.uuid) {
if send.user_uuid != headers.user.uuid {
err!("Send is not owned by user")
}
@ -700,7 +700,7 @@ pub async fn update_send_from_data(
_ => None,
};
send.expiration_date = data.expiration_date.map(|d| d.naive_utc());
send.hide_email = data.hide_email;
send.hide_email = data.hide_email.unwrap_or(false);
send.disabled = data.disabled;
send.emails = data.emails.map(|e| e.to_lowercase());

3
src/api/notifications.rs

@ -465,12 +465,11 @@ impl WebSocketUsers {
if *NOTIFICATIONS_DISABLED {
return;
}
let user_id = convert_option(send.user_uuid.as_deref());
let data = create_update(
vec![
("Id".into(), send.uuid.to_string().into()),
("UserId".into(), user_id),
("UserId".into(), send.user_uuid.to_string().into()),
("RevisionDate".into(), serialize_date(send.revision_date)),
],
ut,

4
src/api/push.rs

@ -244,9 +244,7 @@ pub async fn push_folder_update(ut: UpdateType, folder: &Folder, device: &Device
}
pub async fn push_send_update(ut: UpdateType, send: &Send, device: &Device, conn: &DbConn) {
if let Some(s) = &send.user_uuid
&& Device::check_user_has_push_device(s, conn).await
{
if Device::check_user_has_push_device(&send.user_uuid, conn).await {
tokio::task::spawn(send_to_push_relay(json!({
"userId": send.user_uuid,
"organizationId": null,

47
src/db/models/send.rs

@ -20,7 +20,7 @@ use crate::{
util::{LowerCase, NumberOrString, format_date},
};
use super::{OrganizationId, User, UserId};
use super::{User, UserId};
#[derive(Identifiable, Queryable, Insertable, AsChangeset, Selectable)]
#[diesel(table_name = sends)]
@ -29,8 +29,7 @@ use super::{OrganizationId, User, UserId};
pub struct Send {
pub uuid: SendId,
pub user_uuid: Option<UserId>,
pub organization_uuid: Option<OrganizationId>,
pub user_uuid: UserId,
pub name: String,
pub notes: Option<String>,
@ -52,7 +51,7 @@ pub struct Send {
pub deletion_date: NaiveDateTime,
pub disabled: bool,
pub hide_email: Option<bool>,
pub hide_email: bool,
}
#[derive(Copy, Clone, PartialEq, Eq, num_derive::FromPrimitive)]
@ -85,14 +84,13 @@ impl Send {
expiration_date: Option<NaiveDateTime>,
deletion_date: NaiveDateTime,
disabled: bool,
hide_email: Option<bool>,
hide_email: bool,
) -> Self {
let now = Utc::now().naive_utc();
Self {
uuid: SendId::from(crate::util::get_uuid()),
user_uuid: Some(user_uuid),
organization_uuid: None,
user_uuid,
name,
notes,
atype,
@ -142,19 +140,13 @@ impl Send {
}
pub async fn creator_identifier(&self, conn: &DbConn) -> Option<String> {
if let Some(hide_email) = self.hide_email
&& hide_email
if !self.hide_email
&& let Some(user) = User::find_by_uuid(&self.user_uuid, conn).await
{
return None;
}
if let Some(user_uuid) = &self.user_uuid
&& let Some(user) = User::find_by_uuid(user_uuid, conn).await
{
return Some(user.email);
Some(user.email)
} else {
None
}
None
}
pub fn to_json(&self) -> Value {
@ -181,7 +173,7 @@ impl Send {
"password": self.password_hash.as_deref().map(|h| BASE64URL_NOPAD.encode(h)),
"authType": if self.password_hash.is_some() { SendAuthType::Password } else if self.emails.is_some() { SendAuthType::Email } else { SendAuthType::None } as i32,
"disabled": self.disabled,
"hideEmail": self.hide_email.unwrap_or(false),
"hideEmail": self.hide_email,
"emails": self.emails,
"revisionDate": format_date(&self.revision_date),
@ -263,14 +255,8 @@ impl Send {
}
pub async fn update_users_revision(&self, conn: &DbConn) -> Vec<UserId> {
let mut user_uuids = Vec::new();
if let Some(user_uuid) = &self.user_uuid {
User::update_uuid_revision(user_uuid, conn).await;
user_uuids.push(user_uuid.clone());
} else {
// Belongs to Organization, not implemented
}
user_uuids
User::update_uuid_revision(&self.user_uuid, conn).await;
vec![self.user_uuid.clone()]
}
pub async fn delete_all_by_user(user_uuid: &UserId, conn: &DbConn) -> EmptyResult {
@ -332,13 +318,6 @@ impl Send {
Some(total)
}
pub async fn find_by_org(org_uuid: &OrganizationId, conn: &DbConn) -> Vec<Self> {
conn.run(move |conn| {
sends::table.filter(sends::organization_uuid.eq(org_uuid)).load::<Self>(conn).expect("Error loading sends")
})
.await
}
pub async fn find_by_past_deletion_date(conn: &DbConn) -> Vec<Self> {
let now = Utc::now().naive_utc();
conn.run(move |conn| {

6
src/db/schema.rs

@ -132,8 +132,7 @@ table! {
table! {
sends (uuid) {
uuid -> Text,
user_uuid -> Nullable<Text>,
organization_uuid -> Nullable<Text>,
user_uuid -> Text,
name -> Text,
notes -> Nullable<Text>,
atype -> Integer,
@ -150,7 +149,7 @@ table! {
expiration_date -> Nullable<Timestamp>,
deletion_date -> Timestamp,
disabled -> Bool,
hide_email -> Nullable<Bool>,
hide_email -> Bool,
}
}
@ -376,7 +375,6 @@ joinable!(folders -> users (user_uuid));
joinable!(folders_ciphers -> ciphers (cipher_uuid));
joinable!(folders_ciphers -> folders (folder_uuid));
joinable!(org_policies -> organizations (org_uuid));
joinable!(sends -> organizations (organization_uuid));
joinable!(sends -> users (user_uuid));
joinable!(sends_otp -> sends (send_uuid));
joinable!(twofactor -> users (user_uuid));

Loading…
Cancel
Save