Monorepo for Wisp.place. A static site hosting service built on top of the AT Protocol.
1import { verifyCustomDomain } from './dns-verify'; 2import { db } from './db'; 3 4interface VerificationStats { 5 totalChecked: number; 6 verified: number; 7 failed: number; 8 errors: number; 9} 10 11export class DNSVerificationWorker { 12 private interval: Timer | null = null; 13 private isRunning = false; 14 private lastRunTime: number | null = null; 15 private stats: VerificationStats = { 16 totalChecked: 0, 17 verified: 0, 18 failed: 0, 19 errors: 0, 20 }; 21 22 constructor( 23 private checkIntervalMs: number = 60 * 60 * 1000, // 1 hour default 24 private onLog?: (message: string, data?: any) => void 25 ) {} 26 27 private log(message: string, data?: any) { 28 if (this.onLog) { 29 this.onLog(message, data); 30 } 31 } 32 33 async start() { 34 if (this.isRunning) { 35 this.log('DNS verification worker already running'); 36 return; 37 } 38 39 this.isRunning = true; 40 this.log('Starting DNS verification worker', { 41 intervalMinutes: this.checkIntervalMs / 60000, 42 }); 43 44 // Run immediately on start 45 await this.verifyAllDomains(); 46 47 // Then run on interval 48 this.interval = setInterval(() => { 49 this.verifyAllDomains(); 50 }, this.checkIntervalMs); 51 } 52 53 stop() { 54 if (this.interval) { 55 clearInterval(this.interval); 56 this.interval = null; 57 } 58 this.isRunning = false; 59 this.log('DNS verification worker stopped'); 60 } 61 62 private async verifyAllDomains() { 63 this.log('Starting DNS verification check'); 64 const startTime = Date.now(); 65 66 const runStats: VerificationStats = { 67 totalChecked: 0, 68 verified: 0, 69 failed: 0, 70 errors: 0, 71 }; 72 73 try { 74 // Get all verified custom domains 75 const domains = await db` 76 SELECT id, domain, did FROM custom_domains WHERE verified = true 77 `; 78 79 if (!domains || domains.length === 0) { 80 this.log('No verified custom domains to check'); 81 this.lastRunTime = Date.now(); 82 return; 83 } 84 85 this.log(`Checking ${domains.length} verified custom domains`); 86 87 // Verify each domain 88 for (const row of domains) { 89 runStats.totalChecked++; 90 const { id, domain, did } = row; 91 92 try { 93 // Extract hash from id (SHA256 of did:domain) 94 const expectedHash = id.substring(0, 16); 95 96 // Verify DNS records 97 const result = await verifyCustomDomain(domain, did, expectedHash); 98 99 if (result.verified) { 100 // Update last_verified_at timestamp 101 await db` 102 UPDATE custom_domains 103 SET last_verified_at = EXTRACT(EPOCH FROM NOW()) 104 WHERE id = ${id} 105 `; 106 runStats.verified++; 107 this.log(`Domain verified: ${domain}`, { did }); 108 } else { 109 // Mark domain as unverified 110 await db` 111 UPDATE custom_domains 112 SET verified = false, 113 last_verified_at = EXTRACT(EPOCH FROM NOW()) 114 WHERE id = ${id} 115 `; 116 runStats.failed++; 117 this.log(`Domain verification failed: ${domain}`, { 118 did, 119 error: result.error, 120 found: result.found, 121 }); 122 } 123 } catch (error) { 124 runStats.errors++; 125 this.log(`Error verifying domain: ${domain}`, { 126 did, 127 error: error instanceof Error ? error.message : String(error), 128 }); 129 } 130 } 131 132 // Update cumulative stats 133 this.stats.totalChecked += runStats.totalChecked; 134 this.stats.verified += runStats.verified; 135 this.stats.failed += runStats.failed; 136 this.stats.errors += runStats.errors; 137 138 const duration = Date.now() - startTime; 139 this.lastRunTime = Date.now(); 140 141 this.log('DNS verification check completed', { 142 duration: `${duration}ms`, 143 ...runStats, 144 }); 145 } catch (error) { 146 this.log('Fatal error in DNS verification worker', { 147 error: error instanceof Error ? error.message : String(error), 148 }); 149 } 150 } 151 152 getHealth() { 153 return { 154 isRunning: this.isRunning, 155 lastRunTime: this.lastRunTime, 156 intervalMs: this.checkIntervalMs, 157 stats: this.stats, 158 healthy: this.isRunning && ( 159 this.lastRunTime === null || 160 Date.now() - this.lastRunTime < this.checkIntervalMs * 2 161 ), 162 }; 163 } 164 165 // Manual trigger for testing 166 async trigger() { 167 this.log('Manual DNS verification triggered'); 168 await this.verifyAllDomains(); 169 } 170}