An atproto PDS written in Go
1package server 2 3import ( 4 "bytes" 5 6 "github.com/Azure/go-autorest/autorest/to" 7 "github.com/haileyok/cocoon/internal/helpers" 8 "github.com/haileyok/cocoon/models" 9 "github.com/ipfs/go-cid" 10 "github.com/labstack/echo/v4" 11) 12 13func (s *Server) handleSyncGetBlob(e echo.Context) error { 14 did := e.QueryParam("did") 15 if did == "" { 16 return helpers.InputError(e, nil) 17 } 18 19 cstr := e.QueryParam("cid") 20 if cstr == "" { 21 return helpers.InputError(e, nil) 22 } 23 24 c, err := cid.Parse(cstr) 25 if err != nil { 26 return helpers.InputError(e, nil) 27 } 28 29 urepo, err := s.getRepoActorByDid(did) 30 if err != nil { 31 s.logger.Error("could not find user for requested blob", "error", err) 32 return helpers.InputError(e, nil) 33 } 34 35 status := urepo.Status() 36 if status != nil { 37 if *status == "deactivated" { 38 return helpers.InputError(e, to.StringPtr("RepoDeactivated")) 39 } 40 } 41 42 var blob models.Blob 43 if err := s.db.Raw("SELECT * FROM blobs WHERE did = ? AND cid = ?", nil, did, c.Bytes()).Scan(&blob).Error; err != nil { 44 s.logger.Error("error looking up blob", "error", err) 45 return helpers.ServerError(e, nil) 46 } 47 48 buf := new(bytes.Buffer) 49 50 var parts []models.BlobPart 51 if err := s.db.Raw("SELECT * FROM blob_parts WHERE blob_id = ? ORDER BY idx", nil, blob.ID).Scan(&parts).Error; err != nil { 52 s.logger.Error("error getting blob parts", "error", err) 53 return helpers.ServerError(e, nil) 54 } 55 56 // TODO: we can just stream this, don't need to make a buffer 57 for _, p := range parts { 58 buf.Write(p.Data) 59 } 60 61 e.Response().Header().Set(echo.HeaderContentDisposition, "attachment; filename="+c.String()) 62 63 return e.Stream(200, "application/octet-stream", buf) 64}