@gotza02/sequential-thinking 10000.0.9 → 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/README.md +249 -39
- package/dist/dashboard/server.d.ts +45 -2
- package/dist/dashboard/server.js +250 -64
- package/dist/index.js +1 -4
- package/dist/tools/sports/core/alert-manager.d.ts +96 -0
- package/dist/tools/sports/core/alert-manager.js +319 -0
- package/dist/tools/sports/core/cache.d.ts +19 -8
- package/dist/tools/sports/core/cache.js +95 -38
- package/dist/tools/sports/core/circuit-breaker.d.ts +40 -0
- package/dist/tools/sports/core/circuit-breaker.js +99 -0
- package/dist/tools/sports/core/constants.d.ts +63 -4
- package/dist/tools/sports/core/constants.js +86 -11
- package/dist/tools/sports/core/data-quality.d.ts +36 -0
- package/dist/tools/sports/core/data-quality.js +243 -0
- package/dist/tools/sports/core/historical-analyzer.d.ts +54 -0
- package/dist/tools/sports/core/historical-analyzer.js +261 -0
- package/dist/tools/sports/core/index.d.ts +13 -0
- package/dist/tools/sports/core/index.js +16 -0
- package/dist/tools/sports/core/ml-prediction.d.ts +76 -0
- package/dist/tools/sports/core/ml-prediction.js +260 -0
- package/dist/tools/sports/core/realtime-manager.d.ts +51 -0
- package/dist/tools/sports/core/realtime-manager.js +222 -0
- package/dist/tools/sports/core/retry.d.ts +29 -0
- package/dist/tools/sports/core/retry.js +77 -0
- package/dist/tools/sports/core/types.d.ts +40 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,52 +1,262 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Football Analysis System - Fixed & Enhanced
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
An advanced Model Context Protocol (MCP) server that equips AI with structured reasoning, web search capabilities, and safety protocols.
|
|
3
|
+
## การปรับปรุงที่ทำ (Priority 1-3)
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
### Priority 1: Core Reliability
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- **Execution:** Perform actions (with tool support).
|
|
13
|
-
- **Observation:** Analyze results (Mandatory step).
|
|
14
|
-
- **Reflection:** Critique the outcome and adjust.
|
|
7
|
+
#### 1.1 Circuit Breaker Pattern (`core/circuit-breaker.ts`)
|
|
8
|
+
- ป้องกัน cascading failures เมื่อ API ล่ม
|
|
9
|
+
- สถานะ: CLOSED → HALF-OPEN → OPEN
|
|
10
|
+
- Auto-reset หลังจาก timeout
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
12
|
+
#### 1.2 Retry Mechanism (`core/retry.ts`)
|
|
13
|
+
- Exponential backoff with jitter
|
|
14
|
+
- Configurable retry policies
|
|
15
|
+
- แยก retryable vs non-retryable errors
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
- **Fallback Protection:** Intelligent warning system when API keys are missing.
|
|
17
|
+
#### 1.3 Enhanced Cache (`core/cache.ts`)
|
|
18
|
+
- Stale-while-revalidate pattern
|
|
19
|
+
- TTL ลดลงสำหรับ live data (15 วินาที)
|
|
20
|
+
- Background refresh
|
|
25
21
|
|
|
26
|
-
###
|
|
27
|
-
- Built-in support for **Exa**, **Brave**, **Google Search**, and **DuckDuckGo** (New!).
|
|
28
|
-
- Allows the AI to "pause and research" during the thinking process.
|
|
22
|
+
### Priority 2: Realtime Features
|
|
29
23
|
|
|
30
|
-
|
|
24
|
+
#### 2.1 Realtime Data Manager (`core/realtime-manager.ts`)
|
|
25
|
+
- Polling ทุก 5-15 วินาที
|
|
26
|
+
- Event-driven architecture
|
|
27
|
+
- Score change detection
|
|
28
|
+
- Odds change detection
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
30
|
+
#### 2.2 Alert Manager (`core/alert-manager.ts`)
|
|
31
|
+
- Rule-based alert engine
|
|
32
|
+
- Multiple notification channels (webhook, slack, discord, email)
|
|
33
|
+
- Cooldown และ rate limiting
|
|
34
|
+
- Alert history
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
36
|
+
#### 2.3 Data Quality Validator (`core/data-quality.ts`)
|
|
37
|
+
- Completeness, accuracy, freshness scoring
|
|
38
|
+
- Issue detection
|
|
39
|
+
- Improvement suggestions
|
|
42
40
|
|
|
43
|
-
###
|
|
44
|
-
- **DuckDuckGo Support:** Added a no-API-key search provider fallback.
|
|
45
|
-
- **Graceful Shutdown:** Ensures data (thoughts, notes) is saved correctly when the server stops.
|
|
46
|
-
- **Enhanced API:** Improved `/health` endpoint with system stats and added pagination to `/api/notes`.
|
|
41
|
+
### Priority 3: Advanced Analytics
|
|
47
42
|
|
|
48
|
-
|
|
43
|
+
#### 3.1 ML Prediction Engine (`core/ml-prediction.ts`)
|
|
44
|
+
- Weighted factor analysis
|
|
45
|
+
- Probability calculation
|
|
46
|
+
- Confidence scoring
|
|
47
|
+
- Over/Under & BTTS predictions
|
|
48
|
+
|
|
49
|
+
#### 3.2 Historical Analyzer (`core/historical-analyzer.ts`)
|
|
50
|
+
- Pattern detection
|
|
51
|
+
- Trend analysis
|
|
52
|
+
- Betting pattern analysis
|
|
53
|
+
- Referee bias analysis
|
|
54
|
+
|
|
55
|
+
#### 3.3 Dashboard Server (`dashboard/server.ts`)
|
|
56
|
+
- Real-time monitoring
|
|
57
|
+
- Cache statistics
|
|
58
|
+
- Circuit breaker status
|
|
59
|
+
- Alert management
|
|
60
|
+
|
|
61
|
+
## การใช้งาน
|
|
62
|
+
|
|
63
|
+
### 1. เริ่มต้นระบบ
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { getRealtimeManager, getAlertManager, startDashboard } from './core/index.js';
|
|
67
|
+
|
|
68
|
+
// Start realtime manager
|
|
69
|
+
const realtime = getRealtimeManager();
|
|
70
|
+
realtime.start();
|
|
71
|
+
|
|
72
|
+
// Start alert manager
|
|
73
|
+
const alerts = getAlertManager();
|
|
74
|
+
alerts.start();
|
|
75
|
+
|
|
76
|
+
// Start dashboard
|
|
77
|
+
startDashboard(8080);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 2. สมัครรับข้อมูลแบบ Realtime
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { getRealtimeManager } from './core/index.js';
|
|
84
|
+
|
|
85
|
+
const realtime = getRealtimeManager();
|
|
86
|
+
|
|
87
|
+
// Subscribe to match events
|
|
88
|
+
const unsubscribe = realtime.subscribeToMatch('match-123', (event) => {
|
|
89
|
+
console.log(`Event: ${event.type}`, event.data);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Subscribe to odds changes
|
|
93
|
+
realtime.subscribeToOdds('match-123', (event) => {
|
|
94
|
+
console.log(`Odds changed: ${event.data.newOdds}`);
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 3. ตั้งค่า Alerts
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { getAlertManager } from './core/index.js';
|
|
102
|
+
|
|
103
|
+
const alerts = getAlertManager();
|
|
104
|
+
|
|
105
|
+
// Add odds drop alert
|
|
106
|
+
alerts.addRule({
|
|
107
|
+
name: 'Arsenal Odds Drop',
|
|
108
|
+
type: 'odds_drop',
|
|
109
|
+
condition: {
|
|
110
|
+
type: 'odds_drop',
|
|
111
|
+
matchId: 'match-123',
|
|
112
|
+
threshold: 2.0,
|
|
113
|
+
percentage: 15,
|
|
114
|
+
},
|
|
115
|
+
channels: [
|
|
116
|
+
{ type: 'webhook', config: { url: 'https://your-webhook.com' } },
|
|
117
|
+
{ type: 'slack', config: { webhook: 'https://hooks.slack.com/...' } },
|
|
118
|
+
],
|
|
119
|
+
cooldown: 300000, // 5 minutes
|
|
120
|
+
enabled: true,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Add goal alert
|
|
124
|
+
alerts.addRule({
|
|
125
|
+
name: 'Goal Alert',
|
|
126
|
+
type: 'goal',
|
|
127
|
+
condition: {
|
|
128
|
+
type: 'event',
|
|
129
|
+
eventTypes: ['goal'],
|
|
130
|
+
},
|
|
131
|
+
channels: [{ type: 'console', config: {} }],
|
|
132
|
+
cooldown: 0,
|
|
133
|
+
enabled: true,
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 4. ใช้ Retry และ Circuit Breaker
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { withRetry, getCircuitBreaker } from './core/index.js';
|
|
141
|
+
|
|
142
|
+
// Simple retry
|
|
143
|
+
const data = await withRetry(
|
|
144
|
+
async () => fetchFromAPI(),
|
|
145
|
+
{ maxAttempts: 5, initialDelay: 1000 },
|
|
146
|
+
'api-call'
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// With circuit breaker
|
|
150
|
+
const cb = getCircuitBreaker('api-football', { failureThreshold: 5 });
|
|
151
|
+
const result = await cb.execute(() => fetchFromAPI());
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 5. ML Prediction
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import { getPredictionEngine } from './core/index.js';
|
|
158
|
+
|
|
159
|
+
const ml = getPredictionEngine();
|
|
160
|
+
|
|
161
|
+
const prediction = await ml.predict({
|
|
162
|
+
match: matchData,
|
|
163
|
+
homeForm: { last5: ['W', 'W', 'D', 'L', 'W'], goalsFor: 10, goalsAgainst: 5, xG: 8.5, xGA: 6.2 },
|
|
164
|
+
awayForm: { last5: ['L', 'D', 'W', 'L', 'L'], goalsFor: 6, goalsAgainst: 12, xG: 7.1, xGA: 10.5 },
|
|
165
|
+
h2h: headToHeadData,
|
|
166
|
+
homeStats: homeTeamStats,
|
|
167
|
+
awayStats: awayTeamStats,
|
|
168
|
+
injuries: { home: { keyPlayers: 1, total: 3 }, away: { keyPlayers: 0, total: 2 } },
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
console.log(`Home: ${prediction.homeWin * 100}%`);
|
|
172
|
+
console.log(`Draw: ${prediction.draw * 100}%`);
|
|
173
|
+
console.log(`Away: ${prediction.awayWin * 100}%`);
|
|
174
|
+
console.log(`Confidence: ${prediction.confidence}%`);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 6. Historical Analysis
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { getHistoricalAnalyzer } from './core/index.js';
|
|
181
|
+
|
|
182
|
+
const analyzer = getHistoricalAnalyzer();
|
|
183
|
+
|
|
184
|
+
// Analyze team performance
|
|
185
|
+
const performance = analyzer.analyzeTeamPerformance('team-123');
|
|
186
|
+
|
|
187
|
+
// Find patterns
|
|
188
|
+
const patterns = analyzer.findBettingPatterns({
|
|
189
|
+
league: 'Premier League',
|
|
190
|
+
dateFrom: new Date('2024-01-01'),
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Analyze trends
|
|
194
|
+
const trends = analyzer.analyzeTrends('team-123', 10);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 7. Data Quality Check
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import { getDataQualityValidator } from './core/index.js';
|
|
201
|
+
|
|
202
|
+
const validator = getDataQualityValidator();
|
|
203
|
+
|
|
204
|
+
const report = validator.validateMatchData(matchData);
|
|
205
|
+
|
|
206
|
+
console.log(`Quality Score: ${report.overall}/100`);
|
|
207
|
+
console.log(`Grade: ${validator.getGrade(report.overall)}`);
|
|
208
|
+
|
|
209
|
+
if (report.issues.length > 0) {
|
|
210
|
+
console.log('Issues:', report.issues);
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Dashboard
|
|
215
|
+
|
|
216
|
+
เปิด browser ไปที่ `http://localhost:8080` เพื่อดู:
|
|
217
|
+
- System status
|
|
218
|
+
- Cache statistics
|
|
219
|
+
- Circuit breaker status
|
|
220
|
+
- Realtime data
|
|
221
|
+
- Alert system
|
|
222
|
+
|
|
223
|
+
## Environment Variables
|
|
49
224
|
|
|
50
225
|
```bash
|
|
51
|
-
|
|
52
|
-
|
|
226
|
+
# API Keys
|
|
227
|
+
API_FOOTBALL_KEY=your_key
|
|
228
|
+
FOOTBALL_DATA_KEY=your_key
|
|
229
|
+
ODDS_API_KEY=your_key
|
|
230
|
+
SPORTRADAR_KEY=your_key
|
|
231
|
+
|
|
232
|
+
# Cache
|
|
233
|
+
SPORTS_CACHE_TTL=300000
|
|
234
|
+
SPORTS_CACHE_PATH=.sports_cache.json
|
|
235
|
+
|
|
236
|
+
# Dashboard
|
|
237
|
+
DASHBOARD_PORT=8080
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## ไฟล์ที่สร้างใหม่
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
src/tools/sports/core/
|
|
244
|
+
├── circuit-breaker.ts # Circuit breaker pattern
|
|
245
|
+
├── retry.ts # Retry mechanism
|
|
246
|
+
├── cache.ts # Enhanced cache (modified)
|
|
247
|
+
├── realtime-manager.ts # Realtime data manager
|
|
248
|
+
├── alert-manager.ts # Alert system
|
|
249
|
+
├── data-quality.ts # Data quality validator
|
|
250
|
+
├── ml-prediction.ts # ML prediction engine
|
|
251
|
+
├── historical-analyzer.ts # Historical analysis
|
|
252
|
+
├── types.ts # Type definitions (updated)
|
|
253
|
+
├── constants.ts # Constants (updated)
|
|
254
|
+
└── index.ts # Exports
|
|
255
|
+
|
|
256
|
+
src/dashboard/
|
|
257
|
+
└── server.ts # Dashboard server
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## License
|
|
261
|
+
|
|
262
|
+
MIT
|
|
@@ -1,2 +1,45 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* DASHBOARD SERVER
|
|
3
|
+
*/
|
|
4
|
+
export interface DashboardStats {
|
|
5
|
+
cache: {
|
|
6
|
+
size: number;
|
|
7
|
+
maxSize: number;
|
|
8
|
+
hits: number;
|
|
9
|
+
breakdown: Record<string, number>;
|
|
10
|
+
};
|
|
11
|
+
circuitBreakers: Array<{
|
|
12
|
+
name: string;
|
|
13
|
+
state: string;
|
|
14
|
+
failures: number;
|
|
15
|
+
}>;
|
|
16
|
+
realtime: {
|
|
17
|
+
isRunning: boolean;
|
|
18
|
+
liveMatches: number;
|
|
19
|
+
subscribers: number;
|
|
20
|
+
};
|
|
21
|
+
alerts: {
|
|
22
|
+
totalRules: number;
|
|
23
|
+
enabledRules: number;
|
|
24
|
+
totalAlerts: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export declare class DashboardServer {
|
|
28
|
+
private server;
|
|
29
|
+
private port;
|
|
30
|
+
private updateInterval;
|
|
31
|
+
constructor(port?: number);
|
|
32
|
+
start(): void;
|
|
33
|
+
stop(): void;
|
|
34
|
+
private handleRequest;
|
|
35
|
+
private serveDashboard;
|
|
36
|
+
private serveStats;
|
|
37
|
+
private clearCache;
|
|
38
|
+
private resetCircuitBreakers;
|
|
39
|
+
private serveAlertRules;
|
|
40
|
+
private serveAlertHistory;
|
|
41
|
+
private collectStats;
|
|
42
|
+
private broadcastStats;
|
|
43
|
+
}
|
|
44
|
+
export declare function startDashboard(port?: number): DashboardServer;
|
|
45
|
+
export declare function stopDashboard(): void;
|
package/dist/dashboard/server.js
CHANGED
|
@@ -1,67 +1,253 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
fs.readFile(historyPath, (err, data) => {
|
|
29
|
-
if (err) {
|
|
30
|
-
// Try .tmp if main file is locked/missing (rare fallback)
|
|
31
|
-
fs.readFile(historyPath + '.tmp', (err2, data2) => {
|
|
32
|
-
if (err2) {
|
|
33
|
-
res.writeHead(500);
|
|
34
|
-
res.end(JSON.stringify({ error: 'History not found' }));
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
38
|
-
res.end(data2);
|
|
39
|
-
});
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
43
|
-
res.end(data);
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
res.writeHead(404);
|
|
48
|
-
res.end('Not found');
|
|
49
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* DASHBOARD SERVER
|
|
3
|
+
*/
|
|
4
|
+
import { createServer } from 'http';
|
|
5
|
+
import { CacheService } from '../tools/sports/core/cache.js';
|
|
6
|
+
import { getAllCircuitBreakerStatus } from '../tools/sports/core/circuit-breaker.js';
|
|
7
|
+
import { getRealtimeManager } from '../tools/sports/core/realtime-manager.js';
|
|
8
|
+
import { getAlertManager } from '../tools/sports/core/alert-manager.js';
|
|
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
|
+
};
|
|
16
|
+
export class DashboardServer {
|
|
17
|
+
server = null;
|
|
18
|
+
port;
|
|
19
|
+
updateInterval = null;
|
|
20
|
+
constructor(port = 8080) {
|
|
21
|
+
this.port = port;
|
|
22
|
+
}
|
|
23
|
+
start() {
|
|
24
|
+
if (this.server)
|
|
25
|
+
return;
|
|
26
|
+
this.server = createServer((req, res) => {
|
|
27
|
+
this.handleRequest(req, res);
|
|
50
28
|
});
|
|
51
|
-
server.
|
|
52
|
-
|
|
53
|
-
console.error(`[Dashboard] Port ${port} is busy. Trying ${port + 1}...`);
|
|
54
|
-
// Recursive retry
|
|
55
|
-
startDashboard(port + 1, historyPath).then(resolve).catch(reject);
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
console.error('[Dashboard] Server error:', e);
|
|
59
|
-
reject(e);
|
|
60
|
-
}
|
|
29
|
+
this.server.listen(this.port, () => {
|
|
30
|
+
logger.info(`[Dashboard] Server running on http://localhost:${this.port}`);
|
|
61
31
|
});
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
32
|
+
this.updateInterval = setInterval(() => {
|
|
33
|
+
this.broadcastStats();
|
|
34
|
+
}, 5000);
|
|
35
|
+
}
|
|
36
|
+
stop() {
|
|
37
|
+
if (this.updateInterval) {
|
|
38
|
+
clearInterval(this.updateInterval);
|
|
39
|
+
this.updateInterval = null;
|
|
40
|
+
}
|
|
41
|
+
if (this.server) {
|
|
42
|
+
this.server.close();
|
|
43
|
+
this.server = null;
|
|
44
|
+
logger.info('[Dashboard] Server stopped');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
handleRequest(req, res) {
|
|
48
|
+
const url = new URL(req.url, `http://localhost:${this.port}`);
|
|
49
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
50
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
51
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
52
|
+
if (req.method === 'OPTIONS') {
|
|
53
|
+
res.writeHead(200);
|
|
54
|
+
res.end();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (url.pathname === '/' || url.pathname === '/index.html') {
|
|
58
|
+
this.serveDashboard(res);
|
|
59
|
+
}
|
|
60
|
+
else if (url.pathname === '/api/stats') {
|
|
61
|
+
this.serveStats(res);
|
|
62
|
+
}
|
|
63
|
+
else if (url.pathname === '/api/cache/clear') {
|
|
64
|
+
this.clearCache(res);
|
|
65
|
+
}
|
|
66
|
+
else if (url.pathname === '/api/circuit-breakers/reset') {
|
|
67
|
+
this.resetCircuitBreakers(res);
|
|
68
|
+
}
|
|
69
|
+
else if (url.pathname === '/api/alerts/rules') {
|
|
70
|
+
this.serveAlertRules(res);
|
|
71
|
+
}
|
|
72
|
+
else if (url.pathname === '/api/alerts/history') {
|
|
73
|
+
this.serveAlertHistory(res);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
res.writeHead(404);
|
|
77
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
serveDashboard(res) {
|
|
81
|
+
const html = `<!DOCTYPE html>
|
|
82
|
+
<html lang="en">
|
|
83
|
+
<head>
|
|
84
|
+
<meta charset="UTF-8">
|
|
85
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
86
|
+
<title>Football Analysis Dashboard</title>
|
|
87
|
+
<style>
|
|
88
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
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; }
|
|
96
|
+
.stat:last-child { border-bottom: none; }
|
|
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; }
|
|
99
|
+
.status-online { background: #22c55e; color: #064e3b; }
|
|
100
|
+
.status-offline { background: #ef4444; color: #7f1d1d; }
|
|
101
|
+
.cb-closed { color: #22c55e; }
|
|
102
|
+
.cb-open { color: #ef4444; }
|
|
103
|
+
button { background: #3b82f6; color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 0.9em; margin-top: 10px; }
|
|
104
|
+
button:hover { background: #2563eb; }
|
|
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; } }
|
|
107
|
+
</style>
|
|
108
|
+
</head>
|
|
109
|
+
<body>
|
|
110
|
+
<div class="refresh-indicator"></div>
|
|
111
|
+
<div class="header">
|
|
112
|
+
<h1>Football Analysis Dashboard</h1>
|
|
113
|
+
<p>Real-time System Monitoring</p>
|
|
114
|
+
</div>
|
|
115
|
+
<div class="grid">
|
|
116
|
+
<div class="card">
|
|
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>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="card">
|
|
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>
|
|
127
|
+
<button onclick="clearCache()">Clear Cache</button>
|
|
128
|
+
</div>
|
|
129
|
+
<div class="card">
|
|
130
|
+
<h2>Circuit Breakers</h2>
|
|
131
|
+
<div id="circuit-breakers"><div class="stat"><span>No circuit breakers active</span></div></div>
|
|
132
|
+
<button onclick="resetCircuitBreakers()">Reset All</button>
|
|
133
|
+
</div>
|
|
134
|
+
<div class="card">
|
|
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>
|
|
138
|
+
</div>
|
|
139
|
+
<div class="card">
|
|
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>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
<script>
|
|
147
|
+
async function fetchStats() {
|
|
148
|
+
try {
|
|
149
|
+
const response = await fetch('/api/stats');
|
|
150
|
+
const stats = await response.json();
|
|
151
|
+
updateDashboard(stats);
|
|
152
|
+
} catch (error) { console.error('Failed to fetch stats:', error); }
|
|
153
|
+
}
|
|
154
|
+
function updateDashboard(stats) {
|
|
155
|
+
document.getElementById('last-update').textContent = new Date().toLocaleTimeString();
|
|
156
|
+
document.getElementById('cache-size').textContent = stats.cache.size + ' / ' + stats.cache.maxSize;
|
|
157
|
+
document.getElementById('cache-hits').textContent = stats.cache.hits;
|
|
158
|
+
const cbContainer = document.getElementById('circuit-breakers');
|
|
159
|
+
if (stats.circuitBreakers.length > 0) {
|
|
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('');
|
|
161
|
+
}
|
|
162
|
+
document.getElementById('live-matches').textContent = stats.realtime.liveMatches;
|
|
163
|
+
document.getElementById('subscribers').textContent = stats.realtime.subscribers;
|
|
164
|
+
document.getElementById('total-rules').textContent = stats.alerts.totalRules;
|
|
165
|
+
document.getElementById('enabled-rules').textContent = stats.alerts.enabledRules;
|
|
166
|
+
document.getElementById('total-alerts').textContent = stats.alerts.totalAlerts;
|
|
167
|
+
}
|
|
168
|
+
async function clearCache() {
|
|
169
|
+
try { await fetch('/api/cache/clear', { method: 'POST' }); fetchStats(); } catch (e) { alert('Failed'); }
|
|
170
|
+
}
|
|
171
|
+
async function resetCircuitBreakers() {
|
|
172
|
+
try { await fetch('/api/circuit-breakers/reset', { method: 'POST' }); fetchStats(); } catch (e) { alert('Failed'); }
|
|
173
|
+
}
|
|
174
|
+
fetchStats();
|
|
175
|
+
setInterval(fetchStats, 5000);
|
|
176
|
+
</script>
|
|
177
|
+
</body>
|
|
178
|
+
</html>`;
|
|
179
|
+
res.setHeader('Content-Type', 'text/html');
|
|
180
|
+
res.writeHead(200);
|
|
181
|
+
res.end(html);
|
|
182
|
+
}
|
|
183
|
+
serveStats(res) {
|
|
184
|
+
const stats = this.collectStats();
|
|
185
|
+
res.setHeader('Content-Type', 'application/json');
|
|
186
|
+
res.writeHead(200);
|
|
187
|
+
res.end(JSON.stringify(stats));
|
|
188
|
+
}
|
|
189
|
+
clearCache(res) {
|
|
190
|
+
const cache = new CacheService();
|
|
191
|
+
cache.clear();
|
|
192
|
+
res.setHeader('Content-Type', 'application/json');
|
|
193
|
+
res.writeHead(200);
|
|
194
|
+
res.end(JSON.stringify({ success: true, message: 'Cache cleared' }));
|
|
195
|
+
}
|
|
196
|
+
resetCircuitBreakers(res) {
|
|
197
|
+
res.setHeader('Content-Type', 'application/json');
|
|
198
|
+
res.writeHead(200);
|
|
199
|
+
res.end(JSON.stringify({ success: true, message: 'Circuit breakers will reset automatically' }));
|
|
200
|
+
}
|
|
201
|
+
serveAlertRules(res) {
|
|
202
|
+
const alertManager = getAlertManager();
|
|
203
|
+
const rules = alertManager.getRules();
|
|
204
|
+
res.setHeader('Content-Type', 'application/json');
|
|
205
|
+
res.writeHead(200);
|
|
206
|
+
res.end(JSON.stringify(rules, null, 2));
|
|
207
|
+
}
|
|
208
|
+
serveAlertHistory(res) {
|
|
209
|
+
const alertManager = getAlertManager();
|
|
210
|
+
const history = alertManager.getAlertHistory(50);
|
|
211
|
+
res.setHeader('Content-Type', 'application/json');
|
|
212
|
+
res.writeHead(200);
|
|
213
|
+
res.end(JSON.stringify(history, null, 2));
|
|
214
|
+
}
|
|
215
|
+
collectStats() {
|
|
216
|
+
const cache = new CacheService();
|
|
217
|
+
const cacheStats = cache.getStats();
|
|
218
|
+
const realtimeManager = getRealtimeManager();
|
|
219
|
+
const alertManager = getAlertManager();
|
|
220
|
+
return {
|
|
221
|
+
cache: {
|
|
222
|
+
size: cacheStats.size,
|
|
223
|
+
maxSize: cacheStats.maxSize,
|
|
224
|
+
hits: cacheStats.hits,
|
|
225
|
+
breakdown: cache.getBreakdown(),
|
|
226
|
+
},
|
|
227
|
+
circuitBreakers: getAllCircuitBreakerStatus(),
|
|
228
|
+
realtime: {
|
|
229
|
+
isRunning: true,
|
|
230
|
+
liveMatches: realtimeManager.getLiveMatches().length,
|
|
231
|
+
subscribers: realtimeManager.listenerCount('event'),
|
|
232
|
+
},
|
|
233
|
+
alerts: alertManager.getStats(),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
broadcastStats() {
|
|
237
|
+
// WebSocket broadcast would go here
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
let globalDashboardServer = null;
|
|
241
|
+
export function startDashboard(port) {
|
|
242
|
+
if (!globalDashboardServer) {
|
|
243
|
+
globalDashboardServer = new DashboardServer(port);
|
|
244
|
+
globalDashboardServer.start();
|
|
245
|
+
}
|
|
246
|
+
return globalDashboardServer;
|
|
247
|
+
}
|
|
248
|
+
export function stopDashboard() {
|
|
249
|
+
if (globalDashboardServer) {
|
|
250
|
+
globalDashboardServer.stop();
|
|
251
|
+
globalDashboardServer = null;
|
|
252
|
+
}
|
|
67
253
|
}
|