641 lines
28 KiB
PHP
641 lines
28 KiB
PHP
#!/usr/bin/env php
|
|
<?php
|
|
|
|
/**
|
|
* Glastree — Installer interattivo per amministratori (PHP CLI)
|
|
*
|
|
* Uso:
|
|
* php install.php
|
|
*
|
|
* Richiede: PHP 8.3+, Composer, MySQL o SQLite
|
|
* Opzionale: npm (per asset frontend)
|
|
*
|
|
* Modalità:
|
|
* 1) Fresh Install su Apache (LAMP)
|
|
* 2) Fresh Install via Docker
|
|
* 3) Restore da Backup (Apache)
|
|
*/
|
|
|
|
// ── Safety ──────────────────────────────────────────
|
|
declare(strict_types=1);
|
|
|
|
// ── Constants ────────────────────────────────────────
|
|
const REQUIREMENTS = ['pdo_mysql', 'mbstring', 'xml', 'curl', 'zip', 'gd', 'fileinfo'];
|
|
const MIN_PHP = '8.3.0';
|
|
|
|
// ── Utilities ────────────────────────────────────────
|
|
function println(string $msg = ''): void { echo $msg . PHP_EOL; }
|
|
|
|
function color(string $text, string $code): string {
|
|
$map = [
|
|
'red' => '0;31',
|
|
'green' => '0;32',
|
|
'yellow' => '1;33',
|
|
'cyan' => '0;36',
|
|
'white' => '1;37',
|
|
];
|
|
$c = $map[$code] ?? '0';
|
|
return "\033[{$c}m{$text}\033[0m";
|
|
}
|
|
|
|
function info(string $msg): void { println(color('[INFO]', 'cyan') . ' ' . $msg); }
|
|
function ok(string $msg): void { println(color('[OK]', 'green') . ' ' . $msg); }
|
|
function warn(string $msg): void { println(color('[WARN]', 'yellow') . ' ' . $msg); }
|
|
function error(string $msg): void { println(color('[ERR]', 'red') . ' ' . $msg); }
|
|
|
|
function fail(string $msg): never { error($msg); exit(1); }
|
|
|
|
function ask(string $prompt, ?string $default = null): string {
|
|
$defaultStr = $default !== null ? " [{$default}]" : '';
|
|
echo color('?', 'cyan') . " {$prompt}{$defaultStr}: ";
|
|
$val = trim(fgets(STDIN) ?: '');
|
|
return $val !== '' ? $val : ($default ?? '');
|
|
}
|
|
|
|
function askRequired(string $prompt): string {
|
|
while (true) {
|
|
$val = ask($prompt);
|
|
if ($val !== '') break;
|
|
warn('Valore obbligatorio.');
|
|
}
|
|
return $val;
|
|
}
|
|
|
|
function confirm(string $prompt): bool {
|
|
$resp = strtolower(ask($prompt . ' [S/n]', 'S'));
|
|
return $resp === 's' || $resp === '';
|
|
}
|
|
|
|
function menu(string $prompt, array $options): string {
|
|
foreach ($options as $i => $opt) {
|
|
println(' ' . ($i + 1) . ') ' . $opt);
|
|
}
|
|
while (true) {
|
|
$val = ask($prompt, '1');
|
|
if (isset($options[(int)$val - 1])) return $options[(int)$val - 1];
|
|
warn('Scelta non valida.');
|
|
}
|
|
}
|
|
|
|
function checkCmd(string $cmd): bool {
|
|
$found = !(trim(shell_exec("command -v $cmd 2>/dev/null") ?: '') === '');
|
|
if (!$found) warn("Comando '$cmd' non trovato.");
|
|
return $found;
|
|
}
|
|
|
|
function run(string $cmd, ?string $label = null): bool {
|
|
if ($label) info($label);
|
|
println(" \$ {$cmd}");
|
|
passthru($cmd, $exitCode);
|
|
if ($exitCode !== 0) warn("Comando terminato con codice {$exitCode}");
|
|
return $exitCode === 0;
|
|
}
|
|
|
|
function runCapture(string $cmd): string {
|
|
return trim(shell_exec($cmd) ?: '');
|
|
}
|
|
|
|
// ── Banner ───────────────────────────────────────────
|
|
println(color(' ____ _ _ _', 'white'));
|
|
println(color(' / ___| | __ _ ___| |_ ___ _ __ ___ | |_ ___', 'white'));
|
|
println(color('| | _| |/ _` / __| __/ _ \'__/ _ \ | __/ _ \\', 'white'));
|
|
println(color('| |_| | | (_| \__ \ || __/ | | __/ | || __/', 'white'));
|
|
println(color(' \____|_|\__,_|___/\__\___|_| \___| \__\___|', 'white'));
|
|
println(color(' Installer interattivo per sysadmin', 'cyan'));
|
|
println();
|
|
|
|
// ── Preflight ────────────────────────────────────────
|
|
$scriptDir = dirname(__FILE__);
|
|
chdir($scriptDir);
|
|
|
|
if (!file_exists('.env.example')) {
|
|
fail("File .env.example non trovato in {$scriptDir}. Sei nella cartella dell'applicazione?");
|
|
}
|
|
|
|
// ── Select mode ──────────────────────────────────────
|
|
println('Seleziona la modalità di installazione:');
|
|
$mode = menu('Scegli', [
|
|
'Fresh Install su Apache (LAMP tradizionale)',
|
|
'Fresh Install via Docker',
|
|
'Restore da Backup (Apache)',
|
|
]);
|
|
|
|
// ── Common params ────────────────────────────────────
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' PARAMETRI GENERALI', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
$appName = askRequired('Nome del sito/app (es. Glastree MyChurch)');
|
|
$adminName = askRequired('Nome del primo utente amministratore');
|
|
$adminEmail = askRequired('Email dell\'amministratore');
|
|
$adminPass = askRequired('Password per l\'amministratore');
|
|
$appUrl = askRequired('URL pubblico del sito (es. https://glastree.esempio.it)');
|
|
|
|
// ── DB params (Apache / Restore) ─────────────────────
|
|
$dbType = 'mysql';
|
|
$dbHost = '127.0.0.1';
|
|
$dbPort = '3306';
|
|
$dbName = 'glastree';
|
|
$dbUser = 'glastree';
|
|
$dbPass = '';
|
|
|
|
if (in_array($mode, ['Fresh Install su Apache (LAMP tradizionale)', 'Restore da Backup (Apache)'])) {
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' DATABASE', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
$dbType = menu('Tipo database', ['mysql', 'sqlite']);
|
|
|
|
if ($dbType === 'mysql') {
|
|
$dbHost = askRequired('Host database');
|
|
$dbPort = ask('Porta database', '3306');
|
|
$dbName = ask('Nome database (verrà creato se non esiste)', 'glastree');
|
|
$dbUser = askRequired('Utente database');
|
|
$dbPass = askRequired('Password database');
|
|
}
|
|
}
|
|
|
|
// ══════════════════════════════════════════════════════
|
|
// MODE 1: FRESH INSTALL APACHE
|
|
// ══════════════════════════════════════════════════════
|
|
if ($mode === 'Fresh Install su Apache (LAMP tradizionale)') {
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' PRE-FLIGHT CHECKS', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
// PHP version
|
|
$phpVer = PHP_VERSION;
|
|
info("PHP versione: {$phpVer}");
|
|
if (version_compare($phpVer, MIN_PHP, '<')) {
|
|
fail("PHP >= " . MIN_PHP . " richiesto (trovato {$phpVer})");
|
|
}
|
|
ok("PHP {$phpVer}");
|
|
|
|
// Extensions
|
|
foreach (REQUIREMENTS as $ext) {
|
|
if (!extension_loaded($ext)) {
|
|
warn("Estensione PHP '{$ext}' non trovata (alcune funzionalità potrebbero non funzionare)");
|
|
} else {
|
|
ok("Estensione PHP '{$ext}'");
|
|
}
|
|
}
|
|
|
|
// Composer
|
|
checkCmd('composer');
|
|
checkCmd('node');
|
|
checkCmd('npm');
|
|
|
|
// ── Step 1: Environment ─────────────────────────
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' 1/7 — CONFIGURAZIONE AMBIENTE', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
if (!file_exists('.env')) {
|
|
copy('.env.example', '.env');
|
|
ok('.env creato da .env.example');
|
|
} else {
|
|
info('.env già esistente, lo aggiorno');
|
|
}
|
|
|
|
// APP_KEY
|
|
runCapture("php artisan key:generate --force");
|
|
ok('APP_KEY generata');
|
|
|
|
// Config
|
|
$dotenv = file_get_contents('.env');
|
|
$replacements = [
|
|
'/^APP_NAME=.*/m' => 'APP_NAME="' . addslashes($appName) . '"',
|
|
'/^APP_URL=.*/m' => "APP_URL={$appUrl}",
|
|
'/^APP_ENV=.*/m' => 'APP_ENV=production',
|
|
'/^APP_DEBUG=.*/m' => 'APP_DEBUG=false',
|
|
];
|
|
|
|
if ($dbType === 'mysql') {
|
|
$replacements['/^DB_CONNECTION=.*/m'] = 'DB_CONNECTION=mysql';
|
|
$replacements['/^DB_HOST=.*/m'] = "DB_HOST={$dbHost}";
|
|
$replacements['/^DB_PORT=.*/m'] = "DB_PORT={$dbPort}";
|
|
$replacements['/^DB_DATABASE=.*/m'] = "DB_DATABASE={$dbName}";
|
|
$replacements['/^DB_USERNAME=.*/m'] = "DB_USERNAME={$dbUser}";
|
|
$replacements['/^DB_PASSWORD=.*/m'] = "DB_PASSWORD={$dbPass}";
|
|
} else {
|
|
$replacements['/^DB_CONNECTION=.*/m'] = 'DB_CONNECTION=sqlite';
|
|
$replacements['/^DB_HOST=.*/m'] = '# DB_HOST=';
|
|
$replacements['/^DB_PORT=.*/m'] = '# DB_PORT=';
|
|
$replacements['/^DB_DATABASE=.*/m'] = 'DB_DATABASE=' . $scriptDir . '/database/database.sqlite';
|
|
$replacements['/^DB_USERNAME=.*/m'] = '# DB_USERNAME=';
|
|
$replacements['/^DB_PASSWORD=.*/m'] = '# DB_PASSWORD=';
|
|
}
|
|
|
|
foreach ($replacements as $pattern => $replacement) {
|
|
$dotenv = preg_replace($pattern, $replacement, $dotenv);
|
|
}
|
|
file_put_contents('.env', $dotenv);
|
|
ok('.env configurato');
|
|
|
|
// ── Step 2: Database ────────────────────────────
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' 2/7 — DATABASE', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
if ($dbType === 'mysql') {
|
|
info("Connessione a MySQL {$dbHost}:{$dbPort}...");
|
|
$connected = false;
|
|
for ($i = 0; $i < 30; $i++) {
|
|
try {
|
|
new PDO("mysql:host={$dbHost};port={$dbPort}", $dbUser, $dbPass);
|
|
$connected = true;
|
|
break;
|
|
} catch (\PDOException) {
|
|
sleep(2);
|
|
}
|
|
}
|
|
if ($connected) {
|
|
ok('MySQL raggiungibile');
|
|
} else {
|
|
warn('Timeout MySQL — verificare che il servizio sia attivo');
|
|
}
|
|
|
|
info("Creazione database '{$dbName}'...");
|
|
try {
|
|
$pdo = new PDO("mysql:host={$dbHost};port={$dbPort}", $dbUser, $dbPass);
|
|
$pdo->exec("CREATE DATABASE IF NOT EXISTS `{$dbName}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
|
|
ok("Database '{$dbName}' pronto");
|
|
} catch (\PDOException $e) {
|
|
warn("Crea manualmente: mysql -u root -p -e \"CREATE DATABASE `{$dbName}` CHARACTER SET utf8mb4;\"");
|
|
}
|
|
} else {
|
|
$sqlitePath = $scriptDir . '/database/database.sqlite';
|
|
if (!file_exists($sqlitePath)) {
|
|
touch($sqlitePath);
|
|
chmod($sqlitePath, 0664);
|
|
}
|
|
ok('SQLite pronto: ' . $sqlitePath);
|
|
}
|
|
|
|
// ── Step 3: Composer ────────────────────────────
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' 3/7 — INSTALLAZIONE DIPENDENZE', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
run('composer install --no-dev --optimize-autoloader', 'Installazione dipendenze Composer...');
|
|
ok('Dipendenze PHP installate');
|
|
|
|
// ── Step 4: Package discovery ────────────────────
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' 4/7 — REGISTRAZIONE PACCHETTI', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
runCapture("php artisan package:discover --ansi 2>/dev/null");
|
|
ok('Pacchetti registrati');
|
|
|
|
// ── Step 5: Migration + Seed ─────────────────────
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' 5/7 — MIGRATION E SEED', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
if (!run('php artisan migrate --seed --force', 'Esecuzione migration e seed...')) {
|
|
fail('Migration fallita. Verifica le credenziali del database.');
|
|
}
|
|
ok('Struttura database creata e dati di base inseriti');
|
|
|
|
runCapture('php artisan storage:link --force 2>/dev/null');
|
|
|
|
// ── Step 6: Admin user ───────────────────────────
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' 6/7 — CREAZIONE AMMINISTRATORE', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
$tinkerScript = <<<PHP
|
|
\App\Models\User::where('email', '{$adminEmail}')->delete();
|
|
\$u = new \App\Models\User;
|
|
\$u->name = '{$adminName}';
|
|
\$u->email = '{$adminEmail}';
|
|
\$u->password = bcrypt('{$adminPass}');
|
|
\$u->save();
|
|
echo "OK: " . \$u->id;
|
|
PHP;
|
|
|
|
$result = runCapture("php artisan tinker --execute=" . escapeshellarg($tinkerScript) . " 2>/dev/null");
|
|
if (str_contains($result, 'OK:')) {
|
|
ok("Amministratore {$adminEmail} creato");
|
|
} else {
|
|
warn("Creazione admin fallita — crealo manualmente dopo l'installazione");
|
|
}
|
|
|
|
// ── Step 7: Asset ────────────────────────────────
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' 7/7 — ASSET FRONTEND', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
$hasNpm = checkCmd('npm');
|
|
if ($hasNpm && confirm('Compilare gli asset frontend con npm?')) {
|
|
run('npm install', 'Installazione dipendenze npm...');
|
|
run('npm run build', 'Compilazione asset...');
|
|
ok('Asset compilati');
|
|
} else {
|
|
if (!$hasNpm) warn('npm non disponibile — asset non compilati (AdminLTE via CDN)');
|
|
else info('Asset non compilati (AdminLTE via CDN)');
|
|
}
|
|
|
|
// ── Permissions ──────────────────────────────────
|
|
if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
|
|
info('Impostazione permessi...');
|
|
$wwwUser = trim(shell_exec('ps aux | grep -E "apache|httpd" | grep -v grep | head -1 | awk \'{print \$1}\'') ?: 'www-data');
|
|
runCapture("chown -R {$wwwUser}:{$wwwUser} storage bootstrap/cache 2>/dev/null");
|
|
chmod("{$scriptDir}/storage", 0775);
|
|
chmod("{$scriptDir}/bootstrap/cache", 0775);
|
|
ok("Permessi impostati (utente: {$wwwUser})");
|
|
}
|
|
|
|
// ── Summary ──────────────────────────────────────
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'green'));
|
|
println(color(' INSTALLAZIONE COMPLETATA!', 'green'));
|
|
println(color('══════════════════════════════════════════', 'green'));
|
|
println();
|
|
println(" URL: {$appUrl}");
|
|
println(" Admin: {$adminEmail}");
|
|
println(" Cartella: {$scriptDir}");
|
|
println();
|
|
warn('Configura Apache con il VirtualHost (vedi Guida → Installazione, Passo 4).');
|
|
if ($dbType === 'mysql') {
|
|
warn('Se hai appena creato il database, assicurati che l\'utente abbia privilegi completi.');
|
|
}
|
|
}
|
|
|
|
// ══════════════════════════════════════════════════════
|
|
// MODE 2: DOCKER
|
|
// ══════════════════════════════════════════════════════
|
|
if ($mode === 'Fresh Install via Docker') {
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' PRE-FLIGHT CHECKS (Docker)', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
checkCmd('docker');
|
|
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' CONFIGURAZIONE DOCKER', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
$dockerDb = menu('Tipo database per Docker', ['sqlite', 'mysql']);
|
|
$dockerPort = ask('Porta host (es. 8080)', '8080');
|
|
|
|
$composeFile = $dockerDb === 'mysql' ? 'docker-compose.mysql.yml' : 'docker-compose.yml';
|
|
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' INSTALLAZIONE DOCKER', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
if (!run("docker compose -f {$composeFile} build", 'Build immagine Docker...')) {
|
|
fail('Build fallita');
|
|
}
|
|
|
|
// Override compose con variabili d'ambiente
|
|
$override = [
|
|
'services:',
|
|
' glastree:',
|
|
' environment:',
|
|
" - APP_NAME={$appName}",
|
|
" - APP_URL={$appUrl}",
|
|
' - APP_ENV=production',
|
|
' - APP_DEBUG=false',
|
|
];
|
|
file_put_contents('docker-compose.override.yml', implode(PHP_EOL, $override) . PHP_EOL);
|
|
|
|
if (!run(
|
|
"docker compose -f {$composeFile} -f docker-compose.override.yml up -d",
|
|
'Avvio container...'
|
|
)) {
|
|
@unlink('docker-compose.override.yml');
|
|
fail('Avvio container fallito');
|
|
}
|
|
|
|
// Attendi container pronto
|
|
info('Attendo che il container sia pronto...');
|
|
$containerId = runCapture("docker compose -f {$composeFile} ps -q glastree 2>/dev/null");
|
|
$adminCreated = false;
|
|
|
|
if ($containerId !== '') {
|
|
for ($i = 0; $i < 30; $i++) {
|
|
$status = runCapture("docker inspect -f '{{.State.Status}}' {$containerId} 2>/dev/null");
|
|
if ($status === 'running') {
|
|
$http = runCapture("docker exec {$containerId} sh -c 'wget -q -O- http://localhost/ 2>/dev/null && echo OK'");
|
|
if (str_contains($http, 'OK')) {
|
|
info('Container pronto');
|
|
break;
|
|
}
|
|
}
|
|
sleep(2);
|
|
}
|
|
|
|
$tinkerScript = <<<PHP
|
|
\App\Models\User::where('email', '{$adminEmail}')->delete();
|
|
\$u = new \App\Models\User;
|
|
\$u->name = '{$adminName}';
|
|
\$u->email = '{$adminEmail}';
|
|
\$u->password = bcrypt('{$adminPass}');
|
|
\$u->save();
|
|
echo "OK: " . \$u->id;
|
|
PHP;
|
|
$result = runCapture(
|
|
"docker exec -i {$containerId} php artisan tinker --execute=" . escapeshellarg($tinkerScript) . " 2>/dev/null"
|
|
);
|
|
if (str_contains($result, 'OK:')) {
|
|
ok("Amministratore {$adminEmail} creato nel container");
|
|
$adminCreated = true;
|
|
} else {
|
|
warn("Creazione admin fallita — crealo manualmente dopo l'installazione");
|
|
}
|
|
}
|
|
|
|
@unlink('docker-compose.override.yml');
|
|
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'green'));
|
|
println(color(' INSTALLAZIONE DOCKER COMPLETATA!', 'green'));
|
|
println(color('══════════════════════════════════════════', 'green'));
|
|
println();
|
|
println(" URL: http://localhost:{$dockerPort}");
|
|
println(" Admin: {$adminEmail}");
|
|
println(" Container: {$containerId}");
|
|
println();
|
|
warn('Per URL pubblici, imposta APP_URL nelle environment del container.');
|
|
warn('Dati persistenti: volumi Docker (glastree_storage, glastree_db o mysql_data).');
|
|
}
|
|
|
|
// ══════════════════════════════════════════════════════
|
|
// MODE 3: RESTORE DA BACKUP
|
|
// ══════════════════════════════════════════════════════
|
|
if ($mode === 'Restore da Backup (Apache)') {
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' PRE-FLIGHT CHECKS (Restore)', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
checkCmd('php');
|
|
checkCmd('composer');
|
|
checkCmd('unzip');
|
|
|
|
if ($dbType === 'mysql') {
|
|
checkCmd('mysql');
|
|
}
|
|
|
|
$backupZip = askRequired('Percorso del file ZIP di backup');
|
|
if (!file_exists($backupZip)) {
|
|
fail("File non trovato: {$backupZip}");
|
|
}
|
|
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
println(color(' RESTORE DA BACKUP', 'yellow'));
|
|
println(color('══════════════════════════════════════════', 'yellow'));
|
|
|
|
// Extract
|
|
$tmpDir = sys_get_temp_dir() . '/glastree-restore-' . uniqid();
|
|
mkdir($tmpDir, 0700, true);
|
|
info("Estrazione backup in {$tmpDir}...");
|
|
|
|
if (!run("unzip -qo {$backupZip} -d {$tmpDir}", null)) {
|
|
// pulisci tmp dir prima di fallire
|
|
runCapture("rm -rf {$tmpDir}");
|
|
fail('Estrazione backup fallita');
|
|
}
|
|
|
|
// Environment
|
|
if (!file_exists('.env')) {
|
|
copy('.env.example', '.env');
|
|
}
|
|
|
|
// Restore .env from backup preserving DB credentials
|
|
if (file_exists("{$tmpDir}/.env")) {
|
|
$currentEnv = file_get_contents('.env');
|
|
$backupEnv = file_get_contents("{$tmpDir}/.env");
|
|
|
|
// Save current DB_* values
|
|
$currentDb = [];
|
|
foreach (['DB_CONNECTION', 'DB_HOST', 'DB_PORT', 'DB_DATABASE', 'DB_USERNAME', 'DB_PASSWORD'] as $key) {
|
|
if (preg_match("/^{$key}=(.*)$/m", $currentEnv, $m)) {
|
|
$currentDb[$key] = $m[1];
|
|
}
|
|
}
|
|
|
|
// Use backup .env as base
|
|
file_put_contents('.env', $backupEnv);
|
|
|
|
// Restore current DB credentials
|
|
foreach ($currentDb as $key => $val) {
|
|
$dotenv = file_get_contents('.env');
|
|
$dotenv = preg_replace("/^{$key}=.*/m", "{$key}={$val}", $dotenv);
|
|
file_put_contents('.env', $dotenv);
|
|
}
|
|
|
|
ok('.env ripristinato dal backup (credenziali DB preservate)');
|
|
}
|
|
|
|
// Update APP_* config
|
|
$dotenv = file_get_contents('.env');
|
|
$dotenv = preg_replace('/^APP_NAME=.*/m', 'APP_NAME="' . addslashes($appName) . '"', $dotenv);
|
|
$dotenv = preg_replace('/^APP_URL=.*/m', "APP_URL={$appUrl}", $dotenv);
|
|
$dotenv = preg_replace('/^APP_ENV=.*/m', 'APP_ENV=production', $dotenv);
|
|
$dotenv = preg_replace('/^APP_DEBUG=.*/m', 'APP_DEBUG=false', $dotenv);
|
|
file_put_contents('.env', $dotenv);
|
|
|
|
// Generate APP_KEY
|
|
runCapture('php artisan key:generate --force');
|
|
ok('APP_KEY rigenerata');
|
|
|
|
// Import database
|
|
if ($dbType === 'mysql') {
|
|
$sqlFile = "{$tmpDir}/database.sql";
|
|
if (file_exists($sqlFile)) {
|
|
info("Importazione database MySQL: {$sqlFile}");
|
|
$cmd = "mysql -h {$dbHost} -P {$dbPort} -u {$dbUser} -p{$dbPass} {$dbName} < {$sqlFile}";
|
|
if (run($cmd, null)) {
|
|
ok('Database importato');
|
|
} else {
|
|
warn('Import database fallito — importa manualmente');
|
|
}
|
|
} else {
|
|
warn('database.sql non trovato nel backup');
|
|
}
|
|
} else {
|
|
warn('Restore automatico supportato solo con MySQL. Per SQLite ripristina manualmente i file.');
|
|
}
|
|
|
|
// Restore storage
|
|
$storageBackup = "{$tmpDir}/storage";
|
|
if (is_dir($storageBackup)) {
|
|
info('Ripristino file storage...');
|
|
runCapture("cp -r {$storageBackup}/* {$scriptDir}/storage/ 2>/dev/null");
|
|
ok('Storage ripristinato');
|
|
}
|
|
|
|
// Composer
|
|
run('composer install --no-dev --optimize-autoloader', 'Installazione dipendenze Composer...');
|
|
runCapture('php artisan package:discover --ansi 2>/dev/null');
|
|
|
|
// Storage link + cache
|
|
runCapture('php artisan storage:link --force 2>/dev/null');
|
|
runCapture('php artisan optimize:clear');
|
|
|
|
// Permissions
|
|
if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
|
|
$wwwUser = trim(shell_exec('ps aux | grep -E "apache|httpd" | grep -v grep | head -1 | awk \'{print \$1}\'') ?: 'www-data');
|
|
runCapture("chown -R {$wwwUser}:{$wwwUser} storage bootstrap/cache 2>/dev/null");
|
|
chmod("{$scriptDir}/storage", 0775);
|
|
chmod("{$scriptDir}/bootstrap/cache", 0775);
|
|
ok("Permessi impostati (utente: {$wwwUser})");
|
|
}
|
|
|
|
// Create admin if not exists
|
|
$tinkerScript = <<<PHP
|
|
\$exists = \App\Models\User::where('email', '{$adminEmail}')->exists();
|
|
if (!\$exists) {
|
|
\$u = new \App\Models\User;
|
|
\$u->name = '{$adminName}';
|
|
\$u->email = '{$adminEmail}';
|
|
\$u->password = bcrypt('{$adminPass}');
|
|
\$u->save();
|
|
echo 'OK';
|
|
} else {
|
|
echo 'EXISTS';
|
|
}
|
|
PHP;
|
|
$result = runCapture("php artisan tinker --execute=" . escapeshellarg($tinkerScript) . " 2>/dev/null");
|
|
if (str_contains($result, 'OK')) {
|
|
ok("Amministratore {$adminEmail} creato");
|
|
} elseif (str_contains($result, 'EXISTS')) {
|
|
ok("Amministratore {$adminEmail} già esistente");
|
|
}
|
|
|
|
// Cleanup
|
|
runCapture("rm -rf {$tmpDir}");
|
|
|
|
println();
|
|
println(color('══════════════════════════════════════════', 'green'));
|
|
println(color(' RESTORE COMPLETATO!', 'green'));
|
|
println(color('══════════════════════════════════════════', 'green'));
|
|
println();
|
|
println(" URL: {$appUrl}");
|
|
println(" Admin: {$adminEmail}");
|
|
println(" Cartella: {$scriptDir}");
|
|
println();
|
|
warn('Verifica che APP_KEY non abbia invalidato dati criptati (token OAuth, password IMAP).');
|
|
warn('Se necessario, riconfigura SMTP e Google Drive dalle Impostazioni.');
|
|
}
|
|
|
|
println();
|