Commit 79f28af5 authored by jan.koester's avatar jan.koester
Browse files

test

parent 67ab49eb
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -47,6 +47,22 @@ add_executable(blogi-editor
    src/htmlimport.cpp
)

find_path(LIBNODE_INCLUDE_DIR NAMES node.h PATH_SUFFIXES node include/node)
find_library(LIBNODE_LIBRARY NAMES node libnode)

if(NOT LIBNODE_INCLUDE_DIR OR NOT LIBNODE_LIBRARY)
    message(FATAL_ERROR "libnode is required but was not found. Install libnode headers/library and reconfigure.")
endif()

message(STATUS "libnode enabled: ${LIBNODE_LIBRARY} (${LIBNODE_INCLUDE_DIR})")
target_include_directories(blogi-editor PRIVATE ${LIBNODE_INCLUDE_DIR})
target_link_libraries(blogi-editor ${LIBNODE_LIBRARY})
target_compile_definitions(blogi-editor PRIVATE BLOGI_HAVE_LIBNODE=1)

# Export to widget subdirectories that include the shared node bridge header.
set(BLOGI_LIBNODE_INCLUDE_DIR "${LIBNODE_INCLUDE_DIR}" CACHE INTERNAL "blogi libnode include dir")
set(BLOGI_LIBNODE_LIBRARY "${LIBNODE_LIBRARY}" CACHE INTERNAL "blogi libnode library")

