From b4454495426506e6e1c9936f3db891a819465924 Mon Sep 17 00:00:00 2001 From: andrey Date: Thu, 16 Jul 2026 10:32:26 +0300 Subject: [PATCH] refactor: structured config with validation (ServerConfig class) --- pipeline-runner/src/config.cpp | 103 +++++++++++++++++++++++++++++++++ pipeline-runner/src/config.h | 29 ++++++++++ pipeline-runner/src/main.cpp | 48 +++++---------- 3 files changed, 146 insertions(+), 34 deletions(-) create mode 100644 pipeline-runner/src/config.cpp create mode 100644 pipeline-runner/src/config.h diff --git a/pipeline-runner/src/config.cpp b/pipeline-runner/src/config.cpp new file mode 100644 index 0000000..8374b20 --- /dev/null +++ b/pipeline-runner/src/config.cpp @@ -0,0 +1,103 @@ +#include "config.h" + +#include +#include +#include + +static const int DefaultPort = 8000; +static const PIString DefaultPipelinesDir = "./storage/pipelines/"; +static const PIString DefaultLogsDir = "./storage/logs/"; + +ServerConfig::ServerConfig() + : port_(DefaultPort) + , pipelines_dir_(DefaultPipelinesDir) + , logs_dir_(DefaultLogsDir) + , web_root_() + , valid_(true) {} + +bool ServerConfig::loadFromFile(const PIString & path) { + if (!PIFile::isExists(path)) { + piCout << "Config file not found: " << path << ", using defaults"; + valid_ = true; + return true; + } + + PIByteArray data = PIFile::readAll(path); + PIJSON config = PIJSON::fromJSON(PIString::fromUTF8(data)); + + if (!config.isObject()) { + piCout << "Invalid config file: not a JSON object"; + valid_ = false; + return false; + } + + piCout << "Loaded config from: " << path; + + if (config.contains("port")) { + port_ = config["port"].toInt(); + } + if (config.contains("pipelines_dir")) { + pipelines_dir_ = config["pipelines_dir"].toString(); + } + if (config.contains("logs_dir")) { + logs_dir_ = config["logs_dir"].toString(); + } + if (config.contains("web_root")) { + web_root_ = config["web_root"].toString(); + } + + // Normalize directory paths + pipelines_dir_ = normalizeDir(pipelines_dir_); + logs_dir_ = normalizeDir(logs_dir_); + web_root_ = normalizeDir(web_root_); + + // Validate + valid_ = true; + + if (port_ < 1024 || port_ > 65535) { + piCout << "Invalid port: " << port_ << " (must be 1024-65535)"; + valid_ = false; + } + + if (pipelines_dir_.isEmpty()) { + piCout << "Invalid config: pipelines_dir must not be empty"; + valid_ = false; + } + + if (logs_dir_.isEmpty()) { + piCout << "Invalid config: logs_dir must not be empty"; + valid_ = false; + } + + return valid_; +} + +int ServerConfig::port() const { + return port_; +} + +PIString ServerConfig::pipelinesDir() const { + return pipelines_dir_; +} + +PIString ServerConfig::logsDir() const { + return logs_dir_; +} + +PIString ServerConfig::webRoot() const { + return web_root_; +} + +bool ServerConfig::isValid() const { + return valid_; +} + +PIString ServerConfig::normalizeDir(const PIString & dir) { + if (dir.isEmpty()) { + return dir; + } + if (!dir.endsWith('/')) { + return dir + '/'; + } + return dir; +} diff --git a/pipeline-runner/src/config.h b/pipeline-runner/src/config.h new file mode 100644 index 0000000..1954088 --- /dev/null +++ b/pipeline-runner/src/config.h @@ -0,0 +1,29 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include + +class ServerConfig { +public: + ServerConfig(); + + bool loadFromFile(const PIString & path); + + int port() const; + PIString pipelinesDir() const; + PIString logsDir() const; + PIString webRoot() const; + + bool isValid() const; + +private: + static PIString normalizeDir(const PIString & dir); + + int port_; + PIString pipelines_dir_; + PIString logs_dir_; + PIString web_root_; + bool valid_; +}; + +#endif diff --git a/pipeline-runner/src/main.cpp b/pipeline-runner/src/main.cpp index a18bde1..2999f3e 100644 --- a/pipeline-runner/src/main.cpp +++ b/pipeline-runner/src/main.cpp @@ -1,52 +1,32 @@ +#include "config.h" #include "server.h" #include -#include -#include #include #include -static PIString getConfigValue(const PIJSON & config, const PIString & key, const PIString & def = PIString()) { - if (config.contains(key)) { - return config[key].toString(); - } - return def; -} - -static int getConfigInt(const PIJSON & config, const PIString & key, int def) { - if (config.contains(key)) { - return config[key].toInt(); - } - return def; -} - int main(int argc, char * argv[]) { - // Load config file PIString configPath = "./pipeline-runner.conf"; - PIJSON config; if (argc > 1) { configPath = PIString(argv[1]); } - if (PIFile::isExists(configPath)) { - PIByteArray data = PIFile::readAll(configPath); - config = PIJSON::fromJSON(PIString::fromUTF8(data)); - piCout << "Loaded config from: " << configPath; - } else { - piCout << "Config file not found: " << configPath << ", using defaults"; + ServerConfig config; + if (!config.loadFromFile(configPath)) { + piCout << "Failed to load config, exiting"; + return 1; } - // Parse config - int port = getConfigInt(config, "port", 8000); - PIString pipelinesDir = getConfigValue(config, "pipelines_dir", "./storage/pipelines/"); - PIString logsDir = getConfigValue(config, "logs_dir", "./storage/logs/"); - PIString webRoot = getConfigValue(config, "web_root", ""); + if (!config.isValid()) { + piCout << "Config validation failed, exiting"; + return 1; + } - piCout << "Port: " << port; - piCout << "Pipelines dir: " << pipelinesDir; - piCout << "Logs dir: " << logsDir; - piCout << "Web root: " << (webRoot.isEmpty() ? "(not set)" : webRoot); + piCout << "Port: " << config.port(); + piCout << "Pipelines dir: " << config.pipelinesDir(); + piCout << "Logs dir: " << config.logsDir(); + piCout << "Web root: " << (config.webRoot().isEmpty() ? "(not set)" : config.webRoot()); // Handle signals PISignals::setSlot([](PISignals::Signal s) { @@ -57,7 +37,7 @@ int main(int argc, char * argv[]) { PISignals::grabSignals(PISignals::Interrupt); // Start server - Server server(static_cast(port), pipelinesDir, logsDir, webRoot); + Server server(static_cast(config.port()), config.pipelinesDir(), config.logsDir(), config.webRoot()); server.start(); // Wait for exit