Migrate from file-based configs to database with per-IP site configuration
Major architectural changes: - Replace YAML config files with database-stored ScanConfig model - Remove CIDR block support in favor of individual IP addresses per site - Each IP now has its own expected_ping, expected_tcp_ports, expected_udp_ports - AlertRule now uses config_id FK instead of config_file string API changes: - POST /api/scans now requires config_id instead of config_file - Alert rules API uses config_id with validation - All config dropdowns fetch from /api/configs dynamically Template updates: - scans.html, dashboard.html, alert_rules.html load configs via API - Display format: Config Title (X sites) in dropdowns - Removed Jinja2 config_files loops Migrations: - 008: Expand CIDRs to individual IPs with per-IP port configs - 009: Remove CIDR-related columns - 010: Add config_id to alert_rules, remove config_file
This commit is contained in:
@@ -169,7 +169,8 @@ def list_alert_rules():
|
||||
'webhook_enabled': rule.webhook_enabled,
|
||||
'severity': rule.severity,
|
||||
'filter_conditions': json.loads(rule.filter_conditions) if rule.filter_conditions else None,
|
||||
'config_file': rule.config_file,
|
||||
'config_id': rule.config_id,
|
||||
'config_title': rule.config.title if rule.config else None,
|
||||
'created_at': rule.created_at.isoformat(),
|
||||
'updated_at': rule.updated_at.isoformat() if rule.updated_at else None
|
||||
})
|
||||
@@ -195,7 +196,7 @@ def create_alert_rule():
|
||||
webhook_enabled: Send webhook for this rule (default: false)
|
||||
severity: Alert severity (critical, warning, info)
|
||||
filter_conditions: JSON object with filter conditions
|
||||
config_file: Optional config file to apply rule to
|
||||
config_id: Optional config ID to apply rule to
|
||||
|
||||
Returns:
|
||||
JSON response with created rule
|
||||
@@ -226,6 +227,17 @@ def create_alert_rule():
|
||||
}), 400
|
||||
|
||||
try:
|
||||
# Validate config_id if provided
|
||||
config_id = data.get('config_id')
|
||||
if config_id:
|
||||
from web.models import ScanConfig
|
||||
config = current_app.db_session.query(ScanConfig).filter_by(id=config_id).first()
|
||||
if not config:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Config with ID {config_id} not found'
|
||||
}), 400
|
||||
|
||||
# Create new rule
|
||||
rule = AlertRule(
|
||||
name=data.get('name', f"{data['rule_type']} rule"),
|
||||
@@ -236,7 +248,7 @@ def create_alert_rule():
|
||||
webhook_enabled=data.get('webhook_enabled', False),
|
||||
severity=data.get('severity', 'warning'),
|
||||
filter_conditions=json.dumps(data['filter_conditions']) if data.get('filter_conditions') else None,
|
||||
config_file=data.get('config_file'),
|
||||
config_id=config_id,
|
||||
created_at=datetime.now(timezone.utc),
|
||||
updated_at=datetime.now(timezone.utc)
|
||||
)
|
||||
@@ -257,7 +269,8 @@ def create_alert_rule():
|
||||
'webhook_enabled': rule.webhook_enabled,
|
||||
'severity': rule.severity,
|
||||
'filter_conditions': json.loads(rule.filter_conditions) if rule.filter_conditions else None,
|
||||
'config_file': rule.config_file,
|
||||
'config_id': rule.config_id,
|
||||
'config_title': rule.config.title if rule.config else None,
|
||||
'created_at': rule.created_at.isoformat(),
|
||||
'updated_at': rule.updated_at.isoformat()
|
||||
}
|
||||
@@ -288,7 +301,7 @@ def update_alert_rule(rule_id):
|
||||
webhook_enabled: Send webhook for this rule (optional)
|
||||
severity: Alert severity (optional)
|
||||
filter_conditions: JSON object with filter conditions (optional)
|
||||
config_file: Config file to apply rule to (optional)
|
||||
config_id: Config ID to apply rule to (optional)
|
||||
|
||||
Returns:
|
||||
JSON response with update status
|
||||
@@ -312,6 +325,18 @@ def update_alert_rule(rule_id):
|
||||
}), 400
|
||||
|
||||
try:
|
||||
# Validate config_id if provided
|
||||
if 'config_id' in data:
|
||||
config_id = data['config_id']
|
||||
if config_id:
|
||||
from web.models import ScanConfig
|
||||
config = current_app.db_session.query(ScanConfig).filter_by(id=config_id).first()
|
||||
if not config:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Config with ID {config_id} not found'
|
||||
}), 400
|
||||
|
||||
# Update fields if provided
|
||||
if 'name' in data:
|
||||
rule.name = data['name']
|
||||
@@ -327,8 +352,8 @@ def update_alert_rule(rule_id):
|
||||
rule.severity = data['severity']
|
||||
if 'filter_conditions' in data:
|
||||
rule.filter_conditions = json.dumps(data['filter_conditions']) if data['filter_conditions'] else None
|
||||
if 'config_file' in data:
|
||||
rule.config_file = data['config_file']
|
||||
if 'config_id' in data:
|
||||
rule.config_id = data['config_id']
|
||||
|
||||
rule.updated_at = datetime.now(timezone.utc)
|
||||
|
||||
@@ -347,7 +372,8 @@ def update_alert_rule(rule_id):
|
||||
'webhook_enabled': rule.webhook_enabled,
|
||||
'severity': rule.severity,
|
||||
'filter_conditions': json.loads(rule.filter_conditions) if rule.filter_conditions else None,
|
||||
'config_file': rule.config_file,
|
||||
'config_id': rule.config_id,
|
||||
'config_title': rule.config.title if rule.config else None,
|
||||
'created_at': rule.created_at.isoformat(),
|
||||
'updated_at': rule.updated_at.isoformat()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user