File size: 3,575 Bytes
391c43e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/**
 * Request Stats & Cleanup
 *
 * Reads from the request_log table for the admin dashboard
 * and handles log retention cleanup.
 */

// Lazy-loaded database connection
let db: ReturnType<typeof import('../vfs/adapters/sqlite-connection').getCoreDatabase> | null = null;

/**
 * Get database connection lazily
 */
function getDB() {
  if (!db) {
    // Lazy require keeps better-sqlite3 (native module) out of non-server
    // bundles; getDB() is sync so dynamic import() is not an option here.
    // eslint-disable-next-line @typescript-eslint/no-require-imports
    const { getCoreDatabase } = require('../vfs/adapters/sqlite-connection');
    db = getCoreDatabase();
  }
  return db;
}


/**
 * Get request statistics for dashboard
 */
export function getRequestStats(hoursBack: number = 24): {
  requestsLastHour: number;
  requestsLastDay: number;
  errorCount: number;
  topDeployments: Array<{ deploymentId: string; count: number }>;
  recentErrors: Array<{ deploymentId: string; path: string; statusCode: number; timestamp: string }>;
} {
  try {
    const database = getDB();
    if (!database) {
      return {
        requestsLastHour: 0,
        requestsLastDay: 0,
        errorCount: 0,
        topDeployments: [],
        recentErrors: [],
      };
    }

    // Requests in last hour
    const lastHour = database.prepare(`
      SELECT COUNT(*) as count FROM request_log
      WHERE timestamp > datetime('now', '-1 hour')
    `).get() as { count: number };

    // Requests in last 24 hours
    const lastDay = database.prepare(`
      SELECT COUNT(*) as count FROM request_log
      WHERE timestamp > datetime('now', '-24 hours')
    `).get() as { count: number };

    // Error count (4xx and 5xx) in last 24 hours
    const errors = database.prepare(`
      SELECT COUNT(*) as count FROM request_log
      WHERE timestamp > datetime('now', '-24 hours')
      AND status_code >= 400
    `).get() as { count: number };

    // Top deployments by request count in last 24 hours
    const topDeployments = database.prepare(`
      SELECT site_id as deploymentId, COUNT(*) as count FROM request_log
      WHERE timestamp > datetime('now', '-24 hours')
      GROUP BY site_id
      ORDER BY count DESC
      LIMIT 10
    `).all() as Array<{ deploymentId: string; count: number }>;

    // Recent errors
    const recentErrors = database.prepare(`
      SELECT site_id as deploymentId, path, status_code as statusCode, timestamp FROM request_log
      WHERE status_code >= 400
      ORDER BY timestamp DESC
      LIMIT 10
    `).all() as Array<{ deploymentId: string; path: string; statusCode: number; timestamp: string }>;

    return {
      requestsLastHour: lastHour.count,
      requestsLastDay: lastDay.count,
      errorCount: errors.count,
      topDeployments,
      recentErrors,
    };
  } catch (error) {
    console.error('[RequestLogger] Failed to get stats:', error);
    return {
      requestsLastHour: 0,
      requestsLastDay: 0,
      errorCount: 0,
      topDeployments: [],
      recentErrors: [],
    };
  }
}

/**
 * Clean up old request logs to prevent unbounded growth
 * Keeps logs from the last N days
 */
export function cleanupOldLogs(daysToKeep: number = 7): number {
  try {
    const database = getDB();
    if (!database) return 0;

    const result = database.prepare(`
      DELETE FROM request_log
      WHERE timestamp < datetime('now', '-' || ? || ' days')
    `).run(daysToKeep);

    return result.changes;
  } catch (error) {
    console.error('[RequestLogger] Failed to cleanup logs:', error);
    return 0;
  }
}