Loading config.yaml +41 −0 Original line number Diff line number Diff line Loading @@ -45,3 +45,44 @@ BLOGI: CERT: "" KEY: "" PASSWORD: "" # Multi-domain support: each domain gets its own DB, template, settings, etc. # If DOMAINS is not present, single-domain mode is used (backward compatible). DOMAINS: - NAME: "blog.example.com" DATABASE: DRIVER: "sqlite" CONNECTION: "/var/lib/blogi/blog-example.db" TEMPLATE: "/usr/local/share/blogi/themes/default" STARTPAGE: "/blog/content/index" HTTP: URL: "https://blog.example.com" PREFIX: "/blog" PLUGINDIR: - "/usr/local/lib/blogi/plugins" AUTHDB: URL: "https://auth.example.com" CLIENTNAME: "blogiclient" CLIENTSECRET: "clientsecret" MEDIADB: URL: "https://127.0.0.1:8442" COOKIE: DOMAIN: ".example.com" - NAME: "blog.another.org" DATABASE: DRIVER: "sqlite" CONNECTION: "/var/lib/blogi/blog-another.db" TEMPLATE: "/usr/local/share/blogi/themes/dark" STARTPAGE: "/content/index" HTTP: URL: "https://blog.another.org" PREFIX: "" PLUGINDIR: - "/usr/local/lib/blogi/plugins" AUTHDB: URL: "https://auth.another.org" CLIENTNAME: "anotherclient" CLIENTSECRET: "anothersecret" MEDIADB: URL: "https://media.another.org:8442" COOKIE: DOMAIN: ".another.org" src/blogi.cpp +173 −97 Original line number Diff line number Diff line Loading @@ -98,30 +98,34 @@ static std::string cookieDomain(libhttppp::HttpRequest &req) { std::unique_ptr<blogi::Config> blogi::Blogi::Cfg; blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : HttpEvent(serversocket){ PlgArgs = std::make_unique<PluginArgs>(); PlgArgs->debug=debug; PlgArgs->config=Blogi::Cfg.get(); std::cerr << "Initializing Blogi with DB Driver: " << Blogi::Cfg->getdbdriver() << " on " << Blogi::Cfg->getdbopts() << std::endl; // Helper to initialize a DomainContext auto initCtx = [&](DomainContext &ctx, const std::string &dbDriver, const std::string &dbOpts, 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, const std::string &siteUrl, const std::string &prefix, const std::string &startPage, const std::string &domainName) { ctx.domainName = domainName; ctx.plgArgs = std::make_unique<PluginArgs>(); ctx.plgArgs->debug = debug; ctx.plgArgs->config = Blogi::Cfg.get(); std::cerr << "Initializing domain '" << domainName << "' with DB Driver: " << dbDriver << " on " << dbOpts << std::endl; try { for (int i = 0; i < threads; i++) { PlgArgs->database.emplace_back( std::make_unique<dbpp::Database>( Blogi::Cfg->getdbdriver(), Blogi::Cfg->getdbopts() ) ctx.plgArgs->database.emplace_back( std::make_unique<dbpp::Database>(dbDriver, dbOpts) ); } } catch(std::runtime_error &e) { std::cerr << "can't connect db reason: " << e.what() << std::endl; std::cerr << "can't connect db for domain '" << domainName << "' reason: " << e.what() << std::endl; throw e; } std::unique_ptr<dbpp::Database> initdb = std::make_unique<dbpp::Database>( Blogi::Cfg->getdbdriver(), Blogi::Cfg->getdbopts() ); std::unique_ptr<dbpp::Database> initdb = std::make_unique<dbpp::Database>(dbDriver, dbOpts); Session sess(*initdb); sess._initSession(); Loading @@ -147,60 +151,112 @@ blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : Htt return; } PlgArgs->smtp=std::make_unique<SmtpSettings>(); ctx.plgArgs->smtp = std::make_unique<SmtpSettings>(); try { getConfig(*initdb,"SMTPHOST",PlgArgs->smtp->_SHost); getConfig(*initdb,"SMTPPORT",PlgArgs->smtp->_SPort); getConfig(*initdb,"SMTPUSER",PlgArgs->smtp->_SUser); getConfig(*initdb,"SMTPSENDER",PlgArgs->smtp->_Sender); getConfig(*initdb,"SMTPPASSWORD",PlgArgs->smtp->_SPass); getConfig(*initdb, "SMTPHOST", ctx.plgArgs->smtp->_SHost); getConfig(*initdb, "SMTPPORT", ctx.plgArgs->smtp->_SPort); getConfig(*initdb, "SMTPUSER", ctx.plgArgs->smtp->_SUser); getConfig(*initdb, "SMTPSENDER", ctx.plgArgs->smtp->_Sender); getConfig(*initdb, "SMTPPASSWORD", ctx.plgArgs->smtp->_SPass); } catch(std::runtime_error &e) { std::cerr << "SMTP config not found, skipping: " << e.what() << std::endl; std::cerr << "SMTP config not found for domain '" << domainName << "', skipping: " << e.what() << std::endl; } PlgArgs->auth=std::make_unique<Auth>(PlgArgs->database,*PlgArgs->config); ctx.plgArgs->auth = std::make_unique<Auth>(ctx.plgArgs->database, *ctx.plgArgs->config); ThemeConfig tplcfg( Blogi::Cfg->gettemplate(), PlgArgs->database, *Blogi::Cfg ); ThemeConfig tplcfg(templatePath, ctx.plgArgs->database, *Blogi::Cfg); PlgArgs->theme=std::make_unique<Theme>(tplcfg,debug); ctx.plgArgs->theme = std::make_unique<Theme>(tplcfg, debug); try { PlgArgs->theme->renderPage(0,"index.html",Page,Index); PlgArgs->theme->renderPage(0,"mobile.html",MPage,MIndex); ctx.plgArgs->theme->renderPage(0, "index.html", ctx.page, ctx.index); ctx.plgArgs->theme->renderPage(0, "mobile.html", ctx.mPage, ctx.mIndex); } catch(libhtmlpp::HTMLException &e) { std::cerr << e.what() << std::endl; return; } PlgArgs->maxthreads=threads; libhtmlpp::HtmlPage cookiehtml; ctx.plgArgs->maxthreads = threads; PlgArgs->theme->renderPage(0, "cookie.html", cookiehtml, CookieEl); libhtmlpp::HtmlPage cookiehtml; ctx.plgArgs->theme->renderPage(0, "cookie.html", cookiehtml, ctx.cookieEl); uuid::uuid id(cookieid); CookieBanner cban(*initdb); cban.init(); cban.addRule(id, "Technical Required", "This Cookie is Required for Authentication and some Basic Funtions of this Blog", true); BlogiPlg = std::make_unique<Plugin>(); ctx.plugins = std::make_unique<Plugin>(); for (size_t i = 0; i < plgDirs.size(); ++i) { ctx.plugins->loadPlugins(plgDirs[i], ctx.plgArgs.get()); } }; for(size_t i=0; i<PlgArgs->config->getplgdirs(); ++i){ BlogiPlg->loadPlugins(PlgArgs->config->getplgdir(i),PlgArgs.get()); // Initialize multi-domain contexts if configured if (Blogi::Cfg->getDomainCount() > 0) { for (size_t d = 0; d < Blogi::Cfg->getDomainCount(); ++d) { const DomainConfig &dcfg = Blogi::Cfg->getDomainConfig(d); auto ctx = std::make_unique<DomainContext>(); initCtx(*ctx, dcfg.dbDriver, dcfg.dbConnection, dcfg.authUrl, dcfg.clientName, dcfg.clientSecret, dcfg.authSources, dcfg.templatePath, dcfg.plgDirs, dcfg.siteUrl, dcfg.prefix, dcfg.startPage, dcfg.name); std::string key = dcfg.name; for (auto &c : key) c = std::tolower(c); _domains[key] = std::move(ctx); } } // Always initialize default context from global config (backward compat / fallback) { _defaultCtx = std::make_unique<DomainContext>(); initCtx(*_defaultCtx, Blogi::Cfg->getdbdriver(), Blogi::Cfg->getdbopts(), Blogi::Cfg->getAuthUrl(), Blogi::Cfg->getClientName(), Blogi::Cfg->getClientSecret(), std::vector<AuthSource>(), Blogi::Cfg->gettemplate(), [&]() -> std::vector<std::string> { std::vector<std::string> dirs; for (size_t i = 0; i < Blogi::Cfg->getplgdirs(); ++i) dirs.push_back(Blogi::Cfg->getplgdir(i)); return dirs; }(), Blogi::Cfg->getsiteurl(), Blogi::Cfg->getprefix(), Blogi::Cfg->getstartpage(), Blogi::Cfg->getDomain()); } } blogi::Blogi::~Blogi(){ } void blogi::Blogi::api(libhttppp::HttpRequest& curreq, const int tid){ blogi::DomainContext *blogi::Blogi::resolveDomain(libhttppp::HttpRequest &curreq) { std::string host; auto *fwd = curreq.getHeaderData("x-forwarded-host"); if (fwd) host = fwd->at(0).getvalue(); else host = curreq.getHost(); // Strip port auto colon = host.find(':'); if (colon != std::string::npos) host = host.substr(0, colon); for (auto &c : host) c = std::tolower(c); // Look up exact match in domain map auto it = _domains.find(host); if (it != _domains.end()) return it->second.get(); // Fallback to default return _defaultCtx.get(); } void blogi::Blogi::api(libhttppp::HttpRequest& curreq, const int tid, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; auto &BlogiPlg = ctx.plugins; uuid::uuid aid; size_t clen = curreq.getContentLength(); libhttppp::HttpResponse resp; Loading Loading @@ -492,7 +548,9 @@ void blogi::Blogi::api(libhttppp::HttpRequest& curreq, const int tid){ json_object_put(jrequest); } void blogi::Blogi::loginPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index){ void blogi::Blogi::loginPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; auto &BlogiPlg = ctx.plugins; libhttppp::HTTPException excep; if(PlgArgs->auth->isLoggedIn(tid,curreq,sessionid)){ libhttppp::HTTPException err; Loading Loading @@ -586,7 +644,8 @@ void blogi::Blogi::loginPage(libhttppp::HttpRequest &curreq,const int tid,const } void blogi::Blogi::logoutPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessiondid){ void blogi::Blogi::logoutPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessiondid, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; libhttppp::HttpCookie cook,cookclr; cook.parse(curreq); Loading Loading @@ -1347,7 +1406,9 @@ static void renderSettingsJson(json_object *jroot, libhtmlpp::HtmlString &out, c } } void blogi::Blogi::settingsApi(libhttppp::HttpRequest& curreq,const int tid,const std::string &sessiondid){ void blogi::Blogi::settingsApi(libhttppp::HttpRequest& curreq,const int tid,const std::string &sessiondid, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; auto &BlogiPlg = ctx.plugins; if(!PlgArgs->auth->isLoggedIn(tid,curreq,sessiondid)){ libhttppp::HttpResponse resp; // Clear stale auth cookie Loading Loading @@ -1511,7 +1572,9 @@ static json_object* SettingsLanguage(dbpp::Database &db, libhttppp::HttpRequest return jroot; } void blogi::Blogi::Settings(libhttppp::HttpRequest& curreq,const int tid,const std::string &sessiondid,libhtmlpp::HtmlElement &index){ void blogi::Blogi::Settings(libhttppp::HttpRequest& curreq,const int tid,const std::string &sessiondid,libhtmlpp::HtmlElement &index, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; auto &BlogiPlg = ctx.plugins; libhttppp::HttpCookie cookie; cookie.parse(curreq); Loading @@ -1524,7 +1587,7 @@ void blogi::Blogi::Settings(libhttppp::HttpRequest& curreq,const int tid,const s // Route API requests to settingsApi if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("settings/api/").length(), PlgArgs->config->buildurl("settings/api/"))==0){ settingsApi(curreq,tid,sessiondid); settingsApi(curreq,tid,sessiondid,ctx); return; } Loading Loading @@ -1944,7 +2007,9 @@ void blogi::Blogi::Settings(libhttppp::HttpRequest& curreq,const int tid,const s sendCompressed(curreq,curres,out.c_str(),out.length()); } void blogi::Blogi::searchPage(libhttppp::HttpRequest& curreq, const int tid, const std::string &sessionid,libhtmlpp::HtmlElement &index){ void blogi::Blogi::searchPage(libhttppp::HttpRequest& curreq, const int tid, const std::string &sessionid,libhtmlpp::HtmlElement &index, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; auto &BlogiPlg = ctx.plugins; std::string word,searchstr; SearchRet sdat; Loading Loading @@ -2010,7 +2075,9 @@ void blogi::Blogi::searchPage(libhttppp::HttpRequest& curreq, const int tid, con } void blogi::Blogi::sitemapXML(libhttppp::HttpRequest& curreq, const int tid, const std::string &sessionid){ void blogi::Blogi::sitemapXML(libhttppp::HttpRequest& curreq, const int tid, const std::string &sessionid, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; auto &BlogiPlg = ctx.plugins; std::map<libhttppp::HttpUrl,std::chrono::year_month_day> sitemap; for(const blogi::Plugin::PluginData *curplg=BlogiPlg->getFirstPlugin(); curplg; curplg=curplg->getNextPlg()){ curplg->getInstace()->GetSiteMap(tid,sitemap); Loading @@ -2036,7 +2103,12 @@ void blogi::Blogi::sitemapXML(libhttppp::HttpRequest& curreq, const int tid, con }; void blogi::Blogi::RequestEvent(libhttppp::HttpRequest &curreq,const int tid,ULONG_PTR args){ // Resolve which domain context to use for this request DomainContext *ctx = resolveDomain(curreq); try{; auto &PlgArgs = ctx->plgArgs; auto &BlogiPlg = ctx->plugins; libhttppp::HttpHeader::HeaderData *hip=curreq.getHeaderData("x-real-ip"); libhttppp::HttpHeader::HeaderData *useragent=curreq.getHeaderData("user-agent"); Loading Loading @@ -2068,9 +2140,9 @@ RETRY_REQUEST: Session sess(*PlgArgs->database[tid]); if(curreq.isMobile()) index=MIndex; index=ctx->mIndex; else index=Index; index=ctx->index; try{ cookie.parse(curreq); Loading @@ -2095,7 +2167,7 @@ RETRY_REQUEST: if(!found){ std::string _clang = getLang(*PlgArgs->database[tid]); blogi::print(*PlgArgs->database[tid],_clang,curreq.getRequestURL(),index,CookieEl); blogi::print(*PlgArgs->database[tid],_clang,curreq.getRequestURL(),index,ctx->cookieEl); }else{ std::string buf; Loading Loading @@ -2135,22 +2207,22 @@ RETRY_REQUEST: curres.send(curreq,"", 0); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("api").length(),PlgArgs->config->buildurl("api"))==0){ api(curreq,tid); api(curreq,tid,*ctx); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("logout").length(),PlgArgs->config->buildurl("logout"))==0){ logoutPage(curreq,tid,sessid); logoutPage(curreq,tid,sessid,*ctx); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("login").length(),PlgArgs->config->buildurl("login"))==0){ loginPage(curreq,tid,sessid,index); loginPage(curreq,tid,sessid,index,*ctx); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("settings").length(),PlgArgs->config->buildurl("settings"))==0){ Settings(curreq,tid,sessid,index); Settings(curreq,tid,sessid,index,*ctx); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("search").length(),PlgArgs->config->buildurl("search"))==0){ searchPage(curreq,tid,sessid,index); searchPage(curreq,tid,sessid,index,*ctx); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("sitemap.xml").length(),PlgArgs->config->buildurl("sitemap.xml"))==0){ sitemapXML(curreq,tid,sessid); sitemapXML(curreq,tid,sessid,*ctx); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("render").length(),PlgArgs->config->buildurl("render"))==0){ Loading Loading @@ -2227,9 +2299,9 @@ RETRY_REQUEST: } if(mobile) index=MIndex; index=ctx->mIndex; else index=Index; index=ctx->index; { libhtmlpp::HtmlString out; Loading Loading @@ -2389,7 +2461,7 @@ RETRY_REQUEST: err << "<!DOCTYPE html><html><body style=\"color:rgb(238, 238, 238); background:rgb(35, 38, 39);\"><span>" << hreason << "</span><br/><a style=\"text-decoration: none; color: rgb(58,212, 58);\" href=\"" << PlgArgs->config->getstartpage() << ctx->plgArgs->config->getstartpage() << "\" >Zurück zur Startseite</a></body></html>"; libhtmlpp::print(err.parse(),output); libhttppp::HttpResponse resp; Loading @@ -2402,7 +2474,7 @@ RETRY_REQUEST: err << "<!DOCTYPE html><html><body style=\"color:rgb(238, 238, 238); background:rgb(35, 38, 39);\"><span>" << "unknown error" << "</span><br/><a style=\"text-decoration: none; color: rgb(58,212, 58);\" href=\"" << PlgArgs->config->getstartpage() << ctx->plgArgs->config->getstartpage() << "\" >Zurück zur Startseite</a></body></html>"; libhtmlpp::print(err.parse(),output); libhttppp::HttpResponse resp; Loading @@ -2413,6 +2485,10 @@ RETRY_REQUEST: } void blogi::Blogi::ResponseEvent(libhttppp::HttpRequest& curreq,const int tid,ULONG_PTR args){ DomainContext *ctx = resolveDomain(curreq); auto &PlgArgs = ctx->plgArgs; auto &BlogiPlg = ctx->plugins; if(PlgArgs->theme->Response(tid,curreq)) return; Loading src/blogi.h +25 −12 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ #include <memory> #include <map> #include <httppp//httpd.h> Loading @@ -36,26 +37,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once namespace blogi { struct DomainContext { std::string domainName; std::unique_ptr<PluginArgs> plgArgs; std::unique_ptr<blogi::Plugin> plugins; libhtmlpp::HtmlPage page, mPage; libhtmlpp::HtmlElement index, mIndex; libhtmlpp::HtmlElement cookieEl; }; class Blogi : public libhttppp::HttpEvent{ public: Blogi(std::vector<netplus::socket*> serversocket,bool debug); ~Blogi(); void api(libhttppp::HttpRequest &curreq,const int tid); void loginPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index); void logoutPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid); void Settings(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index); void settingsApi(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid); void searchPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index); void sitemapXML(libhttppp::HttpRequest& curreq, const int tid, const std::string &sessionid); void api(libhttppp::HttpRequest &curreq,const int tid, DomainContext &ctx); void loginPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index, DomainContext &ctx); void logoutPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid, DomainContext &ctx); void Settings(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index, DomainContext &ctx); void settingsApi(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid, DomainContext &ctx); void searchPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index, DomainContext &ctx); void sitemapXML(libhttppp::HttpRequest& curreq, const int tid, const std::string &sessionid, DomainContext &ctx); void RequestEvent(libhttppp::HttpRequest &curreq,const int tid,ULONG_PTR args); void ResponseEvent(libhttppp::HttpRequest &curreq,const int tid,ULONG_PTR args); DomainContext *resolveDomain(libhttppp::HttpRequest &curreq); static std::unique_ptr<blogi::Config> Cfg; private: std::unique_ptr<PluginArgs> PlgArgs; libhtmlpp::HtmlPage Page,MPage; libhtmlpp::HtmlElement Index,MIndex; libhtmlpp::HtmlElement CookieEl; std::unique_ptr<blogi::Plugin> BlogiPlg; void initDomainContext(DomainContext &ctx, const DomainConfig &dcfg, bool debug); // Default domain (single-domain backward compat or fallback) std::unique_ptr<DomainContext> _defaultCtx; // Multi-domain contexts keyed by domain name (lowercase) std::map<std::string, std::unique_ptr<DomainContext>> _domains; }; }; src/conf.cpp +104 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes src/conf.h +27 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include <stdexcept> #include <string> #include <vector> #include <map> #include <confplus/conf.h> Loading @@ -45,6 +46,26 @@ namespace blogi { std::string domain; }; struct DomainConfig { std::string name; std::string dbDriver; std::string dbConnection; std::string authUrl; std::string clientName; std::string clientSecret; std::vector<AuthSource> authSources; std::string siteUrl; std::string prefix; std::string templatePath; std::string startPage; std::vector<std::string> plgDirs; std::string cookieDomain; std::string mediaDBUrl; std::string tmpDir; const std::string buildurl(const std::string &url) const; }; class Config : public confplus::Config { public: Config(const std::string &path); Loading Loading @@ -89,6 +110,11 @@ namespace blogi { const std::string &getMediaDBUrl() const; // Multi-domain support size_t getDomainCount() const; const DomainConfig &getDomainConfig(size_t idx) const; const DomainConfig *findDomainConfig(const std::string &hostname) const; private: std::vector<std::string> _PlgDir; std::string _DBDriver; Loading @@ -112,6 +138,7 @@ namespace blogi { std::string _CookieDomain; std::string _TmpDir; std::string _MediaDBUrl; std::vector<DomainConfig> _Domains; }; inline void getConfig(dbpp::Database &db,const std::string &key,std::string &value){ Loading Loading
config.yaml +41 −0 Original line number Diff line number Diff line Loading @@ -45,3 +45,44 @@ BLOGI: CERT: "" KEY: "" PASSWORD: "" # Multi-domain support: each domain gets its own DB, template, settings, etc. # If DOMAINS is not present, single-domain mode is used (backward compatible). DOMAINS: - NAME: "blog.example.com" DATABASE: DRIVER: "sqlite" CONNECTION: "/var/lib/blogi/blog-example.db" TEMPLATE: "/usr/local/share/blogi/themes/default" STARTPAGE: "/blog/content/index" HTTP: URL: "https://blog.example.com" PREFIX: "/blog" PLUGINDIR: - "/usr/local/lib/blogi/plugins" AUTHDB: URL: "https://auth.example.com" CLIENTNAME: "blogiclient" CLIENTSECRET: "clientsecret" MEDIADB: URL: "https://127.0.0.1:8442" COOKIE: DOMAIN: ".example.com" - NAME: "blog.another.org" DATABASE: DRIVER: "sqlite" CONNECTION: "/var/lib/blogi/blog-another.db" TEMPLATE: "/usr/local/share/blogi/themes/dark" STARTPAGE: "/content/index" HTTP: URL: "https://blog.another.org" PREFIX: "" PLUGINDIR: - "/usr/local/lib/blogi/plugins" AUTHDB: URL: "https://auth.another.org" CLIENTNAME: "anotherclient" CLIENTSECRET: "anothersecret" MEDIADB: URL: "https://media.another.org:8442" COOKIE: DOMAIN: ".another.org"
src/blogi.cpp +173 −97 Original line number Diff line number Diff line Loading @@ -98,30 +98,34 @@ static std::string cookieDomain(libhttppp::HttpRequest &req) { std::unique_ptr<blogi::Config> blogi::Blogi::Cfg; blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : HttpEvent(serversocket){ PlgArgs = std::make_unique<PluginArgs>(); PlgArgs->debug=debug; PlgArgs->config=Blogi::Cfg.get(); std::cerr << "Initializing Blogi with DB Driver: " << Blogi::Cfg->getdbdriver() << " on " << Blogi::Cfg->getdbopts() << std::endl; // Helper to initialize a DomainContext auto initCtx = [&](DomainContext &ctx, const std::string &dbDriver, const std::string &dbOpts, 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, const std::string &siteUrl, const std::string &prefix, const std::string &startPage, const std::string &domainName) { ctx.domainName = domainName; ctx.plgArgs = std::make_unique<PluginArgs>(); ctx.plgArgs->debug = debug; ctx.plgArgs->config = Blogi::Cfg.get(); std::cerr << "Initializing domain '" << domainName << "' with DB Driver: " << dbDriver << " on " << dbOpts << std::endl; try { for (int i = 0; i < threads; i++) { PlgArgs->database.emplace_back( std::make_unique<dbpp::Database>( Blogi::Cfg->getdbdriver(), Blogi::Cfg->getdbopts() ) ctx.plgArgs->database.emplace_back( std::make_unique<dbpp::Database>(dbDriver, dbOpts) ); } } catch(std::runtime_error &e) { std::cerr << "can't connect db reason: " << e.what() << std::endl; std::cerr << "can't connect db for domain '" << domainName << "' reason: " << e.what() << std::endl; throw e; } std::unique_ptr<dbpp::Database> initdb = std::make_unique<dbpp::Database>( Blogi::Cfg->getdbdriver(), Blogi::Cfg->getdbopts() ); std::unique_ptr<dbpp::Database> initdb = std::make_unique<dbpp::Database>(dbDriver, dbOpts); Session sess(*initdb); sess._initSession(); Loading @@ -147,60 +151,112 @@ blogi::Blogi::Blogi(std::vector<netplus::socket*> serversocket,bool debug) : Htt return; } PlgArgs->smtp=std::make_unique<SmtpSettings>(); ctx.plgArgs->smtp = std::make_unique<SmtpSettings>(); try { getConfig(*initdb,"SMTPHOST",PlgArgs->smtp->_SHost); getConfig(*initdb,"SMTPPORT",PlgArgs->smtp->_SPort); getConfig(*initdb,"SMTPUSER",PlgArgs->smtp->_SUser); getConfig(*initdb,"SMTPSENDER",PlgArgs->smtp->_Sender); getConfig(*initdb,"SMTPPASSWORD",PlgArgs->smtp->_SPass); getConfig(*initdb, "SMTPHOST", ctx.plgArgs->smtp->_SHost); getConfig(*initdb, "SMTPPORT", ctx.plgArgs->smtp->_SPort); getConfig(*initdb, "SMTPUSER", ctx.plgArgs->smtp->_SUser); getConfig(*initdb, "SMTPSENDER", ctx.plgArgs->smtp->_Sender); getConfig(*initdb, "SMTPPASSWORD", ctx.plgArgs->smtp->_SPass); } catch(std::runtime_error &e) { std::cerr << "SMTP config not found, skipping: " << e.what() << std::endl; std::cerr << "SMTP config not found for domain '" << domainName << "', skipping: " << e.what() << std::endl; } PlgArgs->auth=std::make_unique<Auth>(PlgArgs->database,*PlgArgs->config); ctx.plgArgs->auth = std::make_unique<Auth>(ctx.plgArgs->database, *ctx.plgArgs->config); ThemeConfig tplcfg( Blogi::Cfg->gettemplate(), PlgArgs->database, *Blogi::Cfg ); ThemeConfig tplcfg(templatePath, ctx.plgArgs->database, *Blogi::Cfg); PlgArgs->theme=std::make_unique<Theme>(tplcfg,debug); ctx.plgArgs->theme = std::make_unique<Theme>(tplcfg, debug); try { PlgArgs->theme->renderPage(0,"index.html",Page,Index); PlgArgs->theme->renderPage(0,"mobile.html",MPage,MIndex); ctx.plgArgs->theme->renderPage(0, "index.html", ctx.page, ctx.index); ctx.plgArgs->theme->renderPage(0, "mobile.html", ctx.mPage, ctx.mIndex); } catch(libhtmlpp::HTMLException &e) { std::cerr << e.what() << std::endl; return; } PlgArgs->maxthreads=threads; libhtmlpp::HtmlPage cookiehtml; ctx.plgArgs->maxthreads = threads; PlgArgs->theme->renderPage(0, "cookie.html", cookiehtml, CookieEl); libhtmlpp::HtmlPage cookiehtml; ctx.plgArgs->theme->renderPage(0, "cookie.html", cookiehtml, ctx.cookieEl); uuid::uuid id(cookieid); CookieBanner cban(*initdb); cban.init(); cban.addRule(id, "Technical Required", "This Cookie is Required for Authentication and some Basic Funtions of this Blog", true); BlogiPlg = std::make_unique<Plugin>(); ctx.plugins = std::make_unique<Plugin>(); for (size_t i = 0; i < plgDirs.size(); ++i) { ctx.plugins->loadPlugins(plgDirs[i], ctx.plgArgs.get()); } }; for(size_t i=0; i<PlgArgs->config->getplgdirs(); ++i){ BlogiPlg->loadPlugins(PlgArgs->config->getplgdir(i),PlgArgs.get()); // Initialize multi-domain contexts if configured if (Blogi::Cfg->getDomainCount() > 0) { for (size_t d = 0; d < Blogi::Cfg->getDomainCount(); ++d) { const DomainConfig &dcfg = Blogi::Cfg->getDomainConfig(d); auto ctx = std::make_unique<DomainContext>(); initCtx(*ctx, dcfg.dbDriver, dcfg.dbConnection, dcfg.authUrl, dcfg.clientName, dcfg.clientSecret, dcfg.authSources, dcfg.templatePath, dcfg.plgDirs, dcfg.siteUrl, dcfg.prefix, dcfg.startPage, dcfg.name); std::string key = dcfg.name; for (auto &c : key) c = std::tolower(c); _domains[key] = std::move(ctx); } } // Always initialize default context from global config (backward compat / fallback) { _defaultCtx = std::make_unique<DomainContext>(); initCtx(*_defaultCtx, Blogi::Cfg->getdbdriver(), Blogi::Cfg->getdbopts(), Blogi::Cfg->getAuthUrl(), Blogi::Cfg->getClientName(), Blogi::Cfg->getClientSecret(), std::vector<AuthSource>(), Blogi::Cfg->gettemplate(), [&]() -> std::vector<std::string> { std::vector<std::string> dirs; for (size_t i = 0; i < Blogi::Cfg->getplgdirs(); ++i) dirs.push_back(Blogi::Cfg->getplgdir(i)); return dirs; }(), Blogi::Cfg->getsiteurl(), Blogi::Cfg->getprefix(), Blogi::Cfg->getstartpage(), Blogi::Cfg->getDomain()); } } blogi::Blogi::~Blogi(){ } void blogi::Blogi::api(libhttppp::HttpRequest& curreq, const int tid){ blogi::DomainContext *blogi::Blogi::resolveDomain(libhttppp::HttpRequest &curreq) { std::string host; auto *fwd = curreq.getHeaderData("x-forwarded-host"); if (fwd) host = fwd->at(0).getvalue(); else host = curreq.getHost(); // Strip port auto colon = host.find(':'); if (colon != std::string::npos) host = host.substr(0, colon); for (auto &c : host) c = std::tolower(c); // Look up exact match in domain map auto it = _domains.find(host); if (it != _domains.end()) return it->second.get(); // Fallback to default return _defaultCtx.get(); } void blogi::Blogi::api(libhttppp::HttpRequest& curreq, const int tid, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; auto &BlogiPlg = ctx.plugins; uuid::uuid aid; size_t clen = curreq.getContentLength(); libhttppp::HttpResponse resp; Loading Loading @@ -492,7 +548,9 @@ void blogi::Blogi::api(libhttppp::HttpRequest& curreq, const int tid){ json_object_put(jrequest); } void blogi::Blogi::loginPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index){ void blogi::Blogi::loginPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; auto &BlogiPlg = ctx.plugins; libhttppp::HTTPException excep; if(PlgArgs->auth->isLoggedIn(tid,curreq,sessionid)){ libhttppp::HTTPException err; Loading Loading @@ -586,7 +644,8 @@ void blogi::Blogi::loginPage(libhttppp::HttpRequest &curreq,const int tid,const } void blogi::Blogi::logoutPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessiondid){ void blogi::Blogi::logoutPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessiondid, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; libhttppp::HttpCookie cook,cookclr; cook.parse(curreq); Loading Loading @@ -1347,7 +1406,9 @@ static void renderSettingsJson(json_object *jroot, libhtmlpp::HtmlString &out, c } } void blogi::Blogi::settingsApi(libhttppp::HttpRequest& curreq,const int tid,const std::string &sessiondid){ void blogi::Blogi::settingsApi(libhttppp::HttpRequest& curreq,const int tid,const std::string &sessiondid, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; auto &BlogiPlg = ctx.plugins; if(!PlgArgs->auth->isLoggedIn(tid,curreq,sessiondid)){ libhttppp::HttpResponse resp; // Clear stale auth cookie Loading Loading @@ -1511,7 +1572,9 @@ static json_object* SettingsLanguage(dbpp::Database &db, libhttppp::HttpRequest return jroot; } void blogi::Blogi::Settings(libhttppp::HttpRequest& curreq,const int tid,const std::string &sessiondid,libhtmlpp::HtmlElement &index){ void blogi::Blogi::Settings(libhttppp::HttpRequest& curreq,const int tid,const std::string &sessiondid,libhtmlpp::HtmlElement &index, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; auto &BlogiPlg = ctx.plugins; libhttppp::HttpCookie cookie; cookie.parse(curreq); Loading @@ -1524,7 +1587,7 @@ void blogi::Blogi::Settings(libhttppp::HttpRequest& curreq,const int tid,const s // Route API requests to settingsApi if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("settings/api/").length(), PlgArgs->config->buildurl("settings/api/"))==0){ settingsApi(curreq,tid,sessiondid); settingsApi(curreq,tid,sessiondid,ctx); return; } Loading Loading @@ -1944,7 +2007,9 @@ void blogi::Blogi::Settings(libhttppp::HttpRequest& curreq,const int tid,const s sendCompressed(curreq,curres,out.c_str(),out.length()); } void blogi::Blogi::searchPage(libhttppp::HttpRequest& curreq, const int tid, const std::string &sessionid,libhtmlpp::HtmlElement &index){ void blogi::Blogi::searchPage(libhttppp::HttpRequest& curreq, const int tid, const std::string &sessionid,libhtmlpp::HtmlElement &index, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; auto &BlogiPlg = ctx.plugins; std::string word,searchstr; SearchRet sdat; Loading Loading @@ -2010,7 +2075,9 @@ void blogi::Blogi::searchPage(libhttppp::HttpRequest& curreq, const int tid, con } void blogi::Blogi::sitemapXML(libhttppp::HttpRequest& curreq, const int tid, const std::string &sessionid){ void blogi::Blogi::sitemapXML(libhttppp::HttpRequest& curreq, const int tid, const std::string &sessionid, DomainContext &ctx){ auto &PlgArgs = ctx.plgArgs; auto &BlogiPlg = ctx.plugins; std::map<libhttppp::HttpUrl,std::chrono::year_month_day> sitemap; for(const blogi::Plugin::PluginData *curplg=BlogiPlg->getFirstPlugin(); curplg; curplg=curplg->getNextPlg()){ curplg->getInstace()->GetSiteMap(tid,sitemap); Loading @@ -2036,7 +2103,12 @@ void blogi::Blogi::sitemapXML(libhttppp::HttpRequest& curreq, const int tid, con }; void blogi::Blogi::RequestEvent(libhttppp::HttpRequest &curreq,const int tid,ULONG_PTR args){ // Resolve which domain context to use for this request DomainContext *ctx = resolveDomain(curreq); try{; auto &PlgArgs = ctx->plgArgs; auto &BlogiPlg = ctx->plugins; libhttppp::HttpHeader::HeaderData *hip=curreq.getHeaderData("x-real-ip"); libhttppp::HttpHeader::HeaderData *useragent=curreq.getHeaderData("user-agent"); Loading Loading @@ -2068,9 +2140,9 @@ RETRY_REQUEST: Session sess(*PlgArgs->database[tid]); if(curreq.isMobile()) index=MIndex; index=ctx->mIndex; else index=Index; index=ctx->index; try{ cookie.parse(curreq); Loading @@ -2095,7 +2167,7 @@ RETRY_REQUEST: if(!found){ std::string _clang = getLang(*PlgArgs->database[tid]); blogi::print(*PlgArgs->database[tid],_clang,curreq.getRequestURL(),index,CookieEl); blogi::print(*PlgArgs->database[tid],_clang,curreq.getRequestURL(),index,ctx->cookieEl); }else{ std::string buf; Loading Loading @@ -2135,22 +2207,22 @@ RETRY_REQUEST: curres.send(curreq,"", 0); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("api").length(),PlgArgs->config->buildurl("api"))==0){ api(curreq,tid); api(curreq,tid,*ctx); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("logout").length(),PlgArgs->config->buildurl("logout"))==0){ logoutPage(curreq,tid,sessid); logoutPage(curreq,tid,sessid,*ctx); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("login").length(),PlgArgs->config->buildurl("login"))==0){ loginPage(curreq,tid,sessid,index); loginPage(curreq,tid,sessid,index,*ctx); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("settings").length(),PlgArgs->config->buildurl("settings"))==0){ Settings(curreq,tid,sessid,index); Settings(curreq,tid,sessid,index,*ctx); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("search").length(),PlgArgs->config->buildurl("search"))==0){ searchPage(curreq,tid,sessid,index); searchPage(curreq,tid,sessid,index,*ctx); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("sitemap.xml").length(),PlgArgs->config->buildurl("sitemap.xml"))==0){ sitemapXML(curreq,tid,sessid); sitemapXML(curreq,tid,sessid,*ctx); return; }else if(curreq.getRequestURL().compare(0,PlgArgs->config->buildurl("render").length(),PlgArgs->config->buildurl("render"))==0){ Loading Loading @@ -2227,9 +2299,9 @@ RETRY_REQUEST: } if(mobile) index=MIndex; index=ctx->mIndex; else index=Index; index=ctx->index; { libhtmlpp::HtmlString out; Loading Loading @@ -2389,7 +2461,7 @@ RETRY_REQUEST: err << "<!DOCTYPE html><html><body style=\"color:rgb(238, 238, 238); background:rgb(35, 38, 39);\"><span>" << hreason << "</span><br/><a style=\"text-decoration: none; color: rgb(58,212, 58);\" href=\"" << PlgArgs->config->getstartpage() << ctx->plgArgs->config->getstartpage() << "\" >Zurück zur Startseite</a></body></html>"; libhtmlpp::print(err.parse(),output); libhttppp::HttpResponse resp; Loading @@ -2402,7 +2474,7 @@ RETRY_REQUEST: err << "<!DOCTYPE html><html><body style=\"color:rgb(238, 238, 238); background:rgb(35, 38, 39);\"><span>" << "unknown error" << "</span><br/><a style=\"text-decoration: none; color: rgb(58,212, 58);\" href=\"" << PlgArgs->config->getstartpage() << ctx->plgArgs->config->getstartpage() << "\" >Zurück zur Startseite</a></body></html>"; libhtmlpp::print(err.parse(),output); libhttppp::HttpResponse resp; Loading @@ -2413,6 +2485,10 @@ RETRY_REQUEST: } void blogi::Blogi::ResponseEvent(libhttppp::HttpRequest& curreq,const int tid,ULONG_PTR args){ DomainContext *ctx = resolveDomain(curreq); auto &PlgArgs = ctx->plgArgs; auto &BlogiPlg = ctx->plugins; if(PlgArgs->theme->Response(tid,curreq)) return; Loading
src/blogi.h +25 −12 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ #include <memory> #include <map> #include <httppp//httpd.h> Loading @@ -36,26 +37,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once namespace blogi { struct DomainContext { std::string domainName; std::unique_ptr<PluginArgs> plgArgs; std::unique_ptr<blogi::Plugin> plugins; libhtmlpp::HtmlPage page, mPage; libhtmlpp::HtmlElement index, mIndex; libhtmlpp::HtmlElement cookieEl; }; class Blogi : public libhttppp::HttpEvent{ public: Blogi(std::vector<netplus::socket*> serversocket,bool debug); ~Blogi(); void api(libhttppp::HttpRequest &curreq,const int tid); void loginPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index); void logoutPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid); void Settings(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index); void settingsApi(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid); void searchPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index); void sitemapXML(libhttppp::HttpRequest& curreq, const int tid, const std::string &sessionid); void api(libhttppp::HttpRequest &curreq,const int tid, DomainContext &ctx); void loginPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index, DomainContext &ctx); void logoutPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid, DomainContext &ctx); void Settings(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index, DomainContext &ctx); void settingsApi(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid, DomainContext &ctx); void searchPage(libhttppp::HttpRequest &curreq,const int tid,const std::string &sessionid,libhtmlpp::HtmlElement &index, DomainContext &ctx); void sitemapXML(libhttppp::HttpRequest& curreq, const int tid, const std::string &sessionid, DomainContext &ctx); void RequestEvent(libhttppp::HttpRequest &curreq,const int tid,ULONG_PTR args); void ResponseEvent(libhttppp::HttpRequest &curreq,const int tid,ULONG_PTR args); DomainContext *resolveDomain(libhttppp::HttpRequest &curreq); static std::unique_ptr<blogi::Config> Cfg; private: std::unique_ptr<PluginArgs> PlgArgs; libhtmlpp::HtmlPage Page,MPage; libhtmlpp::HtmlElement Index,MIndex; libhtmlpp::HtmlElement CookieEl; std::unique_ptr<blogi::Plugin> BlogiPlg; void initDomainContext(DomainContext &ctx, const DomainConfig &dcfg, bool debug); // Default domain (single-domain backward compat or fallback) std::unique_ptr<DomainContext> _defaultCtx; // Multi-domain contexts keyed by domain name (lowercase) std::map<std::string, std::unique_ptr<DomainContext>> _domains; }; };
src/conf.h +27 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include <stdexcept> #include <string> #include <vector> #include <map> #include <confplus/conf.h> Loading @@ -45,6 +46,26 @@ namespace blogi { std::string domain; }; struct DomainConfig { std::string name; std::string dbDriver; std::string dbConnection; std::string authUrl; std::string clientName; std::string clientSecret; std::vector<AuthSource> authSources; std::string siteUrl; std::string prefix; std::string templatePath; std::string startPage; std::vector<std::string> plgDirs; std::string cookieDomain; std::string mediaDBUrl; std::string tmpDir; const std::string buildurl(const std::string &url) const; }; class Config : public confplus::Config { public: Config(const std::string &path); Loading Loading @@ -89,6 +110,11 @@ namespace blogi { const std::string &getMediaDBUrl() const; // Multi-domain support size_t getDomainCount() const; const DomainConfig &getDomainConfig(size_t idx) const; const DomainConfig *findDomainConfig(const std::string &hostname) const; private: std::vector<std::string> _PlgDir; std::string _DBDriver; Loading @@ -112,6 +138,7 @@ namespace blogi { std::string _CookieDomain; std::string _TmpDir; std::string _MediaDBUrl; std::vector<DomainConfig> _Domains; }; inline void getConfig(dbpp::Database &db,const std::string &key,std::string &value){ Loading