Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| edaf98b10d | |||
| 292b91d612 | |||
| c8683f3d72 | |||
| 2cfb6fa0f0 | |||
| 622a6ecf79 | |||
| 43db437ac7 | |||
| 49b2a2f592 | |||
| b73c457c34 | |||
| 4920211eec | |||
| d0f3984d32 | |||
| 5f3b7f892b | |||
| 8f40262dff | |||
| 5aff13e419 | |||
| b120daa9c7 | |||
| ffabc4c2ed | |||
| ad7c850ff0 | |||
| bb055a5503 | |||
| fc1904be60 | |||
| 0842f5ed61 | |||
| f2cf2b3466 | |||
| e73ea22e23 | |||
| 7ad8903393 | |||
| dd8790c5d1 | |||
| a187316a1a | |||
| f431cea768 | |||
| 9fa48f1bac | |||
| baa81243d3 | |||
| 475a23f3dd | |||
| f06483194c | |||
| 8c5ee314d8 | |||
| db83dc3224 | |||
| cdeab85cf6 | |||
| 5b3c95ae26 | |||
| d05a0e39e0 | |||
| 0b6db27ac2 | |||
| 45e1c57842 | |||
| a515ee6b4f | |||
| e52397f64b | |||
| 1abe783dfc | |||
| 8e95bbaabd | |||
| 6208dab2b7 | |||
| 619b3cb447 | |||
| 84e33d06c8 | |||
| 325cbdd87f | |||
| e3433edb68 | |||
| 2aebccd914 | |||
| bee43b29c1 | |||
| fcd57a372d | |||
| ddde786c41 | |||
| 8c57a06a79 | |||
| 7ec32ed077 | |||
| c7bbc26822 | |||
| 3ab12b0b8f | |||
| b717689ace | |||
| 6e97feddde | |||
| 011c07820d | |||
| 804ba35a70 | |||
| ce11431cdf | |||
| cddbe5b2e2 | |||
| 668206e5e5 | |||
| b4f2d1bf0c | |||
| 11c754a8c1 | |||
| e20759d1cb | |||
| fb632c16c6 | |||
| a755e546d0 | |||
| 87f4e5d7bf | |||
| 4211ff5ce0 | |||
| a1c7f72d1d | |||
| d6a6fb9eca | |||
| b0852f781c | |||
| 9df79095f2 | |||
| 0c9f2caa2c | |||
| bea039367d | |||
| cd76d268e2 | |||
| ea6904040a | |||
| ec413f4c6f | |||
| 1a177abb75 | |||
| 6aafaeaa09 | |||
| 67e01c45be | |||
| d1628cf6d5 | |||
| 647fc39054 | |||
| 8999fb99b3 | |||
| cc78b44d41 | |||
| 2f5e5cefc1 | |||
| c3620b487d |
+19
-24
@@ -29,7 +29,6 @@ pub struct AppImageRecord {
|
||||
pub first_seen: String,
|
||||
pub last_scanned: String,
|
||||
pub file_modified: Option<String>,
|
||||
// Phase 2 fields
|
||||
pub fuse_status: Option<String>,
|
||||
pub wayland_status: Option<String>,
|
||||
pub update_info: Option<String>,
|
||||
@@ -38,20 +37,17 @@ pub struct AppImageRecord {
|
||||
pub update_checked: Option<String>,
|
||||
pub update_url: Option<String>,
|
||||
pub notes: Option<String>,
|
||||
// Phase 3 fields
|
||||
pub sandbox_mode: Option<String>,
|
||||
// Phase 5 fields
|
||||
pub runtime_wayland_status: Option<String>,
|
||||
pub runtime_wayland_checked: Option<String>,
|
||||
// Async analysis pipeline
|
||||
pub analysis_status: Option<String>,
|
||||
// Custom launch arguments
|
||||
pub launch_args: Option<String>,
|
||||
// Phase 6 fields
|
||||
pub tags: Option<String>,
|
||||
pub pinned: bool,
|
||||
pub avg_startup_ms: Option<i64>,
|
||||
// Phase 9 fields - extended metadata
|
||||
// extended metadata
|
||||
pub appstream_id: Option<String>,
|
||||
pub appstream_description: Option<String>,
|
||||
pub generic_name: Option<String>,
|
||||
@@ -69,7 +65,7 @@ pub struct AppImageRecord {
|
||||
pub desktop_actions: Option<String>,
|
||||
pub has_signature: bool,
|
||||
pub screenshot_urls: Option<String>,
|
||||
// Phase 11 fields - system modification tracking
|
||||
// system modification tracking
|
||||
pub previous_version_path: Option<String>,
|
||||
pub source_url: Option<String>,
|
||||
pub autostart: bool,
|
||||
@@ -346,7 +342,7 @@ impl Database {
|
||||
}
|
||||
|
||||
fn init_schema(&self) -> SqlResult<()> {
|
||||
// Phase 1 base tables
|
||||
// base tables
|
||||
self.conn.execute_batch(
|
||||
"CREATE TABLE IF NOT EXISTS schema_version (
|
||||
version INTEGER NOT NULL
|
||||
@@ -535,8 +531,8 @@ impl Database {
|
||||
}
|
||||
|
||||
fn migrate_to_v2(&self) -> SqlResult<()> {
|
||||
// Add Phase 2 columns to appimages table
|
||||
let phase2_columns = [
|
||||
// Add columns to appimages table
|
||||
let extra_columns = [
|
||||
"fuse_status TEXT",
|
||||
"wayland_status TEXT",
|
||||
"update_info TEXT",
|
||||
@@ -546,12 +542,11 @@ impl Database {
|
||||
"update_url TEXT",
|
||||
"notes TEXT",
|
||||
];
|
||||
for col in &phase2_columns {
|
||||
for col in &extra_columns {
|
||||
let sql = format!("ALTER TABLE appimages ADD COLUMN {}", col);
|
||||
self.conn.execute_batch(&sql).ok();
|
||||
}
|
||||
|
||||
// Phase 2 tables
|
||||
self.conn.execute_batch(
|
||||
"CREATE TABLE IF NOT EXISTS launch_events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -595,7 +590,7 @@ impl Database {
|
||||
}
|
||||
|
||||
fn migrate_to_v3(&self) -> SqlResult<()> {
|
||||
// Phase 3 tables: security scanning
|
||||
// security scanning
|
||||
self.conn.execute_batch(
|
||||
"CREATE TABLE IF NOT EXISTS bundled_libraries (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -1712,7 +1707,7 @@ impl Database {
|
||||
self.conn.query_row("SELECT COUNT(*) FROM appimages", [], |row| row.get(0))
|
||||
}
|
||||
|
||||
// --- Phase 2: Status updates ---
|
||||
// --- Status updates ---
|
||||
|
||||
pub fn update_fuse_status(&self, id: i64, status: &str) -> SqlResult<()> {
|
||||
self.conn.execute(
|
||||
@@ -1811,7 +1806,7 @@ impl Database {
|
||||
rows.collect()
|
||||
}
|
||||
|
||||
// --- Phase 2: Launch tracking ---
|
||||
// --- Launch tracking ---
|
||||
|
||||
pub fn record_launch(&self, appimage_id: i64, source: &str) -> SqlResult<()> {
|
||||
self.conn.execute(
|
||||
@@ -1854,7 +1849,7 @@ impl Database {
|
||||
rows.collect()
|
||||
}
|
||||
|
||||
// --- Phase 2: Update history ---
|
||||
// --- Update history ---
|
||||
|
||||
pub fn record_update(
|
||||
&self,
|
||||
@@ -1874,7 +1869,7 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// --- Phase 3: Security scanning ---
|
||||
// --- Security scanning ---
|
||||
|
||||
pub fn clear_bundled_libraries(&self, appimage_id: i64) -> SqlResult<()> {
|
||||
self.conn.execute(
|
||||
@@ -2026,7 +2021,7 @@ impl Database {
|
||||
Ok(summary)
|
||||
}
|
||||
|
||||
// --- Phase 3: App data paths ---
|
||||
// --- App data paths ---
|
||||
|
||||
pub fn insert_app_data_path(
|
||||
&self,
|
||||
@@ -2106,7 +2101,7 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// --- Phase 5: Runtime Wayland ---
|
||||
// --- Runtime Wayland ---
|
||||
|
||||
pub fn update_runtime_wayland_status(&self, id: i64, status: &str) -> SqlResult<()> {
|
||||
self.conn.execute(
|
||||
@@ -2116,7 +2111,7 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// --- Phase 5: Config Backups ---
|
||||
// --- Config Backups ---
|
||||
|
||||
pub fn insert_config_backup(
|
||||
&self,
|
||||
@@ -2186,7 +2181,7 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// --- Phase 5: CVE Notifications ---
|
||||
// --- CVE Notifications ---
|
||||
|
||||
pub fn has_cve_been_notified(&self, appimage_id: i64, cve_id: &str) -> SqlResult<bool> {
|
||||
let count: i32 = self.conn.query_row(
|
||||
@@ -2211,7 +2206,7 @@ impl Database {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// --- Phase 5: Sandbox Profiles ---
|
||||
// --- Sandbox Profiles ---
|
||||
|
||||
pub fn insert_sandbox_profile(
|
||||
&self,
|
||||
@@ -2281,7 +2276,7 @@ impl Database {
|
||||
rows.collect()
|
||||
}
|
||||
|
||||
// --- Phase 6: Tags, Pin, Startup Time ---
|
||||
// --- Tags, Pin, Startup Time ---
|
||||
|
||||
pub fn update_tags(&self, id: i64, tags: Option<&str>) -> SqlResult<()> {
|
||||
self.conn.execute(
|
||||
@@ -2355,7 +2350,7 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Phase 5: Runtime Updates ---
|
||||
// --- Runtime Updates ---
|
||||
|
||||
pub fn record_runtime_update(
|
||||
&self,
|
||||
@@ -3259,7 +3254,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_phase2_status_updates() {
|
||||
fn test_status_updates() {
|
||||
let db = Database::open_in_memory().unwrap();
|
||||
let id = db.upsert_appimage("/path/app.AppImage", "app.AppImage", Some(2), 1000, true, None).unwrap();
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ pub fn detect_duplicates(db: &Database) -> Vec<DuplicateGroup> {
|
||||
|
||||
let mut groups = Vec::new();
|
||||
|
||||
// Phase 1: Find exact duplicates by SHA256 hash
|
||||
// Find exact duplicates by SHA256 hash
|
||||
let hash_groups = group_by_hash(&records);
|
||||
for (hash, members) in &hash_groups {
|
||||
if members.len() > 1 {
|
||||
@@ -95,7 +95,7 @@ pub fn detect_duplicates(db: &Database) -> Vec<DuplicateGroup> {
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: Find same app name groups (excluding already-found exact dupes)
|
||||
// Find same app name groups (excluding already-found exact dupes)
|
||||
let exact_dupe_ids: std::collections::HashSet<i64> = groups
|
||||
.iter()
|
||||
.flat_map(|g| g.members.iter().map(|m| m.record.id))
|
||||
|
||||
@@ -117,7 +117,7 @@ pub fn show_drop_dialog(
|
||||
let on_complete_ref = on_complete.clone();
|
||||
|
||||
glib::spawn_future_local(async move {
|
||||
// Phase 1: Fast registration (copy + DB upsert only)
|
||||
// Fast registration (copy + DB upsert only)
|
||||
let result = gio::spawn_blocking(move || {
|
||||
register_dropped_files(&files, copy)
|
||||
})
|
||||
@@ -143,7 +143,7 @@ pub fn show_drop_dialog(
|
||||
toast_ref.add_toast(widgets::info_toast(&msg));
|
||||
}
|
||||
|
||||
// Phase 2: Background analysis for each file
|
||||
// Background analysis for each file
|
||||
let on_complete_bg = on_complete_ref.clone();
|
||||
for reg in registered {
|
||||
let on_complete_inner = on_complete_bg.clone();
|
||||
|
||||
+7
-7
@@ -1571,11 +1571,11 @@ impl DriftwoodWindow {
|
||||
let toast_overlay = self.imp().toast_overlay.get().unwrap().clone();
|
||||
let window_weak = self.downgrade();
|
||||
|
||||
// Two-phase scan:
|
||||
// Phase 1 (fast): discover files, upsert into DB, mark pending analysis
|
||||
// Phase 2 (background): heavy analysis per file
|
||||
// Two-pass scan:
|
||||
// Fast pass: discover files, upsert into DB, mark pending analysis
|
||||
// Background pass: heavy analysis per file
|
||||
glib::spawn_future_local(async move {
|
||||
// Phase 1: Fast registration
|
||||
// Fast registration
|
||||
let result = gio::spawn_blocking(move || {
|
||||
let bg_db = Database::open().expect("Failed to open database for scan");
|
||||
let start = Instant::now();
|
||||
@@ -1646,7 +1646,7 @@ impl DriftwoodWindow {
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"Scan phase 1: {} files, {} new, {} removed, {} skipped (unchanged), took {}ms",
|
||||
"Scan: {} files, {} new, {} removed, {} skipped (unchanged), took {}ms",
|
||||
total, new_count, removed_count, skipped_count, start.elapsed().as_millis()
|
||||
);
|
||||
|
||||
@@ -1665,7 +1665,7 @@ impl DriftwoodWindow {
|
||||
.await;
|
||||
|
||||
if let Ok((total, new_count, needs_analysis)) = result {
|
||||
// Dismiss the "Scanning..." toast now that Phase 1 is done
|
||||
// Dismiss the "Scanning..." toast now that fast scan is done
|
||||
if let Some(ref toast) = scan_toast {
|
||||
toast.dismiss();
|
||||
}
|
||||
@@ -1689,7 +1689,7 @@ impl DriftwoodWindow {
|
||||
};
|
||||
toast_overlay.add_toast(widgets::info_toast(&msg));
|
||||
|
||||
// Phase 2: Background analysis per file with debounced UI refresh
|
||||
// Background analysis per file with debounced UI refresh
|
||||
let running = analysis::running_count();
|
||||
if running > 0 {
|
||||
log::info!("Analyzing {} AppImage(s) in background ({} already running)", needs_analysis.len(), running);
|
||||
|
||||
Reference in New Issue
Block a user