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.
225 lines
8.9 KiB
C++
225 lines
8.9 KiB
C++
#include "App.hpp"
|
|
|
|
#include "../fw/Jobs.hpp"
|
|
#include "../fw/Net.hpp"
|
|
#include "../fw/Observ.hpp"
|
|
#include "../fw/Router.hpp"
|
|
#include "../fw/Templates.hpp"
|
|
#include "../fw/Validate.hpp"
|
|
#include "../fw/Workflow.hpp"
|
|
|
|
namespace molo::example::app {
|
|
|
|
void register_ops_demo(fw::Router& router, Services& services) {
|
|
fw::EventBus* events = services.events.get();
|
|
fw::Metrics* metrics = services.metrics.get();
|
|
fw::Telemetry* telemetry = services.telemetry.get();
|
|
fw::Supervisor* supervisor = services.supervisor.get();
|
|
fw::TaskQueue* tasks = services.tasks.get();
|
|
fw::ReplicationLog* repl = services.repl.get();
|
|
fw::OpenApi* openapi = services.openapi.get();
|
|
fw::Environment* env = services.templates.get();
|
|
|
|
router.post("/events/fire", "events-fire", [events](fw::Request req) {
|
|
auto input = fw::parse_json_object_flat(req.body);
|
|
if (input.empty()) input = fw::parse_form(req.body);
|
|
std::string type = input.count("type") ? input["type"] : "demo.ping";
|
|
std::string payload =
|
|
input.count("payload") ? input["payload"] : "ping";
|
|
events->emit({type, payload, req.correlation_id});
|
|
return fw::JsonResponse(
|
|
"{\"fired\":\"" + fw::json_escape(type) + "\"}", 200);
|
|
});
|
|
router.get("/events/fire", "events-list", [events](fw::Request req) {
|
|
(void)req;
|
|
auto delivered = events->delivered();
|
|
std::string body = "{\"delivered\":[";
|
|
bool first = true;
|
|
for (const auto& e : delivered) {
|
|
if (!first) body += ",";
|
|
first = false;
|
|
body += "{\"type\":\"" + fw::json_escape(e.type) + "\",\"payload\":\"" +
|
|
fw::json_escape(e.payload) + "\"}";
|
|
}
|
|
return fw::JsonResponse(body + "]}", 200);
|
|
});
|
|
|
|
router.get("/metrics/counters", "metrics",
|
|
[metrics](fw::Request req) {
|
|
(void)req;
|
|
metrics->inc("example.page.views");
|
|
fw::Response res(200, metrics->render_prometheus(),
|
|
"text/plain; version=0.0.4");
|
|
return res;
|
|
});
|
|
|
|
router.get("/telemetry/trace", "telemetry",
|
|
[telemetry](fw::Request req) {
|
|
fw::Span span =
|
|
telemetry->start_span("demo", req.correlation_id);
|
|
telemetry->finish_span(span);
|
|
return fw::JsonResponse(
|
|
"{\"trace\":\"" + fw::json_escape(span.trace_id) +
|
|
"\",\"span\":\"" + fw::json_escape(span.span_id) +
|
|
"\",\"correlation\":\"" +
|
|
fw::json_escape(req.correlation_id) + "\"}",
|
|
200);
|
|
});
|
|
|
|
router.get("/replication/state", "repl", [repl](fw::Request req) {
|
|
unsigned long since = 0;
|
|
try {
|
|
since = (unsigned long)std::stoul(req.query_or("since", "0"));
|
|
} catch (...) {
|
|
since = 0;
|
|
}
|
|
auto entries = repl->since(since);
|
|
std::string body = "{\"latest\":" + std::to_string(repl->latest()) +
|
|
",\"entries\":[";
|
|
bool first = true;
|
|
for (const auto& e : entries) {
|
|
if (!first) body += ",";
|
|
first = false;
|
|
body += "{\"seq\":" + std::to_string(e.seq) + ",\"object\":\"" +
|
|
fw::json_escape(e.object) + "\",\"resolution\":\"" +
|
|
fw::json_escape(e.resolution) + "\"}";
|
|
}
|
|
return fw::JsonResponse(body + "]}", 200);
|
|
});
|
|
router.post("/replication/state", "repl-append", [repl](fw::Request req) {
|
|
auto input = fw::parse_json_object_flat(req.body);
|
|
if (input.empty()) input = fw::parse_form(req.body);
|
|
std::string object = input.count("object") ? input["object"] : "doc-1";
|
|
std::string data = input.count("data") ? input["data"] : "{}";
|
|
fw::ReplEntry e = repl->append(object, data);
|
|
return fw::JsonResponse("{\"seq\":" + std::to_string(e.seq) +
|
|
",\"resolution\":\"" +
|
|
fw::json_escape(e.resolution) + "\"}",
|
|
201);
|
|
});
|
|
|
|
router.get("/supervisor/tasks", "tasks-list", [tasks](fw::Request req) {
|
|
(void)req;
|
|
std::string body = "{\"tasks\":[";
|
|
bool first = true;
|
|
for (const auto& t : tasks->list()) {
|
|
if (!first) body += ",";
|
|
first = false;
|
|
body += "{\"id\":\"" + fw::json_escape(t.id) + "\",\"state\":\"" +
|
|
fw::json_escape(t.state) + "\"}";
|
|
}
|
|
return fw::JsonResponse(body + "]}", 200);
|
|
});
|
|
router.post("/supervisor/tasks", "tasks-enqueue", [tasks](fw::Request req) {
|
|
auto input = fw::parse_json_object_flat(req.body);
|
|
if (input.empty()) input = fw::parse_form(req.body);
|
|
std::string kind = input.count("kind") ? input["kind"] : "email";
|
|
std::string payload =
|
|
input.count("payload") ? input["payload"] : "{}";
|
|
std::string id = tasks->enqueue(kind, payload, 0);
|
|
if (id.empty()) return fw::JsonResponse("{\"error\":\"queue full\"}", 500);
|
|
return fw::JsonResponse("{\"id\":\"" + fw::json_escape(id) + "\"}", 201);
|
|
});
|
|
router.get("/supervisor/workers", "workers",
|
|
[supervisor](fw::Request req) {
|
|
(void)req;
|
|
std::string body = "{\"workers\":[";
|
|
bool first = true;
|
|
for (const auto& w : supervisor->status()) {
|
|
if (!first) body += ",";
|
|
first = false;
|
|
body += "{\"name\":\"" + fw::json_escape(w.name) +
|
|
"\",\"running\":" +
|
|
(w.running ? "true" : "false") + ",\"restarts\":" +
|
|
std::to_string(w.restarts) + "}";
|
|
}
|
|
return fw::JsonResponse(body + "]}", 200);
|
|
});
|
|
|
|
router.get("/restclient/self", "restclient", [](fw::Request req) {
|
|
(void)req;
|
|
fw::RestClient client([](fw::Request r) {
|
|
(void)r;
|
|
return fw::JsonResponse("{\"status\":\"ok\"}", 200);
|
|
});
|
|
fw::Response upstream = client.get("/health");
|
|
return fw::JsonResponse(
|
|
"{\"self_call\":" + std::to_string(upstream.status) + "}", 200);
|
|
});
|
|
|
|
openapi->add("/health", "get", "health probe");
|
|
openapi->add("/orm/users", "get", "list users");
|
|
openapi->add("/validate/user", "post", "validate user model");
|
|
router.get("/openapi.json", "openapi", [openapi](fw::Request req) {
|
|
(void)req;
|
|
return fw::Response(200, openapi->to_json(), "application/json");
|
|
});
|
|
router.get("/docs", "docs", [env, openapi](fw::Request req) {
|
|
(void)req;
|
|
(void)openapi;
|
|
fw::TemplateCtx ctx;
|
|
ctx["title"] = std::string("API docs");
|
|
fw::TemplateList rows;
|
|
rows.push_back({{"method", "GET"},
|
|
{"path", "/health"},
|
|
{"summary", "health probe"}});
|
|
rows.push_back({{"method", "GET"},
|
|
{"path", "/orm/users"},
|
|
{"summary", "list users"}});
|
|
rows.push_back({{"method", "POST"},
|
|
{"path", "/validate/user"},
|
|
{"summary", "validate user model"}});
|
|
ctx["routes"] = rows;
|
|
return fw::Response(200, env->render("docs.html", ctx),
|
|
"text/html; charset=utf-8");
|
|
});
|
|
|
|
router.get("/cli/ai", "ai", [](fw::Request req) {
|
|
std::string prompt = req.query_or("prompt", "add a route");
|
|
fw::AiSample sample = fw::ai_harness_sample(prompt);
|
|
std::string body = "{\"base_url\":\"" + sample.base_url +
|
|
"\",\"model\":\"" + sample.model +
|
|
"\",\"prompt\":\"" +
|
|
fw::json_escape(sample.redacted_prompt) +
|
|
"\",\"stream_events\":" +
|
|
std::to_string(sample.stream_chunks.size()) + "}";
|
|
return fw::JsonResponse(body, 200);
|
|
});
|
|
|
|
router.post("/workflow/release", "workflow", [](fw::Request req) {
|
|
auto input = fw::parse_json_object_flat(req.body);
|
|
if (input.empty()) input = fw::parse_form(req.body);
|
|
std::string subjects = input.count("commits") ? input["commits"] : "";
|
|
std::vector<fw::CommitInfo> commits;
|
|
std::size_t start = 0;
|
|
while (start < subjects.size()) {
|
|
std::size_t bar = subjects.find('|', start);
|
|
std::string one =
|
|
subjects.substr(start, bar == std::string::npos ? bar : bar - start);
|
|
fw::CommitInfo info;
|
|
std::string error;
|
|
if (!fw::parse_commit(one, "", info, error))
|
|
return fw::JsonResponse(
|
|
"{\"error\":\"bad commit: " + fw::json_escape(error) + "\"}",
|
|
400);
|
|
commits.push_back(info);
|
|
if (bar == std::string::npos) break;
|
|
start = bar + 1;
|
|
}
|
|
fw::ReleasePlanner planner;
|
|
fw::ChangelogWriter writer;
|
|
fw::ReleaseVerifier verifier;
|
|
verifier.add_check("suite", true, "example suite green");
|
|
verifier.add_check("live", true, "health probe ok");
|
|
std::string version = planner.next_version("0.1.0", commits);
|
|
std::string body =
|
|
"{\"bump\":\"" + fw::bump_name(planner.decide(commits)) +
|
|
"\",\"version\":\"" + version + "\",\"green\":" +
|
|
(verifier.green() ? "true" : "false") + ",\"changelog\":\"" +
|
|
fw::json_escape(writer.entry(version, "2026-10-23", commits)) + "\"}";
|
|
return fw::JsonResponse(body, 200);
|
|
});
|
|
}
|
|
|
|
} // namespace molo::example::app
|