1379 lines
39 KiB
Markdown
Executable File
1379 lines
39 KiB
Markdown
Executable File
# 🛠️ TROUBLESHOOTING - Guida Completa NetGescon
|
|
|
|
> **Guida alla risoluzione problemi dell'interfaccia universale**
|
|
> **📅 Creato:** 21/07/2025
|
|
> **🔄 Ultimo aggiornamento:** 21/07/2025
|
|
> **🎯 Priorità:** ⚡ CRITICO
|
|
|
|
---
|
|
|
|
## 📋 **OVERVIEW TROUBLESHOOTING**
|
|
|
|
Questa guida fornisce **SOLUZIONI IMMEDIATE** per:
|
|
- 🚨 **Problemi Critici** - Sistema non funzionante
|
|
- ⚠️ **Problemi Comuni** - Errori frequenti dell'interfaccia
|
|
- 🔧 **Procedure di Recovery** - Ripristino rapido del sistema
|
|
- 🧪 **Test e Validazione** - Verifiche post-risoluzione
|
|
- 📊 **Monitoring e Prevenzione** - Evitare problemi futuri
|
|
|
|
### 🎯 **Filosofia del Troubleshooting**
|
|
- **Diagnosi Rapida** - Identificazione immediata del problema
|
|
- **Soluzioni Incrementali** - Dal più semplice al più complesso
|
|
- **Backup Safety** - Sempre conservare stati funzionanti
|
|
- **Documentazione Completa** - Tracciare ogni intervento
|
|
|
|
---
|
|
|
|
## 🚨 **PROBLEMI CRITICI**
|
|
|
|
### 💥 **Interfaccia Completamente Rotta**
|
|
|
|
#### 🔍 **Sintomi:**
|
|
- Schermata bianca/nera
|
|
- JavaScript errors nel console
|
|
- CSS non caricato
|
|
- Layout completamente distorto
|
|
|
|
#### 🚑 **Soluzione Immediata:**
|
|
```bash
|
|
# 1. Backup immediato dello stato attuale
|
|
cp -r /var/www/netgescon /var/www/netgescon_backup_$(date +%Y%m%d_%H%M%S)
|
|
|
|
# 2. Ripristino da backup funzionante
|
|
cp -r /var/www/netgescon_backup_working/* /var/www/netgescon/
|
|
|
|
# 3. Clear cache Laravel
|
|
cd /var/www/netgescon
|
|
php artisan cache:clear
|
|
php artisan config:clear
|
|
php artisan view:clear
|
|
php artisan route:clear
|
|
|
|
# 4. Rebuild assets
|
|
npm run build
|
|
|
|
# 5. Fix permissions
|
|
sudo chown -R www-data:www-data /var/www/netgescon
|
|
sudo chmod -R 755 /var/www/netgescon
|
|
```
|
|
|
|
#### 🧪 **Verifica:**
|
|
```bash
|
|
# Test basic functionality
|
|
curl -I http://localhost:8000
|
|
php artisan route:list | grep admin
|
|
npm run dev --dry-run
|
|
```
|
|
|
|
---
|
|
|
|
### 🎨 **CSS Non Caricato / Stili Mancanti**
|
|
|
|
#### 🔍 **Sintomi:**
|
|
- Layout senza stili
|
|
- Elementi sovrapposti
|
|
- Colori di default del browser
|
|
- Font non applicati
|
|
|
|
#### 🚑 **Soluzione Immediata:**
|
|
```bash
|
|
# 1. Check if CSS files exist
|
|
ls -la /var/www/netgescon/public/css/
|
|
ls -la /var/www/netgescon/public/build/
|
|
|
|
# 2. Rebuild CSS
|
|
cd /var/www/netgescon
|
|
npm run build
|
|
|
|
# 3. Check for compilation errors
|
|
npm run dev
|
|
|
|
# 4. Force browser cache clear
|
|
# Add timestamp to CSS includes
|
|
```
|
|
|
|
#### 🛠️ **Soluzione Completa:**
|
|
```blade
|
|
<!-- In layouts/admin.blade.php -->
|
|
@if(app()->environment('local'))
|
|
<!-- Development mode -->
|
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
|
@else
|
|
<!-- Production mode with cache busting -->
|
|
<link rel="stylesheet" href="{{ asset('css/netgescon-admin.css') }}?v={{ filemtime(public_path('css/netgescon-admin.css')) }}">
|
|
<script src="{{ asset('js/netgescon-admin.js') }}?v={{ filemtime(public_path('js/netgescon-admin.js')) }}"></script>
|
|
@endif
|
|
```
|
|
|
|
---
|
|
|
|
### ⚡ **JavaScript Non Funzionante**
|
|
|
|
#### 🔍 **Sintomi:**
|
|
- Sidebar non si apre/chiude
|
|
- Form non validano
|
|
- AJAX requests falliscono
|
|
- Animazioni non funzionano
|
|
|
|
#### 🚑 **Debug JavaScript:**
|
|
```javascript
|
|
// Add to console for immediate debugging
|
|
console.log('NetGescon Debug Info:');
|
|
console.log('App initialized:', window.NetGesconApp?.initialized);
|
|
console.log('jQuery loaded:', typeof $ !== 'undefined');
|
|
console.log('Bootstrap loaded:', typeof bootstrap !== 'undefined');
|
|
console.log('Config:', window.NetGesconConfig?.all());
|
|
console.log('Permissions:', window.PermissionManager?.permissions);
|
|
|
|
// Check for JavaScript errors
|
|
window.addEventListener('error', (e) => {
|
|
console.error('JavaScript Error:', e.error);
|
|
// Send to monitoring if available
|
|
if (window.Sentry) {
|
|
window.Sentry.captureException(e.error);
|
|
}
|
|
});
|
|
```
|
|
|
|
#### 🛠️ **Fix JavaScript Issues:**
|
|
```bash
|
|
# 1. Check JavaScript compilation
|
|
cd /var/www/netgescon
|
|
npm run build
|
|
|
|
# 2. Check for syntax errors
|
|
npm run lint
|
|
|
|
# 3. Test individual components
|
|
node -e "console.log('Node.js working')"
|
|
|
|
# 4. Rebuild node_modules if needed
|
|
rm -rf node_modules package-lock.json
|
|
npm install
|
|
npm run build
|
|
```
|
|
|
|
---
|
|
|
|
## ⚠️ **PROBLEMI COMUNI**
|
|
|
|
### 📱 **Sidebar Non Funziona**
|
|
|
|
#### 🔍 **Sintomi:**
|
|
- Sidebar non si apre su mobile
|
|
- Toggle button non risponde
|
|
- Overlay non appare
|
|
|
|
#### 🚑 **Fix Rapido:**
|
|
```javascript
|
|
// Emergency sidebar fix
|
|
function fixSidebar() {
|
|
const sidebar = document.getElementById('sidebar');
|
|
const toggle = document.getElementById('mobile-nav-toggle');
|
|
const overlay = document.getElementById('sidebar-overlay');
|
|
|
|
if (!sidebar || !toggle) {
|
|
console.error('Sidebar elements missing');
|
|
return;
|
|
}
|
|
|
|
// Create overlay if missing
|
|
if (!overlay) {
|
|
const newOverlay = document.createElement('div');
|
|
newOverlay.id = 'sidebar-overlay';
|
|
newOverlay.className = 'sidebar-overlay';
|
|
document.body.appendChild(newOverlay);
|
|
}
|
|
|
|
// Fix event listeners
|
|
toggle.addEventListener('click', () => {
|
|
sidebar.classList.toggle('show');
|
|
document.getElementById('sidebar-overlay').classList.toggle('show');
|
|
});
|
|
|
|
document.getElementById('sidebar-overlay').addEventListener('click', () => {
|
|
sidebar.classList.remove('show');
|
|
document.getElementById('sidebar-overlay').classList.remove('show');
|
|
});
|
|
}
|
|
|
|
// Run fix
|
|
fixSidebar();
|
|
```
|
|
|
|
---
|
|
|
|
### 📊 **Tabelle Non Responsive**
|
|
|
|
#### 🔍 **Sintomi:**
|
|
- Tabelle escono dal container
|
|
- Scroll orizzontale problematico
|
|
- Mobile view non funziona
|
|
|
|
#### 🚑 **Fix Tabelle:**
|
|
```javascript
|
|
// Emergency table fix
|
|
function fixTables() {
|
|
const tables = document.querySelectorAll('.table-responsive-mobile');
|
|
|
|
tables.forEach(container => {
|
|
const table = container.querySelector('table');
|
|
if (!table) return;
|
|
|
|
// Add horizontal scroll indicator
|
|
if (!container.querySelector('.scroll-indicator')) {
|
|
const indicator = document.createElement('div');
|
|
indicator.className = 'scroll-indicator';
|
|
indicator.innerHTML = '<i class="fas fa-arrow-right"></i> Scorri per vedere più colonne';
|
|
indicator.style.cssText = `
|
|
position: absolute;
|
|
top: 50%;
|
|
right: 10px;
|
|
background: rgba(0,0,0,0.7);
|
|
color: white;
|
|
padding: 5px 10px;
|
|
border-radius: 15px;
|
|
font-size: 12px;
|
|
pointer-events: none;
|
|
z-index: 10;
|
|
`;
|
|
container.style.position = 'relative';
|
|
container.appendChild(indicator);
|
|
|
|
// Hide on scroll
|
|
container.addEventListener('scroll', () => {
|
|
const isScrolledToEnd = container.scrollLeft >= (container.scrollWidth - container.clientWidth - 10);
|
|
indicator.style.opacity = isScrolledToEnd ? '0' : '1';
|
|
});
|
|
}
|
|
|
|
// Mobile stack view for very small screens
|
|
if (window.innerWidth <= 576 && !container.querySelector('.mobile-table-stack')) {
|
|
createMobileTableStack(container, table);
|
|
}
|
|
});
|
|
}
|
|
|
|
function createMobileTableStack(container, table) {
|
|
const mobileStack = document.createElement('div');
|
|
mobileStack.className = 'mobile-table-stack';
|
|
mobileStack.style.display = window.innerWidth <= 576 ? 'block' : 'none';
|
|
|
|
const headers = Array.from(table.querySelectorAll('thead th')).map(th => th.textContent.trim());
|
|
const rows = table.querySelectorAll('tbody tr');
|
|
|
|
rows.forEach(row => {
|
|
const cells = row.querySelectorAll('td');
|
|
const mobileItem = document.createElement('div');
|
|
mobileItem.className = 'mobile-table-item';
|
|
mobileItem.style.cssText = `
|
|
background: white;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 8px;
|
|
padding: 1rem;
|
|
margin-bottom: 1rem;
|
|
`;
|
|
|
|
cells.forEach((cell, index) => {
|
|
const itemRow = document.createElement('div');
|
|
itemRow.style.cssText = `
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 0.25rem 0;
|
|
border-bottom: 1px solid #f1f5f9;
|
|
`;
|
|
|
|
const label = document.createElement('div');
|
|
label.style.cssText = `
|
|
font-weight: 500;
|
|
color: #64748b;
|
|
font-size: 0.875rem;
|
|
`;
|
|
label.textContent = headers[index] || `Campo ${index + 1}`;
|
|
|
|
const value = document.createElement('div');
|
|
value.style.cssText = `
|
|
color: #0f172a;
|
|
text-align: right;
|
|
`;
|
|
value.innerHTML = cell.innerHTML;
|
|
|
|
itemRow.appendChild(label);
|
|
itemRow.appendChild(value);
|
|
mobileItem.appendChild(itemRow);
|
|
});
|
|
|
|
mobileStack.appendChild(mobileItem);
|
|
});
|
|
|
|
container.appendChild(mobileStack);
|
|
|
|
// Hide original table on mobile
|
|
table.style.display = window.innerWidth <= 576 ? 'none' : 'table';
|
|
}
|
|
|
|
// Run fix
|
|
fixTables();
|
|
```
|
|
|
|
---
|
|
|
|
### 🔐 **Permessi Non Funzionano**
|
|
|
|
#### 🔍 **Sintomi:**
|
|
- Elementi visibili che non dovrebbero esserlo
|
|
- Azioni bloccate quando dovrebbero essere permesse
|
|
- Errori 403 inaspettati
|
|
|
|
#### 🚑 **Debug Permessi:**
|
|
```javascript
|
|
// Permission debugging tool
|
|
function debugPermissions() {
|
|
console.log('=== PERMISSION DEBUG ===');
|
|
|
|
// Check user data
|
|
const user = window.PermissionManager?.user;
|
|
console.log('User:', user);
|
|
console.log('Role:', user?.role);
|
|
|
|
// Check permissions
|
|
const permissions = window.PermissionManager?.permissions;
|
|
console.log('Permissions:', permissions);
|
|
|
|
// Test common permissions
|
|
const testResources = ['stabili', 'condomini', 'unita', 'reports', 'users'];
|
|
const testActions = ['read', 'create', 'update', 'delete'];
|
|
|
|
testResources.forEach(resource => {
|
|
console.log(`\n--- ${resource.toUpperCase()} ---`);
|
|
testActions.forEach(action => {
|
|
const hasPermission = window.PermissionManager?.can(resource, action);
|
|
console.log(`${action}: ${hasPermission ? '✅' : '❌'}`);
|
|
});
|
|
});
|
|
|
|
// Check problematic elements
|
|
const permissionElements = document.querySelectorAll('[data-permission], [data-role]');
|
|
console.log(`\nFound ${permissionElements.length} permission-controlled elements`);
|
|
|
|
permissionElements.forEach((el, index) => {
|
|
const permission = el.dataset.permission;
|
|
const role = el.dataset.role;
|
|
const action = el.dataset.action || 'read';
|
|
const visible = el.style.display !== 'none' && !el.hasAttribute('aria-hidden');
|
|
|
|
console.log(`Element ${index + 1}:`, {
|
|
tag: el.tagName,
|
|
permission,
|
|
role,
|
|
action,
|
|
visible,
|
|
shouldBeVisible: permission ? window.PermissionManager?.can(permission, action) : true
|
|
});
|
|
});
|
|
}
|
|
|
|
// Run debug
|
|
debugPermissions();
|
|
```
|
|
|
|
#### 🛠️ **Fix Permessi:**
|
|
```php
|
|
// Add to routes/web.php for emergency permission reset
|
|
Route::get('/debug/permissions/{user}', function($userId) {
|
|
if (!auth()->user()->hasRole('super_admin')) {
|
|
abort(403);
|
|
}
|
|
|
|
$user = User::findOrFail($userId);
|
|
|
|
return response()->json([
|
|
'user' => $user,
|
|
'role' => $user->getRole(),
|
|
'permissions' => $user->permissions,
|
|
'default_level' => $user->getDefaultPermissionLevel(),
|
|
'test_permissions' => [
|
|
'stabili' => [
|
|
'read' => $user->getPermissionLevel('stabili'),
|
|
'create' => $user->getPermissionLevel('stabili'),
|
|
'update' => $user->getPermissionLevel('stabili'),
|
|
'delete' => $user->getPermissionLevel('stabili'),
|
|
]
|
|
]
|
|
]);
|
|
})->middleware(['auth', 'role:super_admin']);
|
|
```
|
|
|
|
---
|
|
|
|
### 📱 **Problemi Mobile**
|
|
|
|
#### 🔍 **Sintomi:**
|
|
- Layout rotto su mobile
|
|
- Touch non funziona
|
|
- Font troppo piccoli
|
|
- Elementi non accessibili
|
|
|
|
#### 🚑 **Fix Mobile:**
|
|
```css
|
|
/* Emergency mobile fixes */
|
|
@media (max-width: 767px) {
|
|
/* Ensure minimum touch targets */
|
|
.btn, .nav-link, .form-input, .form-select {
|
|
min-height: 44px !important;
|
|
min-width: 44px !important;
|
|
}
|
|
|
|
/* Fix font sizes to prevent zoom */
|
|
input, select, textarea {
|
|
font-size: 16px !important;
|
|
}
|
|
|
|
/* Fix overlapping elements */
|
|
.netgescon-content {
|
|
padding: 1rem !important;
|
|
margin-top: 60px !important;
|
|
}
|
|
|
|
/* Fix sidebar on mobile */
|
|
.netgescon-sidebar {
|
|
position: fixed !important;
|
|
top: 60px !important;
|
|
left: -280px !important;
|
|
width: 280px !important;
|
|
height: calc(100vh - 60px) !important;
|
|
z-index: 999 !important;
|
|
transition: left 0.3s ease !important;
|
|
}
|
|
|
|
.netgescon-sidebar.show {
|
|
left: 0 !important;
|
|
}
|
|
|
|
/* Fix tables */
|
|
.table-responsive-mobile {
|
|
overflow-x: auto !important;
|
|
-webkit-overflow-scrolling: touch !important;
|
|
}
|
|
|
|
/* Fix cards */
|
|
.netgescon-card {
|
|
margin-bottom: 1rem !important;
|
|
padding: 1rem !important;
|
|
}
|
|
|
|
/* Fix buttons */
|
|
.btn-group {
|
|
flex-direction: column !important;
|
|
width: 100% !important;
|
|
}
|
|
|
|
.btn-group .btn {
|
|
width: 100% !important;
|
|
margin-bottom: 0.5rem !important;
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 **PROCEDURE DI RECOVERY**
|
|
|
|
### 💾 **Backup e Restore**
|
|
|
|
#### 📁 **Backup Completo:**
|
|
```bash
|
|
#!/bin/bash
|
|
# create-backup.sh
|
|
|
|
DATE=$(date +%Y%m%d_%H%M%S)
|
|
BACKUP_DIR="/var/backups/netgescon"
|
|
SOURCE_DIR="/var/www/netgescon"
|
|
|
|
# Create backup directory
|
|
mkdir -p $BACKUP_DIR
|
|
|
|
# Backup files
|
|
echo "Backing up files..."
|
|
tar -czf "$BACKUP_DIR/netgescon_files_$DATE.tar.gz" -C "$SOURCE_DIR" .
|
|
|
|
# Backup database
|
|
echo "Backing up database..."
|
|
mysqldump -u root -p netgescon > "$BACKUP_DIR/netgescon_db_$DATE.sql"
|
|
|
|
# Backup config files
|
|
echo "Backing up configs..."
|
|
cp /etc/nginx/sites-available/netgescon "$BACKUP_DIR/nginx_config_$DATE"
|
|
cp /etc/php/8.2/fpm/pool.d/netgescon.conf "$BACKUP_DIR/php_config_$DATE"
|
|
|
|
# Create restore script
|
|
cat > "$BACKUP_DIR/restore_$DATE.sh" << EOF
|
|
#!/bin/bash
|
|
echo "Restoring NetGescon from backup $DATE..."
|
|
|
|
# Stop services
|
|
sudo systemctl stop nginx php8.2-fpm
|
|
|
|
# Restore files
|
|
sudo rm -rf /var/www/netgescon/*
|
|
sudo tar -xzf "$BACKUP_DIR/netgescon_files_$DATE.tar.gz" -C /var/www/netgescon/
|
|
|
|
# Restore database
|
|
mysql -u root -p netgescon < "$BACKUP_DIR/netgescon_db_$DATE.sql"
|
|
|
|
# Fix permissions
|
|
sudo chown -R www-data:www-data /var/www/netgescon
|
|
sudo chmod -R 755 /var/www/netgescon
|
|
sudo chmod -R 775 /var/www/netgescon/storage
|
|
sudo chmod -R 775 /var/www/netgescon/bootstrap/cache
|
|
|
|
# Restart services
|
|
sudo systemctl start php8.2-fpm nginx
|
|
|
|
echo "Restore completed!"
|
|
EOF
|
|
|
|
chmod +x "$BACKUP_DIR/restore_$DATE.sh"
|
|
|
|
echo "Backup completed: $BACKUP_DIR"
|
|
echo "To restore: sudo bash $BACKUP_DIR/restore_$DATE.sh"
|
|
```
|
|
|
|
#### 🔄 **Restore da Backup:**
|
|
```bash
|
|
#!/bin/bash
|
|
# quick-restore.sh
|
|
|
|
# Find latest backup
|
|
LATEST_BACKUP=$(ls -t /var/backups/netgescon/netgescon_files_*.tar.gz | head -1)
|
|
LATEST_DB=$(ls -t /var/backups/netgescon/netgescon_db_*.sql | head -1)
|
|
|
|
if [ -z "$LATEST_BACKUP" ]; then
|
|
echo "❌ No backup found!"
|
|
exit 1
|
|
fi
|
|
|
|
echo "🔄 Restoring from: $LATEST_BACKUP"
|
|
|
|
# Backup current state first
|
|
cp -r /var/www/netgescon "/var/www/netgescon_before_restore_$(date +%Y%m%d_%H%M%S)"
|
|
|
|
# Stop services
|
|
sudo systemctl stop nginx php8.2-fpm
|
|
|
|
# Restore files
|
|
sudo rm -rf /var/www/netgescon/*
|
|
sudo tar -xzf "$LATEST_BACKUP" -C /var/www/netgescon/
|
|
|
|
# Restore database if available
|
|
if [ -n "$LATEST_DB" ]; then
|
|
echo "🗄️ Restoring database..."
|
|
mysql -u root -p netgescon < "$LATEST_DB"
|
|
fi
|
|
|
|
# Clear all caches
|
|
cd /var/www/netgescon
|
|
php artisan cache:clear
|
|
php artisan config:clear
|
|
php artisan view:clear
|
|
php artisan route:clear
|
|
|
|
# Fix permissions
|
|
sudo chown -R www-data:www-data /var/www/netgescon
|
|
sudo chmod -R 755 /var/www/netgescon
|
|
sudo chmod -R 775 /var/www/netgescon/storage
|
|
sudo chmod -R 775 /var/www/netgescon/bootstrap/cache
|
|
|
|
# Restart services
|
|
sudo systemctl start php8.2-fpm nginx
|
|
|
|
echo "✅ Restore completed!"
|
|
echo "🧪 Test the application: http://localhost:8000"
|
|
```
|
|
|
|
---
|
|
|
|
### 🔧 **Reset Completo Sistema**
|
|
|
|
#### 🚨 **Reset Interfaccia (EMERGENCY):**
|
|
```bash
|
|
#!/bin/bash
|
|
# emergency-reset.sh
|
|
|
|
echo "🚨 EMERGENCY RESET - This will reset the interface to default state"
|
|
read -p "Are you sure? (type 'RESET' to confirm): " confirm
|
|
|
|
if [ "$confirm" != "RESET" ]; then
|
|
echo "❌ Reset cancelled"
|
|
exit 1
|
|
fi
|
|
|
|
# 1. Backup current state
|
|
BACKUP_DIR="/tmp/emergency_backup_$(date +%Y%m%d_%H%M%S)"
|
|
cp -r /var/www/netgescon "$BACKUP_DIR"
|
|
echo "📁 Current state backed up to: $BACKUP_DIR"
|
|
|
|
# 2. Reset to clean Laravel installation
|
|
cd /var/www/netgescon
|
|
|
|
# Clear all caches
|
|
php artisan cache:clear
|
|
php artisan config:clear
|
|
php artisan view:clear
|
|
php artisan route:clear
|
|
php artisan storage:link
|
|
|
|
# Reset config files
|
|
cp .env.example .env
|
|
php artisan key:generate
|
|
|
|
# 3. Reset database (if needed)
|
|
read -p "Reset database too? (y/N): " reset_db
|
|
if [ "$reset_db" = "y" ] || [ "$reset_db" = "Y" ]; then
|
|
php artisan migrate:fresh --seed
|
|
fi
|
|
|
|
# 4. Rebuild assets
|
|
npm install
|
|
npm run build
|
|
|
|
# 5. Fix permissions
|
|
sudo chown -R www-data:www-data /var/www/netgescon
|
|
sudo chmod -R 755 /var/www/netgescon
|
|
sudo chmod -R 775 /var/www/netgescon/storage
|
|
sudo chmod -R 775 /var/www/netgescon/bootstrap/cache
|
|
|
|
# 6. Restart services
|
|
sudo systemctl restart nginx php8.2-fpm
|
|
|
|
echo "✅ Emergency reset completed!"
|
|
echo "🔧 Configure your .env file and test the application"
|
|
echo "📁 Previous state backed up to: $BACKUP_DIR"
|
|
```
|
|
|
|
---
|
|
|
|
## 🧪 **TEST E VALIDAZIONE**
|
|
|
|
### ✅ **Test Rapidi Post-Fix**
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# quick-tests.sh
|
|
|
|
echo "🧪 Running Quick Validation Tests..."
|
|
|
|
# 1. Test web server
|
|
echo "📡 Testing web server..."
|
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8000)
|
|
if [ "$HTTP_CODE" = "200" ]; then
|
|
echo "✅ Web server responding"
|
|
else
|
|
echo "❌ Web server not responding (HTTP $HTTP_CODE)"
|
|
fi
|
|
|
|
# 2. Test database connection
|
|
echo "🗄️ Testing database..."
|
|
cd /var/www/netgescon
|
|
if php artisan tinker --execute="DB::connection()->getPdo(); echo 'DB Connected';" 2>/dev/null | grep -q "DB Connected"; then
|
|
echo "✅ Database connection working"
|
|
else
|
|
echo "❌ Database connection failed"
|
|
fi
|
|
|
|
# 3. Test assets
|
|
echo "🎨 Testing assets..."
|
|
if [ -f "public/css/netgescon-admin.css" ] && [ -f "public/js/netgescon-admin.js" ]; then
|
|
echo "✅ Assets compiled"
|
|
else
|
|
echo "❌ Assets missing"
|
|
fi
|
|
|
|
# 4. Test permissions
|
|
echo "🔐 Testing file permissions..."
|
|
if [ -w "storage/logs" ] && [ -w "bootstrap/cache" ]; then
|
|
echo "✅ File permissions correct"
|
|
else
|
|
echo "❌ File permissions incorrect"
|
|
fi
|
|
|
|
# 5. Test critical routes
|
|
echo "🛣️ Testing critical routes..."
|
|
ROUTES=$(php artisan route:list --compact | grep -E "(admin|login|dashboard)" | wc -l)
|
|
if [ "$ROUTES" -gt "0" ]; then
|
|
echo "✅ Routes registered ($ROUTES found)"
|
|
else
|
|
echo "❌ No routes found"
|
|
fi
|
|
|
|
echo "🏁 Quick tests completed!"
|
|
```
|
|
|
|
### 🔍 **Test Completi Interfaccia**
|
|
|
|
```javascript
|
|
// Complete interface test suite
|
|
class InterfaceTests {
|
|
constructor() {
|
|
this.results = [];
|
|
this.runTests();
|
|
}
|
|
|
|
async runTests() {
|
|
console.log('🧪 Running Complete Interface Tests...');
|
|
|
|
const tests = [
|
|
() => this.testPageLoad(),
|
|
() => this.testCSS(),
|
|
() => this.testJavaScript(),
|
|
() => this.testSidebar(),
|
|
() => this.testResponsive(),
|
|
() => this.testForms(),
|
|
() => this.testTables(),
|
|
() => this.testPermissions(),
|
|
() => this.testAccessibility(),
|
|
() => this.testPerformance()
|
|
];
|
|
|
|
for (const test of tests) {
|
|
try {
|
|
const result = await test();
|
|
this.results.push(result);
|
|
this.logResult(result);
|
|
} catch (error) {
|
|
const result = { name: test.name, passed: false, error: error.message };
|
|
this.results.push(result);
|
|
this.logResult(result);
|
|
}
|
|
}
|
|
|
|
this.generateReport();
|
|
}
|
|
|
|
testPageLoad() {
|
|
const start = performance.now();
|
|
const loaded = document.readyState === 'complete';
|
|
const loadTime = performance.now() - start;
|
|
|
|
return {
|
|
name: 'Page Load',
|
|
passed: loaded && loadTime < 3000,
|
|
details: { loaded, loadTime: `${loadTime.toFixed(2)}ms` }
|
|
};
|
|
}
|
|
|
|
testCSS() {
|
|
const stylesheets = document.querySelectorAll('link[rel="stylesheet"]');
|
|
const hasNetGesconCSS = Array.from(stylesheets).some(link =>
|
|
link.href.includes('netgescon-admin.css')
|
|
);
|
|
|
|
// Test if critical styles are applied
|
|
const body = document.body;
|
|
const computedStyle = getComputedStyle(body);
|
|
const hasStyles = computedStyle.fontFamily !== 'serif'; // Default would be serif
|
|
|
|
return {
|
|
name: 'CSS Loading',
|
|
passed: hasNetGesconCSS && hasStyles,
|
|
details: {
|
|
stylesheets: stylesheets.length,
|
|
hasNetGesconCSS,
|
|
hasStyles,
|
|
fontFamily: computedStyle.fontFamily
|
|
}
|
|
};
|
|
}
|
|
|
|
testJavaScript() {
|
|
const hasNetGesconApp = typeof window.NetGesconApp !== 'undefined';
|
|
const hasPermissionManager = typeof window.PermissionManager !== 'undefined';
|
|
const hasBootstrap = typeof window.bootstrap !== 'undefined';
|
|
|
|
return {
|
|
name: 'JavaScript Loading',
|
|
passed: hasNetGesconApp && hasPermissionManager && hasBootstrap,
|
|
details: { hasNetGesconApp, hasPermissionManager, hasBootstrap }
|
|
};
|
|
}
|
|
|
|
testSidebar() {
|
|
const sidebar = document.getElementById('sidebar');
|
|
const toggle = document.getElementById('mobile-nav-toggle');
|
|
|
|
if (!sidebar) {
|
|
return { name: 'Sidebar', passed: false, details: { missing: 'sidebar element' } };
|
|
}
|
|
|
|
// Test toggle functionality
|
|
const originalClass = sidebar.className;
|
|
sidebar.classList.add('show');
|
|
const canToggle = sidebar.classList.contains('show');
|
|
sidebar.className = originalClass;
|
|
|
|
return {
|
|
name: 'Sidebar',
|
|
passed: canToggle && (window.innerWidth >= 768 || toggle),
|
|
details: {
|
|
hasSidebar: !!sidebar,
|
|
hasToggle: !!toggle,
|
|
canToggle
|
|
}
|
|
};
|
|
}
|
|
|
|
testResponsive() {
|
|
const viewport = window.innerWidth;
|
|
const isMobile = viewport < 768;
|
|
const isTablet = viewport >= 768 && viewport < 1024;
|
|
const isDesktop = viewport >= 1024;
|
|
|
|
// Test grid systems
|
|
const grids = document.querySelectorAll('.grid, .netgescon-grid, .stats-grid');
|
|
const hasGrids = grids.length > 0;
|
|
|
|
return {
|
|
name: 'Responsive',
|
|
passed: hasGrids,
|
|
details: {
|
|
viewport,
|
|
isMobile,
|
|
isTablet,
|
|
isDesktop,
|
|
grids: grids.length
|
|
}
|
|
};
|
|
}
|
|
|
|
testForms() {
|
|
const forms = document.querySelectorAll('form');
|
|
const inputs = document.querySelectorAll('input, select, textarea');
|
|
|
|
// Test form validation classes
|
|
const hasValidationClasses = Array.from(inputs).some(input =>
|
|
input.classList.contains('form-input') ||
|
|
input.classList.contains('form-control')
|
|
);
|
|
|
|
return {
|
|
name: 'Forms',
|
|
passed: forms.length > 0 && hasValidationClasses,
|
|
details: {
|
|
forms: forms.length,
|
|
inputs: inputs.length,
|
|
hasValidationClasses
|
|
}
|
|
};
|
|
}
|
|
|
|
testTables() {
|
|
const tables = document.querySelectorAll('table');
|
|
const responsiveTables = document.querySelectorAll('.table-responsive, .table-responsive-mobile');
|
|
|
|
return {
|
|
name: 'Tables',
|
|
passed: tables.length === 0 || responsiveTables.length > 0,
|
|
details: {
|
|
tables: tables.length,
|
|
responsiveTables: responsiveTables.length
|
|
}
|
|
};
|
|
}
|
|
|
|
testPermissions() {
|
|
const permissionElements = document.querySelectorAll('[data-permission], [data-role]');
|
|
const hasPermissionManager = typeof window.PermissionManager !== 'undefined';
|
|
|
|
return {
|
|
name: 'Permissions',
|
|
passed: hasPermissionManager,
|
|
details: {
|
|
permissionElements: permissionElements.length,
|
|
hasPermissionManager
|
|
}
|
|
};
|
|
}
|
|
|
|
testAccessibility() {
|
|
// Test for basic accessibility features
|
|
const images = document.querySelectorAll('img');
|
|
const imagesWithAlt = Array.from(images).filter(img => img.hasAttribute('alt'));
|
|
|
|
const buttons = document.querySelectorAll('button');
|
|
const buttonsWithAriaLabel = Array.from(buttons).filter(btn =>
|
|
btn.hasAttribute('aria-label') || btn.textContent.trim()
|
|
);
|
|
|
|
const altRatio = images.length ? imagesWithAlt.length / images.length : 1;
|
|
const buttonRatio = buttons.length ? buttonsWithAriaLabel.length / buttons.length : 1;
|
|
|
|
return {
|
|
name: 'Accessibility',
|
|
passed: altRatio >= 0.8 && buttonRatio >= 0.8,
|
|
details: {
|
|
altRatio: `${(altRatio * 100).toFixed(1)}%`,
|
|
buttonRatio: `${(buttonRatio * 100).toFixed(1)}%`
|
|
}
|
|
};
|
|
}
|
|
|
|
testPerformance() {
|
|
const navigation = performance.getEntriesByType('navigation')[0];
|
|
const loadTime = navigation ? navigation.loadEventEnd - navigation.fetchStart : 0;
|
|
|
|
const resources = performance.getEntriesByType('resource');
|
|
const slowResources = resources.filter(r => r.duration > 1000);
|
|
|
|
return {
|
|
name: 'Performance',
|
|
passed: loadTime < 5000 && slowResources.length < 3,
|
|
details: {
|
|
loadTime: `${loadTime.toFixed(2)}ms`,
|
|
totalResources: resources.length,
|
|
slowResources: slowResources.length
|
|
}
|
|
};
|
|
}
|
|
|
|
logResult(result) {
|
|
const status = result.passed ? '✅' : '❌';
|
|
console.log(`${status} ${result.name}:`, result.details);
|
|
|
|
if (result.error) {
|
|
console.error(` Error: ${result.error}`);
|
|
}
|
|
}
|
|
|
|
generateReport() {
|
|
const passed = this.results.filter(r => r.passed).length;
|
|
const total = this.results.length;
|
|
const passRate = (passed / total * 100).toFixed(1);
|
|
|
|
console.log('\n📊 Test Summary:');
|
|
console.log(`Pass Rate: ${passRate}% (${passed}/${total})`);
|
|
|
|
const failed = this.results.filter(r => !r.passed);
|
|
if (failed.length > 0) {
|
|
console.log('\n❌ Failed Tests:');
|
|
failed.forEach(test => {
|
|
console.log(` - ${test.name}: ${test.error || 'Failed validation'}`);
|
|
});
|
|
}
|
|
|
|
// Store results for later analysis
|
|
window.InterfaceTestResults = {
|
|
timestamp: new Date().toISOString(),
|
|
passRate,
|
|
results: this.results
|
|
};
|
|
}
|
|
}
|
|
|
|
// Run tests automatically in debug mode
|
|
if (window.location.search.includes('test=interface')) {
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
setTimeout(() => new InterfaceTests(), 2000);
|
|
});
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 **MONITORING E PREVENZIONE**
|
|
|
|
### 📈 **Sistema di Monitoring**
|
|
|
|
```javascript
|
|
// /public/js/monitoring/health-check.js
|
|
|
|
class HealthMonitor {
|
|
constructor() {
|
|
this.checks = new Map();
|
|
this.alerts = [];
|
|
this.interval = 30000; // 30 seconds
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
this.setupChecks();
|
|
this.startMonitoring();
|
|
this.setupErrorReporting();
|
|
}
|
|
|
|
setupChecks() {
|
|
// Add health checks
|
|
this.addCheck('javascript', () => this.checkJavaScript());
|
|
this.addCheck('css', () => this.checkCSS());
|
|
this.addCheck('api', () => this.checkAPI());
|
|
this.addCheck('permissions', () => this.checkPermissions());
|
|
this.addCheck('performance', () => this.checkPerformance());
|
|
}
|
|
|
|
addCheck(name, checkFn) {
|
|
this.checks.set(name, {
|
|
name,
|
|
check: checkFn,
|
|
lastRun: null,
|
|
lastResult: null,
|
|
consecutiveFailures: 0
|
|
});
|
|
}
|
|
|
|
async startMonitoring() {
|
|
setInterval(async () => {
|
|
await this.runAllChecks();
|
|
}, this.interval);
|
|
|
|
// Run initial checks
|
|
await this.runAllChecks();
|
|
}
|
|
|
|
async runAllChecks() {
|
|
for (const [name, checkData] of this.checks) {
|
|
try {
|
|
const result = await checkData.check();
|
|
this.handleCheckResult(name, result);
|
|
} catch (error) {
|
|
this.handleCheckResult(name, {
|
|
healthy: false,
|
|
error: error.message
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
handleCheckResult(name, result) {
|
|
const checkData = this.checks.get(name);
|
|
checkData.lastRun = Date.now();
|
|
checkData.lastResult = result;
|
|
|
|
if (result.healthy) {
|
|
checkData.consecutiveFailures = 0;
|
|
} else {
|
|
checkData.consecutiveFailures++;
|
|
|
|
// Alert after 3 consecutive failures
|
|
if (checkData.consecutiveFailures >= 3) {
|
|
this.createAlert(name, result);
|
|
}
|
|
}
|
|
}
|
|
|
|
createAlert(checkName, result) {
|
|
const alert = {
|
|
id: Date.now(),
|
|
timestamp: new Date().toISOString(),
|
|
check: checkName,
|
|
message: result.error || `Health check ${checkName} failed`,
|
|
severity: result.severity || 'warning',
|
|
data: result
|
|
};
|
|
|
|
this.alerts.push(alert);
|
|
this.notifyAlert(alert);
|
|
|
|
// Keep only last 100 alerts
|
|
if (this.alerts.length > 100) {
|
|
this.alerts = this.alerts.slice(-100);
|
|
}
|
|
}
|
|
|
|
notifyAlert(alert) {
|
|
console.warn(`🚨 Health Alert [${alert.check}]:`, alert.message);
|
|
|
|
// Send to external monitoring if available
|
|
if (window.Sentry) {
|
|
window.Sentry.captureMessage(alert.message, alert.severity);
|
|
}
|
|
|
|
// Show user notification for critical alerts
|
|
if (alert.severity === 'critical' && window.NetGesconNotifications) {
|
|
NetGesconNotifications.error(
|
|
`Sistema: ${alert.message}`,
|
|
{ duration: 10000 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// Health check functions
|
|
checkJavaScript() {
|
|
const errors = [];
|
|
|
|
// Check core objects
|
|
if (typeof window.NetGesconApp === 'undefined') {
|
|
errors.push('NetGesconApp not loaded');
|
|
}
|
|
|
|
if (typeof window.PermissionManager === 'undefined') {
|
|
errors.push('PermissionManager not loaded');
|
|
}
|
|
|
|
if (typeof window.bootstrap === 'undefined') {
|
|
errors.push('Bootstrap not loaded');
|
|
}
|
|
|
|
return {
|
|
healthy: errors.length === 0,
|
|
errors,
|
|
severity: errors.length > 0 ? 'critical' : 'info'
|
|
};
|
|
}
|
|
|
|
checkCSS() {
|
|
const stylesheets = document.querySelectorAll('link[rel="stylesheet"]');
|
|
const netgesconCSS = Array.from(stylesheets).find(link =>
|
|
link.href.includes('netgescon-admin.css')
|
|
);
|
|
|
|
if (!netgesconCSS) {
|
|
return {
|
|
healthy: false,
|
|
error: 'NetGescon CSS not found',
|
|
severity: 'critical'
|
|
};
|
|
}
|
|
|
|
// Check if styles are actually applied
|
|
const testElement = document.createElement('div');
|
|
testElement.className = 'netgescon-card';
|
|
testElement.style.position = 'absolute';
|
|
testElement.style.visibility = 'hidden';
|
|
document.body.appendChild(testElement);
|
|
|
|
const styles = getComputedStyle(testElement);
|
|
const hasStyles = styles.borderRadius && styles.borderRadius !== '0px';
|
|
|
|
document.body.removeChild(testElement);
|
|
|
|
return {
|
|
healthy: hasStyles,
|
|
error: hasStyles ? null : 'CSS styles not applied',
|
|
severity: hasStyles ? 'info' : 'warning'
|
|
};
|
|
}
|
|
|
|
async checkAPI() {
|
|
try {
|
|
const response = await fetch('/api/health');
|
|
return {
|
|
healthy: response.ok,
|
|
status: response.status,
|
|
severity: response.ok ? 'info' : 'critical'
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
healthy: false,
|
|
error: 'API not reachable',
|
|
severity: 'critical'
|
|
};
|
|
}
|
|
}
|
|
|
|
checkPermissions() {
|
|
const hasManager = typeof window.PermissionManager !== 'undefined';
|
|
const hasPermissions = window.PermissionManager?.permissions;
|
|
|
|
return {
|
|
healthy: hasManager && hasPermissions,
|
|
error: !hasManager ? 'Permission manager not loaded' :
|
|
!hasPermissions ? 'Permissions not loaded' : null,
|
|
severity: 'warning'
|
|
};
|
|
}
|
|
|
|
checkPerformance() {
|
|
const navigation = performance.getEntriesByType('navigation')[0];
|
|
if (!navigation) {
|
|
return { healthy: true, error: 'Performance data not available' };
|
|
}
|
|
|
|
const loadTime = navigation.loadEventEnd - navigation.fetchStart;
|
|
const healthy = loadTime < 5000; // 5 seconds threshold
|
|
|
|
return {
|
|
healthy,
|
|
loadTime,
|
|
error: healthy ? null : `Page load time too slow: ${loadTime}ms`,
|
|
severity: healthy ? 'info' : 'warning'
|
|
};
|
|
}
|
|
|
|
setupErrorReporting() {
|
|
// Global error handler
|
|
window.addEventListener('error', (e) => {
|
|
this.createAlert('javascript_error', {
|
|
healthy: false,
|
|
error: `${e.error?.message || e.message} at ${e.filename}:${e.lineno}`,
|
|
severity: 'warning',
|
|
stack: e.error?.stack
|
|
});
|
|
});
|
|
|
|
// Unhandled promise rejection handler
|
|
window.addEventListener('unhandledrejection', (e) => {
|
|
this.createAlert('promise_rejection', {
|
|
healthy: false,
|
|
error: `Unhandled promise rejection: ${e.reason}`,
|
|
severity: 'warning'
|
|
});
|
|
});
|
|
}
|
|
|
|
// Public methods for debugging
|
|
getHealthStatus() {
|
|
const status = {};
|
|
for (const [name, checkData] of this.checks) {
|
|
status[name] = {
|
|
healthy: checkData.lastResult?.healthy,
|
|
lastRun: checkData.lastRun ? new Date(checkData.lastRun).toISOString() : null,
|
|
consecutiveFailures: checkData.consecutiveFailures,
|
|
error: checkData.lastResult?.error
|
|
};
|
|
}
|
|
return status;
|
|
}
|
|
|
|
getAlerts(severity = null) {
|
|
return severity ?
|
|
this.alerts.filter(a => a.severity === severity) :
|
|
this.alerts;
|
|
}
|
|
|
|
forceCheck(checkName) {
|
|
const checkData = this.checks.get(checkName);
|
|
if (checkData) {
|
|
return checkData.check();
|
|
}
|
|
throw new Error(`Check '${checkName}' not found`);
|
|
}
|
|
}
|
|
|
|
// Initialize health monitor
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
if (NetGesconConfig?.get('app.debug') || window.location.search.includes('monitor=true')) {
|
|
window.HealthMonitor = new HealthMonitor();
|
|
console.log('🏥 Health Monitor started');
|
|
}
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 📚 **QUICK REFERENCE**
|
|
|
|
### 🚨 **Emergency Commands**
|
|
```bash
|
|
# Complete system restart
|
|
sudo systemctl restart nginx php8.2-fpm mysql
|
|
|
|
# Clear all Laravel caches
|
|
php artisan cache:clear && php artisan config:clear && php artisan view:clear && php artisan route:clear
|
|
|
|
# Rebuild assets
|
|
npm run build
|
|
|
|
# Fix permissions
|
|
sudo chown -R www-data:www-data /var/www/netgescon
|
|
sudo chmod -R 755 /var/www/netgescon
|
|
sudo chmod -R 775 /var/www/netgescon/storage /var/www/netgescon/bootstrap/cache
|
|
|
|
# Emergency backup
|
|
cp -r /var/www/netgescon "/var/backups/netgescon_emergency_$(date +%Y%m%d_%H%M%S)"
|
|
```
|
|
|
|
### 🔍 **Debug URLs**
|
|
```
|
|
# Interface tests
|
|
http://localhost:8000/?test=interface
|
|
|
|
# Health monitoring
|
|
http://localhost:8000/?monitor=true
|
|
|
|
# Permission debug
|
|
http://localhost:8000/debug/permissions/{user_id}
|
|
|
|
# Laravel debug info
|
|
http://localhost:8000/?debug=true
|
|
```
|
|
|
|
### 📞 **Emergency JavaScript**
|
|
```javascript
|
|
// Emergency fixes you can run in browser console
|
|
|
|
// Fix sidebar
|
|
document.getElementById('mobile-nav-toggle')?.addEventListener('click', () => {
|
|
document.getElementById('sidebar')?.classList.toggle('show');
|
|
});
|
|
|
|
// Force permission refresh
|
|
window.PermissionManager?.refreshPermissions();
|
|
|
|
// Clear local storage
|
|
localStorage.clear();
|
|
|
|
// Reload CSS
|
|
document.querySelectorAll('link[rel="stylesheet"]').forEach(link => {
|
|
link.href = link.href + '?v=' + Date.now();
|
|
});
|
|
|
|
// Emergency user info
|
|
console.log('User:', window.NetGesconApp?.user);
|
|
console.log('Permissions:', window.PermissionManager?.permissions);
|
|
console.log('Config:', window.NetGesconConfig?.all());
|
|
```
|
|
|
|
---
|
|
|
|
## 📞 **CONTATTI DI EMERGENZA**
|
|
|
|
### 🛠️ **Escalation Path**
|
|
1. **Self-Service** - Questa guida e procedure automatiche
|
|
2. **Developer** - GitHub Copilot documentation
|
|
3. **System Admin** - Server e infrastructure issues
|
|
4. **Database Admin** - Data corruption o performance issues
|
|
|
|
### 📋 **Incident Report Template**
|
|
```
|
|
INCIDENT REPORT - NetGescon Interface
|
|
=====================================
|
|
|
|
Timestamp: [DATE TIME]
|
|
Severity: [CRITICAL/HIGH/MEDIUM/LOW]
|
|
Affected Users: [NUMBER/ALL/SPECIFIC]
|
|
|
|
PROBLEM DESCRIPTION:
|
|
- What happened?
|
|
- When did it start?
|
|
- What is the impact?
|
|
|
|
SYMPTOMS:
|
|
- Browser errors?
|
|
- UI not loading?
|
|
- Functionality broken?
|
|
|
|
ENVIRONMENT:
|
|
- URL: [URL]
|
|
- Browser: [BROWSER VERSION]
|
|
- Screen size: [RESOLUTION]
|
|
- User role: [ROLE]
|
|
|
|
ATTEMPTED FIXES:
|
|
- [ ] Cleared browser cache
|
|
- [ ] Tried different browser
|
|
- [ ] Checked console errors
|
|
- [ ] Ran quick tests
|
|
- [ ] Applied emergency fixes
|
|
|
|
RESOLUTION:
|
|
[What fixed the issue]
|
|
|
|
PREVENTION:
|
|
[How to prevent this in future]
|
|
```
|
|
|
|
---
|
|
|
|
*📝 Documento mantenuto da: GitHub Copilot AI Assistant*
|
|
*🔗 Componente: Troubleshooting Guide NetGescon Interface v2.1.0*
|
|
*📞 Per supporto immediato: Consulta questa guida → Test automatici → Escalation*
|