An atproto PDS written in Go
1package server 2 3import ( 4 "context" 5 "strings" 6 "time" 7 8 "github.com/Azure/go-autorest/autorest/to" 9 "github.com/bluesky-social/indigo/api/atproto" 10 "github.com/bluesky-social/indigo/atproto/crypto" 11 "github.com/bluesky-social/indigo/events" 12 "github.com/bluesky-social/indigo/util" 13 "github.com/haileyok/cocoon/identity" 14 "github.com/haileyok/cocoon/internal/helpers" 15 "github.com/haileyok/cocoon/models" 16 "github.com/labstack/echo/v4" 17) 18 19type ComAtprotoIdentityUpdateHandleRequest struct { 20 Handle string `json:"handle" validate:"atproto-handle"` 21} 22 23func (s *Server) handleIdentityUpdateHandle(e echo.Context) error { 24 repo := e.Get("repo").(*models.RepoActor) 25 26 var req ComAtprotoIdentityUpdateHandleRequest 27 if err := e.Bind(&req); err != nil { 28 s.logger.Error("error binding", "error", err) 29 return helpers.ServerError(e, nil) 30 } 31 32 req.Handle = strings.ToLower(req.Handle) 33 34 if err := e.Validate(req); err != nil { 35 return helpers.InputError(e, nil) 36 } 37 38 ctx := context.WithValue(e.Request().Context(), "skip-cache", true) 39 40 if strings.HasPrefix(repo.Repo.Did, "did:plc:") { 41 log, err := identity.FetchDidAuditLog(ctx, repo.Repo.Did) 42 if err != nil { 43 s.logger.Error("error fetching doc", "error", err) 44 return helpers.ServerError(e, nil) 45 } 46 47 latest := log[len(log)-1] 48 49 k, err := crypto.ParsePrivateBytesK256(repo.SigningKey) 50 if err != nil { 51 s.logger.Error("error parsing signing key", "error", err) 52 return helpers.ServerError(e, nil) 53 } 54 55 op, err := s.plcClient.FormatAndSignAtprotoOp(k, req.Handle, latest.Operation.RotationKeys, &latest.Cid) 56 if err != nil { 57 return err 58 } 59 60 if err := s.plcClient.SendOperation(context.TODO(), repo.Repo.Did, op); err != nil { 61 return err 62 } 63 } 64 65 s.evtman.AddEvent(context.TODO(), &events.XRPCStreamEvent{ 66 RepoHandle: &atproto.SyncSubscribeRepos_Handle{ 67 Did: repo.Repo.Did, 68 Handle: req.Handle, 69 Seq: time.Now().UnixMicro(), // TODO: no 70 Time: time.Now().Format(util.ISO8601), 71 }, 72 }) 73 74 s.evtman.AddEvent(context.TODO(), &events.XRPCStreamEvent{ 75 RepoIdentity: &atproto.SyncSubscribeRepos_Identity{ 76 Did: repo.Repo.Did, 77 Handle: to.StringPtr(req.Handle), 78 Seq: time.Now().UnixMicro(), // TODO: no 79 Time: time.Now().Format(util.ISO8601), 80 }, 81 }) 82 83 if err := s.db.Exec("UPDATE actors SET handle = ? WHERE did = ?", req.Handle, repo.Repo.Did).Error; err != nil { 84 s.logger.Error("error updating handle in db", "error", err) 85 return helpers.ServerError(e, nil) 86 } 87 88 return nil 89}