CxWebApp/src/routes/pages.cpp
CxAI Agent 79b3f32398
Some checks are pending
build-and-push / image (push) Waiting to run
feat: enhance Slack sidecar with response presets and improved diagnostics
- Added response presets for standup, bug report, and ship update in the Slack sidecar.
- Improved UI elements including a copy button for replies and conversation memory.
- Enhanced diagnostics section with better health indicators and tool listings.
- Introduced performance metrics for response times in the Slack form.

feat: revamp system pane for better process and host introspection

- Updated system pane to include live sparklines for memory and CPU usage.
- Enhanced KPI display with uptime, memory usage, and CPU load metrics.
- Added functionality to capture and display configured services and their statuses.

feat: overhaul tools pane for improved usability and interaction

- Redesigned tools pane to allow filtering, favoriting, and invoking MCP tools.
- Introduced a history feature to track recent tool calls.
- Enhanced form handling for tool arguments with schema-driven inputs.

feat: new files pane for accessing platform data

- Implemented a new files pane to surface data from files.cxllm.io.
- Added tabs for providers, edge functions, health, demand runs, watcher events, and file manager categories.
- Included functionality to capture health snapshots and display recent data.
2026-05-18 06:46:32 -05:00

144 lines
4.9 KiB
C++

#include "crow.h"
#include "routes.h"
#include <cstdlib>
#include <fstream>
#include <initializer_list>
#include <sstream>
#include <string>
namespace routes {
namespace {
std::string static_dir() {
if (const char* env = std::getenv("CXWEBAPP_STATIC_DIR")) {
return std::string(env);
}
#ifdef CXWEBAPP_STATIC_DIR_DEFAULT
return std::string(CXWEBAPP_STATIC_DIR_DEFAULT);
#else
return "static";
#endif
}
std::string read_file(const std::string& path) {
std::ifstream ifs(path, std::ios::binary);
if (!ifs.is_open()) return "";
std::ostringstream ss;
ss << ifs.rdbuf();
return ss.str();
}
std::string mime_for(const std::string& path) {
auto ends = [&](const char* s) {
size_t n = std::char_traits<char>::length(s);
return path.size() >= n && path.compare(path.size() - n, n, s) == 0;
};
if (ends(".html")) return "text/html; charset=utf-8";
if (ends(".css")) return "text/css; charset=utf-8";
if (ends(".js")) return "application/javascript; charset=utf-8";
if (ends(".mjs")) return "application/javascript; charset=utf-8";
if (ends(".worker.js")) return "application/javascript; charset=utf-8";
if (ends(".wasm")) return "application/wasm";
if (ends(".json")) return "application/json; charset=utf-8";
if (ends(".data")) return "application/octet-stream";
if (ends(".svg")) return "image/svg+xml";
if (ends(".png")) return "image/png";
if (ends(".jpg") || ends(".jpeg")) return "image/jpeg";
if (ends(".gif")) return "image/gif";
if (ends(".ico")) return "image/x-icon";
if (ends(".woff2")) return "font/woff2";
if (ends(".woff")) return "font/woff";
if (ends(".map")) return "application/json; charset=utf-8";
if (ends(".txt")) return "text/plain; charset=utf-8";
return "application/octet-stream";
}
bool is_safe(const std::string& path) {
if (path.empty()) return false;
if (path.front() == '/') return false;
if (path.find("..") != std::string::npos) return false;
if (path.find('\0') != std::string::npos) return false;
return true;
}
bool has_segment(const std::string& path, const std::string& segment) {
return path == segment || path.rfind(segment + "/", 0) == 0 ||
path.find("/" + segment + "/") != std::string::npos;
}
std::string cache_for(const std::string& rel) {
if (rel == "index.html" || rel == "service-catalog.json" || rel == "wasm/modules.json") {
return "no-cache, max-age=0, must-revalidate";
}
if (has_segment(rel, "assets") || has_segment(rel, "wasm")) {
return "public, max-age=31536000, immutable";
}
return "public, max-age=300";
}
std::string join_path(std::initializer_list<std::string> parts) {
std::string joined;
for (const auto& part : parts) {
if (joined.empty()) {
joined = part;
} else {
joined += "/" + part;
}
}
return joined;
}
crow::response serve_static(const std::string& rel) {
if (!is_safe(rel)) return crow::response(400, "bad path");
const std::string full = static_dir() + "/" + rel;
auto body = read_file(full);
if (body.empty()) return crow::response(404, "not found");
crow::response resp(body);
resp.add_header("Content-Type", mime_for(rel));
resp.add_header("Cache-Control", cache_for(rel));
return resp;
}
} // anonymous namespace
void register_pages(crow::SimpleApp& app) {
CROW_ROUTE(app, "/")
([]() { return serve_static("index.html"); });
CROW_ROUTE(app, "/favicon.ico")
([]() { return serve_static("favicon.svg"); });
// Crow's `<string>` matches a single segment. Register enough depth for Vite and Wasm side files.
CROW_ROUTE(app, "/static/<string>")
([](const std::string& a) { return serve_static(a); });
CROW_ROUTE(app, "/static/<string>/<string>")
([](const std::string& a, const std::string& b) {
return serve_static(a + "/" + b);
});
CROW_ROUTE(app, "/static/<string>/<string>/<string>")
([](const std::string& a, const std::string& b, const std::string& c) {
return serve_static(join_path({a, b, c}));
});
CROW_ROUTE(app, "/static/<string>/<string>/<string>/<string>")
([](const std::string& a, const std::string& b, const std::string& c, const std::string& d) {
return serve_static(join_path({a, b, c, d}));
});
CROW_ROUTE(app, "/static/<string>/<string>/<string>/<string>/<string>")
([](const std::string& a, const std::string& b, const std::string& c, const std::string& d, const std::string& e) {
return serve_static(join_path({a, b, c, d, e}));
});
CROW_ROUTE(app, "/static/<string>/<string>/<string>/<string>/<string>/<string>")
([](const std::string& a, const std::string& b, const std::string& c, const std::string& d, const std::string& e, const std::string& f) {
return serve_static(join_path({a, b, c, d, e, f}));
});
}
} // namespace routes