import { getDb } from './db' // 상수 정의 const INTERVAL_SUCCESS = 5 * 60 * 1000 // 5분 const INTERVAL_FAILURE = 1 * 60 * 1000 // 1분 const REQUEST_TIMEOUT = 10 * 1000 // 10초 // 타입 정의 interface PubnetTarget { id: number name: string url: string is_active: number } interface PubnetStatus { id: number current_index: number check_interval: number is_healthy: number last_target_id: number | null last_checked_at: string | null scheduler_running: number } interface CheckResult { targetId: number targetName: string url: string isSuccess: boolean } class PubnetScheduler { private timer: ReturnType | null = null private isRunning: boolean = false /** * 스케줄러 시작 */ start(): void { if (this.isRunning) { console.log('[PubnetScheduler] Already running') return } this.isRunning = true this.updateSchedulerRunning(1) console.log('[PubnetScheduler] Started') // 즉시 첫 체크 실행 this.runCheck() } /** * 스케줄러 중지 */ stop(): void { if (this.timer) { clearTimeout(this.timer) this.timer = null } this.isRunning = false this.updateSchedulerRunning(0) console.log('[PubnetScheduler] Stopped') } /** * 실행 상태 확인 */ getIsRunning(): boolean { return this.isRunning } /** * 체크 실행 */ private async runCheck(): Promise { if (!this.isRunning) return try { const result = await this.checkCurrentTargets() // 결과에 따라 다음 간격 결정 const nextInterval = result.isSuccess ? INTERVAL_SUCCESS : INTERVAL_FAILURE // 로그 저장 (1개만) this.saveLog(result) // 상태 업데이트 (인덱스 +1) this.updateStatus(result, nextInterval) console.log( `[PubnetScheduler] ${result.targetName} (${result.url}) - ` + `${result.isSuccess ? 'SUCCESS' : 'FAILED'} - ` + `Next check in ${nextInterval / 1000}s` ) // 다음 체크 예약 this.timer = setTimeout(() => this.runCheck(), nextInterval) } catch (error) { console.error('[PubnetScheduler] Error:', error) // 에러 발생 시 1분 후 재시도 this.timer = setTimeout(() => this.runCheck(), INTERVAL_FAILURE) } } /** * 현재 타겟 1개 체크 */ private async checkCurrentTargets(): Promise { const db = getDb() // 활성화된 타겟 목록 조회 const targets = db.prepare(` SELECT * FROM pubnet_targets WHERE is_active = 1 ORDER BY id ASC `).all() as PubnetTarget[] if (targets.length === 0) { throw new Error('No active targets found') } // 현재 인덱스 조회 const status = db.prepare('SELECT * FROM pubnet_status WHERE id = 1').get() as PubnetStatus // 1개 타겟 선택 const idx = status.current_index % targets.length const target = targets[idx] // 단일 타겟 체크 let isSuccess = false try { const controller = new AbortController() const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT) const response = await fetch(target.url, { method: 'HEAD', signal: controller.signal, headers: { 'User-Agent': 'OSOLIT-Monitor/1.0' } }) clearTimeout(timeoutId) isSuccess = response.status === 200 } catch (err: any) { isSuccess = false } return { targetId: target.id, targetName: target.name, url: target.url, isSuccess } } /** * 체크 결과 로그 저장 */ private saveLog(result: CheckResult): void { const db = getDb() db.prepare(` INSERT INTO pubnet_logs (target_id, is_success) VALUES (@targetId, @isSuccess) `).run({ targetId: result.targetId, isSuccess: result.isSuccess ? 1 : 0 }) } /** * 상태 업데이트 (인덱스 +1 순환) */ private updateStatus(result: CheckResult, nextInterval: number): void { const db = getDb() // 활성 타겟 수 조회 const countResult = db.prepare(` SELECT COUNT(*) as cnt FROM pubnet_targets WHERE is_active = 1 `).get() as { cnt: number } const status = db.prepare('SELECT current_index FROM pubnet_status WHERE id = 1').get() as { current_index: number } // 인덱스 +1 (순환) const nextIndex = (status.current_index + 1) % countResult.cnt db.prepare(` UPDATE pubnet_status SET current_index = @nextIndex, check_interval = @checkInterval, is_healthy = @isHealthy, last_target_id = @lastTargetId, last_checked_at = datetime('now', 'localtime'), updated_at = datetime('now', 'localtime') WHERE id = 1 `).run({ nextIndex, checkInterval: nextInterval, isHealthy: result.isSuccess ? 1 : 0, lastTargetId: result.targetId }) } /** * 스케줄러 실행 상태 업데이트 */ private updateSchedulerRunning(running: number): void { const db = getDb() db.prepare(` UPDATE pubnet_status SET scheduler_running = @running, updated_at = datetime('now', 'localtime') WHERE id = 1 `).run({ running }) } } // 싱글톤 인스턴스 export const pubnetScheduler = new PubnetScheduler()