#!/usr/bin/env php Passwort zuruecksetzen * php bin/ekdos cache:flush n8n-Cache leeren * php bin/ekdos check Postgres, Redis und n8n pruefen * * Der erste Administrator wird mit user:create angelegt. Es gibt bewusst keinen * Bootstrap-Benutzer aus der Umgebung: ein Passwort in einer .env ist ein * Passwort, das niemand mehr aendert. */ use Ekdos\Auth\AuthService; use Ekdos\Bootstrap\Container; use Ekdos\N8n\Cache; use Ekdos\N8n\Client; use Ekdos\N8n\Endpoints; use Ekdos\Support\Config; use Ekdos\Users\Role; use Ekdos\Users\UserRepository; require __DIR__ . '/../vendor/autoload.php'; $projectRoot = dirname(__DIR__); $command = $argv[1] ?? 'help'; function out(string $line = ''): void { fwrite(STDOUT, $line . PHP_EOL); } function fail(string $line): never { fwrite(STDERR, $line . PHP_EOL); exit(1); } /** Liest eine Eingabe ohne Bildschirmausgabe. */ function readSecret(string $prompt): string { fwrite(STDERR, $prompt); if (DIRECTORY_SEPARATOR === '/' && function_exists('shell_exec')) { shell_exec('stty -echo 2>/dev/null'); } $value = trim((string) fgets(STDIN)); if (DIRECTORY_SEPARATOR === '/' && function_exists('shell_exec')) { shell_exec('stty echo 2>/dev/null'); } fwrite(STDERR, PHP_EOL); return $value; } function ask(string $prompt, string $fallback = ''): string { fwrite(STDERR, $prompt); $value = trim((string) fgets(STDIN)); return $value !== '' ? $value : $fallback; } $container = Container::build($projectRoot); switch ($command) { case 'migrate': /** @var PDO $db */ $db = $container->get(PDO::class); $db->exec('create table if not exists schema_migrations (version text primary key, applied_at timestamptz not null default now())'); $applied = $db->query('select version from schema_migrations')->fetchAll(PDO::FETCH_COLUMN); $files = glob($projectRoot . '/migrations/*.sql') ?: []; sort($files); $ran = 0; foreach ($files as $file) { $version = basename($file, '.sql'); if (in_array($version, $applied, true)) { continue; } $sql = file_get_contents($file); if ($sql === false) { fail('Migration nicht lesbar: ' . $file); } $db->beginTransaction(); try { $db->exec($sql); $statement = $db->prepare('insert into schema_migrations (version) values (:version)'); $statement->execute(['version' => $version]); $db->commit(); } catch (Throwable $exception) { $db->rollBack(); fail('Migration ' . $version . ' fehlgeschlagen: ' . $exception->getMessage()); } out(' angewendet: ' . $version); $ran++; } out($ran === 0 ? 'Das Schema ist aktuell.' : $ran . ' Migration(en) angewendet.'); break; case 'user:create': /** @var UserRepository $users */ $users = $container->get(UserRepository::class); $username = ask('Benutzername: '); $displayName = ask('Anzeigename: ', $username); $roleInput = ask('Rolle [admin|inhaber|buero] (admin): ', 'admin'); $role = Role::tryFrom($roleInput) ?? fail('Unbekannte Rolle: ' . $roleInput); if ($username === '') { fail('Der Benutzername darf nicht leer sein.'); } if ($users->usernameTaken($username)) { fail('Dieser Benutzername ist bereits vergeben.'); } $password = readSecret('Passwort: '); if (($problem = AuthService::rejectWeakPassword($password)) !== null) { fail($problem); } if ($password !== readSecret('Passwort wiederholen: ')) { fail('Die Passwörter stimmen nicht überein.'); } $user = $users->create($username, $displayName, $role, AuthService::hash($password)); $users->audit(null, 'CLI', 'user.create', $user->id, $user->username, ['role' => $role->value]); out('Angelegt: ' . $user->username . ' (' . $user->role->label() . ')'); break; case 'user:list': /** @var UserRepository $users */ $users = $container->get(UserRepository::class); $rows = $users->all(); if ($rows === []) { out('Es ist noch kein Benutzer angelegt. Anlegen mit: php bin/ekdos user:create'); break; } out(sprintf('%-22s %-24s %-14s %-8s %s', 'BENUTZER', 'NAME', 'ROLLE', 'AKTIV', 'LETZTE ANMELDUNG')); foreach ($rows as $user) { out(sprintf( '%-22s %-24s %-14s %-8s %s', $user->username, $user->displayName, $user->role->value, $user->isActive ? 'ja' : 'nein', $user->lastLoginAt ?? '–', )); } break; case 'user:password': /** @var UserRepository $users */ $users = $container->get(UserRepository::class); $username = $argv[2] ?? fail('Aufruf: php bin/ekdos user:password '); $user = $users->findByUsernameWithHash($username) ?? fail('Unbekannter Benutzer: ' . $username); $password = readSecret('Neues Passwort für ' . $user->username . ': '); if (($problem = AuthService::rejectWeakPassword($password)) !== null) { fail($problem); } $users->update($user->id, ['password_hash' => AuthService::hash($password)]); $container->get(\Ekdos\Auth\SessionStore::class)->destroyAllFor($user->id); $users->audit(null, 'CLI', 'user.password', $user->id, $user->username); out('Passwort geändert. Offene Sitzungen dieses Benutzers wurden beendet.'); break; case 'cache:flush': /** @var Cache $cache */ $cache = $container->get(Cache::class); out($cache->flushAll() . ' zwischengespeicherte n8n-Antworten verworfen.'); break; case 'check': /** @var Config $config */ $config = $container->get(Config::class); $problems = 0; try { $container->get(PDO::class)->query('select 1'); out(' Postgres erreichbar'); } catch (Throwable $exception) { out(' Postgres FEHLER: ' . $exception->getMessage()); $problems++; } try { $container->get(Redis::class)->ping(); out(' Redis erreichbar'); } catch (Throwable $exception) { out(' Redis FEHLER: ' . $exception->getMessage()); $problems++; } /** @var Endpoints $endpoints */ $endpoints = $container->get(Endpoints::class); /** @var Client $n8n */ $n8n = $container->get(Client::class); $reply = $n8n->get($endpoints->openTickets()); if ($reply->status === 0) { out(' n8n FEHLER: nicht erreichbar unter ' . $endpoints->openTickets()); $problems++; } else { out(' n8n HTTP ' . $reply->status . ' von ' . $endpoints->openTickets()); } foreach (['N8N_CUSTOMER_KEY', 'N8N_INVOICE_SYNC_SECRET'] as $secret) { if (!$config->has($secret)) { out(' Hinweis ' . $secret . ' ist nicht gesetzt; betroffene Funktionen antworten mit 503.'); } } exit($problems === 0 ? 0 : 1); default: out('EK-DOS-WEB Wartungs-CLI'); out(); out(' php bin/ekdos migrate Schema anlegen oder fortschreiben'); out(' php bin/ekdos user:create Benutzer anlegen'); out(' php bin/ekdos user:list Benutzer auflisten'); out(' php bin/ekdos user:password Passwort zurücksetzen'); out(' php bin/ekdos cache:flush n8n-Cache leeren'); out(' php bin/ekdos check Postgres, Redis und n8n prüfen'); }