|
@@ -1,2 +1,38 @@
|
|
|
|
|
+#include "collection_registry.hpp"
|
|
|
|
|
+#include "errors.hpp"
|
|
|
|
|
+#include "json_http.hpp"
|
|
|
#include "server.hpp"
|
|
#include "server.hpp"
|
|
|
-namespace svapi { void registerStatsRoutes(ApiServer&) {} }
|
|
|
|
|
|
|
+namespace svapi {
|
|
|
|
|
+void registerStatsRoutes(ApiServer& s) {
|
|
|
|
|
+ auto& svr = s.raw(); ServerDeps* d = &s.deps();
|
|
|
|
|
+
|
|
|
|
|
+ // Per-project stats (any key granted the project).
|
|
|
|
|
+ svr.Get(R"(/api/v1/projects/([^/]+)/stats)", [d](const httplib::Request& req, httplib::Response& res) {
|
|
|
|
|
+ ApiKey k = requireKey(*d, req); std::string project = req.matches[1];
|
|
|
|
|
+ requireProjectAccess(k, project);
|
|
|
|
|
+ auto cols = d->registry.list(project);
|
|
|
|
|
+ uint64_t docs = 0;
|
|
|
|
|
+ for (const auto& m : cols)
|
|
|
|
|
+ if (auto info = d->db.client().getCollectionInfo(qualify(project, m.name))) docs += info->documentCount;
|
|
|
|
|
+ sendJson(res, 200, {{"project", project}, {"collections", cols.size()}, {"documents", docs}});
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Global server stats (admin only).
|
|
|
|
|
+ svr.Get("/api/v1/stats", [d](const httplib::Request& req, httplib::Response& res) {
|
|
|
|
|
+ requireAdmin(requireKey(*d, req));
|
|
|
|
|
+ nlohmann::json out;
|
|
|
|
|
+ if (auto st = d->db.client().getStats()) {
|
|
|
|
|
+ out["total_documents"] = st->totalDocuments;
|
|
|
|
|
+ out["total_collections"] = st->totalCollections;
|
|
|
|
|
+ out["memory_used_bytes"] = st->memoryUsedBytes;
|
|
|
|
|
+ out["insert_count"] = st->insertCount;
|
|
|
|
|
+ out["query_count"] = st->queryCount;
|
|
|
|
|
+ } else { out["total_documents"] = 0; }
|
|
|
|
|
+ auto mem = d->db.client().getMemoryStats();
|
|
|
|
|
+ out["memory_pressure_level"] = mem.pressureLevel;
|
|
|
|
|
+ out["memory_pressure_percent"] = mem.pressurePercent;
|
|
|
|
|
+ out["projects"] = d->db.listProjects().size();
|
|
|
|
|
+ sendJson(res, 200, out);
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+}
|