107 lines
3.1 KiB
C++
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);
|
|
}
|