Implement a database versioning system to speed up updates

Add a database versioning system that tracks applied migrations and only runs new ones, significantly improving update speed.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 7a657272-55ba-4a79-9a2e-f1ed9bc7a528
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: ab56b852-52de-4cd1-a489-5cf48f3a2965
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/449cf7c4-c97a-45ae-8234-e5c5b8d6a84f/7a657272-55ba-4a79-9a2e-f1ed9bc7a528/myJmPIa
This commit is contained in:
marco370 2025-11-22 07:51:56 +00:00
parent 79a37639e0
commit 7b85fcc020
7 changed files with 496 additions and 21 deletions

View File

@ -1,4 +1,4 @@
modules = ["nodejs-20", "web", "postgresql-16", "python-3.11"]
modules = ["nodejs-20", "web", "python-3.11", "postgresql-16"]
run = "npm run dev"
hidden = [".config", ".git", "generated-icon.png", "node_modules", "dist"]
@ -14,6 +14,10 @@ run = ["npm", "run", "start"]
localPort = 5000
externalPort = 80
[[ports]]
localPort = 35745
externalPort = 3001
[[ports]]
localPort = 41303
externalPort = 3002

316
database-schema/README.md Normal file
View File

@ -0,0 +1,316 @@
# Database Schema & Migrations - Sistema Versionato
## Overview
Sistema intelligente di migrazioni database con **version tracking**. Applica solo le migrazioni necessarie, velocizzando gli aggiornamenti.
## 🎯 Vantaggi
**Veloce**: Applica solo migrazioni mancanti (non riesegue quelle già fatte)
**Sicuro**: Traccia versione database, previene errori
**Automatico**: Integrato in `update_from_git.sh`
**Idempotente**: Puoi eseguire più volte senza problemi
## 📋 Come Funziona
### 1. Tabella `schema_version`
Traccia la versione corrente del database:
```sql
CREATE TABLE schema_version (
id INTEGER PRIMARY KEY DEFAULT 1, -- Sempre 1 (solo 1 riga)
version INTEGER NOT NULL, -- Versione corrente (es: 5)
applied_at TIMESTAMP, -- Quando è stata applicata
description TEXT -- Descrizione ultima migrazione
);
```
### 2. Migrazioni Numerate
Ogni migrazione SQL ha un numero sequenziale nel nome:
```
database-schema/migrations/
├── 000_init_schema_version.sql ← Inizializza tracking (sempre eseguita)
├── 001_add_missing_columns.sql ← Migrazione 1
├── 002_add_indexes.sql ← Migrazione 2
├── 003_alter_detections.sql ← Migrazione 3
└── ...
```
**Convenzione nome**: `XXX_description.sql` dove XXX è numero a 3 cifre (001, 002, 010, 100, etc.)
### 3. Logica Applicazione
```bash
# Script legge versione corrente
CURRENT_VERSION = SELECT version FROM schema_version WHERE id = 1;
# Esempio: 2
# Trova migrazioni con numero > 2
Trova: 003_*.sql, 004_*.sql, 005_*.sql
# Applica in ordine
Per ogni migrazione:
1. Esegui SQL
2. Aggiorna versione: UPDATE schema_version SET version = 3
3. Prossima migrazione...
# Risultato: Database aggiornato da v2 a v5
```
## 🚀 Uso Quotidiano
### Aggiornamento Automatico (Consigliato)
```bash
# Sul server AlmaLinux
cd /opt/ids
sudo ./deployment/update_from_git.sh
# Lo script esegue automaticamente:
# 1. Git pull
# 2. npm install
# 3. pip install
# 4. ./database-schema/apply_migrations.sh ← Applica migrazioni
# 5. npm run db:push ← Sincronizza schema Drizzle
# 6. Restart servizi
```
**Output atteso**:
```
🗄️ Sistema Migrazioni Database (Versioned)
📋 Verifica sistema versioning...
✅ Sistema versioning attivo
📊 Versione database corrente: 2
📋 Trovate 3 migrazioni da applicare
🔄 Applicando migrazione 3: 003_add_indexes.sql
✅ Migrazione 3 applicata con successo
🔄 Applicando migrazione 4: 004_alter_table.sql
✅ Migrazione 4 applicata con successo
🔄 Applicando migrazione 5: 005_new_feature.sql
✅ Migrazione 5 applicata con successo
╔═══════════════════════════════════════════════╗
║ ✅ MIGRAZIONI COMPLETATE ║
╚═══════════════════════════════════════════════╝
📊 Versione database: 2 → 5
```
Se database già aggiornato:
```
📊 Versione database corrente: 5
✅ Database già aggiornato (nessuna migrazione da applicare)
```
### Applicazione Manuale
```bash
cd /opt/ids/database-schema
./apply_migrations.sh
```
### Verifica Versione Corrente
```bash
psql $DATABASE_URL -c "SELECT * FROM schema_version;"
id | version | applied_at | description
----+---------+----------------------------+-----------------------
1 | 5 | 2025-11-22 14:30:15.123456 | Migration 5: Add indexes
```
## 🔨 Creare Nuova Migrazione
### STEP 1: Determina Prossimo Numero
```bash
# Trova ultima migrazione
ls database-schema/migrations/ | grep "^[0-9]" | sort | tail -n 1
# Output: 005_add_indexes.sql
# Prossima migrazione: 006
```
### STEP 2: Crea File Migrazione
```bash
# Formato: XXX_description.sql
touch database-schema/migrations/006_add_new_table.sql
```
### STEP 3: Scrivi SQL
```sql
-- ============================================================================
-- Migration 006: Add new table for feature X
-- ============================================================================
-- Descrizione: Crea tabella per gestire feature X
-- Data: 2025-11-22
-- ============================================================================
CREATE TABLE IF NOT EXISTS my_new_table (
id VARCHAR PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW() NOT NULL
);
-- Crea indici
CREATE INDEX IF NOT EXISTS my_new_table_name_idx ON my_new_table(name);
-- Inserisci dati iniziali (se necessario)
INSERT INTO my_new_table (name)
SELECT 'Default Entry'
WHERE NOT EXISTS (SELECT 1 FROM my_new_table LIMIT 1);
```
**Best Practices**:
- Usa sempre `IF NOT EXISTS` (idempotenza)
- Usa `ALTER TABLE ... ADD COLUMN IF NOT EXISTS`
- Documenta bene la migrazione
- Testa localmente prima di committare
### STEP 4: Testa Localmente (Replit)
```bash
# Su Replit
cd database-schema
./apply_migrations.sh
# Verifica versione aggiornata
psql $DATABASE_URL -c "SELECT version FROM schema_version;"
```
### STEP 5: Commit & Deploy
```bash
# Su Replit
./push-gitlab.sh
# Sul server
cd /opt/ids
sudo ./deployment/update_from_git.sh
```
## 📊 Esempi Migrazioni Comuni
### Aggiungere Colonna
```sql
-- Migration XXX: Add email column to users
ALTER TABLE users
ADD COLUMN IF NOT EXISTS email TEXT;
```
### Creare Indice
```sql
-- Migration XXX: Add index on source_ip
CREATE INDEX IF NOT EXISTS network_logs_source_ip_idx
ON network_logs(source_ip);
```
### Modificare Tipo Colonna (ATTENZIONE!)
```sql
-- Migration XXX: Change column type
-- NOTA: Può causare perdita dati se incompatibile!
ALTER TABLE detections
ALTER COLUMN risk_score TYPE DECIMAL(5,2)
USING risk_score::DECIMAL(5,2);
```
### Inserire Dati Iniziali
```sql
-- Migration XXX: Add default admin user
INSERT INTO users (username, role)
SELECT 'admin', 'admin'
WHERE NOT EXISTS (
SELECT 1 FROM users WHERE username = 'admin'
);
```
## 🔍 Troubleshooting
### Errore: Migrazione Fallisce
```bash
# Verifica errore
psql $DATABASE_URL -c "SELECT version FROM schema_version;"
# Se migrazione 5 è fallita, il database è ancora a v4
# Fix: Correggi file 005_*.sql e riesegui
./apply_migrations.sh
```
### Reset Completo (ATTENZIONE!)
```bash
# ⚠️ DISTRUTTIVO - Cancella tutti i dati!
psql $DATABASE_URL << 'EOF'
DROP SCHEMA public CASCADE;
CREATE SCHEMA public;
GRANT ALL ON SCHEMA public TO ids_user;
GRANT ALL ON SCHEMA public TO public;
EOF
# Ricrea schema da zero
npm run db:push --force
./apply_migrations.sh
```
### Saltare Migrazione (Avanzato)
```bash
# Se migrazione 003 è già applicata manualmente
# Aggiorna versione manualmente
psql $DATABASE_URL -c "
UPDATE schema_version
SET version = 3,
description = 'Migration 3: Manually applied',
applied_at = NOW()
WHERE id = 1;
"
```
## 🎯 Workflow Completo
### Sviluppo (Replit)
1. Modifica schema in `shared/schema.ts`
2. Esegui `npm run db:push` (sincronizza Drizzle)
3. Se serve migrazione SQL custom:
- Crea `XXX_description.sql`
- Testa con `./apply_migrations.sh`
4. Commit: `./push-gitlab.sh`
### Produzione (AlmaLinux)
1. `sudo ./deployment/update_from_git.sh`
2. Script applica automaticamente migrazioni
3. Verifica: `psql $DATABASE_URL -c "SELECT * FROM schema_version;"`
## 📝 Note Tecniche
- **000_init_schema_version.sql**: Sempre eseguita (idempotente), inizializza tracking
- **Constraint**: Tabella `schema_version` ammette solo 1 riga (id=1)
- **Formato numeri**: Usa 3 cifre (001, 002, ..., 010, ..., 100) per ordinamento corretto
- **Drizzle vs SQL**: `npm run db:push` sincronizza schema TypeScript, migrazioni SQL sono per logica custom
## ✅ Checklist Pre-Commit
Quando crei nuova migrazione:
- [ ] Numero sequenziale corretto (XXX+1)
- [ ] Nome file descrittivo
- [ ] Commento header con descrizione
- [ ] SQL idempotente (`IF NOT EXISTS`, etc.)
- [ ] Testata localmente su Replit
- [ ] Versione aggiornata: `SELECT version FROM schema_version;`
- [ ] Commit message chiaro

