Browse Source

Add option to specify backup path

pull/6590/head
Lukas Kirner 2 weeks ago
parent
commit
7c86e28619
No known key found for this signature in database GPG Key ID: AFB2E7B475498BB1
  1. 2
      src/api/admin.rs
  2. 26
      src/db/mod.rs
  3. 8
      src/main.rs

2
src/api/admin.rs

@ -790,7 +790,7 @@ async fn delete_config(_token: AdminToken) -> EmptyResult {
#[post("/config/backup_db", format = "application/json")]
fn backup_db(_token: AdminToken) -> ApiResult<String> {
if *CAN_BACKUP {
match backup_sqlite() {
match backup_sqlite(None) {
Ok(f) => Ok(format!("Backup to '{f}' was successful")),
Err(e) => err!(format!("Backup was unsuccessful {e}")),
}

26
src/db/mod.rs

@ -384,8 +384,13 @@ pub mod models;
/// Creates a back-up of the sqlite database
/// MySQL/MariaDB and PostgreSQL are not supported.
///
/// # Arguments
///
/// * `backup_dir` - Optional custom directory path where the backup file will be created.
/// If `None`, the backup will be created in the same directory as the database file.
#[cfg(sqlite)]
pub fn backup_sqlite() -> Result<String, Error> {
pub fn backup_sqlite(backup_dir: Option<String>) -> Result<String, Error> {
use diesel::Connection;
use std::{fs::File, io::Write};
@ -395,8 +400,21 @@ pub fn backup_sqlite() -> Result<String, Error> {
// This way we can set a readonly flag on the opening mode without issues.
let mut conn = diesel::sqlite::SqliteConnection::establish(&format!("sqlite://{db_url}?mode=ro"))?;
let db_path = std::path::Path::new(&db_url).parent().unwrap();
let backup_file = db_path
let backup_path = match backup_dir {
Some(dir) => {
let path = std::path::Path::new(dir.as_str()).to_path_buf();
// Ensure the backup directory exists
if let Err(e) = std::fs::create_dir_all(&path) {
err_silent!(format!("Unable to create backup directory: {e:?}"))
}
path
},
None => std::path::Path::new(&db_url).parent().unwrap().to_path_buf(),
};
let backup_file = backup_path
.join(format!("db_{}.sqlite3", chrono::Utc::now().format("%Y%m%d_%H%M%S")))
.to_string_lossy()
.into_owned();
@ -417,7 +435,7 @@ pub fn backup_sqlite() -> Result<String, Error> {
}
#[cfg(not(sqlite))]
pub fn backup_sqlite() -> Result<String, Error> {
pub fn backup_sqlite(_backup_dir: Option<&str>) -> Result<String, Error> {
err_silent!("The database type is not SQLite. Backups only works for SQLite databases")
}

8
src/main.rs

@ -105,7 +105,7 @@ FLAGS:
COMMAND:
hash [--preset {bitwarden|owasp}] Generate an Argon2id PHC ADMIN_TOKEN
backup Create a backup of the SQLite database
backup [--path] Create a backup of the SQLite database
You can also send the USR1 signal to trigger a backup
PRESETS: m= t= p=
@ -187,7 +187,9 @@ fn parse_args() {
exit(1);
}
} else if command == "backup" {
match db::backup_sqlite() {
let backup_path: Option<String> = pargs.opt_value_from_str(["-p", "--path"]).unwrap_or_default();
match db::backup_sqlite(backup_path) {
Ok(f) => {
println!("Backup to '{f}' was successful");
exit(0);
@ -606,7 +608,7 @@ async fn launch_rocket(pool: db::DbPool, extra_debug: bool) -> Result<(), Error>
// If we need more signals to act upon, we might want to use select! here.
// With only one item to listen for this is enough.
let _ = signal_user1.recv().await;
match db::backup_sqlite() {
match db::backup_sqlite(None) {
Ok(f) => info!("Backup to '{f}' was successful"),
Err(e) => error!("Backup failed. {e:?}"),
}

Loading…
Cancel
Save