feat: Remove UPDATE_SYSTEM.md from tracking - system not ready for public release
- UPDATE_SYSTEM.md moved to internal documentation - Keep only stable documentation on GitHub - System update features still in development phase
This commit is contained in:
parent
829b2eaddc
commit
14d62db618
863
UPDATE_SYSTEM.md
863
UPDATE_SYSTEM.md
|
|
@ -1,863 +0,0 @@
|
|||
# NetGesCon - Sistema Aggiornamenti Automatici
|
||||
|
||||
## 🎯 Panoramica Sistema
|
||||
|
||||
Il sistema di aggiornamenti automatici NetGesCon permette:
|
||||
- **Registrazione utenti** con codici 8 caratteri univoci
|
||||
- **Download automatico** aggiornamenti via API
|
||||
- **Backup pre-aggiornamento** automatico
|
||||
- **Rollback** in caso di errori
|
||||
- **Gestione versioni** (stable/development)
|
||||
- **Sistema licenze** basato su livello servizio
|
||||
|
||||
## 🏗️ Architettura Sistema
|
||||
|
||||
```
|
||||
NetGesCon Master Server (update.netgescon.com)
|
||||
├── API Registrazione Utenti
|
||||
├── API Download Aggiornamenti
|
||||
├── Database Utenti/Licenze
|
||||
├── Repository Versioni
|
||||
└── Sistema Notifiche
|
||||
|
||||
NetGesCon Client (Installazione Locale)
|
||||
├── Update Service
|
||||
├── Backup Manager
|
||||
├── Version Manager
|
||||
└── License Validator
|
||||
```
|
||||
|
||||
## 📊 Database Schema
|
||||
|
||||
### Tabella: `registered_users`
|
||||
```sql
|
||||
CREATE TABLE registered_users (
|
||||
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
||||
codice_utente VARCHAR(8) UNIQUE NOT NULL, -- es: "USR12345"
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
nome VARCHAR(100) NOT NULL,
|
||||
cognome VARCHAR(100) NOT NULL,
|
||||
azienda VARCHAR(200),
|
||||
telefono VARCHAR(20),
|
||||
|
||||
-- Licenza e Servizi
|
||||
livello_servizio ENUM('basic', 'professional', 'enterprise') DEFAULT 'basic',
|
||||
data_scadenza DATE,
|
||||
max_amministratori INT DEFAULT 5,
|
||||
max_stabili INT DEFAULT 50,
|
||||
features_abilitate JSON, -- {"multi_db": true, "audit": false, "api": true}
|
||||
|
||||
-- Sicurezza
|
||||
api_key VARCHAR(64) UNIQUE,
|
||||
api_secret VARCHAR(128),
|
||||
ultimo_accesso TIMESTAMP NULL,
|
||||
ip_autorizzati TEXT, -- JSON array IP
|
||||
|
||||
-- Sistema
|
||||
stato ENUM('attivo', 'sospeso', 'scaduto') DEFAULT 'attivo',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP NULL
|
||||
);
|
||||
```
|
||||
|
||||
### Tabella: `system_versions`
|
||||
```sql
|
||||
CREATE TABLE system_versions (
|
||||
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
||||
versione VARCHAR(20) NOT NULL, -- "2.1.0"
|
||||
tipo_release ENUM('stable', 'development', 'hotfix') DEFAULT 'stable',
|
||||
|
||||
-- Files
|
||||
download_url VARCHAR(500),
|
||||
checksum_sha256 VARCHAR(64),
|
||||
dimensione_mb DECIMAL(8,2),
|
||||
|
||||
-- Compatibilità
|
||||
versione_php_min VARCHAR(10), -- "8.2"
|
||||
versione_laravel_min VARCHAR(10), -- "10.0"
|
||||
versione_mysql_min VARCHAR(10), -- "8.0"
|
||||
|
||||
-- Descrizione
|
||||
titolo VARCHAR(200),
|
||||
descrizione TEXT,
|
||||
changelog TEXT,
|
||||
breaking_changes TEXT,
|
||||
|
||||
-- Controllo
|
||||
richiede_backup BOOLEAN DEFAULT true,
|
||||
richiede_downtime BOOLEAN DEFAULT false,
|
||||
compatibile_rollback BOOLEAN DEFAULT true,
|
||||
|
||||
-- Metadata
|
||||
data_rilascio TIMESTAMP,
|
||||
stato ENUM('draft', 'testing', 'published', 'deprecated') DEFAULT 'draft',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
### Tabella: `update_logs`
|
||||
```sql
|
||||
CREATE TABLE update_logs (
|
||||
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
||||
codice_utente VARCHAR(8),
|
||||
versione_da VARCHAR(20),
|
||||
versione_a VARCHAR(20),
|
||||
|
||||
-- Processo
|
||||
stato ENUM('started', 'downloading', 'backing_up', 'installing', 'completed', 'failed', 'rolled_back'),
|
||||
percentuale_completamento TINYINT DEFAULT 0,
|
||||
|
||||
-- Dettagli
|
||||
log_output TEXT,
|
||||
errore_dettaglio TEXT,
|
||||
backup_path VARCHAR(500),
|
||||
tempo_inizio TIMESTAMP,
|
||||
tempo_fine TIMESTAMP,
|
||||
|
||||
-- Sistema
|
||||
ip_client VARCHAR(45),
|
||||
user_agent TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
## 🔌 API Endpoints
|
||||
|
||||
### 1. Registrazione Utente
|
||||
```http
|
||||
POST /api/v1/register
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"email": "admin@condominio.it",
|
||||
"nome": "Mario",
|
||||
"cognome": "Rossi",
|
||||
"azienda": "Amministrazioni Rossi SRL",
|
||||
"telefono": "+39 123 456 7890",
|
||||
"livello_servizio": "professional"
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"codice_utente": "USR12345",
|
||||
"api_key": "a1b2c3d4e5f6...",
|
||||
"api_secret": "secret_hash...",
|
||||
"scadenza": "2026-07-07",
|
||||
"features": {
|
||||
"multi_db": true,
|
||||
"audit": true,
|
||||
"api": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Verifica Licenza
|
||||
```http
|
||||
GET /api/v1/license/verify
|
||||
Authorization: Bearer {api_key}
|
||||
X-API-Secret: {api_secret}
|
||||
|
||||
Response:
|
||||
{
|
||||
"valid": true,
|
||||
"scadenza": "2026-07-07",
|
||||
"giorni_rimanenti": 365,
|
||||
"livello": "professional",
|
||||
"limiti": {
|
||||
"amministratori": 20,
|
||||
"stabili": 200
|
||||
},
|
||||
"features": ["multi_db", "audit", "api"]
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Check Aggiornamenti
|
||||
```http
|
||||
GET /api/v1/updates/check
|
||||
Authorization: Bearer {api_key}
|
||||
X-Client-Version: 2.0.5
|
||||
X-Release-Channel: stable
|
||||
|
||||
Response:
|
||||
{
|
||||
"update_available": true,
|
||||
"latest_version": "2.1.0",
|
||||
"download_url": "https://update.netgescon.com/releases/2.1.0/netgescon-2.1.0.zip",
|
||||
"checksum": "sha256:a1b2c3...",
|
||||
"size_mb": 45.2,
|
||||
"changelog": "- Fix bug contabilità\n- Nuova UI dashboard...",
|
||||
"breaking_changes": false,
|
||||
"requires_backup": true
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Download Aggiornamento
|
||||
```http
|
||||
GET /api/v1/updates/download/{version}
|
||||
Authorization: Bearer {api_key}
|
||||
X-API-Secret: {api_secret}
|
||||
|
||||
Response: [Binary ZIP file with update]
|
||||
```
|
||||
|
||||
## 💻 Client Update Service
|
||||
|
||||
### Comando Artisan: `update:check`
|
||||
```php
|
||||
<?php
|
||||
// app/Console/Commands/UpdateCheckCommand.php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Services\UpdateService;
|
||||
|
||||
class UpdateCheckCommand extends Command
|
||||
{
|
||||
protected $signature = 'update:check
|
||||
{--force : Force check anche se già controllato di recente}
|
||||
{--channel=stable : Channel release (stable|development)}';
|
||||
|
||||
protected $description = 'Controlla aggiornamenti disponibili';
|
||||
|
||||
public function handle(UpdateService $updateService)
|
||||
{
|
||||
$this->info('🔍 Controllo aggiornamenti NetGesCon...');
|
||||
|
||||
$channel = $this->option('channel');
|
||||
$force = $this->option('force');
|
||||
|
||||
$result = $updateService->checkUpdates($channel, $force);
|
||||
|
||||
if ($result['update_available']) {
|
||||
$this->info("✅ Aggiornamento disponibile:");
|
||||
$this->table(['Campo', 'Valore'], [
|
||||
['Versione attuale', $result['current_version']],
|
||||
['Nuova versione', $result['latest_version']],
|
||||
['Tipo release', $result['release_type']],
|
||||
['Dimensione', $result['size_mb'] . ' MB'],
|
||||
['Richiede backup', $result['requires_backup'] ? 'Sì' : 'No']
|
||||
]);
|
||||
|
||||
if ($this->confirm('Vuoi procedere con il download?')) {
|
||||
$this->call('update:download', ['version' => $result['latest_version']]);
|
||||
}
|
||||
} else {
|
||||
$this->info('✅ Sistema aggiornato alla versione più recente');
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Comando Artisan: `update:install`
|
||||
```php
|
||||
<?php
|
||||
|
||||
class UpdateInstallCommand extends Command
|
||||
{
|
||||
protected $signature = 'update:install
|
||||
{version : Versione da installare}
|
||||
{--skip-backup : Salta backup pre-installazione}
|
||||
{--rollback-on-error : Auto-rollback in caso di errore}';
|
||||
|
||||
public function handle(UpdateService $updateService, BackupService $backupService)
|
||||
{
|
||||
$version = $this->argument('version');
|
||||
$skipBackup = $this->option('skip-backup');
|
||||
$autoRollback = $this->option('rollback-on-error');
|
||||
|
||||
$this->info("🚀 Installazione NetGesCon v{$version}");
|
||||
|
||||
// 1. Validazione pre-installazione
|
||||
$this->info('📋 Validazione sistema...');
|
||||
if (!$updateService->validateSystem($version)) {
|
||||
$this->error('❌ Sistema non compatibile con questa versione');
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 2. Backup automatico
|
||||
if (!$skipBackup) {
|
||||
$this->info('💾 Creazione backup pre-aggiornamento...');
|
||||
$backupPath = $backupService->createPreUpdateBackup($version);
|
||||
$this->info("Backup salvato: {$backupPath}");
|
||||
}
|
||||
|
||||
// 3. Download se necessario
|
||||
if (!$updateService->isVersionDownloaded($version)) {
|
||||
$this->info('⬇️ Download aggiornamento...');
|
||||
$updateService->downloadVersion($version, function($progress) {
|
||||
$this->output->write("\r📦 Download: {$progress}%");
|
||||
});
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
// 4. Installazione
|
||||
$this->info('⚙️ Installazione in corso...');
|
||||
|
||||
try {
|
||||
$updateService->installVersion($version, function($step, $progress) {
|
||||
$this->output->write("\r🔧 {$step}: {$progress}%");
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
$this->info('✅ Aggiornamento completato con successo!');
|
||||
$this->info("NetGesCon aggiornato alla versione {$version}");
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->error("❌ Errore durante l'installazione: " . $e->getMessage());
|
||||
|
||||
if ($autoRollback && !$skipBackup) {
|
||||
$this->warn('🔄 Avvio rollback automatico...');
|
||||
if ($backupService->restore($backupPath)) {
|
||||
$this->info('✅ Rollback completato');
|
||||
} else {
|
||||
$this->error('❌ Errore durante il rollback');
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 UpdateService Implementation
|
||||
|
||||
```php
|
||||
<?php
|
||||
// app/Services/UpdateService.php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class UpdateService
|
||||
{
|
||||
private $apiBaseUrl;
|
||||
private $apiKey;
|
||||
private $apiSecret;
|
||||
private $currentVersion;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->apiBaseUrl = config('netgescon.update.api_url');
|
||||
$this->apiKey = config('netgescon.update.api_key');
|
||||
$this->apiSecret = config('netgescon.update.api_secret');
|
||||
$this->currentVersion = config('netgescon.version');
|
||||
}
|
||||
|
||||
public function checkUpdates(string $channel = 'stable', bool $force = false): array
|
||||
{
|
||||
// Cache del controllo (evita troppe chiamate API)
|
||||
$cacheKey = "updates_check_{$channel}";
|
||||
|
||||
if (!$force && cache()->has($cacheKey)) {
|
||||
return cache($cacheKey);
|
||||
}
|
||||
|
||||
try {
|
||||
$response = Http::withHeaders([
|
||||
'Authorization' => "Bearer {$this->apiKey}",
|
||||
'X-Client-Version' => $this->currentVersion,
|
||||
'X-Release-Channel' => $channel
|
||||
])->get("{$this->apiBaseUrl}/api/v1/updates/check");
|
||||
|
||||
if ($response->successful()) {
|
||||
$data = $response->json();
|
||||
|
||||
// Cache per 1 ora
|
||||
cache([$cacheKey => $data], 3600);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
throw new \Exception('API non raggiungibile: ' . $response->status());
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Errore controllo aggiornamenti: " . $e->getMessage());
|
||||
|
||||
return [
|
||||
'update_available' => false,
|
||||
'current_version' => $this->currentVersion,
|
||||
'error' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function downloadVersion(string $version, $progressCallback = null): string
|
||||
{
|
||||
$downloadPath = storage_path("app/updates/{$version}");
|
||||
$zipPath = "{$downloadPath}/netgescon-{$version}.zip";
|
||||
|
||||
if (!File::exists($downloadPath)) {
|
||||
File::makeDirectory($downloadPath, 0755, true);
|
||||
}
|
||||
|
||||
$response = Http::withHeaders([
|
||||
'Authorization' => "Bearer {$this->apiKey}",
|
||||
'X-API-Secret' => $this->apiSecret
|
||||
])->withOptions([
|
||||
'sink' => $zipPath,
|
||||
'progress' => function($downloadTotal, $downloadedBytes) use ($progressCallback) {
|
||||
if ($downloadTotal > 0 && $progressCallback) {
|
||||
$progress = round(($downloadedBytes / $downloadTotal) * 100);
|
||||
$progressCallback($progress);
|
||||
}
|
||||
}
|
||||
])->get("{$this->apiBaseUrl}/api/v1/updates/download/{$version}");
|
||||
|
||||
if (!$response->successful()) {
|
||||
throw new \Exception("Errore download versione {$version}");
|
||||
}
|
||||
|
||||
// Verifica checksum
|
||||
$this->verifyChecksum($zipPath, $version);
|
||||
|
||||
return $zipPath;
|
||||
}
|
||||
|
||||
public function installVersion(string $version, $progressCallback = null): void
|
||||
{
|
||||
$zipPath = storage_path("app/updates/{$version}/netgescon-{$version}.zip");
|
||||
$extractPath = storage_path("app/updates/{$version}/extracted");
|
||||
|
||||
if (!File::exists($zipPath)) {
|
||||
throw new \Exception("File aggiornamento non trovato: {$zipPath}");
|
||||
}
|
||||
|
||||
// 1. Estrazione
|
||||
$progressCallback && $progressCallback('Estrazione files', 10);
|
||||
$this->extractUpdate($zipPath, $extractPath);
|
||||
|
||||
// 2. Backup configurazioni attuali
|
||||
$progressCallback && $progressCallback('Backup configurazioni', 20);
|
||||
$this->backupConfigurations();
|
||||
|
||||
// 3. Manutenzione mode
|
||||
$progressCallback && $progressCallback('Attivazione modalità manutenzione', 30);
|
||||
\Artisan::call('down');
|
||||
|
||||
try {
|
||||
// 4. Aggiornamento files
|
||||
$progressCallback && $progressCallback('Aggiornamento files applicazione', 40);
|
||||
$this->updateApplicationFiles($extractPath);
|
||||
|
||||
// 5. Composer install
|
||||
$progressCallback && $progressCallback('Installazione dipendenze', 60);
|
||||
$this->runComposerInstall();
|
||||
|
||||
// 6. Database migrations
|
||||
$progressCallback && $progressCallback('Aggiornamento database', 75);
|
||||
\Artisan::call('migrate', ['--force' => true]);
|
||||
|
||||
// 7. Cache refresh
|
||||
$progressCallback && $progressCallback('Aggiornamento cache', 85);
|
||||
$this->refreshCache();
|
||||
|
||||
// 8. NPM build
|
||||
$progressCallback && $progressCallback('Build assets', 95);
|
||||
$this->buildAssets();
|
||||
|
||||
} finally {
|
||||
// 9. Disattivazione manutenzione
|
||||
$progressCallback && $progressCallback('Riattivazione applicazione', 100);
|
||||
\Artisan::call('up');
|
||||
}
|
||||
|
||||
// 10. Aggiornamento versione
|
||||
$this->updateVersionFile($version);
|
||||
|
||||
// 11. Cleanup
|
||||
$this->cleanupUpdateFiles($version);
|
||||
}
|
||||
|
||||
private function extractUpdate(string $zipPath, string $extractPath): void
|
||||
{
|
||||
$zip = new \ZipArchive();
|
||||
|
||||
if ($zip->open($zipPath) === TRUE) {
|
||||
$zip->extractTo($extractPath);
|
||||
$zip->close();
|
||||
} else {
|
||||
throw new \Exception("Impossibile estrarre {$zipPath}");
|
||||
}
|
||||
}
|
||||
|
||||
private function updateApplicationFiles(string $extractPath): void
|
||||
{
|
||||
// Lista files da NON sovrascrivere
|
||||
$preserveFiles = [
|
||||
'.env',
|
||||
'storage/app/*',
|
||||
'storage/logs/*',
|
||||
'storage/framework/cache/*',
|
||||
'storage/framework/sessions/*',
|
||||
'storage/framework/views/*'
|
||||
];
|
||||
|
||||
// Copia files (eccetto quelli da preservare)
|
||||
File::copyDirectory($extractPath, base_path(), function($path) use ($preserveFiles) {
|
||||
foreach ($preserveFiles as $preserve) {
|
||||
if (fnmatch($preserve, $path)) {
|
||||
return false; // Non copiare
|
||||
}
|
||||
}
|
||||
return true; // Copia
|
||||
});
|
||||
}
|
||||
|
||||
private function verifyChecksum(string $filePath, string $version): void
|
||||
{
|
||||
// Ottieni checksum atteso dall'API
|
||||
$response = Http::withHeaders([
|
||||
'Authorization' => "Bearer {$this->apiKey}"
|
||||
])->get("{$this->apiBaseUrl}/api/v1/updates/checksum/{$version}");
|
||||
|
||||
$expectedChecksum = $response->json()['checksum'];
|
||||
$actualChecksum = hash_file('sha256', $filePath);
|
||||
|
||||
if ($expectedChecksum !== $actualChecksum) {
|
||||
throw new \Exception("Checksum non valido per versione {$version}");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ⚙️ Configurazione Client
|
||||
|
||||
### Config: `config/netgescon.php`
|
||||
```php
|
||||
<?php
|
||||
|
||||
return [
|
||||
'version' => env('NETGESCON_VERSION', '2.0.0'),
|
||||
|
||||
'update' => [
|
||||
'api_url' => env('NETGESCON_UPDATE_API_URL', 'https://update.netgescon.com'),
|
||||
'api_key' => env('NETGESCON_UPDATE_API_KEY'),
|
||||
'api_secret' => env('NETGESCON_UPDATE_API_SECRET'),
|
||||
'check_interval' => env('NETGESCON_UPDATE_CHECK_INTERVAL', 24), // ore
|
||||
'auto_backup' => env('NETGESCON_UPDATE_AUTO_BACKUP', true),
|
||||
'release_channel' => env('NETGESCON_RELEASE_CHANNEL', 'stable'), // stable|development
|
||||
],
|
||||
|
||||
'license' => [
|
||||
'codice_utente' => env('NETGESCON_LICENSE_CODE'),
|
||||
'livello_servizio' => env('NETGESCON_LICENSE_LEVEL', 'basic'),
|
||||
'scadenza' => env('NETGESCON_LICENSE_EXPIRES'),
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
### Environment Variables (`.env`)
|
||||
```env
|
||||
# NetGesCon Update System
|
||||
NETGESCON_VERSION=2.0.0
|
||||
NETGESCON_UPDATE_API_URL=https://update.netgescon.com
|
||||
NETGESCON_UPDATE_API_KEY=your_api_key_here
|
||||
NETGESCON_UPDATE_API_SECRET=your_api_secret_here
|
||||
NETGESCON_UPDATE_CHECK_INTERVAL=24
|
||||
NETGESCON_UPDATE_AUTO_BACKUP=true
|
||||
NETGESCON_RELEASE_CHANNEL=stable
|
||||
|
||||
# License
|
||||
NETGESCON_LICENSE_CODE=USR12345
|
||||
NETGESCON_LICENSE_LEVEL=professional
|
||||
NETGESCON_LICENSE_EXPIRES=2026-07-07
|
||||
```
|
||||
|
||||
## 🕒 Scheduling Automatico
|
||||
|
||||
### `app/Console/Kernel.php`
|
||||
```php
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
// Check aggiornamenti automatico (ogni 6 ore)
|
||||
$schedule->command('update:check --channel=stable')
|
||||
->everySixHours()
|
||||
->runInBackground()
|
||||
->sendOutputTo(storage_path('logs/update-check.log'));
|
||||
|
||||
// Verifica licenza (ogni giorno)
|
||||
$schedule->command('license:verify')
|
||||
->daily()
|
||||
->at('02:00');
|
||||
|
||||
// Cleanup update files (ogni settimana)
|
||||
$schedule->command('update:cleanup')
|
||||
->weekly()
|
||||
->sundays()
|
||||
->at('03:00');
|
||||
}
|
||||
```
|
||||
|
||||
## 🔔 Sistema Notifiche
|
||||
|
||||
### Notifica Aggiornamento Disponibile
|
||||
```php
|
||||
<?php
|
||||
// app/Notifications/UpdateAvailableNotification.php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
||||
class UpdateAvailableNotification extends Notification
|
||||
{
|
||||
private $updateInfo;
|
||||
|
||||
public function __construct(array $updateInfo)
|
||||
{
|
||||
$this->updateInfo = $updateInfo;
|
||||
}
|
||||
|
||||
public function via($notifiable)
|
||||
{
|
||||
return ['mail', 'database'];
|
||||
}
|
||||
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
return (new MailMessage)
|
||||
->subject('NetGesCon: Aggiornamento Disponibile')
|
||||
->greeting('Ciao ' . $notifiable->name)
|
||||
->line("È disponibile una nuova versione di NetGesCon: {$this->updateInfo['latest_version']}")
|
||||
->line("Versione attuale: {$this->updateInfo['current_version']}")
|
||||
->line("Novità principali:")
|
||||
->line($this->updateInfo['changelog'])
|
||||
->action('Aggiorna Ora', url('/admin/updates'))
|
||||
->line('Ti consigliamo di aggiornare per avere le ultime funzionalità e correzioni.');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📱 Frontend Update Manager
|
||||
|
||||
### Update Status Component (Vue.js)
|
||||
```vue
|
||||
<template>
|
||||
<div class="update-manager">
|
||||
<div v-if="updateAvailable" class="update-banner">
|
||||
<div class="update-info">
|
||||
<h4>🎉 Aggiornamento Disponibile!</h4>
|
||||
<p>NetGesCon v{{ latestVersion }} è pronto per l'installazione</p>
|
||||
<small>Versione attuale: v{{ currentVersion }}</small>
|
||||
</div>
|
||||
|
||||
<div class="update-actions">
|
||||
<button @click="viewChangelog" class="btn btn-outline">
|
||||
📋 Novità
|
||||
</button>
|
||||
<button @click="startUpdate" class="btn btn-primary" :disabled="isUpdating">
|
||||
⬆️ Aggiorna Ora
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Modal -->
|
||||
<div v-if="isUpdating" class="update-modal">
|
||||
<div class="modal-content">
|
||||
<h3>⚙️ Aggiornamento in corso...</h3>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" :style="{width: progress + '%'}"></div>
|
||||
</div>
|
||||
<p>{{ currentStep }} ({{ progress }}%)</p>
|
||||
<div class="update-log">
|
||||
<pre v-for="line in updateLog" :key="line">{{ line }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
updateAvailable: false,
|
||||
currentVersion: '2.0.0',
|
||||
latestVersion: null,
|
||||
isUpdating: false,
|
||||
progress: 0,
|
||||
currentStep: '',
|
||||
updateLog: []
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.checkForUpdates();
|
||||
// Check ogni ora
|
||||
setInterval(this.checkForUpdates, 3600000);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async checkForUpdates() {
|
||||
try {
|
||||
const response = await axios.get('/api/updates/check');
|
||||
this.updateAvailable = response.data.update_available;
|
||||
this.latestVersion = response.data.latest_version;
|
||||
} catch (error) {
|
||||
console.error('Errore controllo aggiornamenti:', error);
|
||||
}
|
||||
},
|
||||
|
||||
async startUpdate() {
|
||||
if (!confirm('Sicuro di voler procedere con l\'aggiornamento?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isUpdating = true;
|
||||
this.progress = 0;
|
||||
this.updateLog = [];
|
||||
|
||||
try {
|
||||
// Avvia aggiornamento via WebSocket o polling
|
||||
await this.performUpdate();
|
||||
} catch (error) {
|
||||
alert('Errore durante l\'aggiornamento: ' + error.message);
|
||||
} finally {
|
||||
this.isUpdating = false;
|
||||
}
|
||||
},
|
||||
|
||||
async performUpdate() {
|
||||
// Implementa WebSocket o polling per progress real-time
|
||||
const response = await axios.post('/api/updates/install', {
|
||||
version: this.latestVersion
|
||||
});
|
||||
|
||||
// Simula progress updates
|
||||
const steps = [
|
||||
'Download aggiornamento...',
|
||||
'Backup sistema...',
|
||||
'Installazione files...',
|
||||
'Aggiornamento database...',
|
||||
'Completamento...'
|
||||
];
|
||||
|
||||
for (let i = 0; i < steps.length; i++) {
|
||||
this.currentStep = steps[i];
|
||||
this.progress = ((i + 1) / steps.length) * 100;
|
||||
this.updateLog.push(`[${new Date().toLocaleTimeString()}] ${steps[i]}`);
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
}
|
||||
|
||||
// Ricarica pagina per nuova versione
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## 🔒 Sicurezza e Validazione
|
||||
|
||||
### Validazione Checksum
|
||||
```php
|
||||
private function validateDownload(string $filePath, string $expectedChecksum): bool
|
||||
{
|
||||
$actualChecksum = hash_file('sha256', $filePath);
|
||||
return hash_equals($expectedChecksum, $actualChecksum);
|
||||
}
|
||||
```
|
||||
|
||||
### Signature Verification (GPG)
|
||||
```php
|
||||
private function verifySignature(string $filePath, string $signaturePath): bool
|
||||
{
|
||||
$publicKey = file_get_contents(resource_path('keys/netgescon-public.key'));
|
||||
|
||||
// Implementa verifica GPG signature
|
||||
// ...
|
||||
|
||||
return $isValid;
|
||||
}
|
||||
```
|
||||
|
||||
### Rate Limiting API
|
||||
```php
|
||||
// routes/api.php
|
||||
Route::middleware(['throttle:10,1'])->group(function () {
|
||||
Route::get('/updates/check', [UpdateController::class, 'check']);
|
||||
Route::post('/updates/download', [UpdateController::class, 'download']);
|
||||
});
|
||||
```
|
||||
|
||||
## 📊 Monitoring e Analytics
|
||||
|
||||
### Log Update Events
|
||||
```php
|
||||
class UpdateEventLogger
|
||||
{
|
||||
public static function logUpdateStart(string $version): void
|
||||
{
|
||||
Log::info('Update started', [
|
||||
'version_from' => config('netgescon.version'),
|
||||
'version_to' => $version,
|
||||
'timestamp' => now(),
|
||||
'user_ip' => request()->ip()
|
||||
]);
|
||||
}
|
||||
|
||||
public static function logUpdateComplete(string $version, int $duration): void
|
||||
{
|
||||
Log::info('Update completed', [
|
||||
'version' => $version,
|
||||
'duration_seconds' => $duration,
|
||||
'timestamp' => now()
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Metriche Sistema
|
||||
- Tempo medio aggiornamento
|
||||
- Tasso successo/fallimento
|
||||
- Versioni più utilizzate
|
||||
- Problemi comuni durante update
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Roadmap Implementazione
|
||||
|
||||
### Fase 1: Foundation (Week 1-2)
|
||||
- ✅ Database schema design
|
||||
- ✅ API endpoints base
|
||||
- ⏳ UpdateService implementation
|
||||
- ⏳ Basic Artisan commands
|
||||
|
||||
### Fase 2: Core Features (Week 3-4)
|
||||
- ⏳ Download e installazione automatica
|
||||
- ⏳ Sistema backup/rollback
|
||||
- ⏳ Frontend update manager
|
||||
- ⏳ Notifiche sistema
|
||||
|
||||
### Fase 3: Advanced (Week 5-6)
|
||||
- ⏳ Gestione licenze
|
||||
- ⏳ Release channels
|
||||
- ⏳ Security features
|
||||
- ⏳ Monitoring e analytics
|
||||
|
||||
### Fase 4: Testing & Deployment (Week 7-8)
|
||||
- ⏳ Testing completo
|
||||
- ⏳ Documentation
|
||||
- ⏳ Production deployment
|
||||
- ⏳ User onboarding
|
||||
|
||||
---
|
||||
|
||||
*Ultima modifica: 7 Luglio 2025*
|
||||
Loading…
Reference in New Issue
Block a user