Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,50 @@ docs/_build
.tox
pyrightconfig.json
simplemonitor/html/node_modules

# Web interface database files (but allow the directory structure)
simplemonitor.db
*.db
!webapp/simplemonitor.db

# Generated web interface files (but keep templates and static for the Flask app)
# templates/ - Keep this for Flask templates
# static/ - Keep this for Flask static files

# Docker volumes and exports
_monitor-export/
html/

# Status page files (generated by SimpleMonitor)
status_page.html
html/index.html

# Docker Compose override files
docker-compose.override.yml
docker-compose.local.yml

# Web interface specific
webapp/simplemonitor.db

# Temporary files
cookies.txt

# Python cache
__pycache__/
*.py[cod]
*$py.class

# Environment files
.env
.env.local
.env.*.local

# IDE files
.vscode/
.idea/
*.swp
*.swo

# OS files
.DS_Store
Thumbs.db
85 changes: 85 additions & 0 deletions WEBAPP_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# SimpleMonitor Web Interface

A Flask-based web interface for managing SimpleMonitor configurations with authentication, monitor management, and SMS alerting.

## Features

- **Authentication System**: Secure login with admin account setup
- **Monitor Management**: Add, edit, and delete monitors through a web UI
- **Dynamic Forms**: Automatically generated forms for different monitor types
- **SMS Alerts**: Configure Twilio SMS alerts for monitor failures
- **Real-time Status**: View monitor status and configuration
- **Settings Management**: Configure API keys and system settings

## Monitor Types Supported

- **Host Ping**: Check if a host is pingable
- **HTTP Check**: Verify web services are responding
- **DNS Check**: Test DNS resolution
- **Disk Space**: Monitor available disk space
- **Memory Check**: Monitor system memory usage
- **Process Check**: Verify processes are running
- **Command Check**: Execute custom commands

## Quick Start

1. **Start the services**:
```bash
docker-compose up --build -d
```

2. **Access the web interface**:
- Web Interface: http://localhost:5001
- Status Page: http://localhost:8000

3. **Setup admin account**:
- Go to http://localhost:5001/setup
- Create your admin account

4. **Configure Twilio (optional)**:
- Go to Settings page
- Enter your Twilio credentials
- Enable SMS alerts for monitors

## URLs

- `/` - Landing page (shows SimpleMonitor status)
- `/setup` - Setup admin account (first time only)
- `/login` - Login page
- `/dashboard` - Main dashboard
- `/monitors` - Monitor management
- `/monitors/add` - Add new monitor
- `/settings` - System settings

## Configuration

The web interface automatically generates SimpleMonitor configuration files:
- `monitor.ini` - Main configuration
- `monitors.ini` - Monitor definitions

Configuration is automatically reloaded when changes are made through the web interface.

## SMS Alerts

To enable SMS alerts:

1. Sign up for a Twilio account
2. Get your Account SID and Auth Token
3. Purchase a phone number for sending SMS
4. Configure these in the Settings page
5. Enable SMS alerts when adding/editing monitors

## Development

The web interface is built with:
- Flask (Python web framework)
- Bootstrap 5 (UI framework)
- SQLite (database)
- Twilio (SMS service)

## Security Notes

- Change the SECRET_KEY in docker-compose.yml for production
- Use strong passwords for admin accounts
- Consider using HTTPS in production
- Regularly update dependencies
155 changes: 155 additions & 0 deletions config_watcher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#!/usr/bin/env python3
"""
Configuration Watcher for SimpleMonitor
Monitors configuration files for changes and reloads SimpleMonitor when needed
"""

import os
import time
import hashlib
import requests
import subprocess
import signal
import sys
from pathlib import Path

class ConfigWatcher:
def __init__(self, config_files=['monitor.ini', 'monitors.ini'], check_interval=30):
self.config_files = config_files
self.check_interval = check_interval
self.last_hashes = {}
self.webapp_url = os.environ.get('WEBAPP_URL', 'http://webapp:5000')
self.running = True

# Set up signal handlers
signal.signal(signal.SIGINT, self.signal_handler)
signal.signal(signal.SIGTERM, self.signal_handler)

# Initialize hashes
self.update_hashes()

def signal_handler(self, signum, frame):
"""Handle shutdown signals"""
print(f"Received signal {signum}, shutting down...")
self.running = False
sys.exit(0)

def get_file_hash(self, filepath):
"""Get MD5 hash of a file"""
try:
with open(filepath, 'rb') as f:
return hashlib.md5(f.read()).hexdigest()
except FileNotFoundError:
return None

def update_hashes(self):
"""Update stored hashes for all config files"""
for filepath in self.config_files:
self.last_hashes[filepath] = self.get_file_hash(filepath)

def check_webapp_config(self):
"""Check if webapp has updated configuration"""
try:
response = requests.get(f"{self.webapp_url}/api/config-hash", timeout=5)
if response.status_code == 200:
data = response.json()
webapp_hash = data.get('hash', '')

