Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place

domain check api

Changed files
+71 -6
src
lib
routes
+37
src/lib/db.ts
··· 120 120 return rows.length === 0; 121 121 }; 122 122 123 + export const isDomainRegistered = async (domain: string) => { 124 + const domainLower = domain.toLowerCase().trim(); 125 + 126 + // Check wisp.place subdomains 127 + const wispDomain = await db` 128 + SELECT did, domain, rkey FROM domains WHERE domain = ${domainLower} 129 + `; 130 + 131 + if (wispDomain.length > 0) { 132 + return { 133 + registered: true, 134 + type: 'wisp' as const, 135 + domain: wispDomain[0].domain, 136 + did: wispDomain[0].did, 137 + rkey: wispDomain[0].rkey 138 + }; 139 + } 140 + 141 + // Check custom domains 142 + const customDomain = await db` 143 + SELECT id, domain, did, rkey, verified FROM custom_domains WHERE domain = ${domainLower} 144 + `; 145 + 146 + if (customDomain.length > 0) { 147 + return { 148 + registered: true, 149 + type: 'custom' as const, 150 + domain: customDomain[0].domain, 151 + did: customDomain[0].did, 152 + rkey: customDomain[0].rkey, 153 + verified: customDomain[0].verified 154 + }; 155 + } 156 + 157 + return { registered: false }; 158 + }; 159 + 123 160 export const claimDomain = async (did: string, handle: string): Promise<string> => { 124 161 const h = handle.trim().toLowerCase(); 125 162 if (!isValidHandle(h)) throw new Error('invalid_handle');
+34 -6
src/routes/domain.ts
··· 6 6 claimDomain, 7 7 getDomainByDid, 8 8 isDomainAvailable, 9 + isDomainRegistered, 9 10 isValidHandle, 10 11 toDomain, 11 12 updateDomain, ··· 22 23 23 24 export const domainRoutes = (client: NodeOAuthClient) => 24 25 new Elysia({ prefix: '/api/domain' }) 25 - .derive(async ({ cookie }) => { 26 - const auth = await requireAuth(client, cookie) 27 - return { auth } 28 - }) 26 + // Public endpoints (no auth required) 29 27 .get('/check', async ({ query }) => { 30 28 try { 31 29 const handle = (query.handle || "") 32 30 .trim() 33 31 .toLowerCase(); 34 - 32 + 35 33 if (!isValidHandle(handle)) { 36 34 return { 37 35 available: false, 38 36 reason: "invalid" 39 37 }; 40 38 } 41 - 39 + 42 40 const available = await isDomainAvailable(handle); 43 41 return { 44 42 available, ··· 50 48 available: false 51 49 }; 52 50 } 51 + }) 52 + .get('/registered', async ({ query, set }) => { 53 + try { 54 + const domain = (query.domain || "").trim().toLowerCase(); 55 + 56 + if (!domain) { 57 + set.status = 400; 58 + return { error: 'Domain parameter required' }; 59 + } 60 + 61 + const result = await isDomainRegistered(domain); 62 + 63 + // For Caddy on-demand TLS: 200 = allow, 404 = deny 64 + if (result.registered) { 65 + set.status = 200; 66 + return result; 67 + } else { 68 + set.status = 404; 69 + return { registered: false }; 70 + } 71 + } catch (err) { 72 + console.error("domain/registered error", err); 73 + set.status = 500; 74 + return { error: 'Failed to check domain' }; 75 + } 76 + }) 77 + // Authenticated endpoints (require auth) 78 + .derive(async ({ cookie }) => { 79 + const auth = await requireAuth(client, cookie) 80 + return { auth } 53 81 }) 54 82 .post('/claim', async ({ body, auth }) => { 55 83 try {