diff --git a/api/source/app.d b/api/source/app.d index 42b1feb..7413191 100644 --- a/api/source/app.d +++ b/api/source/app.d @@ -18,10 +18,13 @@ import core.sync.mutex; import shared_utils.server_status; import shared_utils.discord; +import startup_requests; + const AGENT_KEY_HEADER = "X-Agent-Key"; const CLIENT_KEY_HEADER = "X-Client-Key"; __gshared ServerStatus[] serverStatuses; +__gshared SysTime lastServerStatusTimestamp; __gshared Mutex serversMutex; __gshared ApiConfig config; @@ -38,6 +41,18 @@ void main() { configureLoggingProvider(provider); serversMutex = new Mutex(); + lastServerStatusTimestamp = Clock.currTime(); + + // JobScheduler scheduler = JobScheduler.getDefault(); + // scheduler.addJob( + // &removeOldStartupRequests, + // new FixedIntervalSchedule(minutes(5)) + // ); + // scheduler.addJob( + // &checkForOutOfDateServerStatus, + // new FixedIntervalSchedule(minutes(1)) + // ); + // scheduler.start(); PathHandler handler = new PathHandler(); handler.addMapping(Method.POST, "/api/servers", &postServerStatus); @@ -66,11 +81,12 @@ void postServerStatus(ref HttpRequestContext ctx) { serversMutex.lock(); scope(exit) serversMutex.unlock(); serverStatuses = deserializeServerStatuses(jsonBody); + lastServerStatusTimestamp = Clock.currTime(); // Remove startup requests for any servers that are now online. foreach (server; serverStatuses) { if (server.online && isStartupRequested(server.identifier)) { - std.file.remove("request_" ~ server.identifier ~ ".txt"); + removeStartupRequest(server.identifier); } } } @@ -92,9 +108,7 @@ void requestServerStartup(ref HttpRequestContext ctx) { scope(exit) serversMutex.unlock(); foreach (server; serverStatuses) { if (server.identifier == identifier) { - File f = File("request_" ~ identifier ~ ".txt", "w"); - f.writeln(Clock.currTime().toISOExtString()); - f.close(); + createServerStartupRequest(server.identifier); sendDiscordMessage( config.discordWebhookUrl, format!"User requested to start server %s."(server.identifier) @@ -118,10 +132,6 @@ void getServerRequests(ref HttpRequestContext ctx) { ctx.response.writeBodyString(result.toJSON, "application/json"); } -bool isStartupRequested(string id) { - return std.file.exists("request_" ~ id ~ ".txt"); -} - void checkKey(ref HttpRequestContext ctx, string keyHeaderName, string expectedValue) { Optional!string key = ctx.request.headers.getFirst(keyHeaderName); if (!key || key.value != expectedValue) { @@ -140,3 +150,12 @@ ApiConfig readConfig() { props["discordWebhookUrl"] ); } + +void checkForOutOfDateServerStatus() { + Duration statusAge = Clock.currTime() - lastServerStatusTimestamp; + serversMutex.lock(); + scope(exit) serversMutex.unlock(); + if (statusAge > minutes(15) && serverStatuses.length > 0) { + serverStatuses = []; + } +} diff --git a/api/source/startup_requests.d b/api/source/startup_requests.d new file mode 100644 index 0000000..83aa075 --- /dev/null +++ b/api/source/startup_requests.d @@ -0,0 +1,54 @@ +module startup_requests; + +import std.stdio; +import std.datetime; +import std.file; +import std.path; +import std.string; + +void createServerStartupRequest(string serverIdentifier) { + File f = File(getRequestFile(serverIdentifier), "w"); + f.writeln(Clock.currTime(UTC()).toISOExtString()); + f.close(); +} + +bool isStartupRequested(string serverIdentifier) { + return exists(getRequestFile(serverIdentifier)); +} + +void removeStartupRequest(string serverIdentifier) { + string requestFile = getRequestFile(serverIdentifier); + if (exists(requestFile)) std.file.remove(requestFile); +} + +void removeOldStartupRequests() { + foreach (string requestFile; findAllStartupRequests()) { + Duration age = getRequestAge(requestFile); + if (age > hours(1)) { + std.file.remove(requestFile); + } + } +} + + + +private Duration getRequestAge(string requestFile) { + string text = readText(requestFile).strip(); + SysTime timestamp = SysTime.fromISOExtString(text, UTC()); + return Clock.currTime(UTC()) - timestamp; +} + +private string[] findAllStartupRequests() { + string[] requestFiles; + foreach (DirEntry entry; dirEntries(getcwd(), SpanMode.shallow, false)) { + string filename = baseName(entry.name); + if (startsWith(filename, "request_") && endsWith(filename, ".txt")) { + requestFiles ~= entry.name; + } + } + return requestFiles; +} + +private string getRequestFile(string identifier) { + return "request_" ~ identifier ~ ".txt"; +}