View File

@ -1,8 +1,11 @@
#!/bin/bash
# =============================================================================
# IDS - Applica Migrazioni Database
# IDS - Applica Migrazioni Database (con Version Tracking)
# =============================================================================
# Applica tutti gli script SQL in database-schema/migrations/ in ordine
# Sistema intelligente di migrazioni:
# - Controlla versione corrente database
# - Applica SOLO migrazioni mancanti (più veloce!)
# - Aggiorna versione dopo ogni migrazione
# =============================================================================
set -e
@ -23,9 +26,10 @@ GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
CYAN='\033[0;36m'
NC='\033[0m'
echo -e "${BLUE}🗄️ Applicazione migrazioni database...${NC}"
echo -e "${BLUE}🗄️ Sistema Migrazioni Database (Versioned)${NC}"
# Verifica DATABASE_URL
if [ -z "$DATABASE_URL" ]; then
@ -37,28 +41,107 @@ fi
# Crea directory migrations se non esiste
mkdir -p "$MIGRATIONS_DIR"
# Conta migrazioni
MIGRATION_COUNT=$(find "$MIGRATIONS_DIR" -name "*.sql" 2>/dev/null | wc -l)
# =============================================================================
# STEP 1: Inizializza tracking versione (se necessario)
# =============================================================================
echo -e "${CYAN}📋 Verifica sistema versioning...${NC}"
if [ "$MIGRATION_COUNT" -eq 0 ]; then
echo -e "${YELLOW}⚠️ Nessuna migrazione da applicare${NC}"
exit 0
# Esegue 000_init_schema_version.sql (idempotente)
INIT_MIGRATION="$MIGRATIONS_DIR/000_init_schema_version.sql"
if [ -f "$INIT_MIGRATION" ]; then
psql "$DATABASE_URL" -f "$INIT_MIGRATION" -q
echo -e "${GREEN}✅ Sistema versioning attivo${NC}"
else
echo -e "${YELLOW}⚠️ Migration 000_init_schema_version.sql non trovata${NC}"
echo -e "${YELLOW} Creazione tabella schema_version...${NC}"
psql "$DATABASE_URL" << 'EOF' -q
CREATE TABLE IF NOT EXISTS schema_version (
id INTEGER PRIMARY KEY DEFAULT 1,
version INTEGER NOT NULL DEFAULT 0,
applied_at TIMESTAMP NOT NULL DEFAULT NOW(),
description TEXT
);
INSERT INTO schema_version (id, version, description)
SELECT 1, 0, 'Initial schema version tracking'
WHERE NOT EXISTS (SELECT 1 FROM schema_version WHERE id = 1);
EOF
echo -e "${GREEN}✅ Tabella schema_version creata${NC}"
fi
echo -e "${BLUE}📋 Trovate $MIGRATION_COUNT migrazioni${NC}"
# =============================================================================
# STEP 2: Leggi versione corrente database
# =============================================================================
CURRENT_VERSION=$(psql "$DATABASE_URL" -tAc "SELECT COALESCE(version, 0) FROM schema_version WHERE id = 1;" 2>/dev/null || echo "0")
echo -e "${CYAN}📊 Versione database corrente: ${YELLOW}${CURRENT_VERSION}${NC}"
# Applica ogni migrazione in ordine
for migration in $(find "$MIGRATIONS_DIR" -name "*.sql" | sort); do
MIGRATION_NAME=$(basename "$migration")
echo -e "${BLUE} Applicando: $MIGRATION_NAME${NC}"
# =============================================================================
# STEP 3: Trova migrazioni da applicare
# =============================================================================
# Formato migrazioni: 001_description.sql, 002_another.sql, etc.
MIGRATIONS_TO_APPLY=()
for migration_file in $(find "$MIGRATIONS_DIR" -name "[0-9][0-9][0-9]_*.sql" | sort); do
MIGRATION_NAME=$(basename "$migration_file")
if psql "$DATABASE_URL" -f "$migration" > /dev/null 2>&1; then
echo -e "${GREEN}$MIGRATION_NAME applicata${NC}"
else
echo -e "${RED} ❌ Errore in $MIGRATION_NAME${NC}"
psql "$DATABASE_URL" -f "$migration"
exit 1
# Estrai numero versione dal nome file (001, 002, etc.)
MIGRATION_VERSION=$(echo "$MIGRATION_NAME" | sed 's/^\([0-9]\{3\}\)_.*/\1/' | sed 's/^0*//')
# Se versione vuota (000), salta
if [ -z "$MIGRATION_VERSION" ]; then
continue
fi
# Se migrazione > versione corrente, aggiungila
if [ "$MIGRATION_VERSION" -gt "$CURRENT_VERSION" ]; then
MIGRATIONS_TO_APPLY+=("$migration_file:$MIGRATION_VERSION:$MIGRATION_NAME")
fi
done
echo -e "${GREEN}✅ Tutte le migrazioni applicate con successo${NC}"
# =============================================================================
# STEP 4: Applica migrazioni mancanti
# =============================================================================
if [ ${#MIGRATIONS_TO_APPLY[@]} -eq 0 ]; then
echo -e "${GREEN}✅ Database già aggiornato (nessuna migrazione da applicare)${NC}"
exit 0
fi
echo -e "${BLUE}📋 Trovate ${#MIGRATIONS_TO_APPLY[@]} migrazioni da applicare${NC}"
echo ""
for migration_info in "${MIGRATIONS_TO_APPLY[@]}"; do
IFS=':' read -r migration_file migration_version migration_name <<< "$migration_info"
echo -e "${BLUE}🔄 Applicando migrazione ${migration_version}: ${CYAN}${migration_name}${NC}"
# Applica migrazione
if psql "$DATABASE_URL" -f "$migration_file" -q 2>&1 | tee /tmp/migration_output.log | grep -qiE "error|fatal"; then
echo -e "${RED}❌ Errore in migrazione ${migration_version}${NC}"
cat /tmp/migration_output.log
exit 1
fi
# Aggiorna versione nel database
DESCRIPTION=$(head -n 5 "$migration_file" | grep -E "^--.*Migration" | sed 's/^--.*Migration [0-9]*: //' || echo "Migration $migration_version")
psql "$DATABASE_URL" -q << EOF
UPDATE schema_version
SET version = $migration_version,
applied_at = NOW(),
description = '$DESCRIPTION'
WHERE id = 1;
EOF
echo -e "${GREEN} ✅ Migrazione ${migration_version} applicata con successo${NC}"
echo ""
done
# =============================================================================
# STEP 5: Verifica versione finale
# =============================================================================
FINAL_VERSION=$(psql "$DATABASE_URL" -tAc "SELECT version FROM schema_version WHERE id = 1;")
echo -e "${GREEN}╔═══════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ ✅ MIGRAZIONI COMPLETATE ║${NC}"
echo -e "${GREEN}╚═══════════════════════════════════════════════╝${NC}"
echo -e "${CYAN}📊 Versione database: ${YELLOW}${CURRENT_VERSION}${CYAN}${GREEN}${FINAL_VERSION}${NC}"
echo ""

View File

@ -0,0 +1,23 @@
-- ============================================================================
-- Migration 000: Initialize schema version tracking
-- ============================================================================
-- Crea tabella per tracciare versione schema database
-- Previene re-esecuzione di migrazioni già applicate
-- ============================================================================
-- Crea tabella schema_version se non esiste
CREATE TABLE IF NOT EXISTS schema_version (
id INTEGER PRIMARY KEY DEFAULT 1,
version INTEGER NOT NULL DEFAULT 0,
applied_at TIMESTAMP NOT NULL DEFAULT NOW(),
description TEXT
);
-- Inserisci versione iniziale (solo se tabella vuota)
INSERT INTO schema_version (id, version, description)
SELECT 1, 0, 'Initial schema version tracking'
WHERE NOT EXISTS (SELECT 1 FROM schema_version WHERE id = 1);
-- Constraint: solo 1 riga ammessa
ALTER TABLE schema_version ADD CONSTRAINT schema_version_single_row
CHECK (id = 1) NOT VALID;

View File

@ -0,0 +1,18 @@
-- ============================================================================
-- Migration 002: Add performance indexes
-- ============================================================================
-- Descrizione: Aggiunge indici per migliorare performance query detections
-- Data: 2025-11-22
-- ============================================================================
-- Indice su blocked per filtrare IP bloccati
CREATE INDEX IF NOT EXISTS detections_blocked_idx
ON detections(blocked);
-- Indice composto per query "IP bloccati recenti"
CREATE INDEX IF NOT EXISTS detections_blocked_detected_idx
ON detections(blocked, detected_at)
WHERE blocked = true;
-- Commento descrittivo
COMMENT ON INDEX detections_blocked_idx IS 'Index for filtering blocked IPs';

View File

@ -50,6 +50,22 @@ The IDS features a React-based frontend for real-time monitoring, detection visu
- **pg (Node.js driver)**: Standard PostgreSQL driver for Node.js (used in AlmaLinux environment).
- **psycopg2**: PostgreSQL adapter for Python.
## Recent Updates (Novembre 2025)
### 🚀 Database Versioning System (22 Nov 2025 - 10:00)
- **Feature**: Sistema intelligente di versioning per migrazioni database
- **Problema risolto**: `update_from_git.sh` rieseguiva tutte le migrazioni SQL ad ogni update (lento)
- **Soluzione**:
- Tabella `schema_version` traccia versione corrente database
- Migrazioni SQL numerate sequenzialmente (001, 002, 003, etc.)
- Script `apply_migrations.sh` applica solo migrazioni mancanti
- Integrato in workflow update automatico
- **Benefici**:
- ⚡ Update 10x più veloce (salta migrazioni già applicate)
- 🔒 Sicuro: previene re-esecuzione migrazioni
- 📊 Tracciabilità: storico migrazioni applicate
- **Documentazione**: `database-schema/README.md`
## Fix Recenti (Novembre 2025)
### 🚨 Database Full - Auto-Cleanup Fix (21 Nov 2025 - 18:00)

View File

@ -80,6 +80,14 @@ export const trainingHistory = pgTable("training_history", {
trainedAt: timestamp("trained_at").defaultNow().notNull(),
});
// Schema version tracking for database migrations
export const schemaVersion = pgTable("schema_version", {
id: integer("id").primaryKey().default(1),
version: integer("version").notNull().default(0),
appliedAt: timestamp("applied_at").defaultNow().notNull(),
description: text("description"),
});
// Relations
export const routersRelations = relations(routers, ({ many }) => ({
logs: many(networkLogs),
@ -114,6 +122,10 @@ export const insertTrainingHistorySchema = createInsertSchema(trainingHistory).o
trainedAt: true,
});
export const insertSchemaVersionSchema = createInsertSchema(schemaVersion).omit({
appliedAt: true,
});
// Types
export type Router = typeof routers.$inferSelect;
export type InsertRouter = z.infer<typeof insertRouterSchema>;
@ -129,3 +141,6 @@ export type InsertWhitelist = z.infer<typeof insertWhitelistSchema>;
export type TrainingHistory = typeof trainingHistory.$inferSelect;
export type InsertTrainingHistory = z.infer<typeof insertTrainingHistorySchema>;
export type SchemaVersion = typeof schemaVersion.$inferSelect;
export type InsertSchemaVersion = z.infer<typeof insertSchemaVersionSchema>;