Browse Source

Merge 5da1d40994 into 7cf0c5d67e

pull/7061/merge
mfw78 1 day ago
committed by GitHub
parent
commit
dd16de14e3
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 9
      .env.template
  2. 21
      src/config.rs
  3. 34
      src/db/mod.rs

9
.env.template

@ -50,10 +50,11 @@
######################### #########################
## Database URL ## Database URL
## When using SQLite, this is the path to the DB file, and it defaults to ## When using SQLite, this should use the sqlite:// scheme followed by the path
## %DATA_FOLDER%/db.sqlite3. If DATA_FOLDER is set to an external location, this ## to the DB file. It defaults to sqlite://%DATA_FOLDER%/db.sqlite3.
## must be set to a local sqlite3 file path. ## Bare paths without the sqlite:// scheme are supported for backwards compatibility,
# DATABASE_URL=data/db.sqlite3 ## but only if the database file already exists.
# DATABASE_URL=sqlite://data/db.sqlite3
## When using MySQL, specify an appropriate connection URI. ## When using MySQL, specify an appropriate connection URI.
## Details: https://docs.diesel.rs/2.1.x/diesel/mysql/struct.MysqlConnection.html ## Details: https://docs.diesel.rs/2.1.x/diesel/mysql/struct.MysqlConnection.html
# DATABASE_URL=mysql://user:password@host[:port]/database_name # DATABASE_URL=mysql://user:password@host[:port]/database_name

21
src/config.rs

@ -507,7 +507,7 @@ make_config! {
/// Data folder |> Main data folder /// Data folder |> Main data folder
data_folder: String, false, def, "data".to_string(); data_folder: String, false, def, "data".to_string();
/// Database URL /// Database URL
database_url: String, false, auto, |c| format!("{}/db.sqlite3", c.data_folder); database_url: String, false, auto, |c| format!("sqlite://{}/db.sqlite3", c.data_folder);
/// Icon cache folder /// Icon cache folder
icon_cache_folder: String, false, auto, |c| format!("{}/icon_cache", c.data_folder); icon_cache_folder: String, false, auto, |c| format!("{}/icon_cache", c.data_folder);
/// Attachments folder /// Attachments folder
@ -929,14 +929,17 @@ fn validate_config(cfg: &ConfigItems, on_update: bool) -> Result<(), Error> {
{ {
use crate::db::DbConnType; use crate::db::DbConnType;
let url = &cfg.database_url; let url = &cfg.database_url;
if DbConnType::from_url(url)? == DbConnType::Sqlite && url.contains('/') { if DbConnType::from_url(url)? == DbConnType::Sqlite {
let path = std::path::Path::new(&url); let file_path = url.strip_prefix("sqlite://").unwrap_or(url);
if let Some(parent) = path.parent() { if file_path.contains('/') {
if !parent.is_dir() { let path = std::path::Path::new(file_path);
err!(format!( if let Some(parent) = path.parent() {
"SQLite database directory `{}` does not exist or is not a directory", if !parent.is_dir() {
parent.display() err!(format!(
)); "SQLite database directory `{}` does not exist or is not a directory",
parent.display()
));
}
} }
} }
} }

34
src/db/mod.rs

@ -272,13 +272,32 @@ impl DbConnType {
#[cfg(not(postgresql))] #[cfg(not(postgresql))]
err!("`DATABASE_URL` is a PostgreSQL URL, but the 'postgresql' feature is not enabled") err!("`DATABASE_URL` is a PostgreSQL URL, but the 'postgresql' feature is not enabled")
//Sqlite // Sqlite (explicit)
} else { } else if url.len() > 7 && &url[..7] == "sqlite:" {
#[cfg(sqlite)] #[cfg(sqlite)]
return Ok(DbConnType::Sqlite); return Ok(DbConnType::Sqlite);
#[cfg(not(sqlite))] #[cfg(not(sqlite))]
err!("`DATABASE_URL` looks like a SQLite URL, but 'sqlite' feature is not enabled") err!("`DATABASE_URL` is a SQLite URL, but the 'sqlite' feature is not enabled")
// No recognized scheme — assume legacy bare-path SQLite, but the database file must already exist.
// This prevents misconfigured URLs (typos, quoted strings) from silently creating a new empty SQLite database.
} else {
#[cfg(sqlite)]
{
if std::path::Path::new(url).exists() {
return Ok(DbConnType::Sqlite);
}
err!(format!(
"`DATABASE_URL` does not match any known database scheme (mysql://, postgresql://, sqlite://) \
and no existing SQLite database was found at '{url}'. \
If you intend to use SQLite, use an explicit `sqlite://` scheme in your `DATABASE_URL`. \
Otherwise, check your DATABASE_URL for typos or quoting issues."
))
}
#[cfg(not(sqlite))]
err!("`DATABASE_URL` does not match any known database scheme (mysql://, postgresql://, sqlite://)")
} }
} }
@ -390,11 +409,12 @@ pub fn backup_sqlite() -> Result<String, Error> {
let db_url = CONFIG.database_url(); let db_url = CONFIG.database_url();
if DbConnType::from_url(&CONFIG.database_url()).map(|t| t == DbConnType::Sqlite).unwrap_or(false) { if DbConnType::from_url(&CONFIG.database_url()).map(|t| t == DbConnType::Sqlite).unwrap_or(false) {
// Since we do not allow any schema for sqlite database_url's like `file:` or `sqlite:` to be set, we can assume here it isn't // Strip the sqlite:// prefix if present to get the raw file path
// This way we can set a readonly flag on the opening mode without issues. let file_path = db_url.strip_prefix("sqlite://").unwrap_or(&db_url);
let mut conn = diesel::sqlite::SqliteConnection::establish(&format!("sqlite://{db_url}?mode=ro"))?; // Open a read-only connection for the backup
let mut conn = diesel::sqlite::SqliteConnection::establish(&format!("sqlite://{file_path}?mode=ro"))?;
let db_path = std::path::Path::new(&db_url).parent().unwrap(); let db_path = std::path::Path::new(file_path).parent().unwrap();
let backup_file = db_path let backup_file = db_path
.join(format!("db_{}.sqlite3", chrono::Utc::now().format("%Y%m%d_%H%M%S"))) .join(format!("db_{}.sqlite3", chrono::Utc::now().format("%Y%m%d_%H%M%S")))
.to_string_lossy() .to_string_lossy()

Loading…
Cancel
Save