Commit 0ca2baa0 authored by jan.koester's avatar jan.koester
Browse files

test

parent d5e6d089
Loading
Loading
Loading
Loading
+102 −30
Original line number Diff line number Diff line
@@ -49,17 +49,14 @@ function getMod(scriptPath) {
  return { mod, p };
}

globalThis.__blogiEmbeddedRun = function(reqPath, resPath) {
  try {
    const req = JSON.parse(fs.readFileSync(reqPath, 'utf8'));
function executeRequest(req) {
  const hook = (typeof req.hook === 'string' && req.hook) ? req.hook : 'render';
  if (!req.script_path) throw new Error('request.script_path is required');

  const r = getMod(req.script_path);
  const fn = resolveHook(r.mod, hook);
  if (!fn) {
      fs.writeFileSync(resPath, JSON.stringify({ status: 'success', missing_hook: true, result: null }), 'utf8');
      return true;
    return { status: 'success', missing_hook: true, result: null };
  }

  const result = fn(req);
@@ -81,11 +78,17 @@ globalThis.__blogiEmbeddedRun = function(reqPath, resPath) {
    else if (result && typeof result === 'object' && typeof result.html === 'string') html = result.html;
    else throw new Error('render() must return a string or { html: string }');

      fs.writeFileSync(resPath, JSON.stringify({ status: 'success', html, result: { html } }), 'utf8');
      return true;
    return { status: 'success', html, result: { html } };
  }

  return { status: 'success', missing_hook: false, result };
}

    fs.writeFileSync(resPath, JSON.stringify({ status: 'success', missing_hook: false, result }), 'utf8');
globalThis.__blogiEmbeddedRun = function(reqPath, resPath) {
  try {
    const req = JSON.parse(fs.readFileSync(reqPath, 'utf8'));
    const response = executeRequest(req);
    fs.writeFileSync(resPath, JSON.stringify(response), 'utf8');
    return true;
  } catch (err) {
    const msg = (err && err.message) ? err.message : 'Unknown embedded runner error';
@@ -99,6 +102,17 @@ globalThis.__blogiEmbeddedRun = function(reqPath, resPath) {
};
)JS";

globalThis.__blogiEmbeddedRunJson = function(reqJson) {
  try {
    const req = JSON.parse(reqJson);
    const response = executeRequest(req);
    return JSON.stringify(response);
  } catch (err) {
    const msg = (err && err.message) ? err.message : 'Unknown embedded runner error';
    return JSON.stringify({ status: 'error', message: msg });
  }
};

class EmbeddedRuntime {
public:
    int runRequest(const std::string &reqPath, const std::string &resPath) {
@@ -137,6 +151,46 @@ public:
        return 0;
    }

      int runRequestJson(const std::string &requestJson,
                 std::string &responseJson) {
        std::lock_guard<std::mutex> lock(mutex_);
        if (!ensureInitializedLocked()) {
          return 1;
        }

        auto *isolate = setup_->isolate();

        v8::Locker locker(isolate);
        v8::Isolate::Scope isolateScope(isolate);
        v8::HandleScope handleScope(isolate);
        auto context = setup_->context();
        v8::Context::Scope contextScope(context);
        v8::TryCatch tryCatch(isolate);
        node::AsyncResource asyncResource(isolate, context->Global(), "blogi-editor-embedded-json");

        auto fn = runFunctionJson_.Get(isolate);
        v8::Local<v8::Value> argv[1] = {
          v8::String::NewFromUtf8(isolate, requestJson.c_str()).ToLocalChecked()
        };

        auto maybeRet = asyncResource.MakeCallback(fn, 1, argv);
        v8::Local<v8::Value> ret;
        if (maybeRet.IsEmpty() || !maybeRet.ToLocal(&ret)) {
          if (tryCatch.HasCaught()) {
            v8::String::Utf8Value exceptionUtf8(isolate, tryCatch.Exception());
            const char *msg = *exceptionUtf8 ? *exceptionUtf8 : "unknown JS exception";
            std::cerr << "Node embedded runtime error: run json call failed: " << msg << std::endl;
          } else {
            std::cerr << "Node embedded runtime error: run json call failed" << std::endl;
          }
          return 1;
        }

        v8::String::Utf8Value utf8(isolate, ret);
        responseJson = *utf8 ? *utf8 : "";
        return 0;
      }

private:
    bool ensureInitializedLocked() {
        if (initialized_) {
@@ -208,6 +262,18 @@ private:
            }

            runFunction_.Reset(isolate, fnVal.As<v8::Function>());

      v8::Local<v8::Value> fnJsonVal;
      auto fnJsonName = v8::String::NewFromUtf8(isolate, "__blogiEmbeddedRunJson").ToLocalChecked();
      if (!context->Global()->Get(context, fnJsonName).ToLocal(&fnJsonVal) || !fnJsonVal->IsFunction()) {
        std::cerr << "Node embedded bootstrap failed: __blogiEmbeddedRunJson not found" << std::endl;
        setup_.reset();
        node::TearDownOncePerProcess();
        init_.reset();
        return false;
      }

      runFunctionJson_.Reset(isolate, fnJsonVal.As<v8::Function>());
        }

        initialized_ = true;
@@ -219,6 +285,7 @@ private:
    std::shared_ptr<node::InitializationResult> init_;
    std::unique_ptr<node::CommonEnvironmentSetup> setup_;
    v8::Global<v8::Function> runFunction_;
    v8::Global<v8::Function> runFunctionJson_;
};

inline EmbeddedRuntime &runtime() {
@@ -231,4 +298,9 @@ inline int runNodeRunner(const std::string &reqPath,
    return runtime().runRequest(reqPath, resPath);
}

inline int runNodeRunnerJson(const std::string &requestJson,
               std::string &responseJson) {
  return runtime().runRequestJson(requestJson, responseJson);
}

} // namespace blogi::webedit::embedded
+3 −32
Original line number Diff line number Diff line
@@ -65,30 +65,7 @@ std::string getEnvOrDefault(const char *key, const char *fallback) {
    return blogi::webedit::embedded::getEnvOrDefault(key, fallback);
}

std::string makeTempPath(const std::string &name) {
    std::ostringstream path;
    path << "/tmp/blogi_nodecatalog_" << std::chrono::high_resolution_clock::now().time_since_epoch().count() << "_" << name;
    return path.str();
}

void writeFile(const std::string &path, const std::string &data) {
    std::ofstream out(path, std::ios::trunc);
    if (!out.is_open()) return;
    out << data;
}

std::string readFile(const std::string &path) {
    std::ifstream in(path);
    if (!in.is_open()) return "";
    std::ostringstream ss;
    ss << in.rdbuf();
    return ss.str();
}

bool queryNodeWidgetDescriptor(const std::string &scriptPath, NodeWidgetDescriptor &desc) {
    std::string reqPath = makeTempPath("request.json");
    std::string resPath = makeTempPath("response.json");

    bool ok = false;
    json_object *req = json_object_new_object();
    json_object_object_add(req, "script_path", json_object_new_string(scriptPath.c_str()));
@@ -99,26 +76,22 @@ bool queryNodeWidgetDescriptor(const std::string &scriptPath, NodeWidgetDescript
    json_object_object_add(req, "properties", json_object_new_object());

    const char *reqJson = json_object_to_json_string_ext(req, JSON_C_TO_STRING_NOSLASHESCAPE);
    writeFile(reqPath, reqJson ? reqJson : "{}");
    std::string reqJsonStr = reqJson ? reqJson : "{}";
    json_object_put(req);

    std::string raw;
    try {
        (void)blogi::webedit::embedded::runNodeRunner(reqPath, resPath);
        (void)blogi::webedit::embedded::runNodeRunnerJson(reqJsonStr, raw);
    } catch (const std::exception &e) {
        std::cerr << "[webedit] node metadata failed for " << scriptPath
                  << ": " << e.what() << std::endl;
        std::remove(reqPath.c_str());
        std::remove(resPath.c_str());
        return false;
    } catch (...) {
        std::cerr << "[webedit] node metadata failed for " << scriptPath
                  << ": unknown exception" << std::endl;
        std::remove(reqPath.c_str());
        std::remove(resPath.c_str());
        return false;
    }

    std::string raw = readFile(resPath);
    json_object *res = raw.empty() ? nullptr : json_tokener_parse(raw.c_str());
    if (res && json_object_is_type(res, json_type_object)) {
        json_object *statusObj = nullptr;
@@ -159,8 +132,6 @@ bool queryNodeWidgetDescriptor(const std::string &scriptPath, NodeWidgetDescript
    }

    if (res) json_object_put(res);
    std::remove(reqPath.c_str());
    std::remove(resPath.c_str());
    return ok;
}

+6 −9
Original line number Diff line number Diff line
@@ -610,9 +610,6 @@ protected:
            return out;
        }

        std::string reqPath = makeTempPath("request.json");
        std::string resPath = makeTempPath("response.json");

        try {
            json_object *req = json_object_new_object();
            json_object_object_add(req, "script_path", json_object_new_string(scriptPath.c_str()));
@@ -631,13 +628,16 @@ protected:
                json_object_object_add(req, "payload", json_object_get(payload));

            const char *reqJson = json_object_to_json_string_ext(req, JSON_C_TO_STRING_NOSLASHESCAPE);
            writeFile(reqPath, reqJson ? reqJson : "{}");
            std::string reqJsonStr = reqJson ? reqJson : "{}";
            json_object_put(req);

            int rc = blogi::webedit::embedded::runNodeRunner(reqPath, resPath);
            std::string resRaw;
            int rc = blogi::webedit::embedded::runNodeRunnerJson(reqJsonStr, resRaw);
            (void)rc;

            std::string resRaw = readFile(resPath);
            if (resRaw.empty()) {
                throw std::runtime_error("Empty JSON from node runner");
            }
            json_object *res = json_tokener_parse(resRaw.c_str());
            if (!res)
                throw std::runtime_error("Invalid JSON from node runner");
@@ -680,9 +680,6 @@ protected:
            out.ok = false;
            out.message = "Unknown NodeWidget call error";
        }

        std::remove(reqPath.c_str());
        std::remove(resPath.c_str());
        return out;
    }