Write on the margins of the internet. Powered by the AT Protocol. margin.at
extension web atproto comments
at main 130 lines 3.2 kB view raw
1package verification 2 3import ( 4 "fmt" 5 "io" 6 "net/http" 7 "net/url" 8 "regexp" 9 "strings" 10 "time" 11 12 "margin.at/internal/logger" 13) 14 15var client = &http.Client{ 16 Timeout: 10 * time.Second, 17 CheckRedirect: func(req *http.Request, via []*http.Request) error { 18 if len(via) >= 3 { 19 return fmt.Errorf("too many redirects") 20 } 21 return nil 22 }, 23} 24 25var linkTagPattern = regexp.MustCompile(`<link[^>]+rel=["']site\.standard\.document["'][^>]+href=["']([^"']+)["'][^>]*/?>|<link[^>]+href=["']([^"']+)["'][^>]+rel=["']site\.standard\.document["'][^>]*/?>`) 26 27func VerifyPublication(pubURL, expectedURI string) error { 28 pubURL = strings.TrimRight(pubURL, "/") 29 30 parsed, err := url.Parse(pubURL) 31 if err != nil { 32 return fmt.Errorf("invalid publication URL: %w", err) 33 } 34 35 wellKnownPath := "/.well-known/site.standard.publication" 36 if parsed.Path != "" && parsed.Path != "/" { 37 wellKnownPath += parsed.Path 38 } 39 wellKnownURL := fmt.Sprintf("%s://%s%s", parsed.Scheme, parsed.Host, wellKnownPath) 40 41 req, err := http.NewRequest("GET", wellKnownURL, nil) 42 if err != nil { 43 return fmt.Errorf("invalid URL: %w", err) 44 } 45 req.Header.Set("User-Agent", "Margin/1.0 (Standard.site verification)") 46 47 resp, err := client.Do(req) 48 if err != nil { 49 return fmt.Errorf("failed to fetch %s: %w", wellKnownURL, err) 50 } 51 defer resp.Body.Close() 52 53 if resp.StatusCode != http.StatusOK { 54 return fmt.Errorf("well-known endpoint returned %d", resp.StatusCode) 55 } 56 57 body, err := io.ReadAll(io.LimitReader(resp.Body, 1024)) 58 if err != nil { 59 return fmt.Errorf("failed to read response: %w", err) 60 } 61 62 returnedURI := strings.TrimSpace(string(body)) 63 if returnedURI != expectedURI { 64 return fmt.Errorf("URI mismatch: expected %s, got %s", expectedURI, returnedURI) 65 } 66 67 return nil 68} 69 70func VerifyDocument(docURL, expectedURI string) error { 71 req, err := http.NewRequest("GET", docURL, nil) 72 if err != nil { 73 return fmt.Errorf("invalid URL: %w", err) 74 } 75 req.Header.Set("User-Agent", "Margin/1.0 (Standard.site verification)") 76 77 resp, err := client.Do(req) 78 if err != nil { 79 return fmt.Errorf("failed to fetch %s: %w", docURL, err) 80 } 81 defer resp.Body.Close() 82 83 if resp.StatusCode != http.StatusOK { 84 return fmt.Errorf("document URL returned %d", resp.StatusCode) 85 } 86 87 body, err := io.ReadAll(io.LimitReader(resp.Body, 256*1024)) 88 if err != nil { 89 return fmt.Errorf("failed to read document: %w", err) 90 } 91 92 html := string(body) 93 94 matches := linkTagPattern.FindAllStringSubmatch(html, -1) 95 for _, m := range matches { 96 href := m[1] 97 if href == "" { 98 href = m[2] 99 } 100 if strings.TrimSpace(href) == expectedURI { 101 return nil 102 } 103 } 104 105 return fmt.Errorf("no matching <link rel=\"site.standard.document\"> tag found for %s", expectedURI) 106} 107 108func VerifyPublicationAsync(pubURL, uri string, onVerified func(string)) { 109 go func() { 110 if err := VerifyPublication(pubURL, uri); err != nil { 111 return 112 } 113 logger.Info("Publication verified: %s", uri) 114 if onVerified != nil { 115 onVerified(uri) 116 } 117 }() 118} 119 120func VerifyDocumentAsync(docURL, uri string, onVerified func(string)) { 121 go func() { 122 if err := VerifyDocument(docURL, uri); err != nil { 123 return 124 } 125 logger.Info("Document verified: %s", uri) 126 if onVerified != nil { 127 onVerified(uri) 128 } 129 }() 130}