add edit, ls and remove cli commands
This commit is contained in:
@ -46,7 +46,7 @@ pub async fn subcommand(arg: &ArgMatches, pool: DbPool) -> Result<(), String>{
|
||||
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))?;
|
||||
file.write_all(env_settings::args_to_env(arg, true).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))?;
|
||||
|
||||
|
||||
@ -1,6 +1,50 @@
|
||||
use clap::Command;
|
||||
use clap::{ArgMatches, Command, Arg};
|
||||
use diesel::result::Error::NotFound;
|
||||
|
||||
use crate::cli::env_settings;
|
||||
use crate::status::ServerStatus;
|
||||
|
||||
use diesel::prelude::*;
|
||||
|
||||
use crate::{Config, DbPool, schema, models};
|
||||
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
|
||||
pub fn create_subcommand() -> Command {
|
||||
let mut args = vec![Arg::new("NAME").required(true)];
|
||||
|
||||
args.extend(env_settings::get_args());
|
||||
|
||||
Command::new("edit")
|
||||
.about("edit a server")
|
||||
.args(args.as_slice())
|
||||
}
|
||||
|
||||
|
||||
pub async fn subcommand(arguments: &ArgMatches, pool: DbPool) -> Result<(), String> {
|
||||
use schema::servers::dsl::*;
|
||||
let conn = &mut pool.get().unwrap();
|
||||
|
||||
let server = match servers.filter(name.eq(arguments.get_one::<String>("NAME").unwrap())).select(models::Servers::as_select()).first(conn) {
|
||||
Err(NotFound) => { return Err("this server does not exist".to_string()); },
|
||||
Err(e) => { return Err(format!("An error occured : {:?}", e)); },
|
||||
Ok(server) => server
|
||||
};
|
||||
let path = Config::load().config_path + "/" + &server.name;
|
||||
|
||||
if server.status == ServerStatus::Running {
|
||||
eprintln!("warning: the server is currently running, changes will no be applied until the next start");
|
||||
}
|
||||
|
||||
let prev_env = fs::read_to_string(path.to_string() + "/env").map_err(|e| format!("failed to read previous env file : {}", e))?;
|
||||
|
||||
let new_env = env_settings::update_env(arguments, prev_env);
|
||||
println!("\n\nnew env : {}", new_env);
|
||||
|
||||
|
||||
let mut env_file = fs::OpenOptions::new().write(true).truncate(true).open(path + "/env").map_err(|e| format!("failed to reopen env file : {}" ,e))?;
|
||||
env_file.write_all(new_env.as_bytes()).map_err(|e| format!("failed to write to file : {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
use clap::{ Arg, ArgMatches, ArgAction};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn get_args() -> [Arg; 22] {
|
||||
use clap::{ Arg, ArgAction, ArgMatches, parser::ValueSource};
|
||||
|
||||
pub fn get_args() -> [Arg; 21] {
|
||||
[
|
||||
Arg::new("VERSION").short('v').long("version").help("Minecraft version of the server").default_value("LATEST"),
|
||||
Arg::new("MEMORY").short('m').long("memory").help("How much ram does this server have").default_value("4G"),
|
||||
@ -14,7 +16,7 @@ pub fn get_args() -> [Arg; 22] {
|
||||
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),
|
||||
// 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"),
|
||||
@ -27,7 +29,7 @@ pub fn get_args() -> [Arg; 22] {
|
||||
]
|
||||
}
|
||||
|
||||
pub fn args_to_env(arguments: &ArgMatches) -> String {
|
||||
pub fn args_to_settings(arguments: &ArgMatches, default: bool) -> Vec<(String, 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"];
|
||||
|
||||
@ -36,30 +38,63 @@ pub fn args_to_env(arguments: &ArgMatches) -> String {
|
||||
];
|
||||
|
||||
for setting in default_handle {
|
||||
if default == false && arguments.value_source(setting) == Some(ValueSource::DefaultValue) {
|
||||
continue ;
|
||||
}
|
||||
|
||||
if let Some(val) = arguments.get_one::<String>(setting) {
|
||||
res.push((setting.to_string(), val.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(value) = arguments.get_one::<String>("FORGE_VERSION") {
|
||||
if default == true || arguments.value_source("FORGE_VERSION") != Some(ValueSource::DefaultValue) {
|
||||
res.push(("FORGE_VERSION".to_string(), value.to_string()));
|
||||
res.push(("NEOFORGE_VERSION".to_string(), value.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(value) = arguments.get_one::<String>("WHITELIST") {
|
||||
res.push(("ENABLE_WHITELIST".to_string(), "true".to_string()));
|
||||
res.push(("WHITELIST".to_string(),value.to_string()));
|
||||
}
|
||||
|
||||
if let Some(value) = arguments.get_many::<String>("CUSTOM_SERVER_PROPERTIES") {
|
||||
let properties = value.collect::<Vec<&String>>().iter().map(|x| x.to_string()).collect::<Vec<String>>().join("\n");
|
||||
res.push(("CUSTOM_SERVER_PROPERTIES".to_string(), properties));
|
||||
// if let Some(value) = arguments.get_many::<String>("CUSTOM_SERVER_PROPERTIES") {
|
||||
// let properties = value.collect::<Vec<&String>>().iter().map(|x| x.to_string()).collect::<Vec<String>>().join("\n");
|
||||
// res.push(("CUSTOM_SERVER_PROPERTIES".to_string(), properties));
|
||||
// }
|
||||
res
|
||||
}
|
||||
|
||||
// println!("\n\n\narguments : {:?}\nignored:{:?}", arguments, ignored);
|
||||
res.iter()
|
||||
pub fn settings_to_env(settings: Vec<(String, String)>) -> String {
|
||||
settings.iter()
|
||||
.map(|x| format!("{}={}", x.0, x.1))
|
||||
.collect::<Vec<String>>().join("\n")
|
||||
}
|
||||
|
||||
pub fn args_to_env(arguments: &ArgMatches, default: bool) -> String {
|
||||
settings_to_env(args_to_settings(arguments, default))
|
||||
}
|
||||
|
||||
pub fn env_to_settings(env: String) -> Vec<(String, String)> {
|
||||
env.split("\n")
|
||||
.filter_map(|s| s.split_once("=")
|
||||
.map(|(x, y)| (x.to_string(), y.to_string())))
|
||||
.collect::<Vec<(String, String)>>()
|
||||
}
|
||||
|
||||
pub fn update_env(arguments: &ArgMatches, prev_env: String) -> String{
|
||||
println!("env : {}" , prev_env);
|
||||
let settings = env_to_settings(prev_env);
|
||||
let new_settings = args_to_settings(arguments, false);
|
||||
|
||||
let merged: Vec<(String, String)> = settings.into_iter()
|
||||
.chain(new_settings.into_iter())
|
||||
.collect::<HashMap<String, String>>()
|
||||
.into_iter()
|
||||
.collect();
|
||||
settings_to_env(merged)
|
||||
|
||||
}
|
||||
// START COMMAND :
|
||||
// docker run --rm --env-file ./env -u 1000:100 -it --volume $PWD/server:/data itzg/minecraft-server
|
||||
|
||||
@ -1,6 +1,27 @@
|
||||
use clap::ArgMatches;
|
||||
use clap::Command;
|
||||
|
||||
use diesel::prelude::*;
|
||||
use crate::DbPool;
|
||||
use crate::schema;
|
||||
use crate::models;
|
||||
|
||||
pub fn create_subcommand() -> Command {
|
||||
Command::new("ls")
|
||||
.about("list the servers")
|
||||
}
|
||||
|
||||
pub async fn subcommand(_arguments: &ArgMatches, pool: DbPool) -> Result<(), String> {
|
||||
use schema::servers::dsl::*;
|
||||
|
||||
let conn = &mut pool.get().unwrap();
|
||||
|
||||
let list = servers.select(models::Servers::as_select()).load(conn).map_err(|_| "failed to get server list from db".to_string())?;
|
||||
|
||||
|
||||
for server in list {
|
||||
println!("{}", server);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1,6 +1,34 @@
|
||||
use clap::Command;
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
|
||||
use crate::{Config, DbPool};
|
||||
|
||||
use std::fs;
|
||||
|
||||
use diesel::prelude::*;
|
||||
|
||||
pub fn create_subcommand() -> Command {
|
||||
Command::new("remove")
|
||||
.about("remove a server")
|
||||
.arg(Arg::new("NAME").required(true))
|
||||
}
|
||||
|
||||
pub async fn subcommand(arguments: &ArgMatches, pool: DbPool) -> Result<(), String> {
|
||||
use crate::schema::servers::dsl::*;
|
||||
let server_name = arguments.get_one::<String>("NAME").unwrap();
|
||||
|
||||
let config = Config::load();
|
||||
|
||||
let conn = &mut pool.get().unwrap();
|
||||
|
||||
//todo stop the server if it's started
|
||||
|
||||
let deleted = diesel::delete(servers.filter(name.eq(server_name))).execute(conn).map_err(|_| "failed to delete from db".to_string())?;
|
||||
if deleted != 1 {
|
||||
return Err("servver was not found".to_string());
|
||||
}
|
||||
|
||||
let path = config.config_path + "/" + server_name;
|
||||
|
||||
fs::remove_dir_all(path).map_err(|e| format!("failed to remove the server directory, reason : {}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
/* By: tomoron <tomoron@student.42angouleme.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/05/29 21:22:17 by tomoron #+# #+# */
|
||||
/* Updated: 2026/06/05 17:48:58 by tomoron ### ########.fr */
|
||||
/* Updated: 2026/06/06 15:37:52 by tomoron ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@ -66,6 +66,9 @@ async fn main() -> Result<(), String> {
|
||||
match arg.subcommand() {
|
||||
Some(("serverMode", _)) => { mc_socket_listen(pool).await.map_err(|_| "socket error".to_string())?; },
|
||||
Some(("add", sub_matches)) => { add::subcommand(sub_matches, pool).await?; },
|
||||
Some(("remove", sub_matches)) => { remove::subcommand(sub_matches, pool).await?; },
|
||||
Some(("ls", sub_matches)) => { ls::subcommand(sub_matches, pool).await?; },
|
||||
Some(("edit", sub_matches)) => { edit::subcommand(sub_matches, pool).await?; },
|
||||
_ => {panic!("subcommand not implemented")}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
use std::fmt;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use diesel::prelude::*;
|
||||
|
||||
use serde::Deserialize;
|
||||
@ -30,3 +32,18 @@ pub struct CreateServer<'a> {
|
||||
pub status: ServerStatus,
|
||||
pub redirect_ip: Option<&'a str>
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for Servers {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let login = match self.last_login {
|
||||
Some(date) => {
|
||||
let datetime: DateTime<Utc> = date.into();
|
||||
|
||||
format!("{}", datetime.format("%d-%m-%Y at %H:%M").to_string())
|
||||
},
|
||||
None => { "never".to_string() }
|
||||
};
|
||||
write!(f, "name: {} last_login: {} status: {}", self.name, login, self.status)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user