diff options
Diffstat (limited to 'src/zenserver/storage/admin/admin.cpp')
| -rw-r--r-- | src/zenserver/storage/admin/admin.cpp | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/src/zenserver/storage/admin/admin.cpp b/src/zenserver/storage/admin/admin.cpp index 6e78a6179..f1c2daea4 100644 --- a/src/zenserver/storage/admin/admin.cpp +++ b/src/zenserver/storage/admin/admin.cpp @@ -791,6 +791,139 @@ HttpAdminService::HttpAdminService(GcScheduler& Scheduler, }, HttpVerb::kPost); m_Router.RegisterRoute( + "storage", + [this](HttpRouterRequest& Req) { + CbObjectWriter Obj; + + // Collect known storage directories + struct StorageDir + { + std::string_view Name; + std::filesystem::path Path; + }; + + std::vector<StorageDir> Dirs; + const std::filesystem::path& DataDir = m_ServerOptions.DataDir; + Dirs.push_back({"cache"sv, DataDir / "cache"}); + Dirs.push_back({"cas"sv, DataDir / "cas"}); + Dirs.push_back({"projects"sv, DataDir / "projects"}); + Dirs.push_back({"builds"sv, DataDir / "builds"}); + Dirs.push_back({"builds_cas"sv, DataDir / "builds_cas"}); + Dirs.push_back({"obj"sv, DataDir / "obj"}); + + // Group directories by volume (identified by total capacity) + struct VolumeInfo + { + DiskSpace Space; + std::vector<const StorageDir*> Directories; + }; + + // Use canonical path to identify volumes. Directories on the same volume + // will report the same total capacity from DiskSpaceInfo. + std::vector<VolumeInfo> Volumes; + auto FindOrAddVolume = [&](DiskSpace Space) -> VolumeInfo& { + for (VolumeInfo& V : Volumes) + { + if (V.Space.Total == Space.Total && V.Space.Free == Space.Free) + { + return V; + } + } + Volumes.push_back({Space, {}}); + return Volumes.back(); + }; + + for (StorageDir& Dir : Dirs) + { + if (!IsDir(Dir.Path)) + { + continue; + } + DiskSpace Space; + if (DiskSpaceInfo(Dir.Path, Space)) + { + FindOrAddVolume(Space).Directories.push_back(&Dir); + } + } + + Obj.BeginArray("volumes"sv); + for (const VolumeInfo& Vol : Volumes) + { + Obj.BeginObject(); + Obj << "total"sv << Vol.Space.Total; + Obj << "free"sv << Vol.Space.Free; + Obj << "used"sv << (Vol.Space.Total - Vol.Space.Free); + + Obj.BeginArray("directories"sv); + for (const StorageDir* Dir : Vol.Directories) + { + Obj.BeginObject(); + Obj << "name"sv << Dir->Name; + Obj << "path"sv << Dir->Path.string(); + + DirStats Stats = GetStatsForDirectory(Dir->Path); + Obj << "bytes"sv << Stats.ByteCount; + Obj << "files"sv << Stats.FileCount; + Obj.EndObject(); + } + Obj.EndArray(); + Obj.EndObject(); + } + Obj.EndArray(); + + Req.ServerRequest().WriteResponse(HttpResponseCode::OK, Obj.Save()); + }, + HttpVerb::kGet); + + m_Router.RegisterRoute( + "gclog", + [this](HttpRouterRequest& Req) { + const GcSchedulerState State = m_GcScheduler.GetState(); + const std::filesystem::path Path = State.Config.RootDirectory / "gc.log"; + + CbObjectWriter Response; + Response.BeginArray("entries"sv); + + try + { + if (IsFile(Path)) + { + IoBuffer FileData = ReadFile(Path).Flatten(); + + // The log file contains concatenated named CBO object fields. + // Each field is a complete entry: [type+name header][object payload]. + // We wrap each one in a CbObject and add it to the response array. + const uint8_t* Ptr = static_cast<const uint8_t*>(FileData.GetData()); + const uint8_t* End = Ptr + FileData.GetSize(); + + while (Ptr < End) + { + CbFieldView Field(Ptr); + uint64_t FieldSize = Field.GetSize(); + if (FieldSize == 0 || Ptr + FieldSize > End) + { + break; + } + + // Wrap the named field as an object and add it + CbObjectView ObjView = Field.AsObjectView(); + CbObject Entry = CbObject::Clone(ObjView); + Response.AddObject(Entry); + Ptr += FieldSize; + } + } + } + catch (const std::exception& Ex) + { + ZEN_WARN("failed to read gc log '{}': {}", Path, Ex.what()); + } + + Response.EndArray(); + Req.ServerRequest().WriteResponse(HttpResponseCode::OK, Response.Save()); + }, + HttpVerb::kGet); + + m_Router.RegisterRoute( "flush", [this](HttpRouterRequest& Req) { HttpServerRequest& HttpReq = Req.ServerRequest(); |