137 lines
3.9 KiB
C++
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();
|
|
}
|