@gotza02/sequential-thinking 10000.1.0 → 10000.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard/server.d.ts +0 -34
- package/dist/dashboard/server.js +50 -269
- package/dist/tools/sports/core/alert-manager.d.ts +1 -50
- package/dist/tools/sports/core/alert-manager.js +57 -118
- package/dist/tools/sports/core/data-quality.d.ts +0 -44
- package/dist/tools/sports/core/data-quality.js +49 -266
- package/dist/tools/sports/core/historical-analyzer.d.ts +0 -54
- package/dist/tools/sports/core/historical-analyzer.js +56 -256
- package/dist/tools/sports/core/index.d.ts +1 -1
- package/dist/tools/sports/core/index.js +1 -1
- package/dist/tools/sports/core/ml-prediction.d.ts +7 -65
- package/dist/tools/sports/core/ml-prediction.js +43 -185
- package/dist/tools/sports/core/realtime-manager.d.ts +1 -52
- package/dist/tools/sports/core/realtime-manager.js +18 -127
- package/package.json +1 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DASHBOARD SERVER
|
|
3
|
-
* Real-time monitoring dashboard for the football analysis system
|
|
4
3
|
*/
|
|
5
4
|
export interface DashboardStats {
|
|
6
5
|
cache: {
|
|
@@ -30,49 +29,16 @@ export declare class DashboardServer {
|
|
|
30
29
|
private port;
|
|
31
30
|
private updateInterval;
|
|
32
31
|
constructor(port?: number);
|
|
33
|
-
/**
|
|
34
|
-
* Start the dashboard server
|
|
35
|
-
*/
|
|
36
32
|
start(): void;
|
|
37
|
-
/**
|
|
38
|
-
* Stop the dashboard server
|
|
39
|
-
*/
|
|
40
33
|
stop(): void;
|
|
41
|
-
/**
|
|
42
|
-
* Handle HTTP requests
|
|
43
|
-
*/
|
|
44
34
|
private handleRequest;
|
|
45
|
-
/**
|
|
46
|
-
* Serve the dashboard HTML
|
|
47
|
-
*/
|
|
48
35
|
private serveDashboard;
|
|
49
|
-
/**
|
|
50
|
-
* Serve stats JSON
|
|
51
|
-
*/
|
|
52
36
|
private serveStats;
|
|
53
|
-
/**
|
|
54
|
-
* Clear cache
|
|
55
|
-
*/
|
|
56
37
|
private clearCache;
|
|
57
|
-
/**
|
|
58
|
-
* Reset circuit breakers
|
|
59
|
-
*/
|
|
60
38
|
private resetCircuitBreakers;
|
|
61
|
-
/**
|
|
62
|
-
* Serve alert rules
|
|
63
|
-
*/
|
|
64
39
|
private serveAlertRules;
|
|
65
|
-
/**
|
|
66
|
-
* Serve alert history
|
|
67
|
-
*/
|
|
68
40
|
private serveAlertHistory;
|
|
69
|
-
/**
|
|
70
|
-
* Collect all stats
|
|
71
|
-
*/
|
|
72
41
|
private collectStats;
|
|
73
|
-
/**
|
|
74
|
-
* Broadcast stats to all connected clients (for WebSocket upgrade)
|
|
75
|
-
*/
|
|
76
42
|
private broadcastStats;
|
|
77
43
|
}
|
|
78
44
|
export declare function startDashboard(port?: number): DashboardServer;
|
package/dist/dashboard/server.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DASHBOARD SERVER
|
|
3
|
-
* Real-time monitoring dashboard for the football analysis system
|
|
4
3
|
*/
|
|
5
4
|
import { createServer } from 'http';
|
|
6
|
-
import {
|
|
7
|
-
import { fileURLToPath } from 'url';
|
|
8
|
-
import { logger } from '../utils.js';
|
|
9
|
-
import { getGlobalCache } from '../tools/sports/core/cache.js';
|
|
5
|
+
import { CacheService } from '../tools/sports/core/cache.js';
|
|
10
6
|
import { getAllCircuitBreakerStatus } from '../tools/sports/core/circuit-breaker.js';
|
|
11
7
|
import { getRealtimeManager } from '../tools/sports/core/realtime-manager.js';
|
|
12
8
|
import { getAlertManager } from '../tools/sports/core/alert-manager.js';
|
|
13
|
-
|
|
9
|
+
// Simple logger fallback
|
|
10
|
+
const logger = {
|
|
11
|
+
info: (...args) => console.log('[INFO]', ...args),
|
|
12
|
+
warn: (...args) => console.warn('[WARN]', ...args),
|
|
13
|
+
error: (...args) => console.error('[ERROR]', ...args),
|
|
14
|
+
debug: (...args) => { }
|
|
15
|
+
};
|
|
14
16
|
export class DashboardServer {
|
|
15
17
|
server = null;
|
|
16
18
|
port;
|
|
@@ -18,9 +20,6 @@ export class DashboardServer {
|
|
|
18
20
|
constructor(port = 8080) {
|
|
19
21
|
this.port = port;
|
|
20
22
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Start the dashboard server
|
|
23
|
-
*/
|
|
24
23
|
start() {
|
|
25
24
|
if (this.server)
|
|
26
25
|
return;
|
|
@@ -30,14 +29,10 @@ export class DashboardServer {
|
|
|
30
29
|
this.server.listen(this.port, () => {
|
|
31
30
|
logger.info(`[Dashboard] Server running on http://localhost:${this.port}`);
|
|
32
31
|
});
|
|
33
|
-
// Start stats update interval
|
|
34
32
|
this.updateInterval = setInterval(() => {
|
|
35
33
|
this.broadcastStats();
|
|
36
34
|
}, 5000);
|
|
37
35
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Stop the dashboard server
|
|
40
|
-
*/
|
|
41
36
|
stop() {
|
|
42
37
|
if (this.updateInterval) {
|
|
43
38
|
clearInterval(this.updateInterval);
|
|
@@ -49,12 +44,8 @@ export class DashboardServer {
|
|
|
49
44
|
logger.info('[Dashboard] Server stopped');
|
|
50
45
|
}
|
|
51
46
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Handle HTTP requests
|
|
54
|
-
*/
|
|
55
47
|
handleRequest(req, res) {
|
|
56
48
|
const url = new URL(req.url, `http://localhost:${this.port}`);
|
|
57
|
-
// Enable CORS
|
|
58
49
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
59
50
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
60
51
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
@@ -63,7 +54,6 @@ export class DashboardServer {
|
|
|
63
54
|
res.end();
|
|
64
55
|
return;
|
|
65
56
|
}
|
|
66
|
-
// Route handling
|
|
67
57
|
if (url.pathname === '/' || url.pathname === '/index.html') {
|
|
68
58
|
this.serveDashboard(res);
|
|
69
59
|
}
|
|
@@ -87,9 +77,6 @@ export class DashboardServer {
|
|
|
87
77
|
res.end(JSON.stringify({ error: 'Not found' }));
|
|
88
78
|
}
|
|
89
79
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Serve the dashboard HTML
|
|
92
|
-
*/
|
|
93
80
|
serveDashboard(res) {
|
|
94
81
|
const html = `<!DOCTYPE html>
|
|
95
82
|
<html lang="en">
|
|
@@ -99,272 +86,91 @@ export class DashboardServer {
|
|
|
99
86
|
<title>Football Analysis Dashboard</title>
|
|
100
87
|
<style>
|
|
101
88
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
102
|
-
body {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
.
|
|
109
|
-
text-align: center;
|
|
110
|
-
padding: 20px;
|
|
111
|
-
margin-bottom: 30px;
|
|
112
|
-
}
|
|
113
|
-
.header h1 {
|
|
114
|
-
font-size: 2.5em;
|
|
115
|
-
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
|
|
116
|
-
-webkit-background-clip: text;
|
|
117
|
-
-webkit-text-fill-color: transparent;
|
|
118
|
-
}
|
|
119
|
-
.grid {
|
|
120
|
-
display: grid;
|
|
121
|
-
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
122
|
-
gap: 20px;
|
|
123
|
-
max-width: 1400px;
|
|
124
|
-
margin: 0 auto;
|
|
125
|
-
}
|
|
126
|
-
.card {
|
|
127
|
-
background: #1e293b;
|
|
128
|
-
border-radius: 12px;
|
|
129
|
-
padding: 20px;
|
|
130
|
-
border: 1px solid #334155;
|
|
131
|
-
}
|
|
132
|
-
.card h2 {
|
|
133
|
-
font-size: 1.2em;
|
|
134
|
-
margin-bottom: 15px;
|
|
135
|
-
color: #94a3b8;
|
|
136
|
-
text-transform: uppercase;
|
|
137
|
-
letter-spacing: 1px;
|
|
138
|
-
}
|
|
139
|
-
.stat {
|
|
140
|
-
display: flex;
|
|
141
|
-
justify-content: space-between;
|
|
142
|
-
padding: 10px 0;
|
|
143
|
-
border-bottom: 1px solid #334155;
|
|
144
|
-
}
|
|
89
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0f172a; color: #e2e8f0; padding: 20px; }
|
|
90
|
+
.header { text-align: center; padding: 20px; margin-bottom: 30px; }
|
|
91
|
+
.header h1 { font-size: 2.5em; background: linear-gradient(135deg, #3b82f6, #8b5cf6); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
|
92
|
+
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; max-width: 1400px; margin: 0 auto; }
|
|
93
|
+
.card { background: #1e293b; border-radius: 12px; padding: 20px; border: 1px solid #334155; }
|
|
94
|
+
.card h2 { font-size: 1.2em; margin-bottom: 15px; color: #94a3b8; text-transform: uppercase; letter-spacing: 1px; }
|
|
95
|
+
.stat { display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid #334155; }
|
|
145
96
|
.stat:last-child { border-bottom: none; }
|
|
146
|
-
.stat-value {
|
|
147
|
-
|
|
148
|
-
color: #3b82f6;
|
|
149
|
-
}
|
|
150
|
-
.status {
|
|
151
|
-
display: inline-block;
|
|
152
|
-
padding: 4px 12px;
|
|
153
|
-
border-radius: 20px;
|
|
154
|
-
font-size: 0.85em;
|
|
155
|
-
font-weight: 500;
|
|
156
|
-
}
|
|
97
|
+
.stat-value { font-weight: bold; color: #3b82f6; }
|
|
98
|
+
.status { display: inline-block; padding: 4px 12px; border-radius: 20px; font-size: 0.85em; font-weight: 500; }
|
|
157
99
|
.status-online { background: #22c55e; color: #064e3b; }
|
|
158
100
|
.status-offline { background: #ef4444; color: #7f1d1d; }
|
|
159
|
-
.status-warning { background: #f59e0b; color: #78350f; }
|
|
160
101
|
.cb-closed { color: #22c55e; }
|
|
161
102
|
.cb-open { color: #ef4444; }
|
|
162
|
-
|
|
163
|
-
button {
|
|
164
|
-
background: #3b82f6;
|
|
165
|
-
color: white;
|
|
166
|
-
border: none;
|
|
167
|
-
padding: 10px 20px;
|
|
168
|
-
border-radius: 6px;
|
|
169
|
-
cursor: pointer;
|
|
170
|
-
font-size: 0.9em;
|
|
171
|
-
margin-top: 10px;
|
|
172
|
-
}
|
|
103
|
+
button { background: #3b82f6; color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 0.9em; margin-top: 10px; }
|
|
173
104
|
button:hover { background: #2563eb; }
|
|
174
|
-
.refresh-indicator {
|
|
175
|
-
|
|
176
|
-
top: 20px;
|
|
177
|
-
right: 20px;
|
|
178
|
-
width: 10px;
|
|
179
|
-
height: 10px;
|
|
180
|
-
background: #22c55e;
|
|
181
|
-
border-radius: 50%;
|
|
182
|
-
animation: pulse 2s infinite;
|
|
183
|
-
}
|
|
184
|
-
@keyframes pulse {
|
|
185
|
-
0%, 100% { opacity: 1; }
|
|
186
|
-
50% { opacity: 0.5; }
|
|
187
|
-
}
|
|
188
|
-
.alerts-list {
|
|
189
|
-
max-height: 200px;
|
|
190
|
-
overflow-y: auto;
|
|
191
|
-
}
|
|
192
|
-
.alert-item {
|
|
193
|
-
padding: 8px;
|
|
194
|
-
margin: 5px 0;
|
|
195
|
-
background: #334155;
|
|
196
|
-
border-radius: 6px;
|
|
197
|
-
font-size: 0.85em;
|
|
198
|
-
}
|
|
199
|
-
.alert-critical { border-left: 3px solid #ef4444; }
|
|
200
|
-
.alert-warning { border-left: 3px solid #f59e0b; }
|
|
201
|
-
.alert-info { border-left: 3px solid #3b82f6; }
|
|
105
|
+
.refresh-indicator { position: fixed; top: 20px; right: 20px; width: 10px; height: 10px; background: #22c55e; border-radius: 50%; animation: pulse 2s infinite; }
|
|
106
|
+
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
|
|
202
107
|
</style>
|
|
203
108
|
</head>
|
|
204
109
|
<body>
|
|
205
110
|
<div class="refresh-indicator"></div>
|
|
206
|
-
|
|
207
111
|
<div class="header">
|
|
208
|
-
<h1
|
|
112
|
+
<h1>Football Analysis Dashboard</h1>
|
|
209
113
|
<p>Real-time System Monitoring</p>
|
|
210
114
|
</div>
|
|
211
|
-
|
|
212
115
|
<div class="grid">
|
|
213
116
|
<div class="card">
|
|
214
|
-
<h2
|
|
215
|
-
<div class="stat">
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
</div>
|
|
219
|
-
<div class="stat">
|
|
220
|
-
<span>Alert Manager</span>
|
|
221
|
-
<span class="status status-online" id="alert-status">Online</span>
|
|
222
|
-
</div>
|
|
223
|
-
<div class="stat">
|
|
224
|
-
<span>Cache Service</span>
|
|
225
|
-
<span class="status status-online" id="cache-status">Online</span>
|
|
226
|
-
</div>
|
|
227
|
-
<div class="stat">
|
|
228
|
-
<span>Last Update</span>
|
|
229
|
-
<span id="last-update">-</span>
|
|
230
|
-
</div>
|
|
117
|
+
<h2>System Status</h2>
|
|
118
|
+
<div class="stat"><span>Realtime Manager</span><span class="status status-online" id="realtime-status">Online</span></div>
|
|
119
|
+
<div class="stat"><span>Alert Manager</span><span class="status status-online" id="alert-status">Online</span></div>
|
|
120
|
+
<div class="stat"><span>Cache Service</span><span class="status status-online" id="cache-status">Online</span></div>
|
|
121
|
+
<div class="stat"><span>Last Update</span><span id="last-update">-</span></div>
|
|
231
122
|
</div>
|
|
232
|
-
|
|
233
123
|
<div class="card">
|
|
234
|
-
<h2
|
|
235
|
-
<div class="stat">
|
|
236
|
-
|
|
237
|
-
<span class="stat-value" id="cache-size">0 / 2000</span>
|
|
238
|
-
</div>
|
|
239
|
-
<div class="stat">
|
|
240
|
-
<span>Cache Hits</span>
|
|
241
|
-
<span class="stat-value" id="cache-hits">0</span>
|
|
242
|
-
</div>
|
|
243
|
-
<div class="stat">
|
|
244
|
-
<span>Hit Rate</span>
|
|
245
|
-
<span class="stat-value" id="hit-rate">0%</span>
|
|
246
|
-
</div>
|
|
124
|
+
<h2>Cache Statistics</h2>
|
|
125
|
+
<div class="stat"><span>Cache Size</span><span class="stat-value" id="cache-size">0 / 2000</span></div>
|
|
126
|
+
<div class="stat"><span>Cache Hits</span><span class="stat-value" id="cache-hits">0</span></div>
|
|
247
127
|
<button onclick="clearCache()">Clear Cache</button>
|
|
248
128
|
</div>
|
|
249
|
-
|
|
250
129
|
<div class="card">
|
|
251
|
-
<h2
|
|
252
|
-
<div id="circuit-breakers">
|
|
253
|
-
<div class="stat">
|
|
254
|
-
<span>No circuit breakers active</span>
|
|
255
|
-
</div>
|
|
256
|
-
</div>
|
|
130
|
+
<h2>Circuit Breakers</h2>
|
|
131
|
+
<div id="circuit-breakers"><div class="stat"><span>No circuit breakers active</span></div></div>
|
|
257
132
|
<button onclick="resetCircuitBreakers()">Reset All</button>
|
|
258
133
|
</div>
|
|
259
|
-
|
|
260
134
|
<div class="card">
|
|
261
|
-
<h2
|
|
262
|
-
<div class="stat">
|
|
263
|
-
|
|
264
|
-
<span class="stat-value" id="live-matches">0</span>
|
|
265
|
-
</div>
|
|
266
|
-
<div class="stat">
|
|
267
|
-
<span>Active Subscribers</span>
|
|
268
|
-
<span class="stat-value" id="subscribers">0</span>
|
|
269
|
-
</div>
|
|
270
|
-
<div class="stat">
|
|
271
|
-
<span>Events/Minute</span>
|
|
272
|
-
<span class="stat-value" id="events-rate">0</span>
|
|
273
|
-
</div>
|
|
135
|
+
<h2>Realtime Data</h2>
|
|
136
|
+
<div class="stat"><span>Live Matches</span><span class="stat-value" id="live-matches">0</span></div>
|
|
137
|
+
<div class="stat"><span>Active Subscribers</span><span class="stat-value" id="subscribers">0</span></div>
|
|
274
138
|
</div>
|
|
275
|
-
|
|
276
139
|
<div class="card">
|
|
277
|
-
<h2
|
|
278
|
-
<div class="stat">
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
</div>
|
|
282
|
-
<div class="stat">
|
|
283
|
-
<span>Enabled Rules</span>
|
|
284
|
-
<span class="stat-value" id="enabled-rules">0</span>
|
|
285
|
-
</div>
|
|
286
|
-
<div class="stat">
|
|
287
|
-
<span>Total Alerts</span>
|
|
288
|
-
<span class="stat-value" id="total-alerts">0</span>
|
|
289
|
-
</div>
|
|
290
|
-
<button onclick="viewAlertRules()">View Rules</button>
|
|
291
|
-
</div>
|
|
292
|
-
|
|
293
|
-
<div class="card">
|
|
294
|
-
<h2>🔔 Recent Alerts</h2>
|
|
295
|
-
<div class="alerts-list" id="alerts-list">
|
|
296
|
-
<div class="alert-item">No recent alerts</div>
|
|
297
|
-
</div>
|
|
140
|
+
<h2>Alert System</h2>
|
|
141
|
+
<div class="stat"><span>Total Rules</span><span class="stat-value" id="total-rules">0</span></div>
|
|
142
|
+
<div class="stat"><span>Enabled Rules</span><span class="stat-value" id="enabled-rules">0</span></div>
|
|
143
|
+
<div class="stat"><span>Total Alerts</span><span class="stat-value" id="total-alerts">0</span></div>
|
|
298
144
|
</div>
|
|
299
145
|
</div>
|
|
300
|
-
|
|
301
146
|
<script>
|
|
302
|
-
let stats = {};
|
|
303
|
-
|
|
304
147
|
async function fetchStats() {
|
|
305
148
|
try {
|
|
306
149
|
const response = await fetch('/api/stats');
|
|
307
|
-
stats = await response.json();
|
|
308
|
-
updateDashboard();
|
|
309
|
-
} catch (error) {
|
|
310
|
-
console.error('Failed to fetch stats:', error);
|
|
311
|
-
}
|
|
150
|
+
const stats = await response.json();
|
|
151
|
+
updateDashboard(stats);
|
|
152
|
+
} catch (error) { console.error('Failed to fetch stats:', error); }
|
|
312
153
|
}
|
|
313
|
-
|
|
314
|
-
function updateDashboard() {
|
|
154
|
+
function updateDashboard(stats) {
|
|
315
155
|
document.getElementById('last-update').textContent = new Date().toLocaleTimeString();
|
|
316
|
-
|
|
317
|
-
// Cache stats
|
|
318
|
-
document.getElementById('cache-size').textContent =
|
|
319
|
-
\`\${stats.cache.size} / \${stats.cache.maxSize}\`;
|
|
156
|
+
document.getElementById('cache-size').textContent = stats.cache.size + ' / ' + stats.cache.maxSize;
|
|
320
157
|
document.getElementById('cache-hits').textContent = stats.cache.hits;
|
|
321
|
-
|
|
322
|
-
// Circuit breakers
|
|
323
158
|
const cbContainer = document.getElementById('circuit-breakers');
|
|
324
159
|
if (stats.circuitBreakers.length > 0) {
|
|
325
|
-
cbContainer.innerHTML = stats.circuitBreakers.map(cb =>
|
|
326
|
-
<div class="stat">
|
|
327
|
-
<span>\${cb.name}</span>
|
|
328
|
-
<span class="cb-\${cb.state}">\${cb.state.toUpperCase()}</span>
|
|
329
|
-
</div>
|
|
330
|
-
\`).join('');
|
|
160
|
+
cbContainer.innerHTML = stats.circuitBreakers.map(cb => '<div class="stat"><span>' + cb.name + '</span><span class="cb-' + cb.state + '">' + cb.state.toUpperCase() + '</span></div>').join('');
|
|
331
161
|
}
|
|
332
|
-
|
|
333
|
-
// Realtime
|
|
334
162
|
document.getElementById('live-matches').textContent = stats.realtime.liveMatches;
|
|
335
163
|
document.getElementById('subscribers').textContent = stats.realtime.subscribers;
|
|
336
|
-
|
|
337
|
-
// Alerts
|
|
338
164
|
document.getElementById('total-rules').textContent = stats.alerts.totalRules;
|
|
339
165
|
document.getElementById('enabled-rules').textContent = stats.alerts.enabledRules;
|
|
340
166
|
document.getElementById('total-alerts').textContent = stats.alerts.totalAlerts;
|
|
341
167
|
}
|
|
342
|
-
|
|
343
168
|
async function clearCache() {
|
|
344
|
-
try {
|
|
345
|
-
await fetch('/api/cache/clear', { method: 'POST' });
|
|
346
|
-
alert('Cache cleared');
|
|
347
|
-
fetchStats();
|
|
348
|
-
} catch (error) {
|
|
349
|
-
alert('Failed to clear cache');
|
|
350
|
-
}
|
|
169
|
+
try { await fetch('/api/cache/clear', { method: 'POST' }); fetchStats(); } catch (e) { alert('Failed'); }
|
|
351
170
|
}
|
|
352
|
-
|
|
353
171
|
async function resetCircuitBreakers() {
|
|
354
|
-
try {
|
|
355
|
-
await fetch('/api/circuit-breakers/reset', { method: 'POST' });
|
|
356
|
-
alert('Circuit breakers reset');
|
|
357
|
-
fetchStats();
|
|
358
|
-
} catch (error) {
|
|
359
|
-
alert('Failed to reset circuit breakers');
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
function viewAlertRules() {
|
|
364
|
-
window.open('/api/alerts/rules', '_blank');
|
|
172
|
+
try { await fetch('/api/circuit-breakers/reset', { method: 'POST' }); fetchStats(); } catch (e) { alert('Failed'); }
|
|
365
173
|
}
|
|
366
|
-
|
|
367
|
-
// Fetch stats every 5 seconds
|
|
368
174
|
fetchStats();
|
|
369
175
|
setInterval(fetchStats, 5000);
|
|
370
176
|
</script>
|
|
@@ -374,38 +180,24 @@ export class DashboardServer {
|
|
|
374
180
|
res.writeHead(200);
|
|
375
181
|
res.end(html);
|
|
376
182
|
}
|
|
377
|
-
/**
|
|
378
|
-
* Serve stats JSON
|
|
379
|
-
*/
|
|
380
183
|
serveStats(res) {
|
|
381
184
|
const stats = this.collectStats();
|
|
382
185
|
res.setHeader('Content-Type', 'application/json');
|
|
383
186
|
res.writeHead(200);
|
|
384
187
|
res.end(JSON.stringify(stats));
|
|
385
188
|
}
|
|
386
|
-
/**
|
|
387
|
-
* Clear cache
|
|
388
|
-
*/
|
|
389
189
|
clearCache(res) {
|
|
390
|
-
const cache =
|
|
190
|
+
const cache = new CacheService();
|
|
391
191
|
cache.clear();
|
|
392
192
|
res.setHeader('Content-Type', 'application/json');
|
|
393
193
|
res.writeHead(200);
|
|
394
194
|
res.end(JSON.stringify({ success: true, message: 'Cache cleared' }));
|
|
395
195
|
}
|
|
396
|
-
/**
|
|
397
|
-
* Reset circuit breakers
|
|
398
|
-
*/
|
|
399
196
|
resetCircuitBreakers(res) {
|
|
400
|
-
// Circuit breakers auto-reset based on timeout
|
|
401
|
-
// This endpoint just returns success
|
|
402
197
|
res.setHeader('Content-Type', 'application/json');
|
|
403
198
|
res.writeHead(200);
|
|
404
199
|
res.end(JSON.stringify({ success: true, message: 'Circuit breakers will reset automatically' }));
|
|
405
200
|
}
|
|
406
|
-
/**
|
|
407
|
-
* Serve alert rules
|
|
408
|
-
*/
|
|
409
201
|
serveAlertRules(res) {
|
|
410
202
|
const alertManager = getAlertManager();
|
|
411
203
|
const rules = alertManager.getRules();
|
|
@@ -413,9 +205,6 @@ export class DashboardServer {
|
|
|
413
205
|
res.writeHead(200);
|
|
414
206
|
res.end(JSON.stringify(rules, null, 2));
|
|
415
207
|
}
|
|
416
|
-
/**
|
|
417
|
-
* Serve alert history
|
|
418
|
-
*/
|
|
419
208
|
serveAlertHistory(res) {
|
|
420
209
|
const alertManager = getAlertManager();
|
|
421
210
|
const history = alertManager.getAlertHistory(50);
|
|
@@ -423,11 +212,8 @@ export class DashboardServer {
|
|
|
423
212
|
res.writeHead(200);
|
|
424
213
|
res.end(JSON.stringify(history, null, 2));
|
|
425
214
|
}
|
|
426
|
-
/**
|
|
427
|
-
* Collect all stats
|
|
428
|
-
*/
|
|
429
215
|
collectStats() {
|
|
430
|
-
const cache =
|
|
216
|
+
const cache = new CacheService();
|
|
431
217
|
const cacheStats = cache.getStats();
|
|
432
218
|
const realtimeManager = getRealtimeManager();
|
|
433
219
|
const alertManager = getAlertManager();
|
|
@@ -440,22 +226,17 @@ export class DashboardServer {
|
|
|
440
226
|
},
|
|
441
227
|
circuitBreakers: getAllCircuitBreakerStatus(),
|
|
442
228
|
realtime: {
|
|
443
|
-
isRunning: true,
|
|
229
|
+
isRunning: true,
|
|
444
230
|
liveMatches: realtimeManager.getLiveMatches().length,
|
|
445
231
|
subscribers: realtimeManager.listenerCount('event'),
|
|
446
232
|
},
|
|
447
233
|
alerts: alertManager.getStats(),
|
|
448
234
|
};
|
|
449
235
|
}
|
|
450
|
-
/**
|
|
451
|
-
* Broadcast stats to all connected clients (for WebSocket upgrade)
|
|
452
|
-
*/
|
|
453
236
|
broadcastStats() {
|
|
454
|
-
//
|
|
455
|
-
// For now, clients poll via HTTP
|
|
237
|
+
// WebSocket broadcast would go here
|
|
456
238
|
}
|
|
457
239
|
}
|
|
458
|
-
// Singleton instance
|
|
459
240
|
let globalDashboardServer = null;
|
|
460
241
|
export function startDashboard(port) {
|
|
461
242
|
if (!globalDashboardServer) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ALERT MANAGER
|
|
3
|
-
* Real-time alert system with rule engine
|
|
3
|
+
* Real-time alert system with rule engine
|
|
4
4
|
*/
|
|
5
5
|
import { EventEmitter } from 'events';
|
|
6
6
|
import type { LiveEvent } from './realtime-manager.js';
|
|
@@ -61,80 +61,31 @@ export declare class AlertManager extends EventEmitter {
|
|
|
61
61
|
private alertHistory;
|
|
62
62
|
private maxHistorySize;
|
|
63
63
|
constructor();
|
|
64
|
-
/**
|
|
65
|
-
* Start the alert manager
|
|
66
|
-
*/
|
|
67
64
|
start(): void;
|
|
68
|
-
/**
|
|
69
|
-
* Stop the alert manager
|
|
70
|
-
*/
|
|
71
65
|
stop(): void;
|
|
72
|
-
/**
|
|
73
|
-
* Add a new alert rule
|
|
74
|
-
*/
|
|
75
66
|
addRule(rule: Omit<AlertRule, 'id' | 'createdAt' | 'triggerCount'>): AlertRule;
|
|
76
|
-
/**
|
|
77
|
-
* Remove an alert rule
|
|
78
|
-
*/
|
|
79
67
|
removeRule(ruleId: string): boolean;
|
|
80
|
-
/**
|
|
81
|
-
* Get all rules
|
|
82
|
-
*/
|
|
83
68
|
getRules(): AlertRule[];
|
|
84
|
-
/**
|
|
85
|
-
* Get a specific rule
|
|
86
|
-
*/
|
|
87
69
|
getRule(ruleId: string): AlertRule | undefined;
|
|
88
|
-
/**
|
|
89
|
-
* Enable/disable a rule
|
|
90
|
-
*/
|
|
91
70
|
toggleRule(ruleId: string, enabled: boolean): boolean;
|
|
92
|
-
/**
|
|
93
|
-
* Process a live event and check for alerts
|
|
94
|
-
*/
|
|
95
71
|
processEvent(event: LiveEvent): void;
|
|
96
|
-
/**
|
|
97
|
-
* Evaluate an alert condition against an event
|
|
98
|
-
*/
|
|
99
72
|
private evaluateCondition;
|
|
100
73
|
private evaluateOddsDrop;
|
|
101
74
|
private evaluateOddsValue;
|
|
102
75
|
private evaluateEventCondition;
|
|
103
76
|
private evaluateCompositeCondition;
|
|
104
|
-
/**
|
|
105
|
-
* Trigger an alert
|
|
106
|
-
*/
|
|
107
77
|
private triggerAlert;
|
|
108
|
-
/**
|
|
109
|
-
* Create an alert message
|
|
110
|
-
*/
|
|
111
78
|
private createAlertMessage;
|
|
112
79
|
private determineSeverity;
|
|
113
80
|
private formatAlertTitle;
|
|
114
81
|
private formatAlertBody;
|
|
115
|
-
/**
|
|
116
|
-
* Send notification to a channel
|
|
117
|
-
*/
|
|
118
82
|
private sendNotification;
|
|
119
83
|
private sendWebhook;
|
|
120
84
|
private sendSlack;
|
|
121
85
|
private sendDiscord;
|
|
122
|
-
private sendEmail;
|
|
123
|
-
/**
|
|
124
|
-
* Check scheduled alerts (for time-based alerts)
|
|
125
|
-
*/
|
|
126
86
|
private checkScheduledAlerts;
|
|
127
|
-
/**
|
|
128
|
-
* Get alert history
|
|
129
|
-
*/
|
|
130
87
|
getAlertHistory(limit?: number): AlertMessage[];
|
|
131
|
-
/**
|
|
132
|
-
* Clear alert history
|
|
133
|
-
*/
|
|
134
88
|
clearHistory(): void;
|
|
135
|
-
/**
|
|
136
|
-
* Get statistics
|
|
137
|
-
*/
|
|
138
89
|
getStats(): {
|
|
139
90
|
totalRules: number;
|
|
140
91
|
enabledRules: number;
|