diff --git a/modules/pi_control/bootstrap.php b/modules/pi_control/bootstrap.php new file mode 100644 index 0000000..432939a --- /dev/null +++ b/modules/pi_control/bootstrap.php @@ -0,0 +1,103 @@ +registerFunction($moduleName, 'table', function (string $name): string { + $prefix = 'picontrol_'; + $sanitized = preg_replace('/[^a-zA-Z0-9_]/', '', $name); + return $prefix . $sanitized; +}); + +modules()->registerFunction($moduleName, 'pdo', function () use ($moduleName): \PDO { + $settings = modules()->settings($moduleName); + $useSeparate = !empty($settings['use_separate_db']); + + if ($useSeparate) { + // Uses module-specific DB config + $module = modules()->get($moduleName); + $fallback = $module['db_defaults'] ?? []; + return modules()->modulePdo($moduleName, $fallback); + } + + $base = app()->basePdo(); + if (!$base) { + throw new ModuleConfigException( + $moduleName, + 'Base-DB ist deaktiviert. Bitte Base-DB aktivieren oder eigene Modul-DB konfigurieren.' + ); + } + + return $base; +}); + +modules()->registerFunction($moduleName, 'ensure_schema', function () use ($moduleName): void { + $pdo = module_fn($moduleName, 'pdo'); + $table = fn(string $name) => module_fn($moduleName, 'table', $name); + + $driver = (string)$pdo->getAttribute(PDO::ATTR_DRIVER_NAME); + + $hostTable = $table('hosts'); + $cmdTable = $table('commands'); + $runTable = $table('runs'); + + if ($driver === 'pgsql') { + $pdo->exec("CREATE TABLE IF NOT EXISTS {$hostTable} ( + id SERIAL PRIMARY KEY, + name VARCHAR(120) NOT NULL, + host VARCHAR(255) NOT NULL, + port INTEGER NOT NULL DEFAULT 22, + username VARCHAR(120) NOT NULL, + auth_type VARCHAR(20) NOT NULL DEFAULT 'key', + key_path TEXT NULL, + password TEXT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + )"); + $pdo->exec("CREATE TABLE IF NOT EXISTS {$cmdTable} ( + id SERIAL PRIMARY KEY, + label VARCHAR(160) NOT NULL, + command TEXT NOT NULL, + admin_only BOOLEAN NOT NULL DEFAULT false, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + )"); + $pdo->exec("CREATE TABLE IF NOT EXISTS {$runTable} ( + id SERIAL PRIMARY KEY, + host_id INTEGER NULL, + command_id INTEGER NULL, + command_text TEXT NOT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'pending', + output TEXT NULL, + created_by VARCHAR(120) NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP + )"); + } else { + $pdo->exec("CREATE TABLE IF NOT EXISTS {$hostTable} ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR(120) NOT NULL, + host VARCHAR(255) NOT NULL, + port INTEGER NOT NULL DEFAULT 22, + username VARCHAR(120) NOT NULL, + auth_type VARCHAR(20) NOT NULL DEFAULT 'key', + key_path TEXT NULL, + password TEXT NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP + )"); + $pdo->exec("CREATE TABLE IF NOT EXISTS {$cmdTable} ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + label VARCHAR(160) NOT NULL, + command TEXT NOT NULL, + admin_only INTEGER NOT NULL DEFAULT 0, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP + )"); + $pdo->exec("CREATE TABLE IF NOT EXISTS {$runTable} ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + host_id INTEGER NULL, + command_id INTEGER NULL, + command_text TEXT NOT NULL, + status VARCHAR(20) NOT NULL DEFAULT 'pending', + output TEXT NULL, + created_by VARCHAR(120) NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP + )"); + } +}); diff --git a/modules/pi_control/module.json b/modules/pi_control/module.json new file mode 100644 index 0000000..2de09be --- /dev/null +++ b/modules/pi_control/module.json @@ -0,0 +1,45 @@ +{ + "title": "Pi Control", + "version": "0.1.0", + "description": "Verwaltung und Steuerung von Raspberry Pis (SSH/Commands/Presets).", + "menu": [ + { "label": "Übersicht", "href": "/module/pi_control" }, + { "label": "Hosts", "href": "/module/pi_control/hosts" }, + { "label": "Befehle", "href": "/module/pi_control/commands" }, + { "label": "Konsole", "href": "/module/pi_control/console" }, + { "label": "Setup", "href": "/modules/setup/pi_control" } + ], + "sidebar": { + "enabled": true, + "collapsible": true, + "default": "collapsed", + "items": [ + { "label": "Übersicht", "href": "/module/pi_control" }, + { "label": "Hosts", "href": "/module/pi_control/hosts" }, + { "label": "Befehle", "href": "/module/pi_control/commands" }, + { "label": "Konsole", "href": "/module/pi_control/console" }, + { "label": "Setup", "href": "/modules/setup/pi_control" } + ] + }, + "setup": { + "fields": [ + { "name": "use_separate_db", "label": "Eigene Modul-DB nutzen", "type": "checkbox", "required": false, "help": "Wenn aktiv, werden die DB-Daten unten verwendet. Sonst wird die Base-DB genutzt." }, + { "name": "db.driver", "label": "DB Driver", "type": "text", "required": false, "help": "z.B. pgsql, mysql, sqlite" }, + { "name": "db.host", "label": "DB Host", "type": "text", "required": false }, + { "name": "db.port", "label": "DB Port", "type": "number", "required": false }, + { "name": "db.dbname", "label": "DB Name", "type": "text", "required": false }, + { "name": "db.schema", "label": "DB Schema", "type": "text", "required": false }, + { "name": "db.user", "label": "DB User", "type": "text", "required": false }, + { "name": "db.password", "label": "DB Passwort", "type": "password", "required": false } + ] + }, + "db_defaults": { + "driver": "pgsql", + "host": "localhost", + "port": 5432, + "dbname": "", + "schema": "public", + "user": "", + "password": "" + } +} diff --git a/modules/pi_control/pages/commands.php b/modules/pi_control/pages/commands.php new file mode 100644 index 0000000..7a488d1 --- /dev/null +++ b/modules/pi_control/pages/commands.php @@ -0,0 +1,90 @@ + module_fn('pi_control', 'table', $name); + +$notice = null; +$error = null; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + require_admin(); + $label = trim((string)($_POST['label'] ?? '')); + $command = trim((string)($_POST['command'] ?? '')); + $adminOnly = !empty($_POST['admin_only']) ? 1 : 0; + + if ($label === '' || $command === '') { + $error = 'Bitte Label und Command angeben.'; + } else { + $stmt = $pdo->prepare( + 'INSERT INTO ' . $table('commands') . ' (label, command, admin_only) VALUES (:label, :command, :admin_only)' + ); + $stmt->execute([ + 'label' => $label, + 'command' => $command, + 'admin_only' => $adminOnly, + ]); + $notice = 'Befehl gespeichert.'; + } +} + +$commands = $pdo->query('SELECT * FROM ' . $table('commands') . ' ORDER BY id DESC')->fetchAll(PDO::FETCH_ASSOC); +?> +
Verwalte vordefinierte SSH-Befehle.
+ + +| Label | +Command | +Admin | +
|---|---|---|
| Keine Befehle vorhanden. | ||
| = e($c['label'] ?? '') ?> | +
+ = e($c['command'] ?? '') ?>
+ |
+ = !empty($c['admin_only']) ? 'ja' : 'nein' ?> | +
Wähle einen Host und führe einen Befehl aus.
+ + +Hinweis: Execution-Backend wird im nächsten Schritt ergänzt.
+| ID | +Status | +Command | +Von | +
|---|---|---|---|
| Noch keine Runs. | |||
| = e((string)$r['id']) ?> | += e($r['status'] ?? '') ?> | += e($r['command_text'] ?? '') ?> |
+ = e($r['created_by'] ?? '') ?> | +
Verwalte die Raspberry Pis, die du steuern möchtest.
+ + +| Name | +Host | +User | +Port | +Auth | +
|---|---|---|---|---|
| Keine Hosts vorhanden. | ||||
| = e($h['name'] ?? '') ?> | += e($h['host'] ?? '') ?> | += e($h['username'] ?? '') ?> | += e((string)($h['port'] ?? 22)) ?> | += e($h['auth_type'] ?? '') ?> | +
SSH Hosts verwalten, Befehle definieren und Aktionen ausführen.
+ +