mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-14 08:41:21 +10:00
feat(recovery): Added database recovery
Signed-off-by: quexeky <git@quexeky.dev>
This commit is contained in:
15
app.vue
15
app.vue
@ -29,6 +29,21 @@ router.beforeEach(async () => {
|
|||||||
setupHooks();
|
setupHooks();
|
||||||
initialNavigation(state);
|
initialNavigation(state);
|
||||||
|
|
||||||
|
listen("database_corrupted", (event) => {
|
||||||
|
createModal(
|
||||||
|
ModalType.Notification,
|
||||||
|
{
|
||||||
|
title: "Database corrupted",
|
||||||
|
description: `Drop encountered an error while reading your download. A copy can be found at: "${(
|
||||||
|
event.payload as unknown as string
|
||||||
|
).toString()}"`,
|
||||||
|
buttonText: "Close"
|
||||||
|
},
|
||||||
|
(e, c) => c()
|
||||||
|
);
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: "Drop",
|
title: "Drop",
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,12 +2,13 @@ use std::{
|
|||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fs::{self, create_dir_all},
|
fs::{self, create_dir_all},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::{Arc, LazyLock, Mutex, RwLockWriteGuard},
|
sync::{Arc, LazyLock, Mutex, RwLockWriteGuard}, time::{Instant, SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use chrono::Utc;
|
||||||
use directories::BaseDirs;
|
use directories::BaseDirs;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use rustbreak::{DeSerError, DeSerializer, PathDatabase};
|
use rustbreak::{DeSerError, DeSerializer, PathDatabase, RustbreakError};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
use serde_with::serde_as;
|
use serde_with::serde_as;
|
||||||
use tauri::AppHandle;
|
use tauri::AppHandle;
|
||||||
@ -92,6 +93,7 @@ pub struct Database {
|
|||||||
pub auth: Option<DatabaseAuth>,
|
pub auth: Option<DatabaseAuth>,
|
||||||
pub base_url: String,
|
pub base_url: String,
|
||||||
pub applications: DatabaseApplications,
|
pub applications: DatabaseApplications,
|
||||||
|
pub prev_database: Option<PathBuf>
|
||||||
}
|
}
|
||||||
pub static DATA_ROOT_DIR: LazyLock<Mutex<PathBuf>> =
|
pub static DATA_ROOT_DIR: LazyLock<Mutex<PathBuf>> =
|
||||||
LazyLock::new(|| Mutex::new(BaseDirs::new().unwrap().data_dir().join("drop")));
|
LazyLock::new(|| Mutex::new(BaseDirs::new().unwrap().data_dir().join("drop")));
|
||||||
@ -135,7 +137,11 @@ impl DatabaseImpls for DatabaseInterface {
|
|||||||
let exists = fs::exists(db_path.clone()).unwrap();
|
let exists = fs::exists(db_path.clone()).unwrap();
|
||||||
|
|
||||||
match exists {
|
match exists {
|
||||||
true => PathDatabase::load_from_path(db_path).expect("Database loading failed"),
|
true =>
|
||||||
|
match PathDatabase::load_from_path(db_path.clone()) {
|
||||||
|
Ok(db) => db,
|
||||||
|
Err(e) => handle_invalid_database(e, db_path, games_base_dir),
|
||||||
|
},
|
||||||
false => {
|
false => {
|
||||||
let default = Database {
|
let default = Database {
|
||||||
settings: Settings::default(),
|
settings: Settings::default(),
|
||||||
@ -148,6 +154,7 @@ impl DatabaseImpls for DatabaseInterface {
|
|||||||
game_versions: HashMap::new(),
|
game_versions: HashMap::new(),
|
||||||
installed_game_version: HashMap::new(),
|
installed_game_version: HashMap::new(),
|
||||||
},
|
},
|
||||||
|
prev_database: None,
|
||||||
};
|
};
|
||||||
debug!(
|
debug!(
|
||||||
"Creating database at path {}",
|
"Creating database at path {}",
|
||||||
@ -238,3 +245,32 @@ pub fn set_game_status<F: FnOnce(&mut RwLockWriteGuard<'_, Database>, &Downloada
|
|||||||
|
|
||||||
push_game_update(app_handle, &meta, status);
|
push_game_update(app_handle, &meta, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Make the error relelvant rather than just assume that it's a Deserialize error
|
||||||
|
fn handle_invalid_database(_e: RustbreakError, db_path: PathBuf, games_base_dir: PathBuf) -> rustbreak::Database<Database, rustbreak::backend::PathBackend, DropDatabaseSerializer> {
|
||||||
|
let new_path = {
|
||||||
|
let time = Utc::now().timestamp();
|
||||||
|
let mut base = db_path.clone().into_os_string();
|
||||||
|
base.push(time.to_string());
|
||||||
|
base
|
||||||
|
};
|
||||||
|
fs::copy(&db_path, &new_path);
|
||||||
|
|
||||||
|
let db = Database {
|
||||||
|
auth: None,
|
||||||
|
base_url: "".to_string(),
|
||||||
|
applications: DatabaseApplications {
|
||||||
|
install_dirs: vec![games_base_dir.to_str().unwrap().to_string()],
|
||||||
|
game_statuses: HashMap::new(),
|
||||||
|
transient_statuses: HashMap::new(),
|
||||||
|
game_versions: HashMap::new(),
|
||||||
|
installed_game_version: HashMap::new(),
|
||||||
|
},
|
||||||
|
prev_database: Some(new_path.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
PathDatabase::create_at_path(db_path, db)
|
||||||
|
.expect("Database could not be created")
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -50,7 +50,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
use tauri::menu::{Menu, MenuItem, PredefinedMenuItem};
|
use tauri::menu::{Menu, MenuItem, PredefinedMenuItem};
|
||||||
use tauri::tray::TrayIconBuilder;
|
use tauri::tray::TrayIconBuilder;
|
||||||
use tauri::{AppHandle, Manager, RunEvent, WindowEvent};
|
use tauri::{AppHandle, Emitter, Manager, RunEvent, WindowEvent};
|
||||||
use tauri_plugin_deep_link::DeepLinkExt;
|
use tauri_plugin_deep_link::DeepLinkExt;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Serialize)]
|
#[derive(Clone, Copy, Serialize)]
|
||||||
@ -181,7 +181,14 @@ fn setup(handle: AppHandle) -> AppState<'static> {
|
|||||||
.entry(game_id)
|
.entry(game_id)
|
||||||
.and_modify(|v| *v = GameDownloadStatus::Remote {});
|
.and_modify(|v| *v = GameDownloadStatus::Remote {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(original) = db_handle.prev_database.take() {
|
||||||
|
warn!("Database corrupted. Original file at {}", original.canonicalize().unwrap().to_string_lossy().to_string());
|
||||||
|
handle.emit("database_corrupted", original.to_string_lossy().to_string()).unwrap();
|
||||||
|
}
|
||||||
drop(db_handle);
|
drop(db_handle);
|
||||||
|
|
||||||
|
|
||||||
info!("finished setup!");
|
info!("finished setup!");
|
||||||
|
|
||||||
// Sync autostart state
|
// Sync autostart state
|
||||||
|
|||||||
Reference in New Issue
Block a user