fkg-api-proxy/source/app.d

323 lines
8.0 KiB
D

import vibe.vibe;
import std.base64 : Base64;
import std.conv : to;
import botan.filters.pipe : Pipe;
import botan.filters.b64_filt : Base64Encoder, Base64Decoder;
import botan.filters.transform_filter : TransformationFilter;
import botan.compression.zlib: ZlibDecompression;
import botan.libstate.lookup : getCipher;
import botan.algo_base.symkey : SymmetricKey, InitializationVector;
import botan.utils.types;
import config;
void main()
{
auto router = new URLRouter;
router.get("*", reverseProxyRequest(serverHost, serverPort));
router.post("/api/v1/*", &onAPIRequest);
auto settings = new HTTPServerSettings;
settings.port = proxyPortNumber;
settings.options = HTTPServerOption.parseURL | HTTPServerOption.errorStackTraces;
listenHTTP(settings, router);
runApplication();
}
void onAPIRequest(HTTPServerRequest request, HTTPServerResponse response)
{
auto apiEndpoint = request.path.split("/api/v1")[1];
auto nonce = request.headers["auth_nonce"];
auto requestBody = request.bodyReader.readAll();
auto requestJson = getRequestJson(requestBody, apiEndpoint, nonce);
handleRequest(apiEndpoint, requestJson);
//TODO: Allow request editing (requires recalculation of signature)
auto apiResponse = sendRequest(apiEndpoint, request.headers.dup, requestBody);
auto responseBody = apiResponse.bodyReader.readAll();
auto responseJson = getResponseJson(responseBody, apiEndpoint, nonce);
auto finalResponseJson = handleResponse(apiEndpoint, responseJson);
auto finalResponseBody = constructResponseBody(apiEndpoint, finalResponseJson, nonce);
sendResponse(response, apiResponse, finalResponseBody);
import std.stdio : writeln;
writeln(apiEndpoint);
}
Json getRequestJson(ubyte[] input, string endpoint, string nonce)
{
auto originalRequestPipe = Pipe
(
new Base64Decoder,
getCipher
(
cipherName,
SymmetricKey((cast(ubyte[]) keyString).ptr, keyString.length),
InitializationVector((cast(ubyte[]) nonce).ptr, nonce.length),
DECRYPTION
)
);
originalRequestPipe.processMsg(input.ptr, input.length);
auto requestJsonString = originalRequestPipe.toString;
return requestJsonString.parseJson;
}
Json getResponseJson(ubyte[] input, string endpoint, string nonce)
{
import std.zlib : uncompress;
switch(endpoint)
{
case "/config/getGameConfig":
return getRequestJson(input, endpoint, nonce);
case "/master/getMaster":
auto decompressedInput = cast(ubyte[])uncompress(input); //TODO: Use pipe instead (how?)
auto responseJsonString = cast(string) decompressedInput;
return responseJsonString.parseJson;
default:
auto decompressedInput = cast(ubyte[])uncompress(input); //TODO: Use pipe instead (how?)
auto responsePipe = Pipe
(
new Base64Decoder
);
responsePipe.processMsg(decompressedInput.ptr, decompressedInput.length);
auto responseJsonString = responsePipe.toString;
return responseJsonString.parseJson;
}
}
void handleRequest(string endpoint, Json json)
{
}
Json handleResponse(string endpoint, Json json)
{
if(endpoint == "/master/getMaster")
{
import models.master;
auto getMasterResponse = json.deserializeJson!GetMasterResponse;
foreach(character; getMasterResponse.masterCharacter)
{
auto characterId = character[0].to!uint;
import translations.character;
if(characterId in characterNames)
{
character[5] = characterNames[characterId];
}
}
foreach(characterSkill; getMasterResponse.masterCharacterSkill)
{
auto skillId = characterSkill[0].to!uint;
import translations.characterSkill;
if(skillId in skillNames)
{
characterSkill[1] = skillNames[skillId];
characterSkill[6] = skillDescriptions[skillId];
}
}
foreach(stage; getMasterResponse.masterStage)
{
auto stageId = stage[0].to!uint;
import translations.stage;
if(stageId in stageNames)
{
stage[1] = stageNames[stageId];
}
}
foreach(mission; getMasterResponse.masterMission)
{
//TODO: Figure out a way to find the wrong ones
auto missionId = mission[0].to!uint;
import translations.mission;
if(missionId in missionNames)
{
mission[1] = missionNames[missionId];
mission[2] = missionDescriptions[missionId];
mission[3] = missionRewards[missionId];
}
}
foreach(dungeon; getMasterResponse.masterDungeon)
{
auto dungeonId = dungeon[10].to!uint;
import translations.dungeon;
if(dungeonId in dungeonNames)
{
dungeon[12] = dungeonNames[dungeonId];
}
}
foreach(guest; getMasterResponse.masterGuest)
{
auto guestId = guest[0].to!uint;
import translations.guest;
if(guestId in guestPartyNames)
{
guest[1] = guestPartyNames[guestId];
}
}
foreach(item; getMasterResponse.masterItem)
{
auto itemId = item[0].to!uint;
import translations.item;
if(itemId in itemNames)
{
item[1] = itemNames[itemId];
}
}
foreach(location; getMasterResponse.masterWorldMap)
{
auto locationId = location[0].to!uint;
import translations.worldMap;
if(locationId in worldMapLocationNames)
{
location[3] = worldMapLocationNames[locationId];
location[4] = worldMapLocationCaptions[locationId];
location[5] = worldMapLocationDescriptions[locationId];
}
}
foreach(resource; getMasterResponse.masterCharacterTextResource)
{
auto resourceId = resource[0].to!uint;
import translations.characterTextResource;
if(resourceId in characterTextResource)
{
resource[3] = characterTextResource[resourceId];
}
}
foreach(leaderSkill; getMasterResponse.masterCharacterLeaderSkill)
{
auto leaderSkillId = leaderSkill[0].to!uint;
import translations.characterLeaderSkill;
if(leaderSkillId in characterLeaderSkillNames)
{
leaderSkill[1] = characterLeaderSkillNames[leaderSkillId];
leaderSkill[14] = characterLeaderSkillDescriptions[leaderSkillId];
}
}
foreach(characterBookEntry; getMasterResponse.masterCharacterBook)
{
auto characterId = characterBookEntry[0].to!uint;
import translations.characterBook;
if(characterId in characterBookFlowerLanguage)
{
characterBookEntry[5] = characterBookFlowerLanguage[characterId];
}
}
foreach(characterCategory; getMasterResponse.masterCharacterCategory)
{
auto categoryId = characterCategory[0].to!uint;
import translations.characterCategory;
if(categoryId in characterCategories)
{
characterCategory[1] = characterCategories[categoryId];
}
}
return getMasterResponse.serializeToJson;
}
return json;
}
ubyte[] constructResponseBody(string endpoint, Json json, string nonce)
{
import std.zlib : compress;
auto jsonStringBytes = cast(ubyte[]) json.toString;
switch(endpoint)
{
case "/config/getGameConfig":
auto responsePipe = Pipe
(
getCipher
(
cipherName,
SymmetricKey((cast(ubyte[]) keyString).ptr, keyString.length),
InitializationVector((cast(ubyte[]) nonce).ptr, nonce.length),
ENCRYPTION
),
new Base64Encoder
);
//TODO: Refactor this
if(jsonStringBytes.length % 16 != 0)
{
jsonStringBytes.length += 16 - jsonStringBytes.length % 16;
}
responsePipe.processMsg(jsonStringBytes.ptr, jsonStringBytes.length);
return cast(ubyte[]) responsePipe.toString;
case "/master/getMaster":
return compress(jsonStringBytes); //TODO: Use pipe instead (how?)
default:
auto responsePipe = Pipe
(
new Base64Encoder
);
responsePipe.processMsg(jsonStringBytes.ptr, jsonStringBytes.length);
return compress(responsePipe.toString);
}
}
HTTPClientResponse sendRequest(string endpoint, InetHeaderMap headers, ubyte[] requestBody)
{
return requestHTTP("http://" ~ serverHost ~ "/api/v1" ~ endpoint,
(scope HTTPClientRequest r)
{
r.method = HTTPMethod.POST;
r.headers = headers.dup;
r.writeBody(requestBody, r.headers["Content-Type"]);
}
);
}
void sendResponse(HTTPServerResponse response, HTTPClientResponse apiResponse, ubyte[] responseBody)
{
if("Transfer-Encoding" in apiResponse.headers) { apiResponse.headers.remove("Transfer-Encoding"); }
response.statusCode = apiResponse.statusCode;
response.statusPhrase = apiResponse.statusPhrase;
response.headers = apiResponse.headers.dup;
response.writeBody(responseBody);
}