GoAT Site is library that implements Standard.site in Go.
atprotocol standard-site atproto library
1
fork

Configure Feed

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

feat(verify): verification method for document

+94
+65
document.go
··· 1 1 package site 2 2 3 3 import ( 4 + "context" 4 5 "encoding/json" 6 + "errors" 5 7 "fmt" 6 8 "html/template" 9 + "net/http" 10 + "net/url" 7 11 "strings" 8 12 "time" 9 13 10 14 "github.com/bluesky-social/indigo/atproto/syntax" 15 + lexutil "github.com/bluesky-social/indigo/lex/util" 16 + "golang.org/x/net/html" 11 17 ) 12 18 13 19 const CollectionDocument = CollectionBase + ".document" ··· 101 107 } 102 108 *d = Document(v.t) 103 109 return nil 110 + } 111 + 112 + // PublicationURL returns the [url.URL] of the linked [Publication]. 113 + // 114 + // Prefer using [GetRecord] if you plan to retrieve the record. 115 + // Under the hood, this method retrieves the [Publication] record if [Document.Site] is an [ATURL]. 116 + func (d *Document) PublicationURL(ctx context.Context, client lexutil.LexClient) (*url.URL, error) { 117 + if !d.Site.IsAT() { 118 + return d.Site.URL(), nil 119 + } 120 + at := d.Site.AT() 121 + pub, err := GetRecord[*Publication](ctx, client, at.Authority(), at.RecordKey()) 122 + if err != nil { 123 + return nil, err 124 + } 125 + return pub.URL, nil 126 + } 127 + 128 + var ErrCannotVerifyWithNoPath = errors.New("cannot verify a document with no path") 129 + 130 + // Verify the [Document]. 131 + func (d *Document) Verify(ctx context.Context, client *http.Client, pubURL *url.URL, repo syntax.AtIdentifier, rkey syntax.RecordKey) (bool, error) { 132 + if d.Path == nil { 133 + return false, ErrCannotVerifyWithNoPath 134 + } 135 + req, err := http.NewRequest(http.MethodGet, pubURL.String()+*d.Path, nil) 136 + if err != nil { 137 + return false, err 138 + } 139 + resp, err := client.Do(req.WithContext(ctx)) 140 + if err != nil { 141 + return false, err 142 + } 143 + defer resp.Body.Close() 144 + doc, err := html.Parse(resp.Body) 145 + if err != nil { 146 + return false, err 147 + } 148 + for node := range doc.Descendants() { 149 + if node.Type == html.ElementNode && node.Data == "link" { 150 + var rel string 151 + var href string 152 + for _, attr := range node.Attr { 153 + switch attr.Key { 154 + case "rel": 155 + rel = attr.Val 156 + case "href": 157 + href = attr.Val 158 + } 159 + if rel != "" && rel != CollectionDocument { 160 + break 161 + } 162 + if href == getDocumentVerification(repo, rkey) { 163 + return true, nil 164 + } 165 + } 166 + } 167 + } 168 + return false, nil 104 169 } 105 170 106 171 // GetDocumentVerificationTag returns the HTML link tag checked during the verification of the [Document].
+28
document_test.go
··· 121 121 t.Errorf("invalid tag: %s", tag) 122 122 } 123 123 } 124 + 125 + func TestDocument_Verify(t *testing.T) { 126 + if testing.Short() { 127 + t.Skip("not doing http requests in short") 128 + } 129 + uri, client := getClient(t, testDoc, &docURI, &docClient) 130 + doc, err := site.GetRecord[*site.Document](context.Background(), client, uri.Authority(), uri.RecordKey()) 131 + if err != nil { 132 + t.Fatal(err) 133 + } 134 + pubURL, err := doc.PublicationURL(context.Background(), client) 135 + if err != nil { 136 + t.Fatal(err) 137 + } 138 + valid, err := doc.Verify( 139 + context.Background(), 140 + httpClient(client), 141 + pubURL, 142 + uri.Authority(), 143 + uri.RecordKey(), 144 + ) 145 + if err != nil { 146 + t.Fatal(err) 147 + } 148 + if !valid { 149 + t.Errorf("cannot verify %s", uri) 150 + } 151 + }
+1
publication.go
··· 84 84 if err != nil { 85 85 return false, err 86 86 } 87 + defer resp.Body.Close() 87 88 b, err := io.ReadAll(resp.Body) 88 89 if err != nil { 89 90 return false, err