PnPaaS installieren
Die Datenbank muss bereits existieren. Der Assistent benötigt nur den MariaDB-Anwendungsbenutzer, nicht den MariaDB-Root-Zugang.
true, 'cookie_httponly' => true, 'cookie_samesite' => 'Strict', 'cookie_secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off', ]); header('X-Content-Type-Options: nosniff'); header('X-Frame-Options: DENY'); header('Referrer-Policy: no-referrer'); header("Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'none'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'"); $completeFile = __DIR__ . '/.install-complete'; if (is_file($completeFile)) { http_response_code(404); exit('Not found'); } if (empty($_SESSION['csrf'])) $_SESSION['csrf'] = bin2hex(random_bytes(32)); $error = null; $success = false; $values = [ 'db_host' => 'localhost', 'db_name' => 'pnpaas', 'db_user' => 'pnpaas', 'admin_user' => 'admin', 'admin_email' => '', 'app_url' => '', 'portainer_url' => '', 'terms_url' => '', 'terms_version' => '', 'privacy_url' => '', 'privacy_version' => '', 'withdrawal_url' => '', 'withdrawal_version' => '', 'portainer_endpoint_id' => '3', ]; function setup_h(string $value): string { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); } function setup_https_url(string $value, string $label, bool $required = false): string { $value = trim($value); if (!$required && $value === '') return ''; if (filter_var($value, FILTER_VALIDATE_URL) === false || !str_starts_with(strtolower($value), 'https://')) { throw new RuntimeException("$label muss eine gültige HTTPS-URL sein."); } return $value; } if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') { foreach ($values as $key => $_) $values[$key] = trim((string)($_POST[$key] ?? '')); try { if (!hash_equals((string)$_SESSION['csrf'], (string)($_POST['csrf'] ?? ''))) throw new RuntimeException('Die Sitzung ist abgelaufen.'); if (!preg_match('/^[A-Za-z0-9_.:-]{1,253}$/', $values['db_host'])) throw new RuntimeException('Der Datenbank-Host ist ungültig.'); if (!preg_match('/^[A-Za-z0-9_]{1,64}$/', $values['db_name']) || !preg_match('/^[A-Za-z0-9_]{1,32}$/', $values['db_user'])) throw new RuntimeException('Datenbankname oder Benutzername ist ungültig.'); if ((string)($_POST['db_password'] ?? '') === '') throw new RuntimeException('Das Datenbankpasswort ist erforderlich.'); if (!preg_match('/^[A-Za-z0-9_.-]{3,80}$/', $values['admin_user'])) throw new RuntimeException('Der Admin-Benutzername ist ungültig.'); if (!filter_var($values['admin_email'], FILTER_VALIDATE_EMAIL)) throw new RuntimeException('Die Admin-E-Mail-Adresse ist ungültig.'); if (strlen((string)($_POST['admin_password'] ?? '')) < 12) throw new RuntimeException('Das Adminpasswort muss mindestens 12 Zeichen enthalten.'); $appUrl = setup_https_url($values['app_url'], 'Die App-URL', true); $portainerUrl = setup_https_url($values['portainer_url'], 'Die Portainer-URL'); $termsUrl = setup_https_url($values['terms_url'], 'Die AGB-URL', true); $privacyUrl = setup_https_url($values['privacy_url'], 'Die Datenschutz-URL', true); $withdrawalUrl = setup_https_url($values['withdrawal_url'], 'Die Widerrufs-URL', true); if (!ctype_digit($values['portainer_endpoint_id']) || (int)$values['portainer_endpoint_id'] < 1) throw new RuntimeException('Die Portainer-Umgebungs-ID ist ungültig.'); $dsn = 'mysql:host=' . $values['db_host'] . ';dbname=' . $values['db_name'] . ';charset=utf8mb4'; $db = new PDO($dsn, $values['db_user'], (string)($_POST['db_password'] ?? ''), [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => false, ]); $schema = file_get_contents($root . '/database/schema.sql'); if ($schema === false || $schema === '') throw new RuntimeException('Das Datenbankschema fehlt.'); $db->exec($schema); foreach (glob($root . '/database/migrations/*.sql') ?: [] as $migration) { $sql = file_get_contents($migration); if ($sql === false) throw new RuntimeException('Eine Datenbankmigration konnte nicht gelesen werden.'); $db->exec($sql); } $adminHash = password_hash((string)$_POST['admin_password'], PASSWORD_DEFAULT); $admin = $db->prepare("INSERT INTO users (username, email, first_name, last_name, street_address, postal_code, city, country_code, password_hash, role, status) VALUES (:username, :email, '', '', '', '', '', 'AT', :password_hash, 'admin', 'active') ON DUPLICATE KEY UPDATE email = VALUES(email), password_hash = VALUES(password_hash), role = 'admin', status = 'active'"); $admin->execute(['username' => $values['admin_user'], 'email' => $values['admin_email'], 'password_hash' => $adminHash]); $runtimeConfig = [ 'PNPAAS_ADMIN_USER' => $values['admin_user'], 'PNPAAS_ADMIN_PASSWORD_HASH' => $adminHash, 'PNPAAS_DB_DSN' => $dsn, 'PNPAAS_DB_USER' => $values['db_user'], 'PNPAAS_DB_PASSWORD' => (string)$_POST['db_password'], 'PNPAAS_APP_URL' => $appUrl, 'PNPAAS_FORCE_SECURE_COOKIES' => '1', 'PORTAINER_URL' => $portainerUrl, 'PORTAINER_API_KEY' => (string)($_POST['portainer_api_key'] ?? ''), 'PNPAAS_PORTAINER_ENDPOINT_ID' => $values['portainer_endpoint_id'], 'PNPAAS_TERMS_URL' => $termsUrl, 'PNPAAS_TERMS_VERSION' => $values['terms_version'], 'PNPAAS_PRIVACY_URL' => $privacyUrl, 'PNPAAS_PRIVACY_VERSION' => $values['privacy_version'], 'PNPAAS_WITHDRAWAL_URL' => $withdrawalUrl, 'PNPAAS_WITHDRAWAL_VERSION' => $values['withdrawal_version'], ]; $configPath = $root . '/includes/runtime-config.php'; if (!is_writable(dirname($configPath))) throw new RuntimeException('Das Include-Verzeichnis ist für den Webserver nicht beschreibbar.'); $configCode = "getMessage()); $error = 'Die Datenbankverbindung oder der Datenbankimport ist fehlgeschlagen.'; } catch (Throwable $exception) { error_log('PnPaaS web installer error: ' . $exception->getMessage()); $error = $exception->getMessage(); } } ?>
Die Datenbank muss bereits existieren. Der Assistent benötigt nur den MariaDB-Anwendungsbenutzer, nicht den MariaDB-Root-Zugang.