118 lines
3.6 KiB
D
118 lines
3.6 KiB
D
|
import handy_httpd;
|
||
|
import handy_httpd.handlers.path_handler;
|
||
|
import handy_httpd.handlers.file_resolving_handler;
|
||
|
import handy_httpd.components.optional;
|
||
|
import slf4d;
|
||
|
import slf4d.default_provider;
|
||
|
|
||
|
import std.json;
|
||
|
import std.stdio;
|
||
|
import std.file;
|
||
|
import std.algorithm;
|
||
|
import std.string;
|
||
|
import std.array;
|
||
|
import std.format;
|
||
|
import std.datetime;
|
||
|
import core.sync.mutex;
|
||
|
|
||
|
import shared_utils.server_status;
|
||
|
import shared_utils.discord;
|
||
|
|
||
|
__gshared ServerStatus[] serverStatuses;
|
||
|
__gshared Mutex serversMutex;
|
||
|
__gshared string agentKey = "abc";
|
||
|
__gshared string clientKey = "abc";
|
||
|
|
||
|
void main() {
|
||
|
auto provider = new DefaultProvider(false, Levels.INFO);
|
||
|
configureLoggingProvider(provider);
|
||
|
|
||
|
serversMutex = new Mutex();
|
||
|
|
||
|
PathHandler handler = new PathHandler();
|
||
|
handler.addMapping(Method.POST, "/api/servers", &postServerStatus);
|
||
|
handler.addMapping(Method.GET, "/api/servers", &listServers);
|
||
|
handler.addMapping(Method.POST, "/api/servers/:id/requests", &requestServerStartup);
|
||
|
handler.addMapping(Method.GET, "/api/server-requests", &getServerRequests);
|
||
|
handler.addMapping(Method.GET, "/**", new FileResolvingHandler(
|
||
|
"app",
|
||
|
DirectoryResolutionStrategies.serveIndexFiles
|
||
|
));
|
||
|
|
||
|
ServerConfig config;
|
||
|
config.connectionQueueSize = 20;
|
||
|
config.receiveBufferSize = 4096;
|
||
|
config.workerPoolSize = 3;
|
||
|
config.port = 8105;
|
||
|
HttpServer server = new HttpServer(handler, config);
|
||
|
server.start();
|
||
|
}
|
||
|
|
||
|
/// Called when the agent posts the server status to us.
|
||
|
void postServerStatus(ref HttpRequestContext ctx) {
|
||
|
Optional!string key = ctx.request.headers.getFirst("X-Agent-Key");
|
||
|
if (!key || key.value != agentKey) {
|
||
|
ctx.response.status = HttpStatus.UNAUTHORIZED;
|
||
|
return;
|
||
|
}
|
||
|
JSONValue jsonBody = ctx.request.readBodyAsJson();
|
||
|
serversMutex.lock();
|
||
|
scope(exit) serversMutex.unlock();
|
||
|
serverStatuses = deserializeServerStatuses(jsonBody);
|
||
|
|
||
|
// 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");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Called by the web app when a user is refreshing the list of servers.
|
||
|
void listServers(ref HttpRequestContext ctx) {
|
||
|
serversMutex.lock();
|
||
|
scope(exit) serversMutex.unlock();
|
||
|
JSONValue payload = serializeServerStatuses(serverStatuses);
|
||
|
ctx.response.writeBodyString(payload.toJSON, "application/json");
|
||
|
}
|
||
|
|
||
|
/// Called by a user when they request to start a server.
|
||
|
void requestServerStartup(ref HttpRequestContext ctx) {
|
||
|
Optional!string key = ctx.request.headers.getFirst("X-Client-Key");
|
||
|
if (!key || key.value != clientKey) {
|
||
|
ctx.response.status = HttpStatus.UNAUTHORIZED;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
string identifier = ctx.request.getPathParamAs!string("id");
|
||
|
serversMutex.lock();
|
||
|
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();
|
||
|
sendDiscordMessage(format!"User requested to start server %s."(server.identifier));
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
ctx.response.status = HttpStatus.NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
/// Called by the agent to get the list of servers to start.
|
||
|
void getServerRequests(ref HttpRequestContext ctx) {
|
||
|
JSONValue result = JSONValue.emptyArray;
|
||
|
serversMutex.lock();
|
||
|
scope(exit) serversMutex.unlock();
|
||
|
foreach (server; serverStatuses) {
|
||
|
if (isStartupRequested(server.identifier)) {
|
||
|
result.array ~= JSONValue(server.identifier);
|
||
|
}
|
||
|
}
|
||
|
ctx.response.writeBodyString(result.toJSON, "application/json");
|
||
|
}
|
||
|
|
||
|
bool isStartupRequested(string id) {
|
||
|
return std.file.exists("request_" ~ id ~ ".txt");
|
||
|
}
|