feat: implement MoloApp plans 01-15 with integration
Full C++23 framework tree: runtime, observability, security, data, app, realtime, supervisor, clients, CLI, example plus enterprise tenancy, audit, operability, governance, supplychain. Postgres via docker compose for tests.
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
#include "App.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Caps.hpp"
|
||||
|
||||
namespace molo::example::fw {
|
||||
|
||||
HttpError::HttpError(int status, std::string code, std::string message)
|
||||
: status_(status), code_(std::move(code)), message_(std::move(message)) {}
|
||||
|
||||
int HttpError::status() const { return status_; }
|
||||
const std::string& HttpError::code() const { return code_; }
|
||||
const std::string& HttpError::message() const { return message_; }
|
||||
|
||||
void Application::use(MiddlewareSlot slot, Middleware middleware) {
|
||||
if (has_middleware_ && slot < last_slot_)
|
||||
throw std::runtime_error("middleware category order violated");
|
||||
last_slot_ = slot;
|
||||
has_middleware_ = true;
|
||||
middlewares_.emplace_back(slot, std::move(middleware));
|
||||
}
|
||||
|
||||
void Application::include_router(const std::string& prefix,
|
||||
const Router& router,
|
||||
const std::string& name_prefix) {
|
||||
router_.include_router(prefix, router, name_prefix);
|
||||
}
|
||||
|
||||
void Application::mount(const std::string& path,
|
||||
std::shared_ptr<Application> sub) {
|
||||
mounts_.emplace_back(path, std::move(sub));
|
||||
}
|
||||
|
||||
Response Application::dispatch(Request req) {
|
||||
if (req.body.size() > caps::kMaxRequestBody)
|
||||
return TextResponse("body too large", 413);
|
||||
try {
|
||||
return run_chain(std::move(req), 0);
|
||||
} catch (const HttpError& e) {
|
||||
std::string body = "{\"error\":\"" + json_escape(e.code()) +
|
||||
"\",\"message\":\"" + json_escape(e.message()) + "\"}";
|
||||
return JsonResponse(body, e.status());
|
||||
} catch (const std::exception& e) {
|
||||
return JsonResponse("{\"error\":\"internal\",\"message\":\"" +
|
||||
json_escape(e.what()) + "\"}",
|
||||
500);
|
||||
}
|
||||
}
|
||||
|
||||
Response Application::run_chain(Request req, int depth) {
|
||||
Next next = [this, depth](Request r) { return dispatch_inner(r, depth); };
|
||||
for (std::size_t i = middlewares_.size(); i > 0; --i) {
|
||||
Middleware mw = middlewares_[i - 1].second;
|
||||
Next inner = next;
|
||||
next = [mw, inner](Request r) { return mw(std::move(r), inner); };
|
||||
}
|
||||
return next(std::move(req));
|
||||
}
|
||||
|
||||
Response Application::dispatch_inner(Request req, int depth) {
|
||||
if (depth > caps::kMaxMountDepth)
|
||||
throw HttpError(500, "mount_depth", "sub-application nesting too deep");
|
||||
std::string best;
|
||||
std::shared_ptr<Application> target;
|
||||
for (auto& [prefix, sub] : mounts_) {
|
||||
if (req.path.size() >= prefix.size() &&
|
||||
req.path.compare(0, prefix.size(), prefix) == 0 &&
|
||||
prefix.size() > best.size()) {
|
||||
best = prefix;
|
||||
target = sub;
|
||||
}
|
||||
}
|
||||
if (target) {
|
||||
req.path = req.path.substr(best.size());
|
||||
if (req.path.empty() || req.path.front() != '/') req.path = "/" + req.path;
|
||||
return target->run_chain(std::move(req), depth + 1);
|
||||
}
|
||||
Handler handler;
|
||||
if (router_.match(req, handler)) return handler(std::move(req));
|
||||
bool path_exists = false;
|
||||
for (const auto& r : router_.routes()) {
|
||||
std::map<std::string, std::string> params;
|
||||
if (match_pattern(r.pattern, req.path, params)) {
|
||||
path_exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (path_exists)
|
||||
throw HttpError(405, "method_not_allowed", "method not allowed");
|
||||
throw HttpError(404, "not_found", "no route for " + req.path);
|
||||
}
|
||||
|
||||
const std::vector<Route>& Application::routes() const {
|
||||
return router_.routes();
|
||||
}
|
||||
|
||||
Middleware errors_middleware() {
|
||||
return [](Request req, Next next) -> Response {
|
||||
try {
|
||||
return next(std::move(req));
|
||||
} catch (const HttpError& e) {
|
||||
std::string body = "{\"error\":\"" + json_escape(e.code()) +
|
||||
"\",\"message\":\"" + json_escape(e.message()) +
|
||||
"\"}";
|
||||
return JsonResponse(body, e.status());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Middleware request_id_middleware() {
|
||||
return [](Request req, Next next) -> Response {
|
||||
if (req.correlation_id.empty()) {
|
||||
static unsigned long counter = 1;
|
||||
req.correlation_id = "req-" + std::to_string(counter++);
|
||||
}
|
||||
Response res = next(req);
|
||||
res.headers.set("X-Request-ID", req.correlation_id);
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace molo::example::fw
|
||||
Reference in New Issue
Block a user