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