netgescon-master/UPDATE_SYSTEM.md
Pikappa2 1b884feda5 v0.7 - UI Universale e Sistema Database Modernizzato
 Completato:
- Database modernizzato con chiavi id standard Laravel
- Relazioni corrette Amministratore→Stabili→Movimenti
- UI universale responsive con sidebar permission-based
- Codici alfanumerici 8 caratteri implementati
- Seeders con dati di test funzionanti
- Documentazione tecnica completa (INSTALL_LINUX, TECHNICAL_SPECS, UPDATE_SYSTEM)

🔧 Miglioramenti:
- Helper userSetting() funzionante
- Sistema multi-database preparato
- .gitignore aggiornato per sicurezza
- Migration cleanup e ottimizzazione

📚 Documentazione:
- Guida installazione Linux completa
- Specifiche tecniche dettagliate
- Sistema aggiornamenti progettato
- Progress log aggiornato
2025-07-07 17:24:30 +02:00

864 lines
25 KiB
Markdown

# 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*