scratchpad/controllers/Scratchpad.cpp

107 lines
3.1 KiB
C++

#include "Scratchpad.h"
#include <fstream>
#include <array>
#include <memory>
#include <filesystem>
#include <iostream>
// Helper: Execute shell command and capture output
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
void Scratchpad::runCode(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback)
{
// 1. Auth Check
auto authHeader = req->getHeader("X-API-Key");
if (authHeader != API_KEY)
{
Json::Value error;
error["error"] = "Unauthorized. Invalid API Key.";
auto resp = HttpResponse::newHttpJsonResponse(error);
resp->setStatusCode(k401Unauthorized);
callback(resp);
return;
}
// 2. Get Code from Body
auto json = req->getJsonObject();
if (!json || !(*json)["code"].isString())
{
Json::Value error;
error["error"] = "Invalid JSON. 'code' field required.";
auto resp = HttpResponse::newHttpJsonResponse(error);
resp->setStatusCode(k400BadRequest);
callback(resp);
return;
}
std::string userCode = (*json)["code"].asString();
// 3. Compile and Run
Json::Value ret;
try {
std::string output = compileAndRun(userCode);
ret["status"] = "success";
ret["output"] = output;
} catch (const std::exception& e) {
ret["status"] = "error";
ret["error"] = e.what();
}
auto resp = HttpResponse::newHttpJsonResponse(ret);
callback(resp);
}
std::string Scratchpad::compileAndRun(const std::string& code)
{
// A. Write to temp file
std::string filename = "temp_scratchpad.cpp";
std::string binary = "./temp_scratchpad";
std::ofstream out(filename);
out << code;
out.close();
// B. Compile (Clang++)
// Using -std=c++20 and linking nothing extra for now
std::string compileCmd = "clang++ -std=c++20 " + filename + " -o " + binary + " 2>&1";
std::string compileOut = exec(compileCmd.c_str());
// Check if compilation failed (binary doesn't exist)
if (!std::filesystem::exists(binary)) {
return "Compilation Error:\n" + compileOut;
}
// C. Execute
// Timeout logic should be added here later (e.g., using 'timeout' cmd)
std::string runCmd = binary + " 2>&1";
std::string runOut = exec(runCmd.c_str());
// Cleanup
std::filesystem::remove(filename);
std::filesystem::remove(binary);
return runOut;
}
void Scratchpad::status(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback)
{
Json::Value ret;
ret["status"] = "online";
ret["version"] = "2.1 (Execution Engine)";
ret["compiler"] = "Clang 19.1.7";
ret["note"] = "Send POST to /scratchpad/run with header 'X-API-Key'";
auto resp = HttpResponse::newHttpJsonResponse(ret);
callback(resp);
}