Move subnav menus into top-bar, apply Predictalytics glassmorphism design, add project edit and safe deletion modal, secure license deactivation, add API Swagger docs, and add Watchdog entity hierarchy tree with full CRUD

This commit is contained in:
Deploymentcenter Bot
2026-08-05 22:20:18 +02:00
parent fe9f95584e
commit 731965bde5
3 changed files with 579 additions and 406 deletions
+21 -1
View File
@@ -5,12 +5,14 @@ declare(strict_types=1);
header('Content-Type: application/json; charset=utf-8');
require_once __DIR__ . '/../../../../src/Core/Db.php';
require_once __DIR__ . '/../../../../src/Core/Auth.php';
require_once __DIR__ . '/../../../../src/Modules/License/Audit.php';
require_once __DIR__ . '/../../../../src/Modules/License/KeyGen.php';
require_once __DIR__ . '/../../../../src/Modules/License/RateLimiter.php';
require_once __DIR__ . '/../../../../src/Modules/License/LicenseService.php';
use Deploymentcenter\Core\Db;
use Deploymentcenter\Core\Auth;
use Deploymentcenter\Modules\License\LicenseService;
use Deploymentcenter\Modules\License\RateLimiter;
@@ -38,18 +40,36 @@ try {
$licenseService = new LicenseService($pdo);
// Validate Endpoint (Public API for clients)
if (str_ends_with($uri, '/validate') && $method === 'POST') {
$res = $licenseService->validate($inputData, $ip);
sendResponse($res);
}
// Deactivate Endpoint (AUTHENTICATED ONLY - Security Protection)
if (str_ends_with($uri, '/deactivate') && $method === 'POST') {
$authHeader = $_SERVER['HTTP_X_WATCHDOG_KEY'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['HTTP_X_LICENSE_KEY'] ?? null;
if ($authHeader && str_starts_with($authHeader, 'Bearer ')) {
$authHeader = substr($authHeader, 7);
}
$sharedKey = $config['security']['shared_key'] ?? '';
$isAuthenticated = ($authHeader && hash_equals($sharedKey, $authHeader)) || Auth::isLoggedIn();
if (!$isAuthenticated) {
sendResponse([
'error' => 'Unauthorized',
'message' => 'Authentication required for license deactivation. Pass Bearer token or master key.'
], 401);
}
$res = $licenseService->deactivate($inputData, $ip);
sendResponse($res);
}
// Status Endpoint
if (str_ends_with($uri, '/status') && $method === 'GET') {
sendResponse(['status' => 'ok', 'module' => 'LicenseLabrador', 'version' => '1.0']);
sendResponse(['status' => 'ok', 'module' => 'Lizenzen', 'version' => '1.0']);
}
sendResponse(['error' => 'Not Found', 'message' => 'Endpoint not found'], 404);
+512 -404
View File
File diff suppressed because it is too large Load Diff
+46 -1
View File
@@ -38,7 +38,6 @@ class MonitorRepo
?string $groupKey = null,
?string $os = null
): array {
$nowUtc = date('Y-m-d H:i:s');
$metricsJson = is_array($metrics) || is_object($metrics) ? json_encode($metrics, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) : null;
$state = ($status === 'ok') ? 'up' : (($status === 'warning') ? 'warning' : 'down');
@@ -78,6 +77,52 @@ class MonitorRepo
return $this->getMonitor($source, $instance);
}
public function updateMonitor(
string $oldSource,
string $newSource,
string $instance,
?string $groupKey,
?string $parentSource,
?string $notes,
?string $url,
?int $intervalSec,
?string $icon = null,
bool $isMuted = false
): bool {
$stmt = $this->db->prepare('
UPDATE watchdog_monitors SET
source = :new_source,
group_key = :group_key,
parent_source = :parent_source,
notes = :notes,
url = :url,
expected_interval_sec = COALESCE(:interval, expected_interval_sec),
icon = COALESCE(:icon, icon),
is_muted = :is_muted,
updated_utc = NOW()
WHERE source = :old_source AND instance = :instance
');
return $stmt->execute([
':new_source' => $newSource,
':group_key' => !empty($groupKey) ? $groupKey : null,
':parent_source' => !empty($parentSource) ? $parentSource : null,
':notes' => !empty($notes) ? $notes : null,
':url' => !empty($url) ? $url : null,
':interval' => $intervalSec,
':icon' => !empty($icon) ? $icon : null,
':is_muted' => $isMuted ? 1 : 0,
':old_source' => $oldSource,
':instance' => $instance,
]);
}
public function setParentSource(string $source, ?string $parentSource, string $instance = 'default'): bool
{
$stmt = $this->db->prepare('UPDATE watchdog_monitors SET parent_source = :parent, updated_utc = NOW() WHERE source = :s AND instance = :i');
return $stmt->execute([':parent' => !empty($parentSource) ? $parentSource : null, ':s' => $source, ':i' => $instance]);
}
public function updateState(int $id, string $state, ?string $reason = null): bool
{
$stmt = $this->db->prepare('UPDATE watchdog_monitors SET state = :state, metric_state_reason = :reason, updated_utc = NOW() WHERE id = :id');