Initial Kaffeekasse SaaS restart
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use PDO;
|
||||
use RuntimeException;
|
||||
|
||||
final class SetupService
|
||||
{
|
||||
public function __construct(private readonly string $rootPath)
|
||||
{
|
||||
}
|
||||
|
||||
public function isInstalled(): bool
|
||||
{
|
||||
return is_file($this->rootPath . '/storage/installed.lock');
|
||||
}
|
||||
|
||||
public function install(array $input): void
|
||||
{
|
||||
$required = [
|
||||
'app_url',
|
||||
'db_host',
|
||||
'db_port',
|
||||
'db_name',
|
||||
'db_user',
|
||||
'admin_name',
|
||||
'admin_email',
|
||||
'admin_password',
|
||||
];
|
||||
|
||||
foreach ($required as $field) {
|
||||
if (trim((string) ($input[$field] ?? '')) === '') {
|
||||
throw new RuntimeException('Bitte alle Pflichtfelder ausfuellen.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!filter_var($input['admin_email'], FILTER_VALIDATE_EMAIL)) {
|
||||
throw new RuntimeException('Die Admin-E-Mail ist ungueltig.');
|
||||
}
|
||||
|
||||
if (mb_strlen($input['admin_password']) < 12) {
|
||||
throw new RuntimeException('Das Admin-Passwort muss mindestens 12 Zeichen lang sein.');
|
||||
}
|
||||
|
||||
$config = [
|
||||
'host' => trim((string) $input['db_host']),
|
||||
'port' => (int) $input['db_port'],
|
||||
'name' => trim((string) $input['db_name']),
|
||||
'user' => trim((string) $input['db_user']),
|
||||
'pass' => (string) ($input['db_pass'] ?? ''),
|
||||
'charset' => 'utf8mb4',
|
||||
];
|
||||
|
||||
$dsn = sprintf(
|
||||
'mysql:host=%s;port=%d;dbname=%s;charset=%s',
|
||||
$config['host'],
|
||||
$config['port'],
|
||||
$config['name'],
|
||||
$config['charset']
|
||||
);
|
||||
|
||||
$pdo = new PDO($dsn, $config['user'], $config['pass'], [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
]);
|
||||
|
||||
$schema = file_get_contents($this->rootPath . '/database/schema.sql');
|
||||
|
||||
if (!is_string($schema) || $schema === '') {
|
||||
throw new RuntimeException('Das Datenbankschema konnte nicht geladen werden.');
|
||||
}
|
||||
|
||||
$pdo->exec($schema);
|
||||
|
||||
$statement = $pdo->prepare('SELECT id FROM users WHERE email = :email LIMIT 1');
|
||||
$statement->execute(['email' => mb_strtolower((string) $input['admin_email'])]);
|
||||
$existing = $statement->fetchColumn();
|
||||
|
||||
if (!$existing) {
|
||||
$insert = $pdo->prepare(
|
||||
'INSERT INTO users (full_name, email, password_hash, platform_role)
|
||||
VALUES (:full_name, :email, :password_hash, "platform_admin")'
|
||||
);
|
||||
$insert->execute([
|
||||
'full_name' => trim((string) $input['admin_name']),
|
||||
'email' => mb_strtolower((string) $input['admin_email']),
|
||||
'password_hash' => secure_password_hash((string) $input['admin_password']),
|
||||
]);
|
||||
}
|
||||
|
||||
$envContent = $this->buildEnvFile($input);
|
||||
|
||||
if (file_put_contents($this->rootPath . '/.env', $envContent) === false) {
|
||||
throw new RuntimeException('Die .env-Datei konnte nicht geschrieben werden.');
|
||||
}
|
||||
|
||||
$lockContent = json_encode([
|
||||
'installed_at' => gmdate(DATE_ATOM),
|
||||
'app_url' => trim((string) $input['app_url']),
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
file_put_contents($this->rootPath . '/storage/installed.lock', (string) $lockContent);
|
||||
}
|
||||
|
||||
private function buildEnvFile(array $input): string
|
||||
{
|
||||
$appKey = bin2hex(random_bytes(32));
|
||||
$rfidSecret = bin2hex(random_bytes(24));
|
||||
|
||||
$lines = [
|
||||
'APP_NAME="Kaffeekasse SaaS"',
|
||||
'APP_ENV=production',
|
||||
'APP_DEBUG=0',
|
||||
'APP_URL=' . trim((string) $input['app_url']),
|
||||
'APP_TIMEZONE=Europe/Berlin',
|
||||
'APP_KEY=' . $appKey,
|
||||
'',
|
||||
'DB_HOST=' . trim((string) $input['db_host']),
|
||||
'DB_PORT=' . (int) $input['db_port'],
|
||||
'DB_NAME=' . trim((string) $input['db_name']),
|
||||
'DB_USER=' . trim((string) $input['db_user']),
|
||||
'DB_PASS=' . (string) ($input['db_pass'] ?? ''),
|
||||
'',
|
||||
'MAIL_FROM=' . trim((string) ($input['mail_from'] ?? 'noreply@example.com')),
|
||||
'RFID_SHARED_SECRET=' . $rfidSecret,
|
||||
];
|
||||
|
||||
return implode(PHP_EOL, $lines) . PHP_EOL;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user