Sfoglia il codice sorgente

feat(api): project + global stats handlers

Fszontagh 2 mesi fa
parent
commit
1adb7f154d
2 ha cambiato i file con 49 aggiunte e 1 eliminazioni
  1. 37 1
      src/handlers/stats.cpp
  2. 12 0
      tests/test_api_integration.cpp

+ 37 - 1
src/handlers/stats.cpp

@@ -1,2 +1,38 @@
+#include "collection_registry.hpp"
+#include "errors.hpp"
+#include "json_http.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);
+    });
+}
+}

+ 12 - 0
tests/test_api_integration.cpp

@@ -141,3 +141,15 @@ TEST_F(ApiFixture, VectorOnJsonCollectionIs422) {
     ASSERT_EQ(c.Post(base.c_str(), nlohmann::json{{"name","plain"},{"kind","json"}}.dump(),"application/json")->status, 201);
     EXPECT_EQ(c.Post((base + "/plain/vectors").c_str(), nlohmann::json{{"vector",{1,2,3,4}}}.dump(), "application/json")->status, 422);
 }
+
+TEST_F(ApiFixture, ProjectStatsAndGlobalStats) {
+    auto c = admin();
+    auto p = c.Get(("/api/v1/projects/" + project_ + "/stats").c_str());
+    ASSERT_EQ(p->status, 200);
+    auto pJson = nlohmann::json::parse(p->body);
+    EXPECT_TRUE(pJson.contains("collections"));
+    auto g = c.Get("/api/v1/stats");
+    ASSERT_EQ(g->status, 200);
+    auto gJson = nlohmann::json::parse(g->body);
+    EXPECT_TRUE(gJson.contains("memory_pressure_level"));
+}