if(TARGET blogidev)
    target_link_libraries(blogi-editor
        blogidev
+0 −2
Original line number Diff line number Diff line
@@ -17,8 +17,6 @@ WEBEDIT:
  PREFIX: ""
  LANGUAGE: "DE"
  NODE:
    BINARY: "/usr/lib/blogi-editor/node/bin/node"
    RUNNER: "/usr/share/blogi-editor/node/node_widget_runner.js"
    WIDGETDIR:
      - "/usr/share/blogi-editor/node/widgets"
  AUTHDB:
+0 −8
Original line number Diff line number Diff line
@@ -77,14 +77,6 @@ int main(int argc, char *argv[]) {
        std::cout << "Loading config from: " << configPath << std::endl;
        webedit::Config config(configPath);

        if (!config.getNodeBinary().empty()) {
            setenv("BLOGI_WEBEDIT_NODE_BIN", config.getNodeBinary().c_str(), 1);
            std::cout << "Configured Node.js binary: " << config.getNodeBinary() << std::endl;
        }
        if (!config.getNodeRunner().empty()) {
            setenv("BLOGI_WEBEDIT_NODE_RUNNER", config.getNodeRunner().c_str(), 1);
            std::cout << "Configured Node.js widget runner: " << config.getNodeRunner() << std::endl;
        }
        {
            const auto &dirs = config.getNodeWidgetDirs();
            std::string joined;
+80 −0
Original line number Diff line number Diff line
#pragma once

#include <cstdlib>
#include <string>
#include <vector>

#ifndef BLOGI_HAVE_LIBNODE
#error "BLOGI_HAVE_LIBNODE must be defined: this build supports only embedded libnode"
#endif

#include <node.h>

namespace blogi::webedit::nodebridge {

inline std::string getEnvOrDefault(const char *key, const char *fallback) {
    const char *v = std::getenv(key);
    if (v && v[0] != '\0') {
        return std::string(v);
    }
    return std::string(fallback);
}

inline int runNodeRunner(const std::string &reqPath,
                         const std::string &resPath) {
    static const std::string bridgeScript =
        "'use strict';"
        "const fs=require('fs');"
        "const path=require('path');"
        "(async()=>{"
        "const[, ,requestFile,responseFile]=process.argv;"
        "if(!requestFile||!responseFile)throw new Error('Usage: node -e <bridge> <request.json> <response.json>');"
        "const req=JSON.parse(fs.readFileSync(requestFile,'utf8'));"
        "const hook=(typeof req.hook==='string'&&req.hook)?req.hook:'render';"
        "if(!req.script_path)throw new Error('request.script_path is required');"
        "const scriptPath=path.resolve(req.script_path);"
        "const mod=require(scriptPath);"
        "const resolveHook=(m,h)=>{"
        "if(h==='render'){if(typeof m==='function')return m;if(typeof m.render==='function')return m.render;return null;}"
        "if(typeof m[h]==='function')return m[h];"
        "if(m.hooks&&typeof m.hooks[h]==='function')return m.hooks[h];"
        "return null;"
        "};"
        "const fn=resolveHook(mod,hook);"
        "if(!fn){fs.writeFileSync(responseFile,JSON.stringify({status:'success',missing_hook:true,result:null}),'utf8');return;}"
        "const result=await Promise.resolve(fn(req));"
        "if(hook==='metadata'&&result&&typeof result==='object'){"
        "if(!result.type_id||typeof result.type_id!=='string'||!result.type_id.trim()){"
        "const scriptBase=path.basename(scriptPath,path.extname(scriptPath));"
        "const widgetName=(typeof result.name==='string'&&result.name.trim())?result.name.trim():scriptBase;"
        "if(typeof mod.getUUID==='function')result.type_id=String(mod.getUUID());"
        "if(!result.type_id||!result.type_id.trim())throw new Error('Widget '+widgetName+' has no hardcoded UUID (type_id/getUUID missing)');"
        "}"
        "}"
        "if(hook==='render'){"
        "let html='';"
        "if(typeof result==='string')html=result;"
        "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(responseFile,JSON.stringify({status:'success',html,result:{html}}),'utf8');"
        "return;"
        "}"
        "fs.writeFileSync(responseFile,JSON.stringify({status:'success',missing_hook:false,result}),'utf8');"
        "})().catch((err)=>{"
        "const[, , ,responseFile]=process.argv;"
        "const payload=JSON.stringify({status:'error',message:(err&&err.message)?err.message:'Unknown Node runner error'});"
        "if(responseFile){try{fs.writeFileSync(responseFile,payload,'utf8');}catch(_){process.stderr.write(payload+'\\n');}}"
        "else{process.stderr.write(payload+'\\n');}"
        "process.exitCode=1;"
        "});";

    std::vector<std::string> args = {"node", "-e", bridgeScript, reqPath, resPath};
    std::vector<char *> argv;
    argv.reserve(args.size());
    for (auto &arg : args) {
        argv.push_back(const_cast<char *>(arg.c_str()));
    }
    return node::Start(static_cast<int>(argv.size()), argv.data());
}

} // namespace blogi::webedit::nodebridge
+3 −18
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@

#include "webedit_api.h"
#include "htmlimport.h"
#include "node_bridge.h"

namespace {
struct NodeWidgetDescriptor {
@@ -57,20 +58,7 @@ struct NodeWidgetDescriptor {
};

std::string getEnvOrDefault(const char *key, const char *fallback) {
    const char *v = std::getenv(key);
    if (v && v[0] != '\0')
        return std::string(v);
    return std::string(fallback);
}

std::string quoteShell(const std::string &value) {
    std::string out = "'";
    for (char c : value) {
        if (c == '\'') out += "'\\''";
        else out += c;
    }
    out += "'";
    return out;
    return blogi::webedit::nodebridge::getEnvOrDefault(key, fallback);
}

std::string makeTempPath(const std::string &name) {
@@ -94,8 +82,6 @@ std::string readFile(const std::string &path) {
}

bool queryNodeWidgetDescriptor(const std::string &scriptPath, NodeWidgetDescriptor &desc) {
    std::string nodeBin = getEnvOrDefault("BLOGI_WEBEDIT_NODE_BIN", "node");
    std::string runner = getEnvOrDefault("BLOGI_WEBEDIT_NODE_RUNNER", "/usr/share/blogi-editor/node/node_widget_runner.js");
    std::string reqPath = makeTempPath("request.json");
    std::string resPath = makeTempPath("response.json");

@@ -112,8 +98,7 @@ bool queryNodeWidgetDescriptor(const std::string &scriptPath, NodeWidgetDescript
    writeFile(reqPath, reqJson ? reqJson : "{}");
    json_object_put(req);

    std::string cmd = quoteShell(nodeBin) + " " + quoteShell(runner) + " " + quoteShell(reqPath) + " " + quoteShell(resPath);
    (void)std::system(cmd.c_str());
    (void)blogi::webedit::nodebridge::runNodeRunner(reqPath, resPath);

    std::string raw = readFile(resPath);
    json_object *res = raw.empty() ? nullptr : json_tokener_parse(raw.c_str());
Loading