An atproto PDS written in Go
103
fork

Configure Feed

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

at v0.8.0 150 lines 4.6 kB view raw
1package server 2 3import ( 4 "context" 5 "time" 6 7 "github.com/Azure/go-autorest/autorest/to" 8 "github.com/bluesky-social/indigo/api/atproto" 9 "github.com/bluesky-social/indigo/events" 10 "github.com/bluesky-social/indigo/util" 11 "github.com/haileyok/cocoon/internal/helpers" 12 "github.com/labstack/echo/v4" 13 "golang.org/x/crypto/bcrypt" 14) 15 16type ComAtprotoServerDeleteAccountRequest struct { 17 Did string `json:"did" validate:"required"` 18 Password string `json:"password" validate:"required"` 19 Token string `json:"token" validate:"required"` 20} 21 22func (s *Server) handleServerDeleteAccount(e echo.Context) error { 23 ctx := e.Request().Context() 24 logger := s.logger.With("name", "handleServerDeleteAccount") 25 26 var req ComAtprotoServerDeleteAccountRequest 27 if err := e.Bind(&req); err != nil { 28 logger.Error("error binding", "error", err) 29 return helpers.ServerError(e, nil) 30 } 31 32 if err := e.Validate(&req); err != nil { 33 logger.Error("error validating", "error", err) 34 return helpers.ServerError(e, nil) 35 } 36 37 urepo, err := s.getRepoActorByDid(ctx, req.Did) 38 if err != nil { 39 logger.Error("error getting repo", "error", err) 40 return echo.NewHTTPError(400, "account not found") 41 } 42 43 if err := bcrypt.CompareHashAndPassword([]byte(urepo.Repo.Password), []byte(req.Password)); err != nil { 44 logger.Error("password mismatch", "error", err) 45 return echo.NewHTTPError(401, "Invalid did or password") 46 } 47 48 if urepo.Repo.AccountDeleteCode == nil || urepo.Repo.AccountDeleteCodeExpiresAt == nil { 49 logger.Error("no deletion token found for account") 50 return echo.NewHTTPError(400, map[string]interface{}{ 51 "error": "InvalidToken", 52 "message": "Token is invalid", 53 }) 54 } 55 56 if *urepo.Repo.AccountDeleteCode != req.Token { 57 logger.Error("deletion token mismatch") 58 return echo.NewHTTPError(400, map[string]interface{}{ 59 "error": "InvalidToken", 60 "message": "Token is invalid", 61 }) 62 } 63 64 if time.Now().UTC().After(*urepo.Repo.AccountDeleteCodeExpiresAt) { 65 logger.Error("deletion token expired") 66 return echo.NewHTTPError(400, map[string]interface{}{ 67 "error": "ExpiredToken", 68 "message": "Token is expired", 69 }) 70 } 71 72 tx := s.db.BeginDangerously(ctx) 73 if tx.Error != nil { 74 logger.Error("error starting transaction", "error", tx.Error) 75 return helpers.ServerError(e, nil) 76 } 77 78 status := "error" 79 func() { 80 if status == "error" { 81 if err := tx.Rollback().Error; err != nil { 82 logger.Error("error rolling back after delete failure", "err", err) 83 } 84 } 85 }() 86 87 if err := tx.Exec("DELETE FROM blocks WHERE did = ?", nil, req.Did).Error; err != nil { 88 logger.Error("error deleting blocks", "error", err) 89 return helpers.ServerError(e, nil) 90 } 91 92 if err := tx.Exec("DELETE FROM records WHERE did = ?", nil, req.Did).Error; err != nil { 93 logger.Error("error deleting records", "error", err) 94 return helpers.ServerError(e, nil) 95 } 96 97 if err := tx.Exec("DELETE FROM blobs WHERE did = ?", nil, req.Did).Error; err != nil { 98 logger.Error("error deleting blobs", "error", err) 99 return helpers.ServerError(e, nil) 100 } 101 102 if err := tx.Exec("DELETE FROM tokens WHERE did = ?", nil, req.Did).Error; err != nil { 103 logger.Error("error deleting tokens", "error", err) 104 return helpers.ServerError(e, nil) 105 } 106 107 if err := tx.Exec("DELETE FROM refresh_tokens WHERE did = ?", nil, req.Did).Error; err != nil { 108 logger.Error("error deleting refresh tokens", "error", err) 109 return helpers.ServerError(e, nil) 110 } 111 112 if err := tx.Exec("DELETE FROM reserved_keys WHERE did = ?", nil, req.Did).Error; err != nil { 113 logger.Error("error deleting reserved keys", "error", err) 114 return helpers.ServerError(e, nil) 115 } 116 117 if err := tx.Exec("DELETE FROM invite_codes WHERE did = ?", nil, req.Did).Error; err != nil { 118 logger.Error("error deleting invite codes", "error", err) 119 return helpers.ServerError(e, nil) 120 } 121 122 if err := tx.Exec("DELETE FROM actors WHERE did = ?", nil, req.Did).Error; err != nil { 123 logger.Error("error deleting actor", "error", err) 124 return helpers.ServerError(e, nil) 125 } 126 127 if err := tx.Exec("DELETE FROM repos WHERE did = ?", nil, req.Did).Error; err != nil { 128 logger.Error("error deleting repo", "error", err) 129 return helpers.ServerError(e, nil) 130 } 131 132 status = "ok" 133 134 if err := tx.Commit().Error; err != nil { 135 logger.Error("error committing transaction", "error", err) 136 return helpers.ServerError(e, nil) 137 } 138 139 s.evtman.AddEvent(context.TODO(), &events.XRPCStreamEvent{ 140 RepoAccount: &atproto.SyncSubscribeRepos_Account{ 141 Active: false, 142 Did: req.Did, 143 Status: to.StringPtr("deleted"), 144 Seq: time.Now().UnixMicro(), 145 Time: time.Now().Format(util.ISO8601), 146 }, 147 }) 148 149 return e.NoContent(200) 150}