#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 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