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();
|
||||
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({
|
||||
title: "Drop",
|
||||
});
|
||||
|
||||
@ -2,12 +2,13 @@ use std::{
|
||||
collections::HashMap,
|
||||
fs::{self, create_dir_all},
|
||||
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 log::debug;
|
||||
use rustbreak::{DeSerError, DeSerializer, PathDatabase};
|
||||
use rustbreak::{DeSerError, DeSerializer, PathDatabase, RustbreakError};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
use tauri::AppHandle;
|
||||
@ -92,6 +93,7 @@ pub struct Database {
|
||||
pub auth: Option<DatabaseAuth>,
|
||||
pub base_url: String,
|
||||
pub applications: DatabaseApplications,
|
||||
pub prev_database: Option<PathBuf>
|
||||
}
|
||||
pub static DATA_ROOT_DIR: LazyLock<Mutex<PathBuf>> =
|
||||
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();
|
||||
|
||||
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 => {
|
||||
let default = Database {
|
||||
settings: Settings::default(),
|
||||
@ -148,6 +154,7 @@ impl DatabaseImpls for DatabaseInterface {
|
||||
game_versions: HashMap::new(),
|
||||
installed_game_version: HashMap::new(),
|
||||
},
|
||||
prev_database: None,
|
||||
};
|
||||
debug!(
|
||||
"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);
|
||||
}
|
||||
|
||||
// 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::tray::TrayIconBuilder;
|
||||
use tauri::{AppHandle, Manager, RunEvent, WindowEvent};
|
||||
use tauri::{AppHandle, Emitter, Manager, RunEvent, WindowEvent};
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
|
||||
#[derive(Clone, Copy, Serialize)]
|
||||
@ -181,7 +181,14 @@ fn setup(handle: AppHandle) -> AppState<'static> {
|
||||
.entry(game_id)
|
||||
.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);
|
||||
|
||||
|
||||
info!("finished setup!");
|
||||
|
||||
// Sync autostart state
|
||||
|
||||
Reference in New Issue
Block a user