forked from
nekomimi.pet/wisp.place-monorepo
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}