diff --git a/MEMORY.md b/MEMORY.md index f31c4443..7d3f461d 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -24,8 +24,8 @@ - Fallback SQLite: mantiene `php artisan migrate --seed` + tinker ma con `var_export()` per escaping - Step 5 rinominato da "MIGRATION E SEED" a "SCHEMA DATABASE E DATI BASE" - Step 6 usa PDO invece di tinker (MySQL) -- **Auto-creazione utente MySQL**: aggiunta funzione `ensureMysqlUserAndDb()` che chiede password root, crea utente + database + GRANT ALL PRIVILEGES su `localhost` e `%` -- **Sostituito loop 30-retry** con connessione singola PDO (timeout 5s): se credenziali errate, chiede se creare utente automaticamente +- **Unified MySQL setup**: singolo tentativo PDO con `dbname` nel DSN — se fallisce (1044/1045/1049), passa automaticamente in root-mode: chiede password root, crea database + utente + GRANT ALL su `localhost` e `%` +- **Sostituito loop 30-retry** con connessione singola PDO (timeout 5s) - **Fix parse error**: `\$adminName` → `$adminName` in `var_export()` nel ramo SQLite - **Fix composer**: rimosso `sudo -u www-data` da composer (causa git dubious ownership + permission denied), aggiunto `COMPOSER_ROOT_VERSION=dev-main`; `vendor` aggiunto al chown finale @@ -42,7 +42,7 @@ - **`2>/dev/null` rimosso** dal MySQL main path (non serve più, non c'è tinker) - **Docker MySQL credenziali**: hardcoded `mysql:glastree:secret:glastree` (dal docker-compose.mysql.yml) - **Composer senza sudo**: `COMPOSER_ROOT_VERSION=dev-main` per evitare git dubious ownership; composer eseguito come utente corrente (root), non più come www-data — dopo, `chown -R www-data vendor` per permessi lettura webserver -- **Auto-create MySQL user**: `ensureMysqlUserAndDb()` prova prima root senza password (auth_socket), poi chiede password; crea utente su `localhost` e `%`, GRANT ALL sul database, CREATE DATABASE IF NOT EXISTS +- **Unified MySQL setup**: PDO connect con `dbname` nel DSN → se fallisce 1044/1045/1049, root-mode: crea DB + utente + GRANT ALL su `localhost` e `%` + FLUSH ## Next Steps - *(nessuno)* diff --git a/install.php b/install.php index beda2bad..a1e99242 100644 --- a/install.php +++ b/install.php @@ -95,56 +95,6 @@ function runCapture(string $cmd): string { return trim(shell_exec($cmd) ?: ''); } -/** - * Crea un utente MySQL con permessi completi sul database e lo schema. - * Chiede la password root se la connessione senza password fallisce. - */ -function ensureMysqlUserAndDb( - string $dbHost, string $dbPort, string $dbName, - string $dbUser, string $dbPass -): bool { - // Prova senza password (auth_socket / Unix socket) - $rootPdo = null; - try { - $rootPdo = new PDO( - "mysql:host={$dbHost};port={$dbPort}", - 'root', '', - [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 5] - ); - } catch (\PDOException) { - $rootPass = askRequired('Password utente root MySQL (lascia vuoto se senza password)'); - try { - $rootPdo = new PDO( - "mysql:host={$dbHost};port={$dbPort}", - 'root', $rootPass, - [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 5] - ); - } catch (\PDOException $e) { - error('Connessione come root fallita: ' . $e->getMessage()); - return false; - } - } - - $safeUser = str_replace(["'", '"', '`', "\0", '\\'], '', $dbUser); - $safePass = str_replace(["'", '"', '`', "\0", '\\'], '', $dbPass); - $safeName = str_replace(["'", '"', '`', "\0", '\\'], '', $dbName); - - try { - $rootPdo->exec("CREATE DATABASE IF NOT EXISTS `{$safeName}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"); - - foreach (['localhost', '%'] as $host) { - $rootPdo->exec("CREATE USER IF NOT EXISTS '{$safeUser}'@'{$host}' IDENTIFIED BY '{$safePass}'"); - $rootPdo->exec("GRANT ALL PRIVILEGES ON `{$safeName}`.* TO '{$safeUser}'@'{$host}'"); - } - $rootPdo->exec("FLUSH PRIVILEGES"); - - return true; - } catch (\PDOException $e) { - error("Errore creazione utente MySQL: " . $e->getMessage()); - return false; - } -} - /** * Crea l'utente amministratore via PDO prepared statement. * Nessun escaping PHP necessario (tutto via prepared statement). @@ -355,45 +305,58 @@ if ($mode === 'Fresh Install su Apache (LAMP tradizionale)') { println(color('══════════════════════════════════════════', 'yellow')); if ($dbType === 'mysql') { - info("Connessione a MySQL {$dbHost}:{$dbPort}..."); + info("Connessione a MySQL {$dbHost}:{$dbPort}, database '{$dbName}'..."); - $connected = false; try { new PDO( - "mysql:host={$dbHost};port={$dbPort}", + "mysql:host={$dbHost};port={$dbPort};dbname={$dbName}", $dbUser, $dbPass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 5] ); - $connected = true; - } catch (\PDOException) { - // Credenziali non valide o utente inesistente - } - - if ($connected) { - ok('MySQL raggiungibile'); - } else { - warn("Accesso negato per '{$dbUser}' — utente inesistente o password errata"); - if (confirm("Creare l'utente MySQL '{$dbUser}' con permessi completi su '{$dbName}'?")) { - if (!ensureMysqlUserAndDb($dbHost, $dbPort, $dbName, $dbUser, $dbPass)) { - fail("Impossibile creare l'utente MySQL. Verifica la password root."); - } - ok("Utente '{$dbUser}' creato con permessi completi su '{$dbName}'"); - } else { - fail("Riavvia lo script con credenziali valide."); - } - } - - info("Verifica database '{$dbName}'..."); - try { - $pdo = new PDO( - "mysql:host={$dbHost};port={$dbPort}", - $dbUser, $dbPass, - [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION] - ); - $pdo->exec("CREATE DATABASE IF NOT EXISTS `{$dbName}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"); - ok("Database '{$dbName}' pronto"); + ok("Accesso a '{$dbName}' come '{$dbUser}' riuscito"); } catch (\PDOException $e) { - warn("Crea manualmente: mysql -u root -p -e \"CREATE DATABASE `{$dbName}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\""); + // Diagnostica l'errore + $errMsg = $e->getMessage(); + $diagnosi = match (true) { + (bool)preg_match('/1049|Unknown database/', $errMsg) => "Database '{$dbName}' non esiste", + (bool)preg_match('/1045|Access denied for user/', $errMsg) => "Utente '{$dbUser}' o password non validi", + (bool)preg_match('/1044/', $errMsg) => "Utente '{$dbUser}' non ha accesso al database '{$dbName}'", + default => $errMsg, + }; + warn($diagnosi); + + info("Connessione come root per creare/riparare utente e database..."); + $rootPdo = null; + try { + $rootPdo = new PDO( + "mysql:host={$dbHost};port={$dbPort}", + 'root', '', + [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 5] + ); + } catch (\PDOException) { + $rootPass = askRequired('Password utente root MySQL (lascia vuoto se senza password)'); + try { + $rootPdo = new PDO( + "mysql:host={$dbHost};port={$dbPort}", + 'root', $rootPass, + [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 5] + ); + } catch (\PDOException $r) { + fail("Connessione come root fallita: " . $r->getMessage()); + } + } + + $safeUser = str_replace(["'", '"', '`', "\0", '\\'], '', $dbUser); + $safePass = str_replace(["'", '"', '`', "\0", '\\'], '', $dbPass); + $safeName = str_replace(["'", '"', '`', "\0", '\\'], '', $dbName); + + $rootPdo->exec("CREATE DATABASE IF NOT EXISTS `{$safeName}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"); + foreach (['localhost', '%'] as $host) { + $rootPdo->exec("CREATE USER IF NOT EXISTS '{$safeUser}'@'{$host}' IDENTIFIED BY '{$safePass}'"); + $rootPdo->exec("GRANT ALL PRIVILEGES ON `{$safeName}`.* TO '{$safeUser}'@'{$host}'"); + } + $rootPdo->exec("FLUSH PRIVILEGES"); + ok("Database '{$dbName}' e utente '{$dbUser}' configurati con permessi completi"); } } else { $sqlitePath = $scriptDir . '/database/database.sqlite';