Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
130 commits
Select commit Hold shift + click to select a range
389e868
Activate github/workflow
zephyranthes03 Sep 5, 2025
7be0539
Update fmt update
zephyranthes03 Sep 8, 2025
53a7e93
Fix import path from src/bin/rabbit_consumer.rs
zephyranthes03 Sep 8, 2025
539e1a9
Remove crashed marker/Merge import block
zephyranthes03 Sep 8, 2025
ce980ac
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 8, 2025
fb632e8
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 8, 2025
f63361a
Bypass rabbit_consumer code
zephyranthes03 Sep 8, 2025
a53a060
Bypass rabbit_consumer code
zephyranthes03 Sep 8, 2025
e0fa20c
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 8, 2025
fc73fdd
Bypass rabbit_consumer code
zephyranthes03 Sep 8, 2025
12362cf
Update deploy procss
zephyranthes03 Sep 9, 2025
5fdd399
Update cosmic-sync-server health check
zephyranthes03 Sep 9, 2025
f19203b
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 9, 2025
6c879dc
Update cosmic-sync-server health check
zephyranthes03 Sep 9, 2025
1982e98
Update cosmic-sync-server health check
zephyranthes03 Sep 9, 2025
53745b6
Update service loading delay/ Update related service env
zephyranthes03 Sep 10, 2025
c55d84a
Fix build error
zephyranthes03 Sep 10, 2025
a43b5ac
Fix build error
zephyranthes03 Sep 10, 2025
15555aa
Update builder rs
zephyranthes03 Sep 10, 2025
37bc15c
Fix static library issue
zephyranthes03 Sep 10, 2025
987a6a9
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 10, 2025
7cdfe0e
Fix static library issue
zephyranthes03 Sep 10, 2025
12aa27f
Fix static library issue
zephyranthes03 Sep 10, 2025
d015f08
Fix static library issue
zephyranthes03 Sep 10, 2025
53c4809
Update secret location
zephyranthes03 Sep 11, 2025
3c1633f
Check system76/cosmic-sync-server repo
zephyranthes03 Sep 11, 2025
65174ca
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 11, 2025
ea38129
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 11, 2025
51c9b41
Check system76/cosmic-sync-server repo
zephyranthes03 Sep 11, 2025
f66baea
Fix Migration issue
zephyranthes03 Sep 12, 2025
c9b0ce4
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 12, 2025
9134c3c
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 12, 2025
cb41ab1
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 12, 2025
69c18c2
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 12, 2025
7a0ba3a
Fix Migration issue
zephyranthes03 Sep 12, 2025
5c7c781
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 12, 2025
00e2616
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 12, 2025
7b8f4ea
Fix Migration issue
zephyranthes03 Sep 13, 2025
77a767c
Update schema
zephyranthes03 Sep 14, 2025
b34ecc4
git Merge branch 'staging' of github.com:system76/cosmic-sync-server …
zephyranthes03 Sep 14, 2025
c8693d0
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 14, 2025
32dd2c7
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 15, 2025
da1dd7a
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 15, 2025
26bd72b
Fix grpc port connection
zephyranthes03 Sep 15, 2025
d03e4b8
Fix grpc port connection
zephyranthes03 Sep 15, 2025
cb2b8ca
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 15, 2025
f80a24b
Fix grpc port connection
zephyranthes03 Sep 15, 2025
1a07741
git Merge branch 'staging' of github.com:system76/cosmic-sync-server …
zephyranthes03 Sep 15, 2025
7e5baae
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 15, 2025
fb16db2
Fix grpc port connection
zephyranthes03 Sep 15, 2025
477969d
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 15, 2025
1cb4790
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 15, 2025
70f649c
Fix grpc port connection
zephyranthes03 Sep 15, 2025
44f5356
git Merge branch 'staging' of github.com:system76/cosmic-sync-server …
zephyranthes03 Sep 15, 2025
6cff219
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 15, 2025
beafa61
Fix grpc port connection
zephyranthes03 Sep 15, 2025
e7c62fd
Fix grpc port connection
zephyranthes03 Sep 15, 2025
c1d2bc1
Update login process - fix OAUTH infos
zephyranthes03 Sep 19, 2025
3f7b8e0
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 19, 2025
0f2edfd
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 19, 2025
c879fa1
Update login process - fix OAUTH infos
zephyranthes03 Sep 22, 2025
e14710e
Update login process - fix saving auth token
zephyranthes03 Sep 22, 2025
22fe3c8
Merge 443/50051
zephyranthes03 Sep 23, 2025
cae291a
Merge 443/50051
zephyranthes03 Sep 23, 2025
e55115d
Merge 443/50051
zephyranthes03 Sep 24, 2025
00ea3ef
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 24, 2025
1b86395
Merge 443/50051
zephyranthes03 Sep 24, 2025
4b8668a
Merge 443/50051
zephyranthes03 Sep 24, 2025
b05e032
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 24, 2025
24056be
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 24, 2025
c193115
Merge 443/50051
zephyranthes03 Sep 24, 2025
f85eae7
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 24, 2025
d1569a8
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 24, 2025
a035a88
Merge 443/50051
zephyranthes03 Sep 24, 2025
509b93c
Fix http size issue
zephyranthes03 Sep 26, 2025
bda4115
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 26, 2025
1dac212
Fix http size issue
zephyranthes03 Sep 26, 2025
18f0f72
Fix http size issue
zephyranthes03 Sep 26, 2025
2b6c550
Fix http size issue
zephyranthes03 Sep 26, 2025
ad42771
Fix http size issue
zephyranthes03 Sep 26, 2025
98dafbe
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 26, 2025
c652398
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 26, 2025
65c99f0
Fix http size issue
zephyranthes03 Sep 26, 2025
c26386d
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 26, 2025
d849e14
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 26, 2025
266d297
Fix http size issue
zephyranthes03 Sep 26, 2025
34e78d6
Fix http size issue
zephyranthes03 Sep 26, 2025
4331e95
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 26, 2025
aa8fb94
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 26, 2025
7b12ca4
Fix http size issue
zephyranthes03 Sep 27, 2025
2260296
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 27, 2025
26c05fc
Fix http size issue
zephyranthes03 Sep 27, 2025
9c2c299
Fix http size issue
zephyranthes03 Sep 27, 2025
4051873
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 27, 2025
cbfca95
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 27, 2025
c640456
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 27, 2025
f19baac
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 27, 2025
fbe4cc7
Fix http size issue
zephyranthes03 Sep 27, 2025
47ebffb
Fix http size issue
zephyranthes03 Sep 27, 2025
37e11f8
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 27, 2025
5098835
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 27, 2025
47ec562
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Sep 27, 2025
3324e59
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Sep 27, 2025
ca16552
Check point for 2024
zephyranthes03 Sep 27, 2025
b5068f1
Check point for 2024
zephyranthes03 Sep 27, 2025
875870b
Check point for 2024 - fmt
zephyranthes03 Sep 27, 2025
32fe89d
Check point for 2024 - dockerfile
zephyranthes03 Sep 27, 2025
c30060c
Check point for streaming/key_id patch
zephyranthes03 Sep 30, 2025
cbafc38
Fix starting process
zephyranthes03 Oct 1, 2025
948f090
Fix mysql auth_token mismatch
zephyranthes03 Oct 1, 2025
eee66c7
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Oct 1, 2025
c1ec243
Fix starting process
zephyranthes03 Oct 1, 2025
ebd3f03
Fix starting process
zephyranthes03 Oct 1, 2025
9e049c7
Migrate DB table
zephyranthes03 Oct 2, 2025
1e8395a
Merge branch 'staging' of github.com:system76/cosmic-sync-server into…
zephyranthes03 Oct 2, 2025
391cb6b
Merge branch 'staging' into fix_rabbit_consumer
zephyranthes03 Oct 2, 2025
f3f332b
Update delete routine
zephyranthes03 Nov 18, 2025
a8d973d
Update delete routine
zephyranthes03 Nov 18, 2025
aa52b24
Merge branch 'origin' into delete_event_v2
zephyranthes03 Nov 18, 2025
6c297a6
Update delete routine
zephyranthes03 Nov 18, 2025
a154cf6
Update delete routine
zephyranthes03 Nov 18, 2025
89e0dd1
Update delete routine
zephyranthes03 Nov 19, 2025
1aa06a3
Update delete routine
zephyranthes03 Nov 19, 2025
24aad96
Merge branch 'staging' into delete_event_v2
zephyranthes03 Nov 19, 2025
c8bb833
Update delete routine
zephyranthes03 Nov 19, 2025
65aa5a1
Update delete routine
zephyranthes03 Nov 19, 2025
d37553f
Update delete routine
zephyranthes03 Nov 19, 2025
5bec2e5
Merge branch 'staging' into delete_event_v2
zephyranthes03 Nov 19, 2025
0970d7d
Update delete routine
zephyranthes03 Nov 19, 2025
5f13cf2
Update delete routine
zephyranthes03 Nov 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions src/handlers/file_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,7 @@ impl FileHandler {
match self
.app_state
.file
.find_file_by_local_path(
&req.account_hash,
&normalized_file_path,
&req.filename,
req.revision,
)
.find_file_by_local_path(&req.account_hash, &normalized_file_path, &req.filename)
.await
{
Ok(Some(info)) => {
Expand Down
12 changes: 5 additions & 7 deletions src/services/file_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,14 +606,13 @@ impl FileService {

// Try to find actual active file by path/name
// This handles the case where client has wrong file_id (from previous deleted file)
// revision=0 means get the latest active file
// Now searches by updated_time (latest active file)
match self
.storage
.find_file_by_path_and_name(
&file_info.account_hash,
&file_info.file_path,
&file_info.filename,
0, // revision=0: get latest active file (highest revision, not deleted)
)
.await
{
Expand Down Expand Up @@ -808,11 +807,10 @@ impl FileService {
account_hash: &str,
file_path: &str,
filename: &str,
revision: i64,
) -> Result<Option<ModelFileInfo>, StorageError> {
debug!(
"Finding file by local path: account={}, path={}, filename={}, revision={}",
account_hash, file_path, filename, revision
"Finding file by local path (by updated_time): account={}, path={}, filename={}",
account_hash, file_path, filename
);

// Normalize file path to preserve tilde (~) prefix for home directory
Expand All @@ -822,9 +820,9 @@ impl FileService {
file_path, normalized_file_path
);

// Query file information from storage
// Query file information from storage (searches by updated_time for latest active file)
self.storage
.find_file_by_path_and_name(account_hash, &normalized_file_path, filename, revision)
.find_file_by_path_and_name(account_hash, &normalized_file_path, filename)
.await
}

Expand Down
28 changes: 17 additions & 11 deletions src/storage/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,22 +656,28 @@ impl Storage for MemoryStorage {
account_hash: &str,
file_path: &str,
filename: &str,
revision: i64,
) -> crate::storage::Result<Option<FileInfo>> {
let data = self.data.lock().await;

for file in data.files.values() {
// Check if account, path, filename, and revision match
if file.account_hash == account_hash
&& file.file_path == file_path
&& file.filename == filename
&& (revision == 0 || file.revision == revision)
{
return Ok(Some(file.clone()));
}
// Find all matching files and return the most recently updated one
let mut matching_files: Vec<&FileInfo> = data
.files
.values()
.filter(|file| {
file.account_hash == account_hash
&& file.file_path == file_path
&& file.filename == filename
})
.collect();

if matching_files.is_empty() {
return Ok(None);
}

Ok(None)
// Sort by updated_time descending (most recent first)
matching_files.sort_by(|a, b| b.updated_time.seconds.cmp(&a.updated_time.seconds));

Ok(matching_files.first().map(|&f| f.clone()))
}

/// Find file by path, filename, and group ID
Expand Down
1 change: 0 additions & 1 deletion src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,6 @@ pub trait Storage: Sync + Send {
account_hash: &str,
file_path: &str,
filename: &str,
revision: i64,
) -> Result<Option<FileInfo>>;
async fn find_file_by_criteria(
&self,
Expand Down
4 changes: 1 addition & 3 deletions src/storage/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1422,10 +1422,8 @@ impl Storage for MySqlStorage {
account_hash: &str,
file_path: &str,
filename: &str,
revision: i64,
) -> Result<Option<crate::models::file::FileInfo>> {
MySqlFileExt::find_file_by_path_and_name(self, account_hash, file_path, filename, revision)
.await
MySqlFileExt::find_file_by_path_and_name(self, account_hash, file_path, filename).await
}

/// Find file by criteria
Expand Down
132 changes: 59 additions & 73 deletions src/storage/mysql_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ pub trait MySqlFileExt {
account_hash: &str,
file_path: &str,
filename: &str,
revision: i64,
) -> Result<Option<FileInfo>>;

/// Search file by path, filename and group ID
Expand Down Expand Up @@ -161,41 +160,41 @@ impl MySqlFileExt for MySqlStorage {
return Ok(file_info.file_id);
}

debug!("🔍 Querying maximum revision...");
// Query maximum revision of all files (including deleted files) with the same path and filename
let max_revision: Option<i64> = sqlx::query_scalar(
r#"SELECT COALESCE(MAX(revision), 0) FROM files WHERE account_hash = ? AND file_path = ? AND filename = ? AND server_group_id = ?"#
debug!("🔍 Checking active files...");
// Check if there is a non-deleted file with the same file path and name
let existing_active_file: Option<(u64, i64)> = sqlx::query_as(
r#"SELECT file_id, revision FROM files WHERE account_hash = ? AND file_path = ? AND filename = ? AND server_group_id = ? AND is_deleted = FALSE ORDER BY updated_time DESC, revision DESC LIMIT 1"#
)
.bind(&file_info.account_hash)
.bind(&file_info.file_path)
.bind(&file_info.filename)
.bind(file_info.group_id)
.fetch_optional(&mut *tx)
.await
.map_err(|e| { error!("❌ Maximum revision query failed(sqlx): {}", e); StorageError::Database(format!("Maximum revision query failed: {}", e)) })?;
.map_err(|e| { error!("❌ Active file existence check failed(sqlx): {}", e); StorageError::Database(format!("Active file existence check failed: {}", e)) })?;

let new_revision = if let Some((existing_file_id, existing_revision)) = existing_active_file
{
// Active file exists → this is an update, increment revision
debug!(
"📝 Active file found (file_id={}, revision={}), incrementing revision",
existing_file_id, existing_revision
);
existing_revision + 1
} else {
// No active file → this is a new file (or re-upload after deletion), reset to revision 1
debug!("✨ No active file found, starting with revision 1");
1
};

let new_revision = max_revision.unwrap_or(0) + 1;
debug!(
"📄 Preparing to store new file information: file_id={}, revision={} (key_id: {:?})",
file_info.file_id, new_revision, file_info.key_id
);

// Path encryption and index computation omitted as it's handled elsewhere

debug!("🔍 Checking active files...");
// Check if there is a non-deleted file with the same file path and name
let existing_active_file: Option<u64> = sqlx::query_scalar(
r#"SELECT file_id FROM files WHERE account_hash = ? AND file_path = ? AND filename = ? AND server_group_id = ? AND is_deleted = FALSE LIMIT 1"#
)
.bind(&file_info.account_hash)
.bind(&file_info.file_path)
.bind(&file_info.filename)
.bind(file_info.group_id)
.fetch_optional(&mut *tx)
.await
.map_err(|e| { error!("❌ Active file existence check failed(sqlx): {}", e); StorageError::Database(format!("Active file existence check failed: {}", e)) })?;

if let Some(existing_file_id) = existing_active_file {
if let Some((existing_file_id, _)) = existing_active_file {
// If there is an existing active file, mark it as deleted
debug!(
"🗑️ Marking existing active file as deleted: existing_file_id={}",
Expand Down Expand Up @@ -621,12 +620,11 @@ impl MySqlFileExt for MySqlStorage {
account_hash: &str,
file_path: &str,
filename: &str,
revision: i64,
) -> Result<Option<FileInfo>> {
use sqlx::Row;
debug!(
"Searching file by path and filename: account_hash={}, file_path={}, filename={}, revision={}",
account_hash, file_path, filename, revision
"🔍 Searching file by path and filename (by updated_time): account_hash={}, file_path={}, filename={}",
account_hash, file_path, filename
);

// equality by eq_index
Expand All @@ -642,55 +640,38 @@ impl MySqlFileExt for MySqlStorage {
} else {
crate::utils::crypto::make_eq_index(account_hash.as_bytes(), file_path)
};
let row_opt = if revision > 0 {
sqlx::query(
r#"SELECT
file_id, account_hash, device_hash, file_path, filename, file_hash,
UNIX_TIMESTAMP(created_time) as created_ts,
UNIX_TIMESTAMP(updated_time) as updated_ts,
group_id, watcher_id, revision, size, key_id, unix_permissions,
is_deleted
FROM files
WHERE account_hash = ? AND eq_index = ? AND is_deleted = FALSE AND revision = ?
ORDER BY revision DESC LIMIT 1"#,
)
.bind(account_hash)
.bind(&eq_index)
.bind(revision)
.fetch_optional(self.get_sqlx_pool())
.await
.map_err(|e| {
StorageError::Database(format!("File search failed(exact search, sqlx): {}", e))
})?
} else {
sqlx::query(
r#"SELECT
file_id, account_hash, device_hash, file_path, filename, file_hash,
UNIX_TIMESTAMP(created_time) as created_ts,
UNIX_TIMESTAMP(updated_time) as updated_ts,
group_id, watcher_id, revision, size, key_id, unix_permissions,
is_deleted
FROM files
WHERE account_hash = ? AND eq_index = ? AND is_deleted = FALSE
ORDER BY revision DESC LIMIT 1"#,
)
.bind(account_hash)
.bind(&eq_index)
.fetch_optional(self.get_sqlx_pool())
.await
.map_err(|e| {
StorageError::Database(format!("File search failed(exact search, sqlx): {}", e))
})?
};

let row_opt = sqlx::query(
r#"SELECT
file_id, account_hash, device_hash, file_path, filename, file_hash,
UNIX_TIMESTAMP(created_time) as created_ts,
UNIX_TIMESTAMP(updated_time) as updated_ts,
group_id, watcher_id, revision, size, key_id, unix_permissions,
is_deleted
FROM files
WHERE account_hash = ? AND eq_index = ? AND is_deleted = FALSE
ORDER BY updated_time DESC, revision DESC, id DESC
LIMIT 1"#,
)
.bind(account_hash)
.bind(&eq_index)
.fetch_optional(self.get_sqlx_pool())
.await
.map_err(|e| {
StorageError::Database(format!("File search failed(by updated_time, sqlx): {}", e))
})?;

if let Some(row) = row_opt {
// Extract is_deleted first for validation
let is_deleted: bool = row.try_get("is_deleted").unwrap_or(false);
let file_id: u64 = row.try_get("file_id").unwrap_or(0);
let revision: i64 = row.try_get("revision").unwrap_or(0);

debug!("📊 find_file_by_path_and_name query returned: file_id={}, revision={}, is_deleted={}",
file_id, revision, is_deleted);
let updated_ts_value: Option<i64> = row.try_get("updated_ts").ok().flatten();
debug!(
"📊 find_file_by_path_and_name query returned: file_id={}, revision={}, updated_time={:?}, is_deleted={}",
file_id, revision, updated_ts_value, is_deleted
);

// Critical validation: Double-check is_deleted
if is_deleted {
Expand Down Expand Up @@ -767,7 +748,7 @@ impl MySqlFileExt for MySqlStorage {
// Check if file exists and belongs to the user
// Use Vec<u8> for VARBINARY columns (file_path, filename)
let row_opt = sqlx::query(
r#"SELECT file_id, revision, file_path, filename, device_hash, group_id, watcher_id
r#"SELECT file_id, revision, file_path, filename, device_hash, group_id, watcher_id, server_group_id, server_watcher_id
FROM files WHERE file_id = ? AND account_hash = ?"#,
)
.bind(file_id)
Expand Down Expand Up @@ -800,6 +781,8 @@ impl MySqlFileExt for MySqlStorage {
let device_hash: String = row.try_get("device_hash").unwrap_or_default();
let group_id: i32 = row.try_get("group_id").unwrap_or(0);
let watcher_id: i32 = row.try_get("watcher_id").unwrap_or(0);
let server_group_id: i32 = row.try_get("server_group_id").unwrap_or(0);
let server_watcher_id: i32 = row.try_get("server_watcher_id").unwrap_or(0);

let new_revision = current_revision + 1;

Expand Down Expand Up @@ -859,8 +842,8 @@ impl MySqlFileExt for MySqlStorage {
// Use Vec<u8> for VARBINARY columns to avoid type mismatch
sqlx::query(
r#"INSERT INTO files
(file_id, account_hash, device_hash, file_path, filename, file_hash, size, unix_permissions)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)"#,
(file_id, account_hash, device_hash, file_path, filename, file_hash, size, unix_permissions, server_group_id, server_watcher_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"#,
)
.bind(new_file_id)
.bind(account_hash)
Expand All @@ -870,6 +853,8 @@ impl MySqlFileExt for MySqlStorage {
.bind(&file_path_bytes) // file_hash uses file_path_bytes
.bind(0i64)
.bind(None::<u32>)
.bind(server_group_id)
.bind(server_watcher_id)
.execute(&mut *tx)
.await
.map_err(|e| StorageError::Database(format!("Deletion history addition failed (step 1, schema/VARBINARY type mismatch): {}", e)))?;
Expand Down Expand Up @@ -1221,7 +1206,7 @@ impl MySqlFileExt for MySqlStorage {
};

debug!(
"🔍 Final search criteria: path='{}', filename='{}'",
"🔍 Final search criteria (by updated_time): path='{}', filename='{}'",
search_path, search_filename
);

Expand All @@ -1239,7 +1224,7 @@ impl MySqlFileExt for MySqlStorage {
(file_path = ? AND filename = ?) OR
(file_path = ? AND filename = ?)
)
ORDER BY revision DESC
ORDER BY updated_time DESC, revision DESC, id DESC
LIMIT 1"#
)
.bind(account_hash)
Expand All @@ -1259,10 +1244,11 @@ impl MySqlFileExt for MySqlStorage {
let file_id: u64 = row.try_get("file_id").unwrap_or(0);
let revision: i64 = row.try_get("revision").unwrap_or(0);

let updated_ts_val: Option<i64> = row.try_get("updated_ts").ok().flatten();
debug!("✅ File found!");
debug!(
"📊 Query returned: file_id={}, revision={}, is_deleted={}",
file_id, revision, is_deleted
"📊 Query returned: file_id={}, revision={}, updated_time={:?}, is_deleted={}",
file_id, revision, updated_ts_val, is_deleted
);

// Critical validation: Double-check is_deleted
Expand Down