- Add Hook interface (filter + execute contract) in server/hooks/hook.h
- Add HookRunner in server/hooks/hook_runner.h: spawns a detached thread
per matching hook, with try/catch protection against crashes
- Add DnsUpdaterHook in server/hooks/dns_updater_hook.{h,cpp}:
triggers on server name changes, logs in to Technitium, deletes the
old A record (ignores 404), and adds the new A record
Config via env vars: TECHNITIUM_HOST/PORT/USER/PASS/ZONE, DNS_TTL
- Add Database::get_nics_for_server() to resolve a server's IPv4 address
- Wire HookRunner into InventoryServer; cmd_edit_server now fires hooks
with before/after Server snapshots
- Update CMakeLists.txt to include dns_updater_hook.cpp
- Document env vars in Dockerfile
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
79 lines
3.2 KiB
C++
79 lines
3.2 KiB
C++
#pragma once
|
||
#include "server/hooks/hook.h"
|
||
#include <arpa/inet.h>
|
||
#include <cstring>
|
||
#include <iostream>
|
||
#include <netdb.h>
|
||
#include <sstream>
|
||
#include <stdexcept>
|
||
#include <string>
|
||
#include <sys/socket.h>
|
||
#include <unistd.h>
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// DnsUpdaterHook
|
||
//
|
||
// Fires whenever a server's *name* changes. Updates Technitium DNS by:
|
||
// 1. Logging in → GET /api/user/login?user=…&pass=… → token
|
||
// 2. Deleting the old A record (404 errors are silently ignored)
|
||
// 3. Adding the new A record
|
||
//
|
||
// The server's IP is derived from the first NIC ip_address that belongs to the
|
||
// server record. This hook receives the old and new Server structs; to look up
|
||
// IP addresses it needs access to the Database – pass a const ref in the ctor.
|
||
//
|
||
// Config is supplied via environment variables so no secrets land in source:
|
||
// TECHNITIUM_HOST (default: 192.168.2.193)
|
||
// TECHNITIUM_PORT (default: 5380)
|
||
// TECHNITIUM_USER (default: admin)
|
||
// TECHNITIUM_PASS (required – hook logs an error and skips if unset)
|
||
// TECHNITIUM_ZONE (default: homelab)
|
||
// DNS_TTL (default: 300)
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
|
||
class Database; // forward declaration – full header included in .cpp
|
||
|
||
class DnsUpdaterHook : public Hook {
|
||
public:
|
||
explicit DnsUpdaterHook(const Database& db) : db_(db) {}
|
||
|
||
// Trigger only when the server name changes.
|
||
bool filter(const Server& old_server, const Server& new_server) override {
|
||
return old_server.name != new_server.name;
|
||
}
|
||
|
||
void execute(const Server& old_server, const Server& new_server) override;
|
||
|
||
private:
|
||
const Database& db_;
|
||
|
||
// Resolve config from environment, falling back to defaults.
|
||
struct Config {
|
||
std::string host;
|
||
int port;
|
||
std::string user;
|
||
std::string pass;
|
||
std::string zone;
|
||
int ttl;
|
||
};
|
||
static Config load_config();
|
||
|
||
// Percent-encode a string for use in a URL query parameter.
|
||
static std::string url_encode(const std::string& s);
|
||
|
||
// Open a TCP connection; throws on failure.
|
||
static int connect_to(const std::string& host, int port);
|
||
|
||
// Send an HTTP/1.0 GET request and return the full response body.
|
||
static std::string http_get(const std::string& host, int port,
|
||
const std::string& path);
|
||
|
||
// Send an HTTP/1.0 POST request (application/x-www-form-urlencoded body)
|
||
// and return the full response body.
|
||
static std::string http_post(const std::string& host, int port,
|
||
const std::string& path,
|
||
const std::string& body);
|
||
|
||
// Minimal JSON value extractor – finds "key":"value" or "key":value.
|
||
static std::string json_get(const std::string& json, const std::string& key);
|
||
};
|