refactor: structured config with validation (ServerConfig class)

This commit is contained in:
2026-07-16 10:32:26 +03:00
parent dde18673df
commit b445449542
3 changed files with 146 additions and 34 deletions
+103
View File
@@ -0,0 +1,103 @@
#include "config.h"
#include <picout.h>
#include <pifile.h>
#include <pijson.h>
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;
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <pistring.h>
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
+14 -34
View File
@@ -1,52 +1,32 @@
#include "config.h"
#include "server.h"
#include <picout.h>
#include <pifile.h>
#include <pijson.h>
#include <pikbdlistener.h>
#include <pisignals.h>
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<ushort>(port), pipelinesDir, logsDir, webRoot);
Server server(static_cast<ushort>(config.port()), config.pipelinesDir(), config.logsDir(), config.webRoot());
server.start();
// Wait for exit