HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/hi.naniguide.com/app/Http/Controllers/InstallController.php
<?php

namespace App\Http\Controllers;

use App\Models\User;
use App\Models\UserGroup;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;

class InstallController extends Controller
{
    public function welcome()
    {
        return view('install.welcome');
    }

    public function requirements()
    {
        $phpVersion = PHP_VERSION;
        $phpOk = version_compare($phpVersion, '8.1.0', '>=');

        $extensions = [
            'ctype',
            'curl',
            'dom',
            'fileinfo',
            'filter',
            'hash',
            'mbstring',
            'openssl',
            'pcre',
            'pdo',
            'session',
            'tokenizer',
            'xml',
            'zip',
        ];

        $extResults = [];
        foreach ($extensions as $ext) {
            $extResults[$ext] = extension_loaded($ext);
        }

        $paths = [
            base_path(),
            base_path('bootstrap/cache'),
            storage_path(),
        ];

        $writable = [];
        foreach ($paths as $p) {
            $writable[$p] = is_dir($p) ? is_writable($p) : is_writable(dirname($p));
        }

        $allOk = $phpOk
            && !in_array(false, $extResults, true)
            && !in_array(false, $writable, true);

        return view('install.requirements', compact('phpVersion', 'phpOk', 'extResults', 'writable', 'allOk'));
    }

    public function setup(Request $request)
    {
        $host = (string) $request->getHost();
        $host = preg_replace('/^www\./i', '', $host) ?? $host;
        $domainLabel = explode('.', $host)[0] ?? '';

        $guessedAppName = $domainLabel !== ''
            ? Str::studly(str_replace(['-', '_'], ' ', $domainLabel))
            : 'MailPurse';

        $guessedAppUrl = rtrim($request->getSchemeAndHttpHost() . $request->getBaseUrl(), '/');

        return view('install.setup', [
            'guessedAppName' => $guessedAppName,
            'guessedAppUrl' => $guessedAppUrl,
        ]);
    }

