refactor: structured config with validation (ServerConfig class)
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -1,52 +1,32 @@
|
|||||||
|
#include "config.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
||||||
#include <picout.h>
|
#include <picout.h>
|
||||||
#include <pifile.h>
|
|
||||||
#include <pijson.h>
|
|
||||||
#include <pikbdlistener.h>
|
#include <pikbdlistener.h>
|
||||||
#include <pisignals.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[]) {
|
int main(int argc, char * argv[]) {
|
||||||
// Load config file
|
|
||||||
PIString configPath = "./pipeline-runner.conf";
|
PIString configPath = "./pipeline-runner.conf";
|
||||||
PIJSON config;
|
|
||||||
|
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
configPath = PIString(argv[1]);
|
configPath = PIString(argv[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PIFile::isExists(configPath)) {
|
ServerConfig config;
|
||||||
PIByteArray data = PIFile::readAll(configPath);
|
if (!config.loadFromFile(configPath)) {
|
||||||
config = PIJSON::fromJSON(PIString::fromUTF8(data));
|
piCout << "Failed to load config, exiting";
|
||||||
piCout << "Loaded config from: " << configPath;
|
return 1;
|
||||||
} else {
|
|
||||||
piCout << "Config file not found: " << configPath << ", using defaults";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse config
|
if (!config.isValid()) {
|
||||||
int port = getConfigInt(config, "port", 8000);
|
piCout << "Config validation failed, exiting";
|
||||||
PIString pipelinesDir = getConfigValue(config, "pipelines_dir", "./storage/pipelines/");
|
return 1;
|
||||||
PIString logsDir = getConfigValue(config, "logs_dir", "./storage/logs/");
|
}
|
||||||
PIString webRoot = getConfigValue(config, "web_root", "");
|
|
||||||
|
|
||||||
piCout << "Port: " << port;
|
piCout << "Port: " << config.port();
|
||||||
piCout << "Pipelines dir: " << pipelinesDir;
|
piCout << "Pipelines dir: " << config.pipelinesDir();
|
||||||
piCout << "Logs dir: " << logsDir;
|
piCout << "Logs dir: " << config.logsDir();
|
||||||
piCout << "Web root: " << (webRoot.isEmpty() ? "(not set)" : webRoot);
|
piCout << "Web root: " << (config.webRoot().isEmpty() ? "(not set)" : config.webRoot());
|
||||||
|
|
||||||
// Handle signals
|
// Handle signals
|
||||||
PISignals::setSlot([](PISignals::Signal s) {
|
PISignals::setSlot([](PISignals::Signal s) {
|
||||||
@@ -57,7 +37,7 @@ int main(int argc, char * argv[]) {
|
|||||||
PISignals::grabSignals(PISignals::Interrupt);
|
PISignals::grabSignals(PISignals::Interrupt);
|
||||||
|
|
||||||
// Start server
|
// 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();
|
server.start();
|
||||||
|
|
||||||
// Wait for exit
|
// Wait for exit
|
||||||
|
|||||||
Reference in New Issue
Block a user