mirror of
https://git.battle-of-pip.de/root/vpr-mitarbeiterverwaltung.git
synced 2025-06-21 00:03:18 +02:00
485 lines
20 KiB
C#
485 lines
20 KiB
C#
using System.Net.Sockets;
|
||
using System.Runtime.CompilerServices;
|
||
using System.Text.Json;
|
||
using DX86;
|
||
using Library;
|
||
|
||
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(string[] args, TcpClient? client, TcpServer? socket)
|
||
{
|
||
throw new CommandException("Available commands:%break" +
|
||
"1. help - Show this help message%break" +
|
||
"2. exit - Exit the server%break" +
|
||
"3. user - User management%break" +
|
||
"4. settings - Server settings%break" +
|
||
"5. start - Start the server%break" +
|
||
"6. stop - Stop the server%break");
|
||
}*/
|
||
|
||
|
||
#endregion
|
||
|
||
#region Client Commands
|
||
|
||
[Command("login")]
|
||
public static string LoginCommand(string[] args, TcpClient? client, TcpServer? socket)
|
||
{
|
||
// 1) Argument check
|
||
if (args.Length < 2)
|
||
throw new CommandException("Missing arguments: usage is login <employeeCode> <4-digit PIN>");
|
||
|
||
string employeeCode = args[0];
|
||
string pinCode = args[1];
|
||
|
||
// 2) Prevent double‐login
|
||
if (client != null && socket?.LoggedInClients.ContainsKey(client) == true)
|
||
throw new CommandException("User already logged in.");
|
||
|
||
// 3) Look up the employee by Code
|
||
var empParams = new Dictionary<string, object> { { "Code", employeeCode } };
|
||
string empResultJson = Program.mySql.Get("employees", empParams);
|
||
|
||
using var empDoc = JsonDocument.Parse(empResultJson);
|
||
bool empError = empDoc.RootElement.GetProperty("error").GetBoolean();
|
||
if (empError)
|
||
{
|
||
string dbMsg = empDoc.RootElement.GetProperty("data").GetString() ?? "Unknown DB error";
|
||
Program.messageSender.Error($"[LoginCommand] DB error when looking up employee: {dbMsg}");
|
||
throw new CommandException("Internal error while checking credentials.");
|
||
}
|
||
|
||
var empArray = empDoc.RootElement.GetProperty("data");
|
||
if (empArray.GetArrayLength() == 0)
|
||
{
|
||
// No employee with that Code
|
||
throw new CommandException("Invalid employee code or PIN.");
|
||
}
|
||
|
||
// 4) Extract Id from the first row
|
||
var empRow = empArray[0];
|
||
int employeeId = empRow.GetProperty("Id").GetInt32();
|
||
|
||
// 5) Check PIN for that employeeId
|
||
var pinParams = new Dictionary<string, object>
|
||
{
|
||
{ "EmployeeId", employeeId },
|
||
{ "PinCode", pinCode }
|
||
};
|
||
string pinResultJson = Program.mySql.Get("employee_pins", pinParams);
|
||
|
||
using var pinDoc = JsonDocument.Parse(pinResultJson);
|
||
bool pinError = pinDoc.RootElement.GetProperty("error").GetBoolean();
|
||
if (pinError)
|
||
{
|
||
string dbMsg = pinDoc.RootElement.GetProperty("data").GetString() ?? "Unknown DB error";
|
||
Program.messageSender.Error($"[LoginCommand] DB error when checking PIN: {dbMsg}");
|
||
throw new CommandException("Internal error while checking credentials.");
|
||
}
|
||
|
||
var pinArray = pinDoc.RootElement.GetProperty("data");
|
||
if (pinArray.GetArrayLength() == 0)
|
||
{
|
||
// No matching PIN entry
|
||
throw new CommandException("Invalid employee code or PIN.");
|
||
}
|
||
|
||
// 6) Successful login → add client to LoggedInClients
|
||
if (client == null)
|
||
throw new CommandException("No client connection detected.");
|
||
|
||
socket!.LoggedInClients.Add(client, employeeCode);
|
||
|
||
// 7) Return “success” (instead of full Employee JSON)
|
||
return "success";
|
||
}
|
||
|
||
[Command("logout")]
|
||
public static string LogoutCommand(TcpClient? client, TcpServer? socket)
|
||
{
|
||
if (client != null && socket?.LoggedInClients.ContainsKey(client) == true)
|
||
{
|
||
socket.LoggedInClients.Remove(client);
|
||
return "success";
|
||
}
|
||
throw new CommandException("No client provided or not logged in");
|
||
}
|
||
|
||
[Command("getSelfUser")]
|
||
public static string GetSelfUserCommand(TcpClient? client, TcpServer? socket)
|
||
{
|
||
// 1) Check for a valid client/socket
|
||
if (client == null || socket == null)
|
||
throw new CommandException("No client connection detected.");
|
||
|
||
// 2) See if this client is logged in
|
||
if (!socket.LoggedInClients.TryGetValue(client, out var employeeCode))
|
||
throw new CommandException("User not logged in.");
|
||
|
||
// 3) Query the database for that employeeCode
|
||
var empParams = new Dictionary<string, object>
|
||
{
|
||
{ "Code", employeeCode }
|
||
};
|
||
string empResultJson = Program.mySql.Get("employees", empParams);
|
||
|
||
using var empDoc = JsonDocument.Parse(empResultJson);
|
||
bool empError = empDoc.RootElement.GetProperty("error").GetBoolean();
|
||
if (empError)
|
||
{
|
||
// Extract the “data” field as the DB‐side error message, if any
|
||
string dbMsg = empDoc.RootElement.GetProperty("data").GetString()
|
||
?? "Unknown DB error";
|
||
Program.messageSender.Error($"[GetSelfUser] DB error when looking up employee: {dbMsg}");
|
||
throw new CommandException("Internal error while fetching user data.");
|
||
}
|
||
|
||
var dataArray = empDoc.RootElement.GetProperty("data");
|
||
if (dataArray.GetArrayLength() == 0)
|
||
{
|
||
// No employee row found for this code
|
||
throw new CommandException("Logged‐in user not found in database.");
|
||
}
|
||
|
||
// 4) We expect exactly one row. Take the first element:
|
||
var firstRow = dataArray[0];
|
||
|
||
// 5) Deserialize it into an Employee object.
|
||
// This requires that Employee.Id is an int (matching the DB schema),
|
||
// or you map fields manually if Id remains a string.
|
||
Employee self;
|
||
try
|
||
{
|
||
string employeeJson = firstRow.GetRawText();
|
||
self = JsonSerializer.Deserialize<Employee>(employeeJson)
|
||
?? throw new InvalidOperationException("Deserialized Employee was null.");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Program.messageSender.Error($"[GetSelfUser] Failed to deserialize Employee: {ex.Message}");
|
||
throw new CommandException("Internal error while parsing user data.");
|
||
}
|
||
|
||
// 6) Return the Employee’s JSON via the ToJson() helper you already wrote:
|
||
return self.ToJson();
|
||
}
|
||
|
||
[Command("clock")]
|
||
public static string ClockCommand(string[] args, TcpClient? client, TcpServer? socket)
|
||
{
|
||
// 1) Argument check
|
||
if (args.Length < 1)
|
||
throw new CommandException("Missing argument: usage is clock <in|out|break>");
|
||
|
||
string desired = args[0].ToLowerInvariant();
|
||
int desiredState;
|
||
switch (desired)
|
||
{
|
||
case "in":
|
||
desiredState = 1;
|
||
break;
|
||
case "out":
|
||
desiredState = 0;
|
||
break;
|
||
case "break":
|
||
desiredState = 2;
|
||
break;
|
||
default:
|
||
throw new CommandException("Invalid argument. Use in, out, or break.");
|
||
}
|
||
|
||
// 2) Verify client/socket
|
||
if (client == null || socket == null)
|
||
throw new CommandException("No client connection detected.");
|
||
|
||
if (!socket.LoggedInClients.TryGetValue(client, out var employeeCode))
|
||
throw new CommandException("User not logged in.");
|
||
|
||
// 3) Look up the employee by Code to get Id and current state
|
||
var empParams = new Dictionary<string, object> { { "Code", employeeCode } };
|
||
string empResultJson = Program.mySql.Get("employees", empParams);
|
||
|
||
using var empDoc = JsonDocument.Parse(empResultJson);
|
||
bool empError = empDoc.RootElement.GetProperty("error").GetBoolean();
|
||
if (empError)
|
||
{
|
||
string dbMsg = empDoc.RootElement.GetProperty("data").GetString() ?? "Unknown DB error";
|
||
Program.messageSender.Error($"[ClockCommand] DB error when looking up employee: {dbMsg}");
|
||
throw new CommandException("Internal error while fetching employee data.");
|
||
}
|
||
|
||
var empArray = empDoc.RootElement.GetProperty("data");
|
||
if (empArray.GetArrayLength() == 0)
|
||
throw new CommandException("Logged-in user not found in database.");
|
||
|
||
var empRow = empArray[0];
|
||
int employeeId = empRow.GetProperty("Id").GetInt32();
|
||
int currentState = empRow.GetProperty("EmployeeState").GetInt32();
|
||
|
||
// 4) Validate state transition
|
||
if (currentState == desiredState)
|
||
throw new CommandException($"Already {(desiredState == 0 ? "OUT" : desiredState == 1 ? "IN" : "on BREAK")}.");
|
||
|
||
bool valid = false;
|
||
switch (currentState)
|
||
{
|
||
case 0: // OUT → only IN
|
||
valid = (desiredState == 1);
|
||
break;
|
||
case 1: // IN → OUT or BREAK
|
||
valid = (desiredState == 0 || desiredState == 2);
|
||
break;
|
||
case 2: // BREAK → only IN
|
||
valid = (desiredState == 1);
|
||
break;
|
||
}
|
||
if (!valid)
|
||
throw new CommandException("Invalid transition from current status.");
|
||
|
||
// 5) Update employees.EmployeeState to desiredState
|
||
var updateData = new Dictionary<string, object>
|
||
{
|
||
{ "EmployeeState", desiredState }
|
||
};
|
||
var whereData = new Dictionary<string, object>
|
||
{
|
||
{ "Id", employeeId }
|
||
};
|
||
string updateJson = Program.mySql.Update("employees", updateData, whereData);
|
||
|
||
using (var updateDoc = JsonDocument.Parse(updateJson))
|
||
{
|
||
bool updError = updateDoc.RootElement.GetProperty("error").GetBoolean();
|
||
if (updError)
|
||
{
|
||
string dbMsg = updateDoc.RootElement.GetProperty("data").GetString() ?? "Unknown DB error";
|
||
Program.messageSender.Error($"[ClockCommand] DB error when updating state: {dbMsg}");
|
||
throw new CommandException("Internal error while updating status.");
|
||
}
|
||
}
|
||
|
||
// 6) Insert into employee_state_history
|
||
var historyData = new Dictionary<string, object>
|
||
{
|
||
{ "EmployeeId", employeeId },
|
||
{ "NewState", desiredState }
|
||
};
|
||
string historyJson = Program.mySql.Insert("employee_state_history", historyData);
|
||
|
||
using (var histDoc = JsonDocument.Parse(historyJson))
|
||
{
|
||
bool histError = histDoc.RootElement.GetProperty("error").GetBoolean();
|
||
if (histError)
|
||
{
|
||
string dbMsg = histDoc.RootElement.GetProperty("data").GetString() ?? "Unknown DB error";
|
||
Program.messageSender.Error($"[ClockCommand] DB error when logging history: {dbMsg}");
|
||
throw new CommandException("Internal error while logging status change.");
|
||
}
|
||
}
|
||
|
||
// 7) Return success
|
||
return "success";
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Administration Commands
|
||
|
||
[Command("get")]
|
||
public static string GetCommand(string[] args, TcpClient? client, TcpServer? socket)
|
||
{
|
||
// Usage: get employee byid <id>
|
||
// get employee bycode <code>
|
||
if (args.Length < 3)
|
||
throw new CommandException("Usage: get employee byid <id> OR get employee bycode <code>");
|
||
|
||
string entity = args[0].ToLowerInvariant();
|
||
string mode = args[1].ToLowerInvariant();
|
||
string key = args[2];
|
||
|
||
if (entity != "employee")
|
||
throw new CommandException("Usage: get employee byid <id> OR get employee bycode <code>");
|
||
|
||
var filter = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||
|
||
if (mode == "byid")
|
||
{
|
||
if (!int.TryParse(key, out var id))
|
||
throw new CommandException("Invalid id format. Must be an integer.");
|
||
|
||
filter["Id"] = id;
|
||
}
|
||
else if (mode == "bycode")
|
||
{
|
||
filter["Code"] = key;
|
||
}
|
||
else
|
||
{
|
||
throw new CommandException("Usage: get employee byid <id> OR get employee bycode <code>");
|
||
}
|
||
|
||
// Query the database
|
||
string empResultJson = Program.mySql.Get("employees", filter);
|
||
|
||
using var doc = JsonDocument.Parse(empResultJson);
|
||
bool dbError = doc.RootElement.GetProperty("error").GetBoolean();
|
||
if (dbError)
|
||
{
|
||
string dbMsg = doc.RootElement.GetProperty("data").GetString() ?? "Unknown DB error";
|
||
Program.messageSender.Error($"[GetCommand] DB error: {dbMsg}");
|
||
throw new CommandException("Internal error while fetching employee.");
|
||
}
|
||
|
||
var dataArray = doc.RootElement.GetProperty("data");
|
||
if (dataArray.GetArrayLength() == 0)
|
||
throw new CommandException("Employee not found.");
|
||
|
||
// Return the first (and expected single) matching employee as JSON object
|
||
var firstRow = dataArray[0];
|
||
// 5) Deserialize it into an Employee object.
|
||
// This requires that Employee.Id is an int (matching the DB schema),
|
||
// or you map fields manually if Id remains a string.
|
||
Employee self;
|
||
try
|
||
{
|
||
string employeeJson = firstRow.GetRawText();
|
||
self = JsonSerializer.Deserialize<Employee>(employeeJson)
|
||
?? throw new InvalidOperationException("Deserialized Employee was null.");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Program.messageSender.Error($"[GetSelfUser] Failed to deserialize Employee: {ex.Message}");
|
||
throw new CommandException("Internal error while parsing user data.");
|
||
}
|
||
|
||
// 6) Return the Employee’s JSON via the ToJson() helper you already wrote:
|
||
return self.ToJson();
|
||
}
|
||
|
||
[Command("clockHistory")]
|
||
public static string ClockHistoryCommand(string[] args, TcpClient? client, TcpServer? socket)
|
||
{
|
||
// 1) Argument check
|
||
if (args.Length < 1)
|
||
throw new CommandException("Missing argument: usage is clockHistory <employeeCode>");
|
||
|
||
string employeeCode = args[0];
|
||
|
||
// 2) Look up the employee by Code
|
||
var empParams = new Dictionary<string, object> { { "Code", employeeCode } };
|
||
string empResultJson = Program.mySql.Get("employees", empParams);
|
||
|
||
using var empDoc = JsonDocument.Parse(empResultJson);
|
||
bool empError = empDoc.RootElement.GetProperty("error").GetBoolean();
|
||
if (empError)
|
||
{
|
||
string dbMsg = empDoc.RootElement.GetProperty("data").GetString() ?? "Unknown DB error";
|
||
Program.messageSender.Error($"[ClockHistoryCommand] DB error when looking up employee: {dbMsg}");
|
||
throw new CommandException("Internal error while fetching employee data.");
|
||
}
|
||
|
||
var empArray = empDoc.RootElement.GetProperty("data");
|
||
if (empArray.GetArrayLength() == 0)
|
||
throw new CommandException("Employee not found in database.");
|
||
|
||
// 3) Extract Id from the first row
|
||
var empRow = empArray[0];
|
||
int employeeId = empRow.GetProperty("Id").GetInt32();
|
||
|
||
// 4) Query the history for that employeeId
|
||
var histParams = new Dictionary<string, object> { { "EmployeeId", employeeId } };
|
||
string histResultJson = Program.mySql.Get("employee_state_history", histParams);
|
||
|
||
using var histDoc = JsonDocument.Parse(histResultJson);
|
||
bool histError = histDoc.RootElement.GetProperty("error").GetBoolean();
|
||
if (histError)
|
||
{
|
||
string dbMsg = histDoc.RootElement.GetProperty("data").GetString() ?? "Unknown DB error";
|
||
Program.messageSender.Error($"[ClockHistoryCommand] DB error when fetching history: {dbMsg}");
|
||
throw new CommandException("Internal error while fetching clock history.");
|
||
}
|
||
|
||
return histDoc.RootElement.GetProperty("data").GetRawText();
|
||
}
|
||
|
||
[Command("fullClockHistory")]
|
||
public static string FullClockHistoryCommand(string[] args, TcpClient? client, TcpServer? socket)
|
||
{
|
||
// 1) Determine maxHistoryLength (default = 50)
|
||
int maxHistoryLength;
|
||
if (args.Length >= 1 && int.TryParse(args[0], out var parsed))
|
||
{
|
||
maxHistoryLength = parsed;
|
||
}
|
||
else
|
||
{
|
||
maxHistoryLength = 50;
|
||
}
|
||
|
||
// 2) Verify client/socket
|
||
if (client == null || socket == null)
|
||
throw new CommandException("No client connection detected.");
|
||
|
||
if (!socket.LoggedInClients.TryGetValue(client, out var employeeCode))
|
||
throw new CommandException("User not logged in.");
|
||
|
||
// 3) Look up the employee to get its Id
|
||
var empParams = new Dictionary<string, object> { { "Code", employeeCode } };
|
||
string empResultJson = Program.mySql.Get("employees", empParams);
|
||
|
||
using var empDoc = JsonDocument.Parse(empResultJson);
|
||
bool empError = empDoc.RootElement.GetProperty("error").GetBoolean();
|
||
if (empError)
|
||
{
|
||
string dbMsg = empDoc.RootElement.GetProperty("data").GetString() ?? "Unknown DB error";
|
||
Program.messageSender.Error($"[FullClockHistory] DB error when looking up employee: {dbMsg}");
|
||
throw new CommandException("Internal error while fetching user data.");
|
||
}
|
||
|
||
var empArray = empDoc.RootElement.GetProperty("data");
|
||
if (empArray.GetArrayLength() == 0)
|
||
throw new CommandException("Logged-in user not found in database.");
|
||
|
||
int employeeId = empArray[0].GetProperty("Id").GetInt32();
|
||
|
||
// 4) Query the history table for this employeeId, ordered by ChangeTime DESC, limited to maxHistoryLength
|
||
string sql = @"
|
||
SELECT `EmployeeId`, `NewState`, `ChangeTime`
|
||
FROM `employee_state_history`
|
||
ORDER BY `ChangeTime` DESC
|
||
LIMIT ?
|
||
";
|
||
var values = new List<object> {maxHistoryLength };
|
||
// Pass an empty string ("") for 'types' so that parameters get bound correctly
|
||
string historyJson = Program.mySql.Query(sql, /*types:*/ "", /*values:*/ values);
|
||
|
||
// 5) Return the JSON from Query (the client will receive {"error":…, "data":[…]} )
|
||
return historyJson;
|
||
}
|
||
|
||
|
||
#endregion
|
||
|
||
#region DEV-TEST
|
||
|
||
private static Employee GenerateTestEmployee()
|
||
{
|
||
return new Employee(
|
||
code: "TEST",
|
||
surname: "Musermann",
|
||
forename: "Max",
|
||
email: "max.mustermann@company.de"
|
||
);
|
||
}
|
||
|
||
#endregion
|
||
} |