81 lines
1.7 KiB
TypeScript
81 lines
1.7 KiB
TypeScript
import { logger } from '../../utils/logger.js'
|
|
|
|
export interface HeartbeatCallback {
|
|
(): Promise<void>
|
|
}
|
|
|
|
export interface HeartbeatState {
|
|
lastHeartbeat: Date
|
|
isRunning: boolean
|
|
}
|
|
|
|
const DEFAULT_HEARTBEAT_INTERVAL = 60000
|
|
|
|
export class HeartbeatService {
|
|
private interval: NodeJS.Timeout | null = null
|
|
private lastHeartbeat: Date = new Date()
|
|
private readonly intervalMs: number
|
|
private callback: HeartbeatCallback | null = null
|
|
|
|
constructor(intervalMs: number = DEFAULT_HEARTBEAT_INTERVAL) {
|
|
this.intervalMs = intervalMs
|
|
}
|
|
|
|
setCallback(callback: HeartbeatCallback): void {
|
|
this.callback = callback
|
|
}
|
|
|
|
start(): void {
|
|
if (this.interval) {
|
|
this.stop()
|
|
}
|
|
|
|
this.interval = setInterval(async () => {
|
|
if (this.callback) {
|
|
try {
|
|
this.lastHeartbeat = new Date()
|
|
await this.callback()
|
|
} catch (err) {
|
|
logger.error('Heartbeat callback failed:', err)
|
|
}
|
|
}
|
|
}, this.intervalMs)
|
|
|
|
this.lastHeartbeat = new Date()
|
|
}
|
|
|
|
stop(): void {
|
|
if (this.interval) {
|
|
clearInterval(this.interval)
|
|
this.interval = null
|
|
}
|
|
}
|
|
|
|
isRunning(): boolean {
|
|
return this.interval !== null
|
|
}
|
|
|
|
getLastHeartbeat(): Date {
|
|
return this.lastHeartbeat
|
|
}
|
|
|
|
updateHeartbeat(): void {
|
|
this.lastHeartbeat = new Date()
|
|
}
|
|
|
|
getState(): HeartbeatState {
|
|
return {
|
|
lastHeartbeat: this.lastHeartbeat,
|
|
isRunning: this.isRunning()
|
|
}
|
|
}
|
|
|
|
restoreState(state: { lastHeartbeat: string }): void {
|
|
this.lastHeartbeat = new Date(state.lastHeartbeat)
|
|
}
|
|
}
|
|
|
|
export const createHeartbeatService = (intervalMs?: number): HeartbeatService => {
|
|
return new HeartbeatService(intervalMs)
|
|
}
|