An atproto PDS written in Go
103
fork

Configure Feed

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

at 0.0.2 203 lines 4.2 kB view raw
1package identity 2 3import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net" 9 "net/http" 10 "strings" 11 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 "github.com/bluesky-social/indigo/util" 14) 15 16func ResolveHandle(ctx context.Context, cli *http.Client, handle string) (string, error) { 17 if cli == nil { 18 cli = util.RobustHTTPClient() 19 } 20 21 var did string 22 23 _, err := syntax.ParseHandle(handle) 24 if err != nil { 25 return "", err 26 } 27 28 recs, err := net.LookupTXT(fmt.Sprintf("_atproto.%s", handle)) 29 if err == nil { 30 for _, rec := range recs { 31 if strings.HasPrefix(rec, "did=") { 32 did = strings.Split(rec, "did=")[1] 33 break 34 } 35 } 36 } else { 37 fmt.Printf("erorr getting txt records: %v\n", err) 38 } 39 40 if did == "" { 41 req, err := http.NewRequestWithContext( 42 ctx, 43 "GET", 44 fmt.Sprintf("https://%s/.well-known/atproto-did", handle), 45 nil, 46 ) 47 if err != nil { 48 return "", nil 49 } 50 51 resp, err := http.DefaultClient.Do(req) 52 if err != nil { 53 return "", nil 54 } 55 defer resp.Body.Close() 56 57 if resp.StatusCode != http.StatusOK { 58 io.Copy(io.Discard, resp.Body) 59 return "", fmt.Errorf("unable to resolve handle") 60 } 61 62 b, err := io.ReadAll(resp.Body) 63 if err != nil { 64 return "", err 65 } 66 67 maybeDid := string(b) 68 69 if _, err := syntax.ParseDID(maybeDid); err != nil { 70 return "", fmt.Errorf("unable to resolve handle") 71 } 72 73 did = maybeDid 74 } 75 76 return did, nil 77} 78 79func FetchDidDoc(ctx context.Context, cli *http.Client, did string) (*DidDoc, error) { 80 if cli == nil { 81 cli = util.RobustHTTPClient() 82 } 83 84 var ustr string 85 if strings.HasPrefix(did, "did:plc:") { 86 ustr = fmt.Sprintf("https://plc.directory/%s", did) 87 } else if strings.HasPrefix(did, "did:web:") { 88 ustr = fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")) 89 } else { 90 return nil, fmt.Errorf("did was not a supported did type") 91 } 92 93 req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil) 94 if err != nil { 95 return nil, err 96 } 97 98 resp, err := http.DefaultClient.Do(req) 99 if err != nil { 100 return nil, err 101 } 102 defer resp.Body.Close() 103 104 if resp.StatusCode != 200 { 105 io.Copy(io.Discard, resp.Body) 106 return nil, fmt.Errorf("could not find identity in plc registry") 107 } 108 109 var diddoc DidDoc 110 if err := json.NewDecoder(resp.Body).Decode(&diddoc); err != nil { 111 return nil, err 112 } 113 114 return &diddoc, nil 115} 116 117func FetchDidData(ctx context.Context, cli *http.Client, did string) (*DidData, error) { 118 if cli == nil { 119 cli = util.RobustHTTPClient() 120 } 121 122 var ustr string 123 ustr = fmt.Sprintf("https://plc.directory/%s/data", did) 124 125 req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil) 126 if err != nil { 127 return nil, err 128 } 129 130 resp, err := http.DefaultClient.Do(req) 131 if err != nil { 132 return nil, err 133 } 134 defer resp.Body.Close() 135 136 if resp.StatusCode != 200 { 137 io.Copy(io.Discard, resp.Body) 138 return nil, fmt.Errorf("could not find identity in plc registry") 139 } 140 141 var diddata DidData 142 if err := json.NewDecoder(resp.Body).Decode(&diddata); err != nil { 143 return nil, err 144 } 145 146 return &diddata, nil 147} 148 149func FetchDidAuditLog(ctx context.Context, cli *http.Client, did string) (DidAuditLog, error) { 150 if cli == nil { 151 cli = util.RobustHTTPClient() 152 } 153 154 var ustr string 155 ustr = fmt.Sprintf("https://plc.directory/%s/log/audit", did) 156 157 req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil) 158 if err != nil { 159 return nil, err 160 } 161 162 resp, err := http.DefaultClient.Do(req) 163 if err != nil { 164 return nil, err 165 } 166 defer resp.Body.Close() 167 168 if resp.StatusCode != 200 { 169 io.Copy(io.Discard, resp.Body) 170 return nil, fmt.Errorf("could not find identity in plc registry") 171 } 172 173 var didlog DidAuditLog 174 if err := json.NewDecoder(resp.Body).Decode(&didlog); err != nil { 175 return nil, err 176 } 177 178 return didlog, nil 179} 180 181func ResolveService(ctx context.Context, cli *http.Client, did string) (string, error) { 182 if cli == nil { 183 cli = util.RobustHTTPClient() 184 } 185 186 diddoc, err := FetchDidDoc(ctx, cli, did) 187 if err != nil { 188 return "", err 189 } 190 191 var service string 192 for _, svc := range diddoc.Service { 193 if svc.Id == "#atproto_pds" { 194 service = svc.ServiceEndpoint 195 } 196 } 197 198 if service == "" { 199 return "", fmt.Errorf("could not find atproto_pds service in identity services") 200 } 201 202 return service, nil 203}