# Compare with local hash
local_hash = ':'.join([
self.last_hashes.get('monitor.ini', ''),
self.last_hashes.get('monitors.ini', '')
])

if webapp_hash != local_hash and webapp_hash != 'no-config':
print(f"Configuration change detected: {webapp_hash} != {local_hash}")
return True
except Exception as e:
print(f"Error checking webapp config: {e}")

return False

def reload_simplemonitor(self):
"""Reload SimpleMonitor configuration"""
try:
print("Reloading SimpleMonitor configuration...")

# Method 1: Try to send SIGHUP to SimpleMonitor process
result = subprocess.run(['pkill', '-HUP', 'simplemonitor'],
capture_output=True, text=True, timeout=10)

if result.returncode == 0:
print("SimpleMonitor configuration reloaded via SIGHUP")
return True
else:
# Method 2: Try to restart via webapp API
try:
response = requests.post(f"{self.webapp_url}/api/reload-config",
timeout=10)
if response.status_code == 200:
print("SimpleMonitor configuration reloaded via API")
return True
except Exception as e:
print(f"API reload failed: {e}")

print("Failed to reload SimpleMonitor configuration")
return False

except Exception as e:
print(f"Error reloading SimpleMonitor: {e}")
return False

def check_local_changes(self):
"""Check for local file changes"""
changes_detected = False

for filepath in self.config_files:
current_hash = self.get_file_hash(filepath)
last_hash = self.last_hashes.get(filepath)

if current_hash != last_hash and current_hash is not None:
print(f"Local change detected in {filepath}")
changes_detected = True
self.last_hashes[filepath] = current_hash

return changes_detected

def run(self):
"""Main monitoring loop"""
print("Configuration watcher started...")
print(f"Monitoring files: {self.config_files}")
print(f"Check interval: {self.check_interval} seconds")
print(f"Webapp URL: {self.webapp_url}")

while self.running:
try:
# Check for local file changes
if self.check_local_changes():
if self.reload_simplemonitor():
print("Configuration reloaded successfully")
else:
print("Failed to reload configuration")

# Check for webapp changes
if self.check_webapp_config():
if self.reload_simplemonitor():
print("Configuration reloaded from webapp changes")
# Update local hashes to match webapp
self.update_hashes()
else:
print("Failed to reload configuration from webapp")

time.sleep(self.check_interval)

except KeyboardInterrupt:
print("Received keyboard interrupt, shutting down...")
break
except Exception as e:
print(f"Error in monitoring loop: {e}")
time.sleep(self.check_interval)

print("Configuration watcher stopped")

if __name__ == '__main__':
watcher = ConfigWatcher()
watcher.run()
58 changes: 58 additions & 0 deletions debug_form_error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env python3
import requests
import re
from bs4 import BeautifulSoup

# Create a session to maintain cookies
session = requests.Session()

# Login
login_data = {'username': 'admin', 'password': 'admin123'}
session.post('http://localhost:5001/login', data=login_data)

# Test form submission
monitor_data = {
'name': 'Debug Monitor',
'monitor_type': 'host',
'config_host': '8.8.8.8',
'config_tolerance': '2'
}

response = session.post('http://localhost:5001/monitors/add', data=monitor_data)
print(f"Status Code: {response.status_code}")

# Parse the HTML to find error messages
soup = BeautifulSoup(response.text, 'html.parser')

# Look for alert messages
alerts = soup.find_all('div', class_='alert')
print(f"\nFound {len(alerts)} alert messages:")
for i, alert in enumerate(alerts, 1):
print(f"Alert {i}: {alert.get_text(strip=True)}")

# Look for invalid feedback messages
invalid_feedbacks = soup.find_all('div', class_='invalid-feedback')
print(f"\nFound {len(invalid_feedbacks)} invalid feedback messages:")
for i, feedback in enumerate(invalid_feedbacks, 1):
if feedback.get_text(strip=True):
print(f"Invalid feedback {i}: {feedback.get_text(strip=True)}")

# Look for any text containing "error" or "required"
error_text = soup.find_all(text=re.compile(r'error|required|invalid', re.I))
print(f"\nFound {len(error_text)} error-related text elements:")
for i, text in enumerate(error_text[:10], 1): # Limit to first 10
if text.strip():
print(f"Error text {i}: {text.strip()}")

# Check if the form has validation classes
form = soup.find('form', id='monitorForm')
if form:
print(f"\nForm validation classes: {form.get('class', [])}")

# Check input fields for validation classes
inputs = form.find_all('input')
for input_field in inputs:
name = input_field.get('name', 'unnamed')
classes = input_field.get('class', [])
if 'is-invalid' in classes or 'is-valid' in classes:
print(f"Input '{name}' classes: {classes}")
Loading