using CommandLine; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Linq; using RedisManager.Utils; namespace RedisManager.Commands { /// /// Contains command line options and implementations for Redis server operations. /// Provides functionality for INFO, CONFIG, SAVE, BGSAVE, LASTSAVE, TIME, and PING commands. /// [Verb("info", HelpText = "Get information and statistics about the server.")] public class InfoOptions { [Option('i', "instance", Required = true, HelpText = "Instance name.")] public string Instance { get; set; } [Option("section", Required = false, HelpText = "Info section (server, clients, memory, etc.).")] public string Section { get; set; } [Option("table", Required = false, HelpText = "Output as table.")] public bool Table { get; set; } } [Verb("config", HelpText = "Get/set Redis configuration parameters.")] public class ConfigOptions { [Option("list-custom", Required = false, HelpText = "List all custom config parameters for this instance.")] public bool ListCustom { get; set; } [Option('i', "instance", Required = true, HelpText = "Instance name.")] public string Instance { get; set; } [Option("get", Required = false, HelpText = "Get configuration parameter.")] public string Get { get; set; } [Option("set", Required = false, HelpText = "Set configuration parameter (format: parameter value).")] public string Set { get; set; } [Option("table", Required = false, HelpText = "Output as table.")] public bool Table { get; set; } } [Verb("save", HelpText = "Synchronously save the dataset to disk.")] public class SaveOptions { [Option('i', "instance", Required = true, HelpText = "Instance name.")] public string Instance { get; set; } [Option("table", Required = false, HelpText = "Output as table.")] public bool Table { get; set; } } [Verb("bgsave", HelpText = "Asynchronously save the dataset to disk.")] public class BgSaveOptions { [Option('i', "instance", Required = true, HelpText = "Instance name.")] public string Instance { get; set; } [Option("table", Required = false, HelpText = "Output as table.")] public bool Table { get; set; } } [Verb("lastsave", HelpText = "Get the timestamp of the last successful save.")] public class LastSaveOptions { [Option('i', "instance", Required = true, HelpText = "Instance name.")] public string Instance { get; set; } [Option("table", Required = false, HelpText = "Output as table.")] public bool Table { get; set; } } [Verb("time", HelpText = "Return the current server time.")] public class TimeOptions { [Option('i', "instance", Required = true, HelpText = "Instance name.")] public string Instance { get; set; } [Option("table", Required = false, HelpText = "Output as table.")] public bool Table { get; set; } } [Verb("ping", HelpText = "Ping the server.")] public class PingOptions { [Option('i', "instance", Required = true, HelpText = "Instance name.")] public string Instance { get; set; } [Option("table", Required = false, HelpText = "Output as table.")] public bool Table { get; set; } } public static class ServerCommands { /// /// Executes the INFO command to get information and statistics about the server. /// /// The InfoOptions containing instance and section. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). public static int RunInfo(InfoOptions opts, Config config) { var instance = RedisUtils.GetInstance(config, opts.Instance); using var redis = RedisUtils.ConnectRedis(instance); var db = redis.GetDatabase(); var info = db.Execute("INFO", string.IsNullOrEmpty(opts.Section) ? null : opts.Section).ToString(); if (opts.Table) { var lines = info.Split('\n', StringSplitOptions.RemoveEmptyEntries); var rows = lines.Select(l => l.Split(':', 2)).Where(parts => parts.Length == 2) .Select(parts => new[] { parts[0], parts[1] }).ToList(); RedisUtils.PrintTable(new[] { "Parameter", "Value" }, rows); } else { Console.WriteLine(Output.Green(info)); } return 0; } /// /// Executes the CONFIG command to get or set Redis configuration parameters. /// /// The ConfigOptions containing instance, get, set, and table options. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). public static int RunConfig(ConfigOptions opts, Config config) { var instance = RedisUtils.GetInstance(config, opts.Instance); using var redis = RedisUtils.ConnectRedis(instance); var db = redis.GetDatabase(); if (opts.ListCustom) { if (instance.CustomConfig != null && instance.CustomConfig.Count > 0) { RedisUtils.PrintTable(new[] { "Parameter", "Value" }, instance.CustomConfig.Select(kv => new[] { kv.Key, kv.Value }).ToList()); } else { Console.WriteLine(Output.Yellow("No custom config parameters set for this instance.")); } return 0; } if (!string.IsNullOrEmpty(opts.Get)) { var result = db.Execute("CONFIG", "GET", opts.Get); if (opts.Table) { var arr = (RedisResult[])result; var rows = new List(); for (int i = 0; i < arr.Length; i += 2) { if (i + 1 < arr.Length) rows.Add(new[] { arr[i].ToString(), arr[i + 1].ToString() }); } RedisUtils.PrintTable(new[] { "Parameter", "Value" }, rows); } else { var arr = (RedisResult[])result; for (int i = 0; i < arr.Length; i += 2) { if (i + 1 < arr.Length) Console.WriteLine(Output.Green($"{arr[i]} {arr[i + 1]}")); } } } else if (!string.IsNullOrEmpty(opts.Set)) { var parts = opts.Set.Split(' ', 2); if (parts.Length == 2) { db.Execute("CONFIG", "SET", parts[0], parts[1]); // Persist custom config in InstanceConfig and save config file var instanceConfig = config.Instances.FirstOrDefault(i => i.Name == opts.Instance); if (instanceConfig != null) { instanceConfig.CustomConfig[parts[0]] = parts[1]; RedisManager.ConfigManager.SaveConfig(config); } if (opts.Table) RedisUtils.PrintTable(new[] { "Operation", "Status" }, new List { new[] { "CONFIG SET", "OK" } }); else Console.WriteLine(Output.Green("OK")); } else { Console.WriteLine(Output.Red("Invalid format. Use: parameter value")); return 1; } } else { Console.WriteLine(Output.Red("Please specify --get or --set")); return 1; } return 0; } /// /// Executes the SAVE command to synchronously save the dataset to disk. /// /// The SaveOptions containing instance and table option. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). public static int RunSave(SaveOptions opts, Config config) { var instance = RedisUtils.GetInstance(config, opts.Instance); using var redis = RedisUtils.ConnectRedis(instance); var db = redis.GetDatabase(); db.Execute("SAVE"); if (opts.Table) RedisUtils.PrintTable(new[] { "Operation", "Status" }, new List { new[] { "SAVE", "OK" } }); else Console.WriteLine(Output.Green("OK")); return 0; } /// /// Executes the BGSAVE command to asynchronously save the dataset to disk. /// /// The BgSaveOptions containing instance and table option. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). public static int RunBgSave(BgSaveOptions opts, Config config) { var instance = RedisUtils.GetInstance(config, opts.Instance); using var redis = RedisUtils.ConnectRedis(instance); var db = redis.GetDatabase(); db.Execute("BGSAVE"); if (opts.Table) RedisUtils.PrintTable(new[] { "Operation", "Status" }, new List { new[] { "BGSAVE", "Background save started" } }); else Console.WriteLine(Output.Green("Background save started")); return 0; } /// /// Executes the LASTSAVE command to get the timestamp of the last successful save. /// /// The LastSaveOptions containing instance and table option. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). public static int RunLastSave(LastSaveOptions opts, Config config) { var instance = RedisUtils.GetInstance(config, opts.Instance); using var redis = RedisUtils.ConnectRedis(instance); var db = redis.GetDatabase(); var timestamp = db.Execute("LASTSAVE").ToString(); var dateTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(timestamp)); if (opts.Table) RedisUtils.PrintTable(new[] { "Last Save", "Timestamp", "DateTime" }, new List { new[] { "Last Save", timestamp, dateTime.ToString() } }); else Console.WriteLine(Output.Green($"{timestamp} ({dateTime})")); return 0; } /// /// Executes the TIME command to return the current server time. /// /// The TimeOptions containing instance and table option. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). public static int RunTime(TimeOptions opts, Config config) { var instance = RedisUtils.GetInstance(config, opts.Instance); using var redis = RedisUtils.ConnectRedis(instance); var db = redis.GetDatabase(); var result = db.Execute("TIME"); var arr = (RedisResult[])result; var timestamp = arr[0].ToString(); var microseconds = arr[1].ToString(); var dateTime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(timestamp)); if (opts.Table) RedisUtils.PrintTable(new[] { "Timestamp", "Microseconds", "DateTime" }, new List { new[] { timestamp, microseconds, dateTime.ToString() } }); else Console.WriteLine(Output.Green($"{timestamp} {microseconds} ({dateTime})")); return 0; } /// /// Executes the PING command to ping the server. /// /// The PingOptions containing instance and table option. /// The RedisManager configuration. /// Exit code (0 for success, non-zero for failure). public static int RunPing(PingOptions opts, Config config) { var instance = RedisUtils.GetInstance(config, opts.Instance); using var redis = RedisUtils.ConnectRedis(instance); var db = redis.GetDatabase(); var response = db.Ping(); if (opts.Table) RedisUtils.PrintTable(new[] { "Ping", "Response" }, new List { new[] { "PING", response.ToString() } }); else Console.WriteLine(Output.Green(response.ToString())); return 0; } } }