Experimenting with AT Protocol to hit up your friends

Add basic logging and convert from fmt.Fprint*

Changed files
+84 -65
cmd
appview
internal
+5 -3
cmd/appview/main.go
··· 1 1 package main 2 2 3 3 import ( 4 - "fmt" 4 + "log/slog" 5 5 "net/http" 6 - "os" 7 6 7 + "atyo.app/internal/logging" 8 8 appview "atyo.app/internal/server" 9 9 ) 10 10 11 11 func main() { 12 + logging.Level.Set(slog.LevelDebug) // for active development 13 + 12 14 server := appview.NewServer() 13 15 mux := server.InitializeRoutes() 14 16 15 17 // TODO configurable listen address, TLS, etc 16 18 listenAddr := "127.0.0.1:3000" 17 - fmt.Fprintln(os.Stderr, "Starting server on http://"+listenAddr) 19 + slog.Info("Starting server on http://" + listenAddr) 18 20 http.ListenAndServe(listenAddr, mux) 19 21 }
+2
go.mod
··· 37 37 github.com/ipfs/go-log v1.0.5 // indirect 38 38 github.com/ipfs/go-log/v2 v2.5.1 // indirect 39 39 github.com/ipfs/go-metrics-interface v0.0.1 // indirect 40 + github.com/jba/slog v0.2.0 // indirect 40 41 github.com/jbenet/goprocess v0.1.4 // indirect 41 42 github.com/klauspost/cpuid/v2 v2.2.7 // indirect 42 43 github.com/mattn/go-isatty v0.0.20 // indirect ··· 49 50 github.com/multiformats/go-multihash v0.2.3 // indirect 50 51 github.com/multiformats/go-varint v0.0.7 // indirect 51 52 github.com/opentracing/opentracing-go v1.2.0 // indirect 53 + github.com/phsym/console-slog v0.3.1 // indirect 52 54 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 53 55 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect 54 56 github.com/prometheus/client_golang v1.17.0 // indirect
+4
go.sum
··· 67 67 github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= 68 68 github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= 69 69 github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= 70 + github.com/jba/slog v0.2.0 h1:jI0U5NRR3EJKGsbeEVpItJNogk0c4RMeCl7vJmogCJI= 71 + github.com/jba/slog v0.2.0/go.mod h1:0Dh7Vyz3Td68Z1OwzadfincHwr7v+PpzadrS2Jua338= 70 72 github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= 71 73 github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= 72 74 github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= ··· 104 106 github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= 105 107 github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= 106 108 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= 109 + github.com/phsym/console-slog v0.3.1 h1:Fuzcrjr40xTc004S9Kni8XfNsk+qrptQmyR+wZw9/7A= 110 + github.com/phsym/console-slog v0.3.1/go.mod h1:oJskjp/X6e6c0mGpfP8ELkfKUsrkDifYRAqJQgmdDS0= 107 111 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 108 112 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 109 113 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+2 -3
internal/auth/auth.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "fmt" 6 - "os" 5 + "log/slog" 7 6 8 7 "atyo.app/internal/lookup" 9 8 "github.com/bluesky-social/indigo/api/atproto" ··· 50 49 Rkey: rkey, 51 50 }) 52 51 53 - fmt.Fprintln(os.Stderr, "Created record: "+output.Uri) 52 + slog.Info("Created ping record", "uri", output.Uri) 54 53 55 54 return err 56 55 }
+2 -2
internal/auth/basic.go
··· 3 3 import ( 4 4 "context" 5 5 "fmt" 6 - "os" 6 + "log/slog" 7 7 8 8 "github.com/bluesky-social/indigo/api/atproto" 9 9 "github.com/bluesky-social/indigo/atproto/identity" ··· 26 26 return nil, fmt.Errorf("failed to retrieve account PDS") 27 27 } 28 28 29 - fmt.Fprintln(os.Stderr, "sending login request to "+pds) 29 + slog.Info("sending login request", "pds", pds) 30 30 31 31 // clear out existing auth first 32 32 // TODO: use refresh token etc instead of this
+24
internal/logging/logging.go
··· 1 + // Package log provides a simple facade for [log/slog] to write text logs to 2 + // stderr. TODO: access logs to stdout. 3 + 4 + package logging 5 + 6 + import ( 7 + "log/slog" 8 + "os" 9 + 10 + "github.com/phsym/console-slog" 11 + ) 12 + 13 + const rfc3339Millis = "2006-01-02 15:04:05.000Z07:00" 14 + 15 + var Level = new(slog.LevelVar) // Info by default 16 + 17 + func init() { 18 + logger := slog.New(console.NewHandler(os.Stderr, &console.HandlerOptions{ 19 + AddSource: true, 20 + TimeFormat: rfc3339Millis, 21 + })) 22 + 23 + slog.SetDefault(logger) 24 + }
+2 -6
internal/lookup/identity.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "fmt" 5 + "log/slog" 6 6 "net/http" 7 - "os" 8 7 "strings" 9 8 10 9 "atyo.app/internal/errs" ··· 39 38 } 40 39 } 41 40 42 - fmt.Fprintln( 43 - os.Stderr, 44 - "resolved to `"+targetId.Handle.String()+"` ("+targetId.DID.String()+")", 45 - ) 41 + slog.Debug("resolved identity", "did", targetId.DID, "handle", targetId.Handle) 46 42 47 43 return targetId, nil 48 44 }
+28 -35
internal/ping/ingest_pings.go
··· 5 5 "encoding/hex" 6 6 "errors" 7 7 "fmt" 8 + "log/slog" 8 9 "net/http" 9 - "os" 10 10 "slices" 11 11 12 12 "atyo.app/api" ··· 64 64 for _, repo := range response.Repos { 65 65 id, lookupErr := c.directory.LookupID(ctx, repo.Did) 66 66 if lookupErr != nil { 67 - fmt.Fprintf(os.Stderr, "failed to resolve id %s: %s\n", repo.Did, lookupErr) 67 + slog.Warn("failed to resolve identity", "did", repo.Did, "err", lookupErr) 68 68 continue 69 69 } 70 70 ··· 84 84 ) 85 85 86 86 if err != nil { 87 - fmt.Fprintf(os.Stderr, "failed to fetch from %s: %s\n", repo.Did, err) 87 + slog.Warn("failed to fetch repo", "did", repo.Did, "err", err) 88 88 continue 89 89 } 90 - fmt.Fprintf(os.Stderr, "got %d records for %s\n", len(records.Records), repo.Did) 90 + slog.Debug("got repo records", "did", repo.Did, "count", len(records.Records)) 91 91 92 92 for _, record := range records.Records { 93 93 ping, ok := record.Value.Val.(*atyo.Ping) 94 94 if !ok { 95 - fmt.Fprintf(os.Stderr, "failed to convert %s to ping", record.Uri) 95 + slog.Warn("failed to convert to ping", "uri", record.Uri) 96 96 continue 97 97 } 98 98 99 99 contents, err := decodePingContents(ping, key) 100 100 if err != nil { 101 - fmt.Fprintln(os.Stderr, "failed to decode ping ", record.Uri, " - ", err) 101 + slog.Warn("failed to decode ping", "uri", record.Uri, "err", err) 102 102 continue 103 103 } 104 - if contents != nil { 105 - from, _ := c.directory.LookupID(ctx, repo.Did) 106 - var fromHandle *string 107 - if from != nil { 108 - handle := from.Handle.String() 109 - fromHandle = &handle 110 - } 111 104 112 - result = append(result, Ping{ 113 - FromDID: repo.Did, 114 - FromHandle: fromHandle, 115 - Contents: contents, 116 - }) 117 - } else { 118 - fmt.Fprintln(os.Stderr, "ping ", record.Uri, "not targeted to self") 105 + if contents == nil { 106 + slog.Debug("ping not targeted to self", "uri", record.Uri) 107 + continue 108 + } 109 + 110 + from, _ := c.directory.LookupID(ctx, repo.Did) 111 + var fromHandle *string 112 + if from != nil { 113 + handle := from.Handle.String() 114 + fromHandle = &handle 119 115 } 116 + 117 + result = append(result, Ping{ 118 + FromDID: repo.Did, 119 + FromHandle: fromHandle, 120 + Contents: contents, 121 + }) 120 122 } 121 123 } 122 124 ··· 143 145 var sharedKey *[sharedKeyLen]byte 144 146 145 147 for len(contents) > encryptedHeaderLen { 146 - fmt.Fprintf( 147 - os.Stderr, 148 - "attempting to decode header bytes %s\n", 149 - hex.EncodeToString(contents[:encryptedHeaderLen]), 148 + slog.Debug( 149 + "attempting to decode header", 150 + "bytes", hex.EncodeToString(contents[:encryptedHeaderLen]), 150 151 ) 151 152 header, ok := box.Open( 152 153 nil, ··· 157 158 ) 158 159 159 160 if ok { 160 - fmt.Fprintln(os.Stderr, "Success! found shared key for ping") 161 + slog.Info("found shared key for ping") 161 162 162 163 numRecipients := int(header[0]) 163 164 sharedKey = new([sharedKeyLen]byte) ··· 171 172 172 173 contents = contents[encryptedHeaderLen:] 173 174 174 - fmt.Fprintf( 175 - os.Stderr, 176 - "failed to decode header, %d bytes remain\n", 177 - len(contents), 178 - ) 175 + slog.Debug("failed to decode header", "remaining", len(contents)) 179 176 } 180 177 181 178 if sharedKey == nil { ··· 217 214 return nil, fmt.Errorf("unexpected type %T decoded", val) 218 215 } 219 216 220 - fmt.Fprintf( 221 - os.Stderr, 222 - "Decoded contents: %s\nCBOR: %+v\n", 223 - hex.EncodeToString(decryptedContents), pingContents, 224 - ) 217 + slog.Debug("decoded ping", "contents", pingContents) 225 218 226 219 return pingContents, nil 227 220 }
+3 -2
internal/ping/keys.go
··· 3 3 import ( 4 4 "context" 5 5 "crypto/rand" 6 + "encoding/hex" 6 7 "fmt" 7 - "os" 8 + "log/slog" 8 9 9 10 "atyo.app/api" 10 11 "atyo.app/api/atyo" ··· 137 138 return err 138 139 } 139 140 140 - fmt.Fprintln(os.Stderr, "created pubkey record") 141 + slog.Info("created pubkey record", "public_key", hex.EncodeToString(selfPubKey[:])) 141 142 142 143 k.selfPrivateKey = selfPrivKey 143 144 return nil
+6 -9
internal/ping/send_ping.go
··· 7 7 "encoding/hex" 8 8 "errors" 9 9 "fmt" 10 - "os" 10 + "log/slog" 11 11 12 12 "atyo.app/api" 13 13 "atyo.app/api/atyo" ··· 27 27 // Basic ping functionality. 28 28 // - `target` should be a DID or handle 29 29 func (c *Client) SendPing(ctx context.Context, target string) error { 30 - fmt.Fprintln(os.Stderr, "got request for ping to `"+target+"`") 30 + slog.Info("Got ping request", "target", target) 31 31 32 32 targetId, err := c.directory.LookupID(ctx, target) 33 33 if err != nil { ··· 111 111 // CBOR/JSON encoding, or a schema version? idk if that's really useful for any reason 112 112 contents := append(encHeader, encryptedMessage...) 113 113 114 - fmt.Fprintf( 115 - os.Stderr, 116 - "Targeting ping to pubkey %s\nHeader bytes: %s\n", 117 - hex.EncodeToString(targetPubkey[:]), 118 - hex.EncodeToString(encHeader[:]), 114 + slog.Debug( 115 + "built ping", 116 + "target_pubkey", hex.EncodeToString(targetPubkey[:]), 117 + "len", len(contents), 119 118 ) 120 - 121 - fmt.Fprintln(os.Stderr, "contents total len: ", len(contents)) 122 119 123 120 return &atyo.Ping{ 124 121 Contents: contents,
+6 -5
internal/server/router.go
··· 3 3 import ( 4 4 "encoding/json" 5 5 "errors" 6 - "fmt" 6 + "log/slog" 7 7 "net/http" 8 - "os" 9 8 10 9 "atyo.app/internal/auth" 11 10 "atyo.app/internal/errs" ··· 85 84 86 85 _, err = w.Write(bytes) 87 86 if err != nil { 88 - fmt.Fprintln(os.Stderr, "failed to write response: "+err.Error()) 87 + slog.Error("failed to write response", "err", err) 89 88 } 90 89 } 91 90 ··· 93 92 user := req.FormValue("username") 94 93 pass := req.FormValue("password") 95 94 96 - fmt.Fprintln(os.Stderr, "Attempting login as `"+user+"`") 95 + slog.Info("Attempting login", "user", user) 97 96 98 97 var authFactorToken *string 99 98 if req.Form.Has("authFactorToken") { ··· 129 128 130 129 _, err = w.Write(bytes) 131 130 if err != nil { 132 - fmt.Fprintln(os.Stderr, "failed to write JSON response: "+err.Error()) 131 + slog.Warn("failed to write JSON response", "err", err) 133 132 } 134 133 } 135 134 136 135 func handleErr(w http.ResponseWriter, err error) { 136 + slog.Info("sending error response", "err", err) 137 + 137 138 var xErr *xrpc.Error 138 139 if errors.As(err, &xErr) { 139 140 // Hmm maybe this should be a 50x or certain codes should translate...