add command works ,fix warnings, proxy repaired, docker start arguments defined

This commit is contained in:
2026-06-05 22:03:22 +02:00
parent e52763c35e
commit 25d7a6ffd1
19 changed files with 819 additions and 132 deletions

View File

@ -1,27 +1,69 @@
use clap::{ArgMatches, Command, arg};
use crate::cli::env_settings;
use crate::utils::validate_name;
use crate::status::ServerStatus;
use crate::utils::{ validate_name, extract_archive, move_all_elements};
use diesel::{insert_into, prelude::*};
use crate::{DbPool, models};
use crate::schema;
use tokio::{fs::{File, create_dir}, io::AsyncWriteExt};
use std::path::Path;
use std::time::SystemTime;
pub fn create_subcommand() -> Command {
let mut arguments = vec![
arg!(<NAME> "Name of the server"),
arg!([SERVER_ZIP] "initial server zip"),
arg!([SERVER_ARCHIVE] "initial server archive (supported : prism zip)"),
];
arguments.extend(env_settings::get_args());
Command::new("add")
.about("add a server")
.about("Add a server")
.args(arguments.as_slice())
}
pub fn subcommand(arg: &ArgMatches) -> Result<(), String>{
if !validate_name(arg.get_one::<String>("NAME").unwrap()) {
pub async fn subcommand(arg: &ArgMatches, pool: DbPool) -> Result<(), String>{
let config = crate::Config::load();
let name = arg.get_one::<String>("NAME").unwrap();
if !validate_name(name) {
return Err("Invalid name provided".to_string());
}
println!("{}", env_settings::args_to_env(arg));
let conn = &mut pool.get().unwrap();
if let Ok(_) = schema::servers::table.filter(schema::servers::name.eq(name)).select(models::Servers::as_select()).first(conn) {
return Err("Server with this name already exists".to_string())
}
let path = config.config_path + "/" + name + "/";
create_dir(&path).await.map_err(|e| format!("failed to create the config folder, reason : {}", e))?;
let mut file = File::create(path.to_string() + "env").await.map_err(|e| format!("failed to create .env file, reason {}", e))?;
file.write_all(env_settings::args_to_env(arg).as_bytes()).await.map_err(|e| format!("Failed to write to env file, reason : {}", e))?;
create_dir(path.to_string() + "server").await.map_err(|e| format!("failed to create server subfolder, reason: {}", e))?;
if let Some(archive) = arg.get_one::<String>("SERVER_ARCHIVE") {
extract_archive(archive.to_owned(), path.to_string() + "tmp").await?;
move_all_elements(Path::new(&(path.to_string() + "tmp/minecraft")), Path::new(&(path.to_string() + "server"))).map_err(|e| format!("failed to move the elements, reason: {}", e))?;
std::fs::remove_dir_all(path.to_string() + "tmp").map_err(|e| format!("failed to remove the tmp dir, reason : {}", e))?;
}
let new_server = models::CreateServer {
name: name,
last_login: Some(SystemTime::now()),
container_id: None,
status: ServerStatus::Stopped,
redirect_ip: None
};
insert_into(schema::servers::dsl::servers).values(&new_server).execute(conn).map_err(|e| format!("Failed to insert in db, error : {:?}", e))?;
Ok(())
}

View File

@ -1,4 +1,4 @@
use clap::{Command, arg};
use clap::Command;
pub fn create_subcommand() -> Command {
Command::new("edit")

View File

@ -8,13 +8,13 @@ pub fn get_args() -> [Arg; 22] {
Arg::new("SPAWN_PROTECTION").short('s').long("spawn-protection").help("spawn protection size").default_value("0"),
Arg::new("ONLINE_MODE").short('o').long("online-mode").help("false to allow cracked accounts to join").default_value("true"),
Arg::new("TYPE").short('t').long("type").help("Server type (VANILLA, FORGE, NEOFORGE, PAPER)").default_value("VANILLA"),
Arg::new("FORGE_VERSION").long("forge-version").help("forge version if set type is FORGE or NEOFORGE").default_value("latest"), //TODO diff handle
Arg::new("FORGE_VERSION").long("forge-version").help("forge version if set type is FORGE or NEOFORGE").default_value("latest"),
Arg::new("MOTD").short('M').long("motd").help("MOTD of the server (only shown when the server is running)"),
Arg::new("DIFFICULTY").short('d').long("difficulty").help("Difficulty of the server").default_value("easy"),
Arg::new("WHITELIST").short('w').long("whitelist").help("comma separated list of the playsrs to whitelist (disabled if not provied)"), //TODO diff handle
Arg::new("WHITELIST").short('w').long("whitelist").help("comma separated list of the playsrs to whitelist (disabled if not provied)"),
Arg::new("OPS").short('O').long("ops").help("comma separated list of the players to give operator permissions to"),
Arg::new("MODE").long("gamemode").default_value("survival"),
Arg::new("CUSTOM_SERVER_PROPERTIES").long("custom-property").help("add a custom property").action(ArgAction::Append), //TODO diff handle
Arg::new("CUSTOM_SERVER_PROPERTIES").long("custom-property").help("add a custom property").action(ArgAction::Append),
Arg::new("ALLOW_FLIGHT").short('f').long("alllow-flight").help("Allow flight in suvival mode").default_value("false"),
Arg::new("ALLOW_NETHER").short('n').long("allow-nether").help("Enable the nether dimension").default_value("true"),
Arg::new("HARDCORE").short('H').long("hardcore").help("Enable hardcore mode").default_value("false"),
@ -30,7 +30,10 @@ pub fn get_args() -> [Arg; 22] {
pub fn args_to_env(arguments: &ArgMatches) -> String {
let default_handle = vec!["VERSION", "MEMORY", "MAX_PLAYERS", "SPAWN_PROTECTION", "ONLINE_MODE", "TYPE", "MOTD", "DIFFICULTY", "OPS", "MODE", "ALLOW_FLIGHT",
"ALLOW_NETHER", "HARDCORE", "MAX_TICK_TIME", "PAUSE_WHEN_EMPTY_SECONDS", "PVP", "SPAWN_ANIMALS", "SPAWN_MONSTERS", "SPAWN_NPCS"];
let mut res = vec![];
let mut res = vec![
("EULA".to_string(), "TRUE".to_string()),
];
for setting in default_handle {
if let Some(val) = arguments.get_one::<String>(setting) {
@ -55,6 +58,8 @@ pub fn args_to_env(arguments: &ArgMatches) -> String {
// println!("\n\n\narguments : {:?}\nignored:{:?}", arguments, ignored);
res.iter()
.map(|x| format!("{}=\"{}\"", x.0, x.1))
.map(|x| format!("{}={}", x.0, x.1))
.collect::<Vec<String>>().join("\n")
}
// START COMMAND :
// docker run --rm --env-file ./env -u 1000:100 -it --volume $PWD/server:/data itzg/minecraft-server

View File

@ -1,4 +1,4 @@
use clap::{Command, arg};
use clap::Command;
pub fn create_subcommand() -> Command {
Command::new("ls")

View File

@ -1,4 +1,4 @@
use clap::{Command, arg};
use clap::Command;
pub fn create_subcommand() -> Command {
Command::new("remove")

View File

@ -1,3 +1,88 @@
use std::fs;
use std::io;
use zip::{ZipArchive, result::ZipError};
use std::path::Path;
pub fn validate_name(name: &str) -> bool{
name.chars().all(|c| c.is_alphanumeric() || c == '_' || c == '-')
}
pub async fn extract_archive(archive_path: String, output: String) -> Result<(), String> {
println!("starting extraction of {}", archive_path);
fs::create_dir(&output).map_err(|e| format!("Failed to create archive output folder, reason : {}", e))?;
let mut archive = match fs::File::open(archive_path)
.map_err(ZipError::from)
.and_then(ZipArchive::new)
{
Ok(archive) => archive,
Err(e) => {
return Err(format!("failed to open archive, reason : {:?}", e));
}
};
for i in 0..archive.len() {
let mut file = match archive.by_index(i) {
Ok(file) => file,
Err(e) => {
return Err(format!("extraction failed, reason : {}", e));
}
};
let mut out_path = match file.enclosed_name() {
Some(path) => path,
None => {
return Err("Invalid file path".to_string());
}
};
out_path = Path::new(&output).join(out_path);
if file.is_dir() {
if let Err(e) = fs::create_dir_all(&out_path) {
return Err(format!("extraction failed, reason : {}", e));
}
} else {
if let Some(p) = out_path.parent()
&& !p.exists()
&& let Err(e) = fs::create_dir_all(p)
{
return Err(format!("extraction failed, reason : {}", e));
}
let _ = fs::File::create(&out_path)
.and_then(|mut outfile| io::copy(&mut file, &mut outfile))
.map_err(|e| format!("extraction failed, reason: {}", e))?;
}
}
println!("extraction done");
Ok(())
}
pub fn move_all_elements(src: &Path, dst: &Path) -> io::Result<()> {
// 1. Ensure the destination directory exists; if not, create it
if !dst.exists() {
fs::create_dir_all(dst)?;
println!("Created destination directory: {:?}", dst);
}
// 2. Read the contents of the source directory
for entry in fs::read_dir(src)? {
let entry = entry?;
let file_name = entry.file_name();
// 3. Construct the new path (destination + filename)
let old_path = entry.path();
let new_path = dst.join(file_name);
// 4. Perform the move
// fs::rename works for both files and directories
fs::rename(&old_path, &new_path)?;
println!("Moved: {:?} -> {:?}", old_path, new_path);
}
Ok(())
}