mirror of
https://git.ahines.net/joeyahines/Albatross.git
synced 2026-11-08 09:13:57 +00:00
Code cleanup
+ Split up main.rs into multiple modules + Used discord-hook-rs for webhooks + General code cleanup (clippy+fmt) + Bumped version number
This commit is contained in:
1 parent
42c0bcc7bc
commit
b54ac0efef
7 files changed
+418
-271
No files matched your search
+24
-237
@@ -1,203 +1,17 @@
|
||||
extern crate serde;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use chrono::{NaiveDateTime, Utc};
|
||||
use clap::{App, Arg};
|
||||
use flate2::write::GzEncoder;
|
||||
use flate2::Compression;
|
||||
use regex::Regex;
|
||||
use std::fs::{copy, create_dir, create_dir_all, remove_dir_all, remove_file, DirEntry, File};
|
||||
use std::fs::{create_dir_all, remove_dir_all, remove_file, DirEntry};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Instant;
|
||||
|
||||
mod albatross_config;
|
||||
mod backup;
|
||||
mod config;
|
||||
mod discord;
|
||||
mod region;
|
||||
|
||||
use albatross_config::{AlbatrossConfig, WorldConfig, WorldType};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Struct to store information about the region
|
||||
struct Region {
|
||||
/// x position of the region
|
||||
x: i64,
|
||||
/// y position of the region
|
||||
y: i64,
|
||||
}
|
||||
|
||||
impl Region {
|
||||
fn from_string(string: String) -> Option<Self> {
|
||||
let re = Regex::new(r"r\.(?P<x>-?[0-9]*)+\.(?P<y>-?[0-9]*)").unwrap();
|
||||
if re.is_match(string.as_str()) {
|
||||
let captures = re.captures(string.as_str()).unwrap();
|
||||
|
||||
return Some(Region {
|
||||
x: captures["x"].parse::<i64>().unwrap(),
|
||||
y: captures["y"].parse::<i64>().unwrap(),
|
||||
});
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Backup a directory
|
||||
///
|
||||
/// # Param
|
||||
/// * `world_path` - path to the world folder
|
||||
/// * `backup_path` - path to the backup folder
|
||||
fn backup_dir(
|
||||
dir_name: &str,
|
||||
world_path: &PathBuf,
|
||||
backup_path: &PathBuf,
|
||||
) -> Result<u64, std::io::Error> {
|
||||
let mut src_dir = world_path.clone();
|
||||
src_dir.push(dir_name);
|
||||
let mut backup_dir = backup_path.clone();
|
||||
backup_dir.push(dir_name);
|
||||
create_dir(&backup_dir)?;
|
||||
|
||||
let mut file_count = 0;
|
||||
for entry in src_dir.read_dir()? {
|
||||
let entry = entry?;
|
||||
let mut target = backup_dir.clone();
|
||||
target.push(entry.file_name());
|
||||
|
||||
copy(entry.path(), target)?;
|
||||
file_count += 1;
|
||||
}
|
||||
|
||||
Ok(file_count)
|
||||
}
|
||||
|
||||
/// Backup the regions
|
||||
///
|
||||
/// # Param
|
||||
/// * `dir_name` - name of the backup folder
|
||||
/// * `save_radius` - block radius to save
|
||||
/// * `world_path` - path to the world folder
|
||||
/// * `backup_path` - path to the backup folder
|
||||
fn backup_region(
|
||||
dir_name: &str,
|
||||
save_radius: u64,
|
||||
world_path: &PathBuf,
|
||||
backup_path: &PathBuf,
|
||||
) -> Result<u64, std::io::Error> {
|
||||
let mut count: u64 = 0;
|
||||
let mut src_dir = world_path.clone();
|
||||
src_dir.push(dir_name);
|
||||
let mut backup_dir = backup_path.clone();
|
||||
backup_dir.push(dir_name);
|
||||
create_dir(&backup_dir)?;
|
||||
|
||||
let save_radius = (save_radius as f64 / 512.0).ceil() as i64;
|
||||
|
||||
for entry in src_dir.read_dir()? {
|
||||
let entry = entry?;
|
||||
let file_name = entry.file_name().to_str().unwrap().to_string();
|
||||
|
||||
if let Some(region) = Region::from_string(file_name) {
|
||||
if region.x.abs() <= save_radius && region.y.abs() <= save_radius {
|
||||
let mut target = backup_dir.clone();
|
||||
target.push(entry.file_name());
|
||||
|
||||
copy(entry.path(), target)?;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
/// Backup a world
|
||||
///
|
||||
/// # Param
|
||||
/// * `world_path` - path to the world folder
|
||||
/// * `backup_path` - path to the backup folder
|
||||
/// * `world_config` - world config options
|
||||
fn backup_world(
|
||||
world_path: PathBuf,
|
||||
backup_path: PathBuf,
|
||||
world_config: &WorldConfig,
|
||||
) -> Result<u64, std::io::Error> {
|
||||
let mut backup_path = backup_path.clone();
|
||||
let region_count;
|
||||
backup_path.push(&world_config.world_name);
|
||||
create_dir(backup_path.as_path())?;
|
||||
|
||||
backup_region("poi", world_config.save_radius, &world_path, &backup_path)?;
|
||||
region_count = backup_region(
|
||||
"region",
|
||||
world_config.save_radius,
|
||||
&world_path,
|
||||
&backup_path,
|
||||
)?;
|
||||
backup_dir("data", &world_path, &backup_path)?;
|
||||
|
||||
Ok(region_count)
|
||||
}
|
||||
|
||||
/// Backup the overworld
|
||||
///
|
||||
/// # Param
|
||||
/// * `world_path` - path to the world folder
|
||||
/// * `backup_path` - path to the backup folder
|
||||
/// * `world_config` - world config options
|
||||
fn backup_overworld(
|
||||
world_path: PathBuf,
|
||||
backup_path: PathBuf,
|
||||
world_config: &WorldConfig,
|
||||
) -> Result<u64, std::io::Error> {
|
||||
backup_world(world_path, backup_path, world_config)
|
||||
}
|
||||
|
||||
/// Backup the nether
|
||||
///
|
||||
/// # Param
|
||||
/// * `world_path` - path to the world folder
|
||||
/// * `backup_path` - path to the backup folder
|
||||
/// * `world_config` - world config options
|
||||
fn backup_nether(
|
||||
world_path: PathBuf,
|
||||
backup_path: PathBuf,
|
||||
world_config: &WorldConfig,
|
||||
) -> Result<u64, std::io::Error> {
|
||||
let mut nether_path = world_path.clone();
|
||||
nether_path.push("DIM-1");
|
||||
|
||||
backup_world(nether_path, backup_path, world_config)
|
||||
}
|
||||
|
||||
/// Backup the end
|
||||
///
|
||||
/// # Param
|
||||
/// * `world_path` - path to the world folder
|
||||
/// * `backup_path` - path to the backup folder
|
||||
/// * `world_config` - world config options
|
||||
fn backup_end(
|
||||
world_path: PathBuf,
|
||||
backup_path: PathBuf,
|
||||
world_config: &WorldConfig,
|
||||
) -> Result<u64, std::io::Error> {
|
||||
let mut end_path = world_path.clone();
|
||||
end_path.push("DIM1");
|
||||
|
||||
backup_world(end_path, backup_path, world_config)
|
||||
}
|
||||
|
||||
/// Compress the backup after the files have been copied
|
||||
///
|
||||
/// # Param
|
||||
/// * `tmp_dir`: tmp directory with the backed up files
|
||||
/// * `output_file`: output archive
|
||||
fn compress_backup(tmp_dir: &PathBuf, output_file: &PathBuf) -> Result<(), std::io::Error> {
|
||||
let archive = File::create(output_file)?;
|
||||
let enc = GzEncoder::new(archive, Compression::default());
|
||||
let mut tar_builder = tar::Builder::new(enc);
|
||||
tar_builder.append_dir_all(".", tmp_dir)?;
|
||||
Ok(())
|
||||
}
|
||||
use crate::config::{AlbatrossConfig, WorldType};
|
||||
use backup::{backup_end, backup_nether, backup_overworld};
|
||||
use discord::send_webhook;
|
||||
|
||||
/// Get the time of the backup from a file name
|
||||
///
|
||||
@@ -207,9 +21,9 @@ fn get_time_from_file_name(
|
||||
archive_entry: &DirEntry,
|
||||
) -> Result<Option<NaiveDateTime>, std::io::Error> {
|
||||
let file_name = archive_entry.file_name().to_str().unwrap().to_string();
|
||||
let name: Vec<&str> = file_name.split("_").collect();
|
||||
let name: Vec<&str> = file_name.split('_').collect();
|
||||
|
||||
Ok(chrono::NaiveDateTime::parse_from_str(name[0], "%d-%m-%y-%H:%M:%S").ok())
|
||||
Ok(chrono::NaiveDateTime::parse_from_str(name[0], "%d-%m-%y-%H.%M.%S").ok())
|
||||
}
|
||||
|
||||
/// Removes the old backups from the ouput directory
|
||||
@@ -250,22 +64,6 @@ fn remove_old_backups(output_dir: &PathBuf, keep: u64) -> Result<usize, std::io:
|
||||
Ok(num_of_removed_backups)
|
||||
}
|
||||
|
||||
/// Sends a webhook to Discord if its configured
|
||||
///
|
||||
/// # Params
|
||||
/// * `msg` - Message to send to discord
|
||||
/// * `cfg` - Albatross config
|
||||
fn send_webhook(msg: &str, cfg: &AlbatrossConfig) {
|
||||
if let Some(webhook) = &cfg.backup.discord_webhook {
|
||||
let mut map = HashMap::new();
|
||||
|
||||
map.insert("content".to_string(), msg.to_string());
|
||||
|
||||
let client = reqwest::blocking::Client::new();
|
||||
client.post(webhook).json(&map).send().ok();
|
||||
}
|
||||
}
|
||||
|
||||
/// Backup the configured worlds from a minecraft server
|
||||
///
|
||||
/// # Params
|
||||
@@ -273,7 +71,7 @@ fn send_webhook(msg: &str, cfg: &AlbatrossConfig) {
|
||||
fn do_backup(cfg: AlbatrossConfig) -> Result<(), std::io::Error> {
|
||||
let server_base_dir = cfg.backup.minecraft_dir.clone();
|
||||
let worlds = cfg.world_config.clone().expect("No worlds configured");
|
||||
let time_str = Utc::now().format("%d-%m-%y-%H:%M:%S").to_string();
|
||||
let time_str = Utc::now().format("%d-%m-%y-%H.%M.%S").to_string();
|
||||
let backup_name = format!("{}_backup.tar.gz", time_str);
|
||||
let mut output_archive = cfg.backup.output_dir.clone();
|
||||
output_archive.push(backup_name);
|
||||
@@ -299,42 +97,33 @@ fn do_backup(cfg: AlbatrossConfig) -> Result<(), std::io::Error> {
|
||||
format!("Starting backup of **{}**", world_name).as_str(),
|
||||
&cfg,
|
||||
);
|
||||
match world_type {
|
||||
let webhook_msg = match world_type {
|
||||
WorldType::OVERWORLD => {
|
||||
let region_count =
|
||||
let (region_count, player_count) =
|
||||
backup_overworld(world_dir.clone(), tmp_dir.clone(), &world)?;
|
||||
let player_count = backup_dir("playerdata", &world_dir, &tmp_dir)?;
|
||||
send_webhook(
|
||||
format!(
|
||||
"{} regions and {} player files backed up.",
|
||||
region_count, player_count
|
||||
)
|
||||
.as_str(),
|
||||
&cfg,
|
||||
);
|
||||
format!(
|
||||
"{} regions and {} player files backed up.",
|
||||
region_count, player_count
|
||||
)
|
||||
}
|
||||
WorldType::NETHER => {
|
||||
let region_count = backup_nether(world_dir, tmp_dir.clone(), &world)?;
|
||||
send_webhook(
|
||||
format!("{} regions backed up.", region_count).as_str(),
|
||||
&cfg,
|
||||
);
|
||||
format!("{} regions backed up.", region_count)
|
||||
}
|
||||
WorldType::END => {
|
||||
let region_count = backup_end(world_dir, tmp_dir.clone(), &world)?;
|
||||
send_webhook(
|
||||
format!("{} regions backed up.", region_count).as_str(),
|
||||
&cfg,
|
||||
);
|
||||
format!("{} regions backed up.", region_count)
|
||||
}
|
||||
};
|
||||
|
||||
send_webhook(&webhook_msg, &cfg);
|
||||
} else {
|
||||
send_webhook(format!("Error: {} not found.", world_name).as_str(), &cfg);
|
||||
println!("World \"{}\" not found", world_name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
compress_backup(&tmp_dir, &output_archive)?;
|
||||
backup::compress_backup(&tmp_dir, &output_archive)?;
|
||||
|
||||
remove_dir_all(&tmp_dir)?;
|
||||
|
||||
@@ -377,11 +166,9 @@ fn main() {
|
||||
if cfg.world_config.is_some() {
|
||||
println!("Starting backup");
|
||||
match do_backup(cfg) {
|
||||
Err(e) => println!("Error doing backup: {}", e),
|
||||
_ => {}
|
||||
Ok(_) => println!("Backup complete"),
|
||||
Err(e) => println!("Error doing backup: {:?}", e),
|
||||
}
|
||||
|
||||
println!("Backup complete");
|
||||
} else {
|
||||
println!("No worlds specified to backed up!")
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user