Open source release: versioningcpp academic achievement
This commit is contained in:
+104
@@ -0,0 +1,104 @@
|
||||
#include "versiond/versiond.hpp"
|
||||
#include <arpa/inet.h>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <netinet/in.h>
|
||||
#include <sstream>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
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 "<html><body><h1>versiond</h1></body></html>";
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user