From a0b9f05ae385a8c2bd7fbf581bd0aa1331c1f4c5 Mon Sep 17 00:00:00 2001 From: Din Date: Mon, 28 Apr 2025 15:16:44 +0800 Subject: [PATCH] Implement API key validation middleware and add force shutdown endpoint --- README.md | 14 +++++- Worker.cs | 108 ++++++++++++++++++++++++++++++++++++++++++++++- appsettings.json | 3 ++ 3 files changed, 122 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3310347..12fbfe1 100644 --- a/README.md +++ b/README.md @@ -104,4 +104,16 @@ dotnet run git add . git commit -m "Add steam running games" git push origin master -dotnet publish -c Release -o ./publish \ No newline at end of file +dotnet publish -c Release -o ./publish + +# devtest +Invoke-WebRequest -Uri "http://localhost:5000/api/kill-process" -Method POST -Body "1234" -Headers @{ "X-API-KEY" = "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a" } + +Invoke-WebRequest -Uri "http://192.168.50.52:5000/api/resource-usage" -Method GET -Headers @{ "X-API-KEY" = "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a" } + +Use 'shutdown', 'restart', or 'cancel'. +Invoke-WebRequest -Uri "http://192.168.50.52:5000/api/force-shutdown" ` +-Method POST ` +-Headers @{ "X-API-KEY" = "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a" } ` +-Body '{"Action": "shutdown", "DelaySeconds": 120}' ` +-ContentType "application/json" diff --git a/Worker.cs b/Worker.cs index 70114ee..34b218b 100644 --- a/Worker.cs +++ b/Worker.cs @@ -30,7 +30,29 @@ namespace ResourceMonitorService .AllowAnyMethod()); }); builder.Services.AddControllers().AddNewtonsoftJson(); + + // Read the API key from appsettings.json + var configuration = builder.Configuration; + var apiKey = configuration["ApiSettings:ApiKey"]; + var app = builder.Build(); + + // Middleware to validate API key + // This middleware checks for the presence of the API key in the request headers + // and compares it with the expected API key from appsettings.json. + // If the API key is missing or invalid, it returns a 401 Unauthorized response. + // + app.Use(async (context, next) => + { + if (!context.Request.Headers.TryGetValue("X-API-KEY", out var extractedApiKey) || extractedApiKey != apiKey) + { + context.Response.StatusCode = StatusCodes.Status401Unauthorized; + await context.Response.WriteAsync("Unauthorized: Invalid API Key"); + return; + } + + await next(); + }); // Apply CORS policy to allow all origins app.UseCors("AllowAllOrigins"); @@ -94,6 +116,88 @@ namespace ResourceMonitorService } }); + /* curl -X POST http://localhost:5000/api/force-shutdown -d "5000" */ + /* Invoke-WebRequest -Uri "http://localhost:5000/api/force-shutdown" -Method POST -Body "50000" -ContentType "text/plain" */ + app.MapPost("/api/force-shutdown", async context => + { + try + { + var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync(); + var parameters = JsonConvert.DeserializeObject(requestBody); + + string action = parameters?.Action?.ToString()?.ToLower(); // "shutdown" or "restart" + int delaySeconds = parameters?.DelaySeconds ?? 0; + + // Validate action input + if (action != "shutdown" && action != "restart" && action != "cancel") + { + await context.Response.WriteAsync("Invalid action. Use 'shutdown', 'restart', or 'cancel'."); + return; + } + + //if action is stop, then cancel the shutdown + if (action == "cancel") + { + var processStartInfoCancel = new ProcessStartInfo + { + FileName = "shutdown", + Arguments = "/a", + CreateNoWindow = true, + UseShellExecute = false + }; + + Process.Start(processStartInfoCancel); + await context.Response.WriteAsync("Shutdown cancelled."); + return; + } + + // Validate delay input + if (delaySeconds < 0) + { + await context.Response.WriteAsync("Delay must be a non-negative integer."); + return; + } + + // Determine the shutdown command + string shutdownCommand = action == "shutdown" ? $"/s /f /t {delaySeconds}" : $"/r /f /t {delaySeconds}"; + + var processStartInfo = new ProcessStartInfo + { + FileName = "shutdown", + Arguments = shutdownCommand, + CreateNoWindow = true, + UseShellExecute = false + }; + + Process.Start(processStartInfo); + + await context.Response.WriteAsync($"{action.ToUpper()} command executed with a delay of {delaySeconds} seconds."); + } + catch (Exception ex) + { + await context.Response.WriteAsync($"An error occurred: {ex.Message}"); + } + }); + + app.MapGet("/api/stop", async context => + { + await context.Response.WriteAsync("Stopping the service..."); + _lifetime.StopApplication(); + }); + + app.MapGet("/", () => "Resource Monitor Service is running."); + app.MapGet("/api/current-time", () => Results.Ok(GetCurrentTime())); + app.MapGet("/api/computer-info", () => Results.Ok(GetComputerInfo())); + app.MapGet("/api/cpu-usage", () => Results.Ok(GetCpuUsage())); + app.MapGet("/api/ram-usage", () => Results.Ok(GetRamUsage())); + app.MapGet("/api/gpu-usage", () => Results.Ok(GetGpuUsage())); + app.MapGet("/api/running-game", () => Results.Ok(GetCurrentlyRunningGame())); + app.MapGet("/api/total-physical-memory", () => Results.Ok(GetTotalPhysicalMemory())); + app.MapGet("/api/total-available-memory", () => Results.Ok(new { TotalAvailableMemory = Environment.WorkingSet })); + + app.MapGet("/health", () => Results.Ok("Service is healthy.")); + + _ = app.RunAsync(stoppingToken); await Task.Delay(Timeout.Infinite, stoppingToken); @@ -190,7 +294,7 @@ namespace ResourceMonitorService private object GetGpuUsage() { - if (!IsNvidiaGpuPresent()) + /* if (!IsNvidiaGpuPresent()) { return new { @@ -200,7 +304,7 @@ namespace ResourceMonitorService IsAvailable = false, Message = "No NVIDIA GPU detected" }; - } + } */ try { diff --git a/appsettings.json b/appsettings.json index 7d910a4..78a1652 100644 --- a/appsettings.json +++ b/appsettings.json @@ -12,5 +12,8 @@ "Url": "http://*:5000" } } + }, + "ApiSettings": { + "ApiKey": "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a" } }