diff --git a/DX86/DX86.cs b/DX86/DX86.cs index 83c476e..a1957dd 100644 --- a/DX86/DX86.cs +++ b/DX86/DX86.cs @@ -155,6 +155,7 @@ namespace DX86 catch (Exception ex) { ms.Error("[DX86] Error handling client: " + ex.Message); + ms.Debug("[DX86] Stack Trace: " + ex.StackTrace); } finally { diff --git a/DX86/Modules/MessageSender.cs b/DX86/Modules/MessageSender.cs index ee7ea42..a4c48c1 100644 --- a/DX86/Modules/MessageSender.cs +++ b/DX86/Modules/MessageSender.cs @@ -37,6 +37,11 @@ public class MessageSender } + public async void Debug(string message) + { + Console.ForegroundColor = ConsoleColor.Green; + this.Send($"[{DateTime.Now}] [DEBUG] {message}"); + } public async void Log(string message) { Console.ForegroundColor = ConsoleColor.Gray; @@ -76,6 +81,8 @@ public class MessageSender int i = 0; foreach (var s in messageHistory) { + if (s.Contains("[DEBUG]")) + Console.ForegroundColor = ConsoleColor.Green; if (s.Contains("[INFO ]")) Console.ForegroundColor = ConsoleColor.Gray; if (s.Contains("[WARN ]")) diff --git a/DX86/TcpServer.cs b/DX86/TcpServer.cs index 7704cc3..e16d755 100644 --- a/DX86/TcpServer.cs +++ b/DX86/TcpServer.cs @@ -12,6 +12,7 @@ public abstract class TcpServer protected MessageSender ms; private DX86 dx86; public List optionsList; + public Dictionary LoggedInClients { get; } public TcpServer(string address, int port, MessageSender ms) @@ -26,8 +27,9 @@ public abstract class TcpServer ms.Log("[DX86] Server Setup Complete."); _ = RunServer(); } - - + + + private async Task RunServer() { ms.Log("[DX86] Starting server."); diff --git a/Library/Server.cs b/Library/Server.cs index 9baa4ad..9f04414 100644 --- a/Library/Server.cs +++ b/Library/Server.cs @@ -69,8 +69,8 @@ public class Server // Create the JSON request var request = new JsonRequest { - Id = requestId, - Command = command + cmd = command, + cid = requestId }; string jsonString = JsonSerializer.Serialize(request); @@ -92,8 +92,8 @@ public class Server private class JsonRequest { - public string Id { get; set; } - public string Command { get; set; } + public string cmd { get; set; } + public string cid { get; set; } } private class JsonResponse @@ -119,9 +119,9 @@ public class Server // Implement logic to get a worker by ID or code if (id != null) - commandResult = ExecuteCommandAsync(".get worker byid " + id).Result; + commandResult = ExecuteCommandAsync("get worker byid " + id).Result; else - commandResult = ExecuteCommandAsync(".get worker bycode " + id).Result; + commandResult = ExecuteCommandAsync("get worker bycode " + id).Result; // Deserialize the command result to an Employee object try @@ -140,7 +140,7 @@ public class Server public Task Login(string username, string password) { // Implement login logic - var commandResult = ExecuteCommandAsync(".login " + username + " " + password).Result; + var commandResult = ExecuteCommandAsync("login " + username + " " + password).Result; if (commandResult == "success") { return Task.FromResult("Success"); @@ -151,7 +151,7 @@ public class Server public Task Logout() { // Implement logout logic - var commandResult = ExecuteCommandAsync(".logout").Result; + var commandResult = ExecuteCommandAsync("logout").Result; if (commandResult == "success") { return Task.FromResult("Success"); @@ -161,7 +161,7 @@ public class Server public Task clockIn(Employee employee) { - var commandResult = ExecuteCommandAsync(".clockin ").Result; + var commandResult = ExecuteCommandAsync("clock in").Result; if (commandResult == "success") { return Task.FromResult("Success"); @@ -170,7 +170,7 @@ public class Server } public Task clockOut(Employee employee) { - var commandResult = ExecuteCommandAsync(".clockout ").Result; + var commandResult = ExecuteCommandAsync("clock out").Result; if (commandResult == "success") { return Task.FromResult("Success"); @@ -182,7 +182,7 @@ public class Server } public Task clockBreak(Employee employee) { - var commandResult = ExecuteCommandAsync(".clockbreak").Result; + var commandResult = ExecuteCommandAsync("clock break").Result; if (commandResult == "success") { return Task.FromResult("Success"); diff --git a/Server/BackendServer.cs b/Server/BackendServer.cs index 91b44f5..88e8784 100644 --- a/Server/BackendServer.cs +++ b/Server/BackendServer.cs @@ -1,8 +1,16 @@ using System.Net.Sockets; +using System.Text.Json; using DX86.Modules; +using MySqlX.XDevAPI; namespace Server; +class JsonMessage +{ + public string cmd { get; set; } + public string cid { get; set; } +} + public class BackendServer : DX86.TcpServer { CommandManager commandManager; @@ -31,11 +39,25 @@ public class BackendServer : DX86.TcpServer protected override void MessageReceivedEvent(TcpClient client, string message) { ms.Log("[BACKENDSERVER] Message received from client: " + message); - if (message.StartsWith(".")) + // format json recieved + JsonMessage? jsonMessage; + if (message.StartsWith("{") && message.EndsWith("}")) { - handleCommand(cmd: message, client: client); - + try + { + jsonMessage = JsonSerializer.Deserialize(message); + if (jsonMessage != null) + { + ms.Log("[BACKENDSERVER] JSON Message received: cmd=" + jsonMessage.cmd + ", cid=" + jsonMessage.cid); + handleCommand(cmd: jsonMessage.cmd, cid: jsonMessage.cid, client: client); + } + } + catch (JsonException ex) + { + ms.Log("[BACKENDSERVER] Error parsing JSON: " + ex.Message); + } } + } protected override void ClientDisconnectEvent(TcpClient client) @@ -43,12 +65,16 @@ public class BackendServer : DX86.TcpServer throw new NotImplementedException(); } - private void handleCommand(string cmd, TcpClient client) + private void handleCommand(string cmd, string cid, TcpClient client) { string[] args = cmd.Split(' '); - string command = args[0].Substring(1); // Remove the leading dot + string command = args[0]; // Remove the leading dot string[] commandArgs = args.Skip(1).ToArray(); // Get the arguments after the command + foreach (var commandArg in commandArgs) + { + ms.Debug("[BACKENDSERVER] Command argument: " + commandArg); + } - commandManager.ExecuteCommand(executor:command, args:commandArgs, client:client, clientSocket:this); + commandManager.ExecuteCommand(executor:command, args:commandArgs, client:client, clientSocket:this, cid: cid); } } \ No newline at end of file diff --git a/Server/CommandAttribute.cs b/Server/CommandAttribute.cs new file mode 100644 index 0000000..cd87618 --- /dev/null +++ b/Server/CommandAttribute.cs @@ -0,0 +1,12 @@ +namespace Server; + +[AttributeUsage(AttributeTargets.Method, Inherited = false)] +public sealed class CommandAttribute : Attribute +{ + public string Name { get; } + + public CommandAttribute(string name) + { + Name = name; + } +} \ No newline at end of file diff --git a/Server/CommandException.cs b/Server/CommandException.cs new file mode 100644 index 0000000..1c8cf6f --- /dev/null +++ b/Server/CommandException.cs @@ -0,0 +1,10 @@ +namespace Server; + +using System; + +public class CommandException : Exception +{ + public CommandException(string message) : base(message) + { + } +} \ No newline at end of file diff --git a/Server/CommandLibrary.cs b/Server/CommandLibrary.cs new file mode 100644 index 0000000..0f6b2c8 --- /dev/null +++ b/Server/CommandLibrary.cs @@ -0,0 +1,73 @@ +using System.Net.Sockets; +using DX86; +using Server.Commands; + +namespace Server; + +public class CommandLibrary +{ + [Command("example")] + public static string ExampleCommand(string[] args, TcpClient? client, TcpServer? socket) => + $"Example command executed with arguments: {string.Join(", ", args)}"; + + #region Basic Commands + + [Command("help")] + public static string HelpCommand() => + "Available commands:\n" + + "1. help - Show this help message\n" + + "2. exit - Exit the server\n" + + "3. user - User management\n" + + "4. settings - Server settings\n" + + "5. start - Start the server\n" + + "6. stop - Stop the server\n"; + + #endregion + + #region Client Commands + + [Command("login")] + public static string LoginCommand(string[] args, TcpClient? client, TcpServer? socket) + { + if (args.Length < 2) + throw new CommandException("Missing arguments: usage is login "); + + string username = args[0]; + string password = args[1]; + + if (username == "test" && password == "1234") + { + if (client != null) + { + socket?.LoggedInClients.Add(client, username); + return "Login successful"; + } + + throw new CommandException("No client connection detected."); + } + + throw new CommandException("Invalid username or password."); + } + + [Command("logout")] + public static string LogoutCommand(TcpClient? client, TcpServer? socket) + { + if (client != null && socket?.LoggedInClients.ContainsKey(client) == true) + { + socket.LoggedInClients.Remove(client); + return "Logout successful"; + } + throw new CommandException("No client provided or not logged in"); + } + + #endregion + + #region Administration Commands + + [Command("get")] + public static string GetCommand(string[] args, TcpClient? client, TcpServer? socket) => + $"not implemented yet, args: {string.Join(", ", args)}"; + + #endregion + +} \ No newline at end of file diff --git a/Server/CommandManager.cs b/Server/CommandManager.cs index d61fa28..f7154a8 100644 --- a/Server/CommandManager.cs +++ b/Server/CommandManager.cs @@ -1,5 +1,6 @@ using System.Net.Sockets; using System.Reflection; +using System.Text.Json; using DX86; using Server.Commands; @@ -7,7 +8,7 @@ namespace Server; public class CommandManager { - private readonly Dictionary _commands = new(); + private readonly Dictionary _commands = new(); public CommandManager() { @@ -16,31 +17,84 @@ public class CommandManager private void LoadCommands() { - // Get all types implementing ICommand - var commandTypes = Assembly.GetExecutingAssembly() + var methods = Assembly.GetExecutingAssembly() .GetTypes() - .Where(t => typeof(ICommand).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract); + .SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.Static)) + .Where(m => m.GetCustomAttribute() != null); - foreach (var type in commandTypes) + foreach (var method in methods) { - // Create an instance of the command - if (Activator.CreateInstance(type) is ICommand command) - { - // Use the Executor property as the key - _commands[command.Executor] = command; - } + var attr = method.GetCustomAttribute()!; + _commands[attr.Name.ToLower()] = method; + Program.messageSender.Log($"[COMMANDMANAGER] Registered command: {attr.Name}"); } } - - public void ExecuteCommand(string executor, TcpClient? client = null, TcpServer? clientSocket = null, params string[] args) + public void ExecuteCommand(string executor, TcpClient? client = null, TcpServer? clientSocket = null, string cid = "", params string[] args) +{ + if (_commands.TryGetValue(executor.ToLower(), out var method)) { - if (_commands.TryGetValue(executor, out var command)) + try { - command.Exec(args:args, client:client, clientSocket:clientSocket); + var parameters = method.GetParameters(); + var arguments = new object?[parameters.Length]; + + for (int i = 0; i < parameters.Length; i++) + { + var param = parameters[i]; + + if (param.ParameterType == typeof(string[])) + arguments[i] = args; + else if (param.ParameterType == typeof(TcpClient)) + arguments[i] = client; + else if (param.ParameterType == typeof(TcpServer)) + arguments[i] = clientSocket; + else + { + Program.messageSender.Warn($"[COMMANDMANAGER] Unknown parameter type '{param.ParameterType.Name}' in command '{executor}'. Defaulting to null."); + arguments[i] = null; + } + } + + Program.messageSender.Debug($"[COMMANDMANAGER] Executing command '{executor}'..."); + string result = method.Invoke(null, arguments)?.ToString() ?? ""; + + var returnMessage = new JsonMessage + { + cid = cid, + cmd = result + }; + + var sendClientMessage = JsonSerializer.Serialize(returnMessage); + clientSocket?.SendMessageAsync(client, sendClientMessage + "\n"); + Program.messageSender.Debug($"[COMMANDMANAGER] Response sent to client."); } - else + catch (TargetInvocationException ex) when (ex.InnerException is CommandException cmdEx) { - Console.WriteLine($"Command '{executor}' not found."); + Program.messageSender.Warn($"[COMMANDMANAGER] Command '{executor}' failed: {cmdEx.Message}"); + + var returnMessage = new JsonMessage + { + cid = cid, + cmd = $"Error: {cmdEx.Message}" + }; + + var sendClientMessage = JsonSerializer.Serialize(returnMessage); + clientSocket?.SendMessageAsync(client, sendClientMessage + "\n"); + } + catch (TargetInvocationException ex) + { + var actualError = ex.InnerException?.Message ?? ex.Message; + Program.messageSender.Error($"[COMMANDMANAGER] Unexpected error in command '{executor}': {actualError}"); + Program.messageSender.Debug($"[COMMANDMANAGER] Stack Trace: {ex.InnerException?.StackTrace ?? ex.StackTrace}"); + } + catch (Exception ex) + { + Program.messageSender.Error($"[COMMANDMANAGER] Failed to invoke command '{executor}': {ex.Message}"); } } + else + { + Program.messageSender.Warn($"[COMMANDMANAGER] Unknown command '{executor}' received."); + } +} } \ No newline at end of file diff --git a/Server/Commands/ClockCommand.cs b/Server/Commands/ClockCommand.cs new file mode 100644 index 0000000..f1da70a --- /dev/null +++ b/Server/Commands/ClockCommand.cs @@ -0,0 +1,6 @@ +namespace Server.Commands; + +public class ClockCommand +{ + +} \ No newline at end of file diff --git a/Server/Commands/GetCommand.cs b/Server/Commands/GetCommand.cs new file mode 100644 index 0000000..58ce44d --- /dev/null +++ b/Server/Commands/GetCommand.cs @@ -0,0 +1,6 @@ +namespace Server.Commands; + +public class GetCommand +{ + +} \ No newline at end of file diff --git a/Server/Commands/HelpCommand.cs b/Server/Commands/HelpCommand.cs index 822cac7..956e424 100644 --- a/Server/Commands/HelpCommand.cs +++ b/Server/Commands/HelpCommand.cs @@ -6,19 +6,21 @@ namespace Server.Commands; public class HelpCommand : ICommand { public string Executor { get; } + public HelpCommand() { Executor = "help"; } - public void Exec(string[] args, TcpClient? client = null, TcpServer? clientSocket = null) + public string Exec(string[] args, TcpClient? client = null, TcpServer? clientSocket = null) { - clientSocket.SendMessageAsync(client, "" + + return "Available commands:\n" + "1. help - Show this help message\n" + "2. exit - Exit the server\n" + "3. user - User management\n" + "4. settings - Server settings\n" + "5. start - Start the server\n" + - "6. stop - Stop the server\n"); + "6. stop - Stop the server\n" + ; } } \ No newline at end of file diff --git a/Server/Commands/ICommand.cs b/Server/Commands/ICommand.cs index 6532034..f5e3f04 100644 --- a/Server/Commands/ICommand.cs +++ b/Server/Commands/ICommand.cs @@ -6,5 +6,5 @@ namespace Server.Commands; public interface ICommand { string Executor { get; } - public void Exec(string[] args, TcpClient? client = null, TcpServer? clientSocket = null); + public string Exec(string[] args, TcpClient? client, TcpServer? clientSocket); } \ No newline at end of file diff --git a/Server/Commands/LoginCommand.cs b/Server/Commands/LoginCommand.cs new file mode 100644 index 0000000..daf5f62 --- /dev/null +++ b/Server/Commands/LoginCommand.cs @@ -0,0 +1,34 @@ +using System.Net.Sockets; +using DX86; +using MySqlX.XDevAPI; + +namespace Server.Commands; + +public class LoginCommand : ICommand +{ + public string Executor { get; } + + public LoginCommand() + { + Executor = "login"; + } + public string Exec(string[] args, TcpClient? client, TcpServer? clientSocket) + { + + if (args[0] == "test" && args[1] == "test") + { + if (client != null) + { + //clientSocket?.SendMessageAsync(client, "Login successful"); + clientSocket?.LoggedInClients.Add(client, "test"); + return "Login successful"; + } + } + else + { + if (client != null) return "Invalid credentials"; + } + + return "No client provided or invalid credentials"; + } +} \ No newline at end of file diff --git a/Server/Commands/LogoutCommand.cs b/Server/Commands/LogoutCommand.cs new file mode 100644 index 0000000..ae8713e --- /dev/null +++ b/Server/Commands/LogoutCommand.cs @@ -0,0 +1,30 @@ +using System.Net.Sockets; +using DX86; + +namespace Server.Commands; + +public class LogoutCommand : ICommand +{ + public string Executor { get; } + public LogoutCommand() + { + Executor = "logout"; + } + public string Exec(string[] args, TcpClient? client = null, TcpServer? clientSocket = null) + { + if (clientSocket == null && client == null) + return "Please provide a valid client or clientSocket."; + + if (client != null && clientSocket != null && clientSocket.LoggedInClients.ContainsKey(client)) + { + clientSocket.LoggedInClients.Remove(client); + //clientSocket.SendMessageAsync(client, "Success"); + return "Logout successful."; + } + else + { + //clientSocket?.SendMessageAsync(client, "Logout failed. You are not logged in."); + return "Logout failed. You are not logged in."; + } + } +} \ No newline at end of file