my own indieAuth provider! indiko.dunkirk.sh/docs
indieauth oauth2-server
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

bug: fix parser for verification

+52 -11
+52 -11
src/routes/indieauth.ts
··· 342 342 method: "GET", 343 343 headers: { 344 344 Accept: "text/html", 345 + "User-Agent": "indiko/1.0 (+https://indiko.dunkirk.sh/)", 345 346 }, 346 347 signal: controller.signal, 347 348 }); ··· 349 350 clearTimeout(timeoutId); 350 351 351 352 if (!response.ok) { 353 + const errorBody = await response.text(); 354 + console.error(`[verifyDomain] Failed to fetch ${domainUrl}: HTTP ${response.status}`, { 355 + status: response.status, 356 + contentType: response.headers.get("content-type"), 357 + bodyPreview: errorBody.substring(0, 200), 358 + }); 352 359 return { success: false, error: `Failed to fetch domain: HTTP ${response.status}` }; 353 360 } 354 361 355 362 const html = await response.text(); 356 363 357 - // Look for <link rel="me"> or <a rel="me"> pointing to indiko profile 358 - // Match both <link> and <a> tags with rel="me" 359 - const relMeRegex = /<(?:link|a)\s+[^>]*rel=["']me["'][^>]*href=["']([^"']+)["'][^>]*>/gi; 360 - const relMeRegex2 = /<(?:link|a)\s+[^>]*href=["']([^"']+)["'][^>]*rel=["']me["'][^>]*>/gi; 364 + // Extract rel="me" links using regex 365 + // Matches both <link> and <a> tags with rel attribute containing "me" 366 + const relMeLinks: string[] = []; 361 367 362 - const relMeLinks: string[] = []; 363 - let match: RegExpExecArray | null; 368 + // Simpler approach: find all link and a tags, then check if they have rel="me" and href 369 + const linkRegex = /<link\s+[^>]*>/gi; 370 + const aRegex = /<a\s+[^>]*>/gi; 371 + 372 + const processTag = (tagHtml: string) => { 373 + // Check if has rel containing "me" (handle quoted and unquoted attributes) 374 + const relMatch = tagHtml.match(/rel=["']?([^"'\s>]+)["']?/i); 375 + if (!relMatch) return null; 376 + 377 + const relValue = relMatch[1]; 378 + // Check if "me" is a separate word in the rel attribute 379 + if (!relValue.split(/\s+/).includes("me")) return null; 380 + 381 + // Extract href (handle quoted and unquoted attributes) 382 + const hrefMatch = tagHtml.match(/href=["']?([^"'\s>]+)["']?/i); 383 + if (!hrefMatch) return null; 384 + 385 + return hrefMatch[1]; 386 + }; 364 387 365 - while ((match = relMeRegex.exec(html)) !== null) { 366 - relMeLinks.push(match[1]); 388 + // Process all link tags 389 + let linkMatch; 390 + while ((linkMatch = linkRegex.exec(html)) !== null) { 391 + const href = processTag(linkMatch[0]); 392 + if (href && !relMeLinks.includes(href)) { 393 + relMeLinks.push(href); 394 + } 367 395 } 368 396 369 - while ((match = relMeRegex2.exec(html)) !== null) { 370 - if (!relMeLinks.includes(match[1])) { 371 - relMeLinks.push(match[1]); 397 + // Process all a tags 398 + let aMatch; 399 + while ((aMatch = aRegex.exec(html)) !== null) { 400 + const href = processTag(aMatch[0]); 401 + if (href && !relMeLinks.includes(href)) { 402 + relMeLinks.push(href); 372 403 } 373 404 } 374 405 ··· 384 415 }); 385 416 386 417 if (!hasRelMe) { 418 + console.error(`[verifyDomain] No rel="me" link found on ${domainUrl} pointing to ${indikoProfileUrl}`, { 419 + foundLinks: relMeLinks, 420 + normalizedTarget: normalizedIndikoUrl, 421 + }); 387 422 return { 388 423 success: false, 389 424 error: `Domain must have <link rel="me" href="${indikoProfileUrl}" /> or <a rel="me" href="${indikoProfileUrl}">...</a> to verify ownership`, ··· 394 429 } catch (error) { 395 430 if (error instanceof Error) { 396 431 if (error.name === "AbortError") { 432 + console.error(`[verifyDomain] Timeout verifying ${domainUrl}`); 397 433 return { success: false, error: "Timeout verifying domain" }; 398 434 } 435 + console.error(`[verifyDomain] Error verifying ${domainUrl}: ${error.message}`, { 436 + name: error.name, 437 + stack: error.stack, 438 + }); 399 439 return { success: false, error: `Failed to verify domain: ${error.message}` }; 400 440 } 441 + console.error(`[verifyDomain] Unknown error verifying ${domainUrl}:`, error); 401 442 return { success: false, error: "Failed to verify domain" }; 402 443 } 403 444 }