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(); } } ?> PnPaaS installieren

PnPaaS installieren

Installation abgeschlossen. Der Webinstaller ist deaktiviert. Bitte härten Sie jetzt die Dateirechte und konfigurieren Sie HTTPS.

Die Datenbank muss bereits existieren. Der Assistent benötigt nur den MariaDB-Anwendungsbenutzer, nicht den MariaDB-Root-Zugang.

MariaDB
Administrator
Anwendung und Portainer
Rechtstexte