import { Env } from '.' import { IncidentRecord, LatencyRecord, MonitorStateCompacted } from '../../types/config' export async function getFromStore(env: Env, key: string): Promise { const stmt = env.UPTIMEFLARE_D1.prepare('SELECT value FROM uptimeflare WHERE key = ?') const result = await stmt.bind(key).first<{ value: string }>() return result?.value || null } export async function setToStore(env: Env, key: string, value: string): Promise { const stmt = env.UPTIMEFLARE_D1.prepare( 'INSERT INTO uptimeflare (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value;' ) await stmt.bind(key, value).run() } export class CompactedMonitorStateWrapper { data: MonitorStateCompacted constructor(compactedStateStr: string | null) { if (!compactedStateStr) { // Initialize empty state this.data = { lastUpdate: 0, overallUp: 0, overallDown: 0, incident: {}, latency: {}, } return } this.data = JSON.parse(compactedStateStr) } getCompactedStateStr(): string { return JSON.stringify(this.data) } incidentLen(monitorId: string): number { const incidents = this.data.incident[monitorId] if (!incidents) return 0 return incidents.start.length } getIncident(monitorId: string, index: number): IncidentRecord { const incidents = this.data.incident[monitorId] if (!incidents || index < 0 || index >= incidents.start.length) { throw new Error('Index out of bounds or monitor not found') } return { start: incidents.start[index], end: incidents.end[index], error: incidents.error[index], } } setIncident(monitorId: string, index: number, incident: IncidentRecord) { const incidents = this.data.incident[monitorId] if (!incidents || index < 0 || index >= incidents.start.length) { throw new Error('Index out of bounds or monitor not found') } incidents.start[index] = incident.start incidents.end[index] = incident.end incidents.error[index] = incident.error } appendIncident(monitorId: string, incident: IncidentRecord) { let incidents = this.data.incident[monitorId] if (!incidents) { // Initialize incident arrays this.data.incident[monitorId] = { start: [], end: [], error: [], } incidents = this.data.incident[monitorId] } incidents.start.push(incident.start) incidents.end.push(incident.end) incidents.error.push(incident.error) } shiftIncident(monitorId: string) { const incidents = this.data.incident[monitorId] incidents.start.shift() incidents.end.shift() incidents.error.shift() } unshiftIncident(monitorId: string, incident: IncidentRecord) { const incidents = this.data.incident[monitorId] incidents.start.unshift(incident.start) incidents.end.unshift(incident.end) incidents.error.unshift(incident.error) } latencyLen(monitorId: string): number { const latencies = this.data.latency[monitorId] if (!latencies) return 0 return latencies.ping.length / 4 // Uint16Array, 4 characters per entry in hex } appendLatency(monitorId: string, record: LatencyRecord) { let latencies = this.data.latency[monitorId] if (!latencies) { // Initialize latency arrays this.data.latency[monitorId] = { time: '', ping: '', loc: { c: [], v: [], }, } latencies = this.data.latency[monitorId] } // @ts-expect-error latencies.time += new Uint8Array(new Uint32Array([record.time]).buffer).toHex() // @ts-expect-error latencies.ping += new Uint8Array(new Uint16Array([record.ping]).buffer).toHex() if (latencies.loc.v[latencies.loc.v.length - 1] !== record.loc) { latencies.loc.c.push(1) latencies.loc.v.push(record.loc) } else { latencies.loc.c[latencies.loc.c.length - 1] += 1 } } getFirstLatency(monitorId: string): LatencyRecord { let latencies = this.data.latency[monitorId] return { // @ts-expect-error time: new Uint32Array(Uint8Array.fromHex(latencies.time.slice(0, 8)).buffer)[0], // @ts-expect-error ping: new Uint16Array(Uint8Array.fromHex(latencies.ping.slice(0, 4)).buffer)[0], loc: latencies.loc.v[0], } } unshiftLatency(monitorId: string) { let latencies = this.data.latency[monitorId] latencies.time = latencies.time.slice(8) latencies.ping = latencies.ping.slice(4) latencies.loc.c[0] -= 1 if (latencies.loc.c[0] === 0) { latencies.loc.c.shift() latencies.loc.v.shift() } } }