#include "versiond/versiond.hpp" #include #include #include #include #include #include #include #include namespace fs = std::filesystem; namespace versiond { static std::string dash_html() { std::ifstream f("src/versiond/dashboard.html", std::ios::binary); if (f) { std::ostringstream s; s << f.rdbuf(); return s.str(); } return "

versiond

"; } ApiResp api_build_response(Db& db, const std::string& path) { ApiResp r{200, "application/json", ""}; if (path == "/" || path == "/dashboard") { r.ctype = "text/html"; r.body = dash_html(); } else if (path == "/api/stats") { auto m = collect_stats(db); std::ostringstream o; o << "{\"versions\":" << m["versions"] << ",\"blobs\":" << m["blobs"] << "}"; r.body = o.str(); } else if (path == "/api/versions") { auto rows = db.query("SELECT id,path,digest,reason FROM versions ORDER BY id DESC LIMIT 100;"); std::ostringstream o; o << "["; bool first = true; for (auto& x : rows) { if (!first) o << ","; first = false; o << "{\"id\":" << x.at("id") << ",\"digest\":\"" << x.at("digest") << "\"}"; } o << "]"; r.body = o.str(); } else if (path == "/api/health") { r.body = "{\"ok\":true}"; } else if (path == "/api/blobs") { auto m = collect_stats(db); std::ostringstream o; o << "{\"blobs\":" << m["blobs"] << "}"; r.body = o.str(); } else { r.code = 404; r.body = "{\"error\":\"not found\"}"; } return r; } std::string api_format_response(const ApiResp& r) { std::ostringstream h; h << "HTTP/1.1 " << r.code << (r.code == 200 ? " OK" : " Not Found") << "\r\n"; h << "Content-Type: " << r.ctype << "\r\n"; h << "Content-Length: " << r.body.size() << "\r\nConnection: close\r\n\r\n"; return h.str() + r.body; } static void send_all(int fd, const std::string& s) { size_t n = 0; while (n < s.size()) { ssize_t w = send(fd, s.data() + n, s.size() - n, 0); if (w <= 0) break; n += (size_t)w; } } int api_main(const Config& c) { int srv = socket(AF_INET, SOCK_STREAM, 0); if (srv < 0) return 1; int one = 1; setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); sockaddr_in a{}; a.sin_family = AF_INET; a.sin_port = htons((uint16_t)c.port); a.sin_addr.s_addr = inet_addr(c.host.c_str()); if (bind(srv, (sockaddr*)&a, sizeof(a)) < 0) return 1; if (listen(srv, 16) < 0) return 1; printf("serve %s:%d\n", c.host.c_str(), c.port); fflush(stdout); Db db(c.index_file); Store st(c.spool_dir); (void)st; char buf[65536]; for (;;) { int fd = accept(srv, nullptr, nullptr); if (fd < 0) break; ssize_t n = recv(fd, buf, sizeof(buf) - 1, 0); std::string req = n > 0 ? std::string(buf, (size_t)n) : ""; std::istringstream rs(req); std::string method, path; rs >> method >> path; ApiResp r = api_build_response(db, path); std::string full = api_format_response(r); send_all(fd, full); close(fd); } close(srv); return 0; } }