    public function storeSetup(Request $request)
    {
        $validated = $request->validate([
            'app_name' => ['required', 'string', 'max:100'],
            'app_url' => ['required', 'url'],
            'db_connection' => ['required', 'in:mysql,pgsql'],
            'db_host' => ['required', 'string', 'max:255'],
            'db_port' => ['required', 'integer', 'min:1', 'max:65535'],
            'db_database' => ['required', 'string', 'max:255'],
            'db_username' => ['required', 'string', 'max:255'],
            'db_password' => ['nullable', 'string'],
            'admin_email' => ['required', 'email', 'max:255'],
            'admin_password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);

        $appUrl = rtrim($validated['app_url'], '/');

        $testError = $this->testDatabaseConnection(
            $validated['db_connection'],
            $validated['db_host'],
            (int) $validated['db_port'],
            $validated['db_database'],
            $validated['db_username'],
            (string) ($validated['db_password'] ?? '')
        );

        if ($testError !== null) {
            return back()->withInput()->withErrors(['db_database' => $testError]);
        }

        $envUpdates = [
            'APP_NAME' => $validated['app_name'],
            'APP_ENV' => 'production',
            'APP_DEBUG' => 'false',
            'APP_URL' => $appUrl,
            'DB_CONNECTION' => $validated['db_connection'],
            'DB_HOST' => $validated['db_host'],
            'DB_PORT' => (string) $validated['db_port'],
            'DB_DATABASE' => $validated['db_database'],
            'DB_USERNAME' => $validated['db_username'],
            'DB_PASSWORD' => (string) ($validated['db_password'] ?? ''),
            'CACHE_STORE' => 'file',
            'SESSION_DRIVER' => 'file',
            'QUEUE_CONNECTION' => 'database',
        ];

        $this->ensureEnvExists();
        $this->ensureAppKey();
        $this->updateEnvFile($envUpdates);

        config([
            'app.name' => $validated['app_name'],
            'app.url' => $appUrl,
            'database.default' => $validated['db_connection'],
        ]);

        config([
            'cache.default' => 'file',
        ]);

        config([
            'database.connections.' . $validated['db_connection'] . '.host' => $validated['db_host'],
            'database.connections.' . $validated['db_connection'] . '.port' => (int) $validated['db_port'],
            'database.connections.' . $validated['db_connection'] . '.database' => $validated['db_database'],
            'database.connections.' . $validated['db_connection'] . '.username' => $validated['db_username'],
            'database.connections.' . $validated['db_connection'] . '.password' => (string) ($validated['db_password'] ?? ''),
        ]);

        try {
            DB::connection()->getPdo();
        } catch (\Throwable $e) {
            return back()->withInput()->withErrors(['db_database' => 'Could not connect to the database with the provided settings.']);
        }

        try {
            Artisan::call('migrate', ['--force' => true]);
        } catch (\Throwable $e) {
            if (str_contains($e->getMessage(), 'Unsupported operand types: string + int')) {
                try {
                    $this->repairMigrationsTableIfNeeded();
                    Artisan::call('migrate', ['--force' => true]);
                } catch (\Throwable $e2) {
                    $errorDetails = $e2->getMessage() . "\n\n" . $e2->getTraceAsString();
                    @file_put_contents(storage_path('migration_error.txt'), $errorDetails);
                    return back()->withInput()->withErrors(['db_database' => 'Migrations failed: ' . $e2->getMessage()]);
                }
            } else {
                $errorDetails = $e->getMessage() . "\n\n" . $e->getTraceAsString();
                @file_put_contents(storage_path('migration_error.txt'), $errorDetails);
                return back()->withInput()->withErrors(['db_database' => 'Migrations failed: ' . $e->getMessage()]);
            }
        }

        $superadminGroup = UserGroup::firstOrCreate(
            ['name' => 'Superadmin'],
            [
                'description' => 'Full access to all admin actions.',
                'permissions' => ['*'],
                'is_system' => true,
            ]
        );

        UserGroup::firstOrCreate(
            ['name' => 'Admin'],
            [
                'description' => 'Default admin access (configurable via Accessibility Control).',
                'permissions' => ['admin.*'],
                'is_system' => true,
            ]
        );

        $admin = User::firstOrCreate(
            ['email' => $validated['admin_email']],
            [
                'first_name' => 'Super',
                'last_name' => 'Admin',
                'password' => Hash::make($validated['admin_password']),
                'status' => 'active',
                'email_verified_at' => now(),
            ]
        );

        $admin->userGroups()->syncWithoutDetaching([$superadminGroup->id]);

        try {
            Artisan::call('storage:link');
        } catch (\Throwable $e) {
        }

        try {
            Artisan::call('optimize:clear');
        } catch (\Throwable $e) {
        }

        $this->markInstalled(['admin_email' => $validated['admin_email']]);

        return redirect()->route('install.done');
    }

    public function done()
    {
        return view('install.done');
    }

    private function testDatabaseConnection(string $driver, string $host, int $port, string $database, string $username, string $password): ?string
    {
        try {
            if ($driver === 'mysql') {
                $dsn = "mysql:host={$host};port={$port};dbname={$database};charset=utf8mb4";
            } elseif ($driver === 'pgsql') {
                $dsn = "pgsql:host={$host};port={$port};dbname={$database}";
            } else {
                return 'Unsupported database connection.';
            }

            $pdo = new \PDO($dsn, $username, $password, [
                \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
                \PDO::ATTR_TIMEOUT => 5,
            ]);
            $pdo->query('SELECT 1');
            return null;
        } catch (\Throwable $e) {
            return 'Database connection failed. Please check host, port, database name, username and password.';
        }
    }

    private function ensureEnvExists(): void
    {
        $envPath = base_path('.env');
        if (is_file($envPath)) {
            return;
        }

        $examplePath = base_path('.env.example');
        if (!is_file($examplePath)) {
            return;
        }

        @copy($examplePath, $envPath);
    }

    private function ensureAppKey(): void
    {
        $envPath = base_path('.env');
        if (!is_file($envPath)) {
            return;
        }

        $contents = (string) @file_get_contents($envPath);
        if ($contents === '') {
            return;
        }

        if (preg_match('/^APP_KEY=(.+)$/m', $contents, $m)) {
            $current = trim((string) ($m[1] ?? ''));
            if ($current !== '') {
                return;
            }
        }

        $key = 'base64:' . base64_encode(random_bytes(32));
        if (preg_match('/^APP_KEY=.*$/m', $contents)) {
            $contents = preg_replace('/^APP_KEY=.*$/m', 'APP_KEY=' . $key, $contents);
        } else {
            $contents = rtrim($contents, "\r\n") . "\nAPP_KEY={$key}\n";
        }

        @file_put_contents($envPath, $contents);
    }

    private function updateEnvFile(array $updates): void
    {
        $envPath = base_path('.env');
        $contents = (string) @file_get_contents($envPath);
        $hasExistingContents = $contents !== '';

        foreach ($updates as $key => $value) {
            $line = $key . '=' . $this->formatEnvValue($value);
            if ($hasExistingContents && preg_match('/^' . preg_quote($key, '/') . '=.*/m', $contents)) {
                $contents = preg_replace('/^' . preg_quote($key, '/') . '=.*/m', $line, $contents);
                continue;
            }

            $contents = rtrim($contents, "\r\n") . "\n{$line}\n";
        }

        @file_put_contents($envPath, $contents);
    }

    private function formatEnvValue(string $value): string
    {
        if ($value === '') {
            return '';
        }

        if (preg_match('/\s|#|=|"|\\\\/', $value)) {
            $escaped = str_replace('\\', '\\\\', $value);
            $escaped = str_replace('"', '\\"', $escaped);
            return '"' . $escaped . '"';
        }

        return $value;
    }

    private function markInstalled(array $meta = []): void
    {
        $path = storage_path('app/private/installed.json');
        $dir = dirname($path);
        if (!is_dir($dir)) {
            @mkdir($dir, 0755, true);
        }

        $payload = array_merge([
            'installed_at' => now()->toIso8601String(),
        ], $meta);

        @file_put_contents($path, json_encode($payload));
    }

    private function repairMigrationsTableIfNeeded(): void
    {
        if (!Schema::hasTable('migrations')) {
            return;
        }

        $needsRepair = false;

        try {
            if (!Schema::hasColumn('migrations', 'batch')) {
                $needsRepair = true;
            }
        } catch (\Throwable $e) {
            $needsRepair = true;
        }

        if (!$needsRepair) {
            try {
                $maxBatch = DB::table('migrations')->max('batch');
                if ($maxBatch !== null && !is_numeric($maxBatch)) {
                    $needsRepair = true;
                }
            } catch (\Throwable $e) {
                $needsRepair = true;
            }
        }

        if (!$needsRepair) {
            return;
        }

        $backupName = 'migrations_backup_' . date('Ymd_His');

        try {
            Schema::rename('migrations', $backupName);
        } catch (\Throwable $e) {
            try {
                Schema::drop('migrations');
            } catch (\Throwable $e2) {
            }
        }
    }
}