shrijayan commited on
Commit
cbe7108
·
verified ·
1 Parent(s): 4bc1bb8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +210 -86
app.py CHANGED
@@ -3,102 +3,226 @@ import requests
3
  import yaml
4
  import os
5
  import json
6
- from datetime import datetime
7
  import threading
8
  import time
 
 
 
 
 
9
 
10
  # Configuration
11
  CONFIG_FILE = "config.yaml"
12
  DATA_FILE = "/data/status.json"
 
13
 
14
- # Ensure the /data directory exists
15
- os.makedirs(os.path.dirname(DATA_FILE), exist_ok=True)
16
-
17
- def load_config():
18
- with open(CONFIG_FILE, "r") as f:
19
- return yaml.safe_load(f)["endpoints"]
20
-
21
- def load_data():
22
- try:
23
- with open(DATA_FILE, "r") as f:
24
- return json.load(f)
25
- except (FileNotFoundError, json.JSONDecodeError):
26
- return {}
27
-
28
- def save_data(data):
29
- try:
30
- with open(DATA_FILE, "w") as f:
31
- json.dump(data, f)
32
- except PermissionError:
33
- temp_file = "/tmp/status.json"
34
- with open(temp_file, "w") as f:
35
- json.dump(data, f)
36
-
37
- def check_status(endpoint):
38
- try:
39
- start = time.time()
40
- response = requests.get(endpoint["url"], timeout=10)
41
- return {
42
- "status": "UP" if response.ok else "DOWN",
43
- "response_time": time.time() - start,
44
- "error": None
45
- }
46
- except Exception as e:
47
- return {"status": "DOWN", "response_time": None, "error": str(e)}
48
-
49
- def update_dashboard():
50
- endpoints = load_config()
51
- data = load_data()
52
- current_time = datetime.now().isoformat()
53
-
54
- for endpoint in endpoints:
55
- result = check_status(endpoint)
56
- key = endpoint["name"]
57
- if key not in data:
58
- data[key] = []
59
- data[key].append({
60
- "timestamp": current_time,
61
- **result
62
- })
63
-
64
- save_data(data)
65
- return format_dashboard(data)
66
-
67
- def format_dashboard(data):
68
- output = []
69
- for name, history in data.items():
70
- last_check = history[-1] if history else {}
71
- uptime = sum(1 for h in history if h["status"] == "UP") / len(history) * 100 if history else 0
72
- output.append(
73
- f"**{name}**\n"
74
- f"Status: {last_check.get('status', 'UNKNOWN')}\n"
75
- f"Last Response: {last_check.get('response_time', 'N/A'):.2f}s\n"
76
- f"Uptime: {uptime:.1f}%\n"
77
- )
78
- return "\n".join(output)
79
-
80
- def start_background_updates():
81
- def worker():
82
- while True:
83
- update_dashboard()
84
- time.sleep(60)
85
-
86
- thread = threading.Thread(target=worker, daemon=True)
87
- thread.start()
88
 
89
- # Start background updates when app launches
90
- start_background_updates()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
- # Create Gradio interface
93
- with gr.Blocks() as demo:
94
- gr.Markdown("# Website Status Monitor")
95
- status_output = gr.Markdown()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
- # Initial load
98
- demo.load(
99
- fn=update_dashboard,
100
- outputs=status_output
101
- )
102
 
103
  if __name__ == "__main__":
104
  demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", 7860)))
 
3
  import yaml
4
  import os
5
  import json
6
+ from datetime import datetime, timedelta
7
  import threading
8
  import time
9
+ import smtplib
10
+ from email.message import EmailMessage
11
+ import matplotlib.pyplot as plt
12
+ import numpy as np
13
+ from collections import defaultdict
14
 
15
  # Configuration
16
  CONFIG_FILE = "config.yaml"
17
  DATA_FILE = "/data/status.json"
18
+ STATUS_HISTORY = 24 * 60 * 60 # 24 hours in seconds
19
 
20
+ class Monitor:
21
+ def __init__(self):
22
+ self.config = self.load_config()
23
+ self.lock = threading.Lock()
24
+ self.last_status = {}
25
+ self.incidents = defaultdict(list)
26
+
27
+ def load_config(self):
28
+ with open(CONFIG_FILE, "r") as f:
29
+ config = yaml.safe_load(f)
30
+ config.setdefault('settings', {})
31
+ config['settings'].setdefault('check_interval_seconds', 300)
32
+ return config
33
+
34
+ def load_data(self):
35
+ try:
36
+ with open(DATA_FILE, "r") as f:
37
+ return json.load(f)
38
+ except (FileNotFoundError, json.JSONDecodeError):
39
+ return {}
40
+
41
+ def save_data(self, data):
42
+ with self.lock:
43
+ try:
44
+ with open(DATA_FILE, "w") as f:
45
+ json.dump(data, f)
46
+ except Exception as e:
47
+ print(f"Error saving data: {e}")
48
+
49
+ def check_status(self, endpoint):
50
+ try:
51
+ start = time.time()
52
+ response = requests.get(
53
+ endpoint["url"],
54
+ timeout=10,
55
+ headers={'User-Agent': 'StatusMonitor/1.0'}
56
+ )
57
+ response_time = time.time() - start
58
+ return {
59
+ "status": "UP" if response.ok else "DOWN",
60
+ "response_time": response_time,
61
+ "error": None
62
+ }
63
+ except Exception as e:
64
+ return {"status": "DOWN", "response_time": None, "error": str(e)}
65
+
66
+ def send_alert(self, endpoint, result):
67
+ try:
68
+ msg = EmailMessage()
69
+ msg.set_content(
70
+ f"Website {endpoint['name']} is DOWN\n"
71
+ f"URL: {endpoint['url']}\n"
72
+ f"Time: {datetime.now().isoformat()}\n"
73
+ f"Error: {result['error'] or 'Unknown error'}"
74
+ )
75
+ msg['Subject'] = f"ALERT: {endpoint['name']} is DOWN"
76
+ msg['From'] = self.config['settings']['smtp']['from']
77
+ msg['To'] = ", ".join(self.config['settings']['alert_emails'])
78
+
79
+ with smtplib.SMTP(
80
+ self.config['settings']['smtp']['host'],
81
+ self.config['settings']['smtp']['port']
82
+ ) as server:
83
+ server.starttls()
84
+ server.login(
85
+ self.config['settings']['smtp']['username'],
86
+ self.config['settings']['smtp']['password']
87
+ )
88
+ server.send_message(msg)
89
+ except Exception as e:
90
+ print(f"Failed to send email: {e}")
 
 
 
91
 
92
+ def update_status(self):
93
+ endpoints = self.config['endpoints']
94
+ data = self.load_data()
95
+ current_time = datetime.now().isoformat()
96
+
97
+ for endpoint in endpoints:
98
+ key = endpoint["name"]
99
+ result = self.check_status(endpoint)
100
+
101
+ # Track incidents
102
+ if result['status'] != self.last_status.get(key, 'UP'):
103
+ if result['status'] == 'DOWN':
104
+ self.incidents[key].append({
105
+ 'start': current_time,
106
+ 'end': None,
107
+ 'duration': None
108
+ })
109
+ else:
110
+ if self.incidents[key] and not self.incidents[key][-1]['end']:
111
+ self.incidents[key][-1]['end'] = current_time
112
+ start_time = datetime.fromisoformat(self.incidents[key][-1]['start'])
113
+ end_time = datetime.fromisoformat(current_time)
114
+ self.incidents[key][-1]['duration'] = (end_time - start_time).total_seconds()
115
+
116
+ # Update last status and check if need to send alert
117
+ if self.last_status.get(key) == 'UP' and result['status'] == 'DOWN':
118
+ self.send_alert(endpoint, result)
119
+ self.last_status[key] = result['status']
120
+
121
+ # Update data
122
+ if key not in data:
123
+ data[key] = []
124
+ data[key].append({
125
+ "timestamp": current_time,
126
+ **result
127
+ })
128
+
129
+ # Trim old data
130
+ cutoff = datetime.now() - timedelta(seconds=STATUS_HISTORY)
131
+ data[key] = [entry for entry in data[key]
132
+ if datetime.fromisoformat(entry['timestamp']) > cutoff]
133
 
134
+ self.save_data(data)
135
+ return data
136
+
137
+ def get_metrics(self, data):
138
+ metrics = {}
139
+ for name, history in data.items():
140
+ if not history:
141
+ continue
142
+
143
+ uptime = sum(1 for h in history if h["status"] == "UP") / len(history)
144
+ response_times = [h["response_time"] for h in history if h["response_time"] is not None]
145
+
146
+ metrics[name] = {
147
+ "uptime": uptime,
148
+ "response_times": response_times,
149
+ "current_status": history[-1]["status"],
150
+ "incidents": self.incidents.get(name, [])
151
+ }
152
+ return metrics
153
+
154
+ def create_plots(self, metrics):
155
+ plots = {}
156
+ for name, metric in metrics.items():
157
+ # Response time plot
158
+ plt.figure(figsize=(8, 3))
159
+ plt.plot(metric['response_times'])
160
+ plt.title(f"{name} Response Times")
161
+ plt.ylabel("Seconds")
162
+ plt.xlabel("Last 24h checks")
163
+ plt.tight_layout()
164
+ plots[f"{name}_response"] = plt.gcf()
165
+ plt.close()
166
+
167
+ # Uptime plot
168
+ plt.figure(figsize=(3, 3))
169
+ plt.pie([metric['uptime'], 1-metric['uptime']],
170
+ labels=['Uptime', 'Downtime'],
171
+ autopct='%1.1f%%',
172
+ colors=['#4CAF50', '#F44336'])
173
+ plt.title(f"{name} Uptime")
174
+ plots[f"{name}_uptime"] = plt.gcf()
175
+ plt.close()
176
+
177
+ return plots
178
+
179
+ monitor = Monitor()
180
+
181
+ def background_updates():
182
+ while True:
183
+ monitor.update_status()
184
+ time.sleep(monitor.config['settings']['check_interval_seconds'])
185
+
186
+ threading.Thread(target=background_updates, daemon=True).start()
187
+
188
+ with gr.Blocks(title="Website Status Monitor") as demo:
189
+ gr.Markdown("# 🚦 Website Status Monitor")
190
+
191
+ def update_ui():
192
+ data = monitor.load_data()
193
+ metrics = monitor.get_metrics(data)
194
+ plots = monitor.create_plots(metrics)
195
+
196
+ elements = []
197
+ for name, metric in metrics.items():
198
+ with gr.Row():
199
+ with gr.Column(scale=1):
200
+ status_color = "#4CAF50" if metric['current_status'] == 'UP' else "#F44336"
201
+ gr.Markdown(f"### {name}\n"
202
+ f"**Status:** <span style='color: {status_color}'>"
203
+ f"{metric['current_status']}</span>\n"
204
+ f"**Last Response:** {np.mean(metric['response_times'][-5:]):.2f}s\n"
205
+ f"**24h Uptime:** {metric['uptime']*100:.1f}%")
206
+
207
+ with gr.Column(scale=2):
208
+ gr.Plot(plots[f"{name}_response"], label="Response Times")
209
+
210
+ with gr.Column(scale=1):
211
+ gr.Plot(plots[f"{name}_uptime"], label="Uptime")
212
+
213
+ with gr.Accordion("Incident History", open=False):
214
+ if not metric['incidents']:
215
+ gr.Markdown("No incidents in past 24 hours")
216
+ else:
217
+ for incident in metric['incidents'][-5:]:
218
+ duration = incident['duration'] or "Ongoing"
219
+ gr.Markdown(f"**Start:** {incident['start']}\n"
220
+ f"**End:** {incident['end'] or 'Still down'}\n"
221
+ f"**Duration:** {duration}s\n")
222
+
223
+ return elements
224
 
225
+ demo.load(update_ui, outputs=gr.get_all_components())
 
 
 
 
226
 
227
  if __name__ == "__main__":
228
  demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", 7860)))