AkurAI Build
Menu

Public repository / master

AkurAI-Drive / desktop/src-tauri/src/main.rs

Latest change 76b668ab797c5342c1cccf4955c8f1943cba1bb7 - OLI-13: pin toolchain 1.98.1 at repo root; desktop: fmt, clippy, plist/quick-xml advisory fix, first unit tests by AkurAI Agent

//! AkurAI Drive desktop — a KDE-tray, Google-Drive-style file browser that reuses
//! the `akurai-drive-sync` library for auth, the API client, and bidirectional
//! sync. The window lives in the tray (starts hidden; closing hides it), a single
//! background task runs the debounced sync loop, and everything is logged to both
//! stdout and `~/.local/state/akurai-drive/desktop.log` for screenshot-free
//! diagnosis.
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

mod commands;
mod logging;
mod sync;
mod tray;

use std::sync::Arc;

use crate::sync::SyncManager;
use akurai_drive_sync::config::Config;

fn main() {
    // Logging first, so even startup + config resolution is captured.
    logging::init();
    tracing::info!(
        version = env!("CARGO_PKG_VERSION"),
        log = %logging::log_path().display(),
        "AkurAI Drive desktop starting"
    );

    let manager = Arc::new(SyncManager::new());

    let result = tauri::Builder::default()
        // single-instance MUST be registered first.
        .plugin(tauri_plugin_single_instance::init(|app, argv, _cwd| {
            tracing::info!(?argv, "second instance launched; focusing existing window");
            tray::show_window(app);
        }))
        .plugin(tauri_plugin_opener::init())
        .plugin(tauri_plugin_dialog::init())
        .manage(manager.clone())
        .invoke_handler(tauri::generate_handler![
            commands::login,
            commands::login_password,
            commands::me,
            commands::sign_out,
            commands::buckets,
            commands::set_target,
            commands::list,
            commands::pick_files,
            commands::upload,
            commands::download,
            commands::delete,
            commands::rename,
            commands::mkdir,
            commands::sync_now,
            commands::status,
            commands::open_folder,
            commands::log_frontend,
        ])
        .on_window_event(|window, event| {
            if let tauri::WindowEvent::CloseRequested { api, .. } = event {
                if window.label() == "main" {
                    tracing::info!("close requested → hiding window to tray");
                    if let Err(e) = window.hide() {
                        tracing::error!(error = %e, "hide-to-tray failed");
                    }
                    api.prevent_close();
                }
            }
        })
        .setup(move |app| {
            let handle = app.handle().clone();

            // Log resolved config at startup (never the token itself).
            let cfg = Config::load().unwrap_or_default();
            tracing::info!(
                api = %cfg.resolved_api(),
                folder = cfg.folder.as_deref().unwrap_or("(unset)"),
                bucket = cfg.bucket.as_deref().unwrap_or("(unset)"),
                logged_in = cfg.is_logged_in(),
                "resolved config"
            );

            tray::build_tray(&handle)?;
            sync::spawn_sync_loop(handle.clone(), manager.clone());
            Ok(())
        })
        .run(tauri::generate_context!());

    if let Err(e) = result {
        tracing::error!(error = %e, "fatal: tauri run failed");
        std::process::exit(1);
    }
}