2025-05-11 14:16:05 +02:00

137 lines
3.9 KiB
C++

#include <crow.h>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <string>
#include <array>
#include <format>
#include <optional>
using namespace std;
string load_file(const string& path) {
ifstream f(path);
stringstream buffer;
buffer << f.rdbuf();
return buffer.str();
}
bool is_service_active(string_view service_name) {
const string cmd = format("systemctl is-active --quiet {}", service_name);
return system(cmd.c_str()) == 0;
}
bool is_service_enabled(string_view service_name) {
const string cmd = format("systemctl is-enabled --quiet {}", service_name);
return system(cmd.c_str()) == 0;
}
void toggle_service(string_view serviceName){
string_view toggle = is_service_active(serviceName) ? "stop" : "start";
const string cmd = format("systemctl {} {}", toggle, serviceName);
system(cmd.c_str());
}
string_view get_button_class(bool isActive) {
return isActive ? "active-button" : "inactive-button";
}
optional<string> get_body_name(const string& body) {
const auto pos = body.find('=');
if (pos == std::string::npos) return {};
const string key = body.substr(0, pos);
string value = body.substr(pos + 1);
if (key != "name") return {};
return value;
}
string create_htmx_button(string_view endpoint, string_view serviceName, string_view text) {
return format(
"<td>\
<button\
hx-post=\"{}\"\
hx-vals='{{\"name\":\"{}\"}}'\
hx-target=\"closest tr\"\
hx-swap=\"outerHTML\">\
{} \
</button> \
</td>", endpoint, serviceName, text);
}
string create_htmx_table_row(string_view serviceName){
const bool isRunning = is_service_active(serviceName);
const bool isEnabled = is_service_active(serviceName);
const auto running = isRunning ? "Running" : "Stopped";
const auto enabled = isEnabled ? "Enabled" : "Disabled";
// create status indicators
string html = format(
"<tr>\
<td>{}</td>\
<td class='{}'>{}</td>\
<td class='{}'>{}</td>",
serviceName, get_button_class(isRunning), running, get_button_class(isEnabled), enabled);
// create buttons
html += create_htmx_button("/toggle-service", serviceName, isRunning ? "Stop" : "Start");
html += create_htmx_button("/enable-service", serviceName, isEnabled ? "Disable" : "Enable");
html += create_htmx_button("/restart-service", serviceName, "Restart");
return html;
}
const array<string, 1>& get_service_names() {
static const array<string, 1> arr = {
"cups.service"
};
return arr;
}
int main() {
crow::SimpleApp app;
CROW_ROUTE(app, "/")([] {
return crow::response(load_file("templates/index.html"));
});
CROW_ROUTE(app, "/static/<string>")([](const std::string& file) {
return crow::response(load_file("static/" + file));
});
CROW_ROUTE(app, "/status")([] {
auto names = get_service_names();
// define the table header
string html = "<table><tr>\
<th>Service</th>\
<th>Status</th>\
<th>State</th>\
</tr>";
// display each service as an entry
for (auto& serviceName : names) {
html += create_htmx_table_row(serviceName);
}
html += " </tr></table>";
return crow::response{html};
});
CROW_ROUTE(app, "/toggle-service").methods(crow::HTTPMethod::Post)([](const crow::request& req) {
auto body = get_body_name(req.body);
if (!body.has_value())
return crow::response(400);
const string& serviceName = body.value();
toggle_service(serviceName);
const string html = create_htmx_table_row(serviceName);
return crow::response{html};
});
app.port(8080).multithreaded().run();
}