Loading config.yaml +9 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,12 @@ BLOGI: DATABASE: DRIVER: "sqlite" CONNECTION: "/var/lib/blogi/blog.db" # Replication: writes are sent to primary AND all replicas # REPLICAS: # - DRIVER: "pqxx" # CONNECTION: "host=replica1.example.com dbname=blogi user=blogi" # - DRIVER: "sqlite" # CONNECTION: "/var/lib/blogi/blog-backup.db" AUTHDB: - URL: "https://auth.example.com" CLIENTNAME: "blogiclient" Loading Loading @@ -52,6 +58,9 @@ BLOGI: DATABASE: DRIVER: "sqlite" CONNECTION: "/var/lib/blogi/blog-example.db" # REPLICAS: # - DRIVER: "pqxx" # CONNECTION: "host=replica.example.com dbname=blogi user=blogi" TEMPLATE: "/usr/local/share/blogi/themes/default" STARTPAGE: "/blog/content/index" HTTP: Loading src/blogi.cpp +21 −3 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : Htt // Helper to initialize a DomainContext auto initCtx = [&](DomainContext &ctx, const std::string &dbDriver, const std::string &dbOpts, const std::vector<ReplicaConfig> &replicas, const std::string &authUrl, const std::string &clientName, const std::string &clientSecret, const std::vector<AuthSource> &authSources, const std::string &templatePath, const std::vector<std::string> &plgDirs, Loading @@ -116,9 +117,16 @@ blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : Htt try { for (int i = 0; i < threads; i++) { ctx.plgArgs->database.emplace_back( std::make_unique<dbpp::ReplicatedDatabase>(dbDriver, dbOpts) ); auto db = std::make_unique<dbpp::ReplicatedDatabase>(dbDriver, dbOpts); for (const auto &rep : replicas) { try { db->addReplica(rep.driver, rep.connection); std::cerr << " Added replica: " << rep.driver << " on " << rep.connection << std::endl; } catch (const std::exception &e) { std::cerr << " Failed to add replica (" << rep.connection << "): " << e.what() << std::endl; } } ctx.plgArgs->database.emplace_back(std::move(db)); } } catch(std::runtime_error &e) { std::cerr << "can't connect db for domain '" << domainName << "' reason: " << e.what() << std::endl; Loading @@ -126,6 +134,9 @@ blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : Htt } std::unique_ptr<dbpp::ReplicatedDatabase> initdb = std::make_unique<dbpp::ReplicatedDatabase>(dbDriver, dbOpts); for (const auto &rep : replicas) { try { initdb->addReplica(rep.driver, rep.connection); } catch (...) {} } Session sess(*initdb); sess._initSession(); Loading Loading @@ -199,6 +210,7 @@ blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : Htt const DomainConfig &dcfg = Blogi::Cfg->getDomainConfig(d); auto ctx = std::make_unique<DomainContext>(); initCtx(*ctx, dcfg.dbDriver, dcfg.dbConnection, dcfg.dbReplicas, dcfg.authUrl, dcfg.clientName, dcfg.clientSecret, dcfg.authSources, dcfg.templatePath, dcfg.plgDirs, Loading @@ -214,6 +226,12 @@ blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : Htt { _defaultCtx = std::make_unique<DomainContext>(); initCtx(*_defaultCtx, Blogi::Cfg->getdbdriver(), Blogi::Cfg->getdbopts(), [&]() -> std::vector<ReplicaConfig> { std::vector<ReplicaConfig> reps; for (size_t i = 0; i < Blogi::Cfg->getReplicaCount(); ++i) reps.push_back(Blogi::Cfg->getReplica(i)); return reps; }(), Blogi::Cfg->getAuthUrl(), Blogi::Cfg->getClientName(), Blogi::Cfg->getClientSecret(), std::vector<AuthSource>(), Blogi::Cfg->gettemplate(), Loading src/conf.cpp +38 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,20 @@ blogi::Config::Config(const std::string &path) : confplus::Config(path){ _DBDriver=getValue(getKey("/BLOGI/DATABASE/DRIVER"),0); _DBConnection=getValue(getKey("/BLOGI/DATABASE/CONNECTION"),0); // Parse global database replicas try { auto *repKey = getKey("/BLOGI/DATABASE/REPLICAS"); size_t repCount = getElements(repKey); for (size_t i = 0; i < repCount; ++i) { ReplicaConfig rc; std::string repBase = "/BLOGI/DATABASE/REPLICAS/" + std::to_string(i); try { rc.driver = getValue(getKey((repBase + "/DRIVER").c_str()), 0); } catch(...) { rc.driver = _DBDriver; } try { rc.connection = getValue(getKey((repBase + "/CONNECTION").c_str()), 0); } catch(...) { continue; } _DBReplicas.push_back(std::move(rc)); } } catch(...) {} _AuthUrl=getValue(getKey("/BLOGI/AUTHDB/URL"),0); _ClientName=getValue(getKey("/BLOGI/AUTHDB/CLIENTNAME"),0); _ClientSecret=getValue(getKey("/BLOGI/AUTHDB/CLIENTSECRET"),0); Loading Loading @@ -128,6 +142,22 @@ blogi::Config::Config(const std::string &path) : confplus::Config(path){ try { dc.name = getValue(getKey((basePath + "/NAME").c_str()), 0); } catch(...) { continue; } try { dc.dbDriver = getValue(getKey((basePath + "/DATABASE/DRIVER").c_str()), 0); } catch(...) { dc.dbDriver = _DBDriver; } try { dc.dbConnection = getValue(getKey((basePath + "/DATABASE/CONNECTION").c_str()), 0); } catch(...) { dc.dbConnection = _DBConnection; } // Parse per-domain database replicas try { auto *repKey = getKey((basePath + "/DATABASE/REPLICAS").c_str()); size_t repCount = getElements(repKey); for (size_t r = 0; r < repCount; ++r) { ReplicaConfig rc; std::string repBase = basePath + "/DATABASE/REPLICAS/" + std::to_string(r); try { rc.driver = getValue(getKey((repBase + "/DRIVER").c_str()), 0); } catch(...) { rc.driver = dc.dbDriver; } try { rc.connection = getValue(getKey((repBase + "/CONNECTION").c_str()), 0); } catch(...) { continue; } dc.dbReplicas.push_back(std::move(rc)); } } catch(...) { dc.dbReplicas = _DBReplicas; } try { dc.authUrl = getValue(getKey((basePath + "/AUTHDB/URL").c_str()), 0); } catch(...) { dc.authUrl = _AuthUrl; } try { dc.clientName = getValue(getKey((basePath + "/AUTHDB/CLIENTNAME").c_str()), 0); } catch(...) { dc.clientName = _ClientName; } try { dc.clientSecret = getValue(getKey((basePath + "/AUTHDB/CLIENTSECRET").c_str()), 0); } catch(...) { dc.clientSecret = _ClientSecret; } Loading Loading @@ -216,6 +246,14 @@ const std::string & blogi::Config::getdbopts() const { return _DBConnection; } size_t blogi::Config::getReplicaCount() const { return _DBReplicas.size(); } const blogi::ReplicaConfig &blogi::Config::getReplica(size_t idx) const { return _DBReplicas[idx]; } const std::string & blogi::Config::getAuthUrl() const { return _AuthUrl; } Loading src/conf.h +9 −0 Original line number Diff line number Diff line Loading @@ -46,10 +46,16 @@ namespace blogi { std::string domain; }; struct ReplicaConfig { std::string driver; std::string connection; }; struct DomainConfig { std::string name; std::string dbDriver; std::string dbConnection; std::vector<ReplicaConfig> dbReplicas; std::string authUrl; std::string clientName; std::string clientSecret; Loading Loading @@ -87,6 +93,8 @@ namespace blogi { const std::string &getdbdriver() const; const std::string &getdbopts() const; size_t getReplicaCount() const; const ReplicaConfig &getReplica(size_t idx) const; const std::string &getAuthUrl() const; const std::string &getClientName() const; Loading Loading @@ -119,6 +127,7 @@ namespace blogi { std::vector<std::string> _PlgDir; std::string _DBDriver; std::string _DBConnection; std::vector<ReplicaConfig> _DBReplicas; std::string _AuthUrl; std::string _ClientName; std::string _ClientSecret; Loading Loading
config.yaml +9 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,12 @@ BLOGI: DATABASE: DRIVER: "sqlite" CONNECTION: "/var/lib/blogi/blog.db" # Replication: writes are sent to primary AND all replicas # REPLICAS: # - DRIVER: "pqxx" # CONNECTION: "host=replica1.example.com dbname=blogi user=blogi" # - DRIVER: "sqlite" # CONNECTION: "/var/lib/blogi/blog-backup.db" AUTHDB: - URL: "https://auth.example.com" CLIENTNAME: "blogiclient" Loading Loading @@ -52,6 +58,9 @@ BLOGI: DATABASE: DRIVER: "sqlite" CONNECTION: "/var/lib/blogi/blog-example.db" # REPLICAS: # - DRIVER: "pqxx" # CONNECTION: "host=replica.example.com dbname=blogi user=blogi" TEMPLATE: "/usr/local/share/blogi/themes/default" STARTPAGE: "/blog/content/index" HTTP: Loading
src/blogi.cpp +21 −3 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : Htt // Helper to initialize a DomainContext auto initCtx = [&](DomainContext &ctx, const std::string &dbDriver, const std::string &dbOpts, const std::vector<ReplicaConfig> &replicas, const std::string &authUrl, const std::string &clientName, const std::string &clientSecret, const std::vector<AuthSource> &authSources, const std::string &templatePath, const std::vector<std::string> &plgDirs, Loading @@ -116,9 +117,16 @@ blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : Htt try { for (int i = 0; i < threads; i++) { ctx.plgArgs->database.emplace_back( std::make_unique<dbpp::ReplicatedDatabase>(dbDriver, dbOpts) ); auto db = std::make_unique<dbpp::ReplicatedDatabase>(dbDriver, dbOpts); for (const auto &rep : replicas) { try { db->addReplica(rep.driver, rep.connection); std::cerr << " Added replica: " << rep.driver << " on " << rep.connection << std::endl; } catch (const std::exception &e) { std::cerr << " Failed to add replica (" << rep.connection << "): " << e.what() << std::endl; } } ctx.plgArgs->database.emplace_back(std::move(db)); } } catch(std::runtime_error &e) { std::cerr << "can't connect db for domain '" << domainName << "' reason: " << e.what() << std::endl; Loading @@ -126,6 +134,9 @@ blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : Htt } std::unique_ptr<dbpp::ReplicatedDatabase> initdb = std::make_unique<dbpp::ReplicatedDatabase>(dbDriver, dbOpts); for (const auto &rep : replicas) { try { initdb->addReplica(rep.driver, rep.connection); } catch (...) {} } Session sess(*initdb); sess._initSession(); Loading Loading @@ -199,6 +210,7 @@ blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : Htt const DomainConfig &dcfg = Blogi::Cfg->getDomainConfig(d); auto ctx = std::make_unique<DomainContext>(); initCtx(*ctx, dcfg.dbDriver, dcfg.dbConnection, dcfg.dbReplicas, dcfg.authUrl, dcfg.clientName, dcfg.clientSecret, dcfg.authSources, dcfg.templatePath, dcfg.plgDirs, Loading @@ -214,6 +226,12 @@ blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : Htt { _defaultCtx = std::make_unique<DomainContext>(); initCtx(*_defaultCtx, Blogi::Cfg->getdbdriver(), Blogi::Cfg->getdbopts(), [&]() -> std::vector<ReplicaConfig> { std::vector<ReplicaConfig> reps; for (size_t i = 0; i < Blogi::Cfg->getReplicaCount(); ++i) reps.push_back(Blogi::Cfg->getReplica(i)); return reps; }(), Blogi::Cfg->getAuthUrl(), Blogi::Cfg->getClientName(), Blogi::Cfg->getClientSecret(), std::vector<AuthSource>(), Blogi::Cfg->gettemplate(), Loading
src/conf.cpp +38 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,20 @@ blogi::Config::Config(const std::string &path) : confplus::Config(path){ _DBDriver=getValue(getKey("/BLOGI/DATABASE/DRIVER"),0); _DBConnection=getValue(getKey("/BLOGI/DATABASE/CONNECTION"),0); // Parse global database replicas try { auto *repKey = getKey("/BLOGI/DATABASE/REPLICAS"); size_t repCount = getElements(repKey); for (size_t i = 0; i < repCount; ++i) { ReplicaConfig rc; std::string repBase = "/BLOGI/DATABASE/REPLICAS/" + std::to_string(i); try { rc.driver = getValue(getKey((repBase + "/DRIVER").c_str()), 0); } catch(...) { rc.driver = _DBDriver; } try { rc.connection = getValue(getKey((repBase + "/CONNECTION").c_str()), 0); } catch(...) { continue; } _DBReplicas.push_back(std::move(rc)); } } catch(...) {} _AuthUrl=getValue(getKey("/BLOGI/AUTHDB/URL"),0); _ClientName=getValue(getKey("/BLOGI/AUTHDB/CLIENTNAME"),0); _ClientSecret=getValue(getKey("/BLOGI/AUTHDB/CLIENTSECRET"),0); Loading Loading @@ -128,6 +142,22 @@ blogi::Config::Config(const std::string &path) : confplus::Config(path){ try { dc.name = getValue(getKey((basePath + "/NAME").c_str()), 0); } catch(...) { continue; } try { dc.dbDriver = getValue(getKey((basePath + "/DATABASE/DRIVER").c_str()), 0); } catch(...) { dc.dbDriver = _DBDriver; } try { dc.dbConnection = getValue(getKey((basePath + "/DATABASE/CONNECTION").c_str()), 0); } catch(...) { dc.dbConnection = _DBConnection; } // Parse per-domain database replicas try { auto *repKey = getKey((basePath + "/DATABASE/REPLICAS").c_str()); size_t repCount = getElements(repKey); for (size_t r = 0; r < repCount; ++r) { ReplicaConfig rc; std::string repBase = basePath + "/DATABASE/REPLICAS/" + std::to_string(r); try { rc.driver = getValue(getKey((repBase + "/DRIVER").c_str()), 0); } catch(...) { rc.driver = dc.dbDriver; } try { rc.connection = getValue(getKey((repBase + "/CONNECTION").c_str()), 0); } catch(...) { continue; } dc.dbReplicas.push_back(std::move(rc)); } } catch(...) { dc.dbReplicas = _DBReplicas; } try { dc.authUrl = getValue(getKey((basePath + "/AUTHDB/URL").c_str()), 0); } catch(...) { dc.authUrl = _AuthUrl; } try { dc.clientName = getValue(getKey((basePath + "/AUTHDB/CLIENTNAME").c_str()), 0); } catch(...) { dc.clientName = _ClientName; } try { dc.clientSecret = getValue(getKey((basePath + "/AUTHDB/CLIENTSECRET").c_str()), 0); } catch(...) { dc.clientSecret = _ClientSecret; } Loading Loading @@ -216,6 +246,14 @@ const std::string & blogi::Config::getdbopts() const { return _DBConnection; } size_t blogi::Config::getReplicaCount() const { return _DBReplicas.size(); } const blogi::ReplicaConfig &blogi::Config::getReplica(size_t idx) const { return _DBReplicas[idx]; } const std::string & blogi::Config::getAuthUrl() const { return _AuthUrl; } Loading
src/conf.h +9 −0 Original line number Diff line number Diff line Loading @@ -46,10 +46,16 @@ namespace blogi { std::string domain; }; struct ReplicaConfig { std::string driver; std::string connection; }; struct DomainConfig { std::string name; std::string dbDriver; std::string dbConnection; std::vector<ReplicaConfig> dbReplicas; std::string authUrl; std::string clientName; std::string clientSecret; Loading Loading @@ -87,6 +93,8 @@ namespace blogi { const std::string &getdbdriver() const; const std::string &getdbopts() const; size_t getReplicaCount() const; const ReplicaConfig &getReplica(size_t idx) const; const std::string &getAuthUrl() const; const std::string &getClientName() const; Loading Loading @@ -119,6 +127,7 @@ namespace blogi { std::vector<std::string> _PlgDir; std::string _DBDriver; std::string _DBConnection; std::vector<ReplicaConfig> _DBReplicas; std::string _AuthUrl; std::string _ClientName; std::string _ClientSecret; Loading