mirror of
https://github.com/ferdzo/fs.git
synced 2026-04-05 17:06:26 +00:00
Updated error handling to be S3 XML compatible. Implemented DeleteObject.
This commit is contained in:
73
api/api.go
73
api/api.go
@@ -1,7 +1,9 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"fs/metadata"
|
||||||
"fs/service"
|
"fs/service"
|
||||||
"fs/utils"
|
"fs/utils"
|
||||||
"io"
|
"io"
|
||||||
@@ -46,6 +48,7 @@ func (h *Handler) setupRoutes() {
|
|||||||
h.router.Get("/{bucket}/*", h.handleGetObject)
|
h.router.Get("/{bucket}/*", h.handleGetObject)
|
||||||
h.router.Put("/{bucket}/*", h.handlePutObject)
|
h.router.Put("/{bucket}/*", h.handlePutObject)
|
||||||
h.router.Head("/{bucket}/*", h.handleHeadObject)
|
h.router.Head("/{bucket}/*", h.handleHeadObject)
|
||||||
|
h.router.Delete("/{bucket}/*", h.handleDeleteObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) handleWelcome(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) handleWelcome(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -59,22 +62,27 @@ func (h *Handler) handleWelcome(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (h *Handler) handleGetObject(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) handleGetObject(w http.ResponseWriter, r *http.Request) {
|
||||||
bucket := chi.URLParam(r, "bucket")
|
bucket := chi.URLParam(r, "bucket")
|
||||||
key := chi.URLParam(r, "*")
|
key := chi.URLParam(r, "*")
|
||||||
|
|
||||||
if key == "" {
|
if key == "" {
|
||||||
http.Error(w, "object key is required", http.StatusBadRequest)
|
writeS3Error(w, r, s3ErrInvalidObjectKey, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.URL.Query().Get("uploadId") != "" {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
stream, manifest, err := h.svc.GetObject(bucket, key)
|
stream, manifest, err := h.svc.GetObject(bucket, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
writeMappedS3Error(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", manifest.ContentType)
|
w.Header().Set("Content-Type", manifest.ContentType)
|
||||||
w.Header().Set("Content-Length", strconv.FormatInt(manifest.Size, 10))
|
w.Header().Set("Content-Length", strconv.FormatInt(manifest.Size, 10))
|
||||||
w.Header().Set("ETag", manifest.ETag)
|
w.Header().Set("ETag", `"`+manifest.ETag+`"`)
|
||||||
|
w.Header().Set("Last-Modified", time.Unix(manifest.CreatedAt, 0).UTC().Format(http.TimeFormat))
|
||||||
w.Header().Set("Accept-Ranges", "bytes")
|
w.Header().Set("Accept-Ranges", "bytes")
|
||||||
w.Header().Set("Last-Modified", time.Unix(manifest.CreatedAt, 0).UTC().Format(time.RFC1123))
|
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
_, err = io.Copy(w, stream)
|
_, err = io.Copy(w, stream)
|
||||||
|
|
||||||
@@ -84,7 +92,7 @@ func (h *Handler) handlePutObject(w http.ResponseWriter, r *http.Request) {
|
|||||||
bucket := chi.URLParam(r, "bucket")
|
bucket := chi.URLParam(r, "bucket")
|
||||||
key := chi.URLParam(r, "*")
|
key := chi.URLParam(r, "*")
|
||||||
if key == "" {
|
if key == "" {
|
||||||
http.Error(w, "object key is required", http.StatusBadRequest)
|
writeS3Error(w, r, s3ErrInvalidObjectKey, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,11 +105,11 @@ func (h *Handler) handlePutObject(w http.ResponseWriter, r *http.Request) {
|
|||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
writeMappedS3Error(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("ETag", manifest.ETag)
|
w.Header().Set("ETag", `"`+manifest.ETag+`"`)
|
||||||
w.Header().Set("Content-Length", "0")
|
w.Header().Set("Content-Length", "0")
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
@@ -111,26 +119,26 @@ func (h *Handler) handleHeadObject(w http.ResponseWriter, r *http.Request) {
|
|||||||
bucket := chi.URLParam(r, "bucket")
|
bucket := chi.URLParam(r, "bucket")
|
||||||
key := chi.URLParam(r, "*")
|
key := chi.URLParam(r, "*")
|
||||||
if key == "" {
|
if key == "" {
|
||||||
http.Error(w, "object key is required", http.StatusBadRequest)
|
writeS3Error(w, r, s3ErrInvalidObjectKey, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest, err := h.svc.HeadObject(bucket, key)
|
manifest, err := h.svc.HeadObject(bucket, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusNotFound)
|
writeMappedS3Error(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("ETag", manifest.ETag)
|
w.Header().Set("ETag", `"`+manifest.ETag+`"`)
|
||||||
w.Header().Set("Content-Length", "0")
|
w.Header().Set("Content-Length", "0")
|
||||||
w.Header().Set("Last-Modified", time.Unix(manifest.CreatedAt, 0).UTC().Format(time.RFC1123))
|
w.Header().Set("Last-Modified", time.Unix(manifest.CreatedAt, 0).UTC().Format(http.TimeFormat))
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) handlePutBucket(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) handlePutBucket(w http.ResponseWriter, r *http.Request) {
|
||||||
bucket := chi.URLParam(r, "bucket")
|
bucket := chi.URLParam(r, "bucket")
|
||||||
if h.svc.CreateBucket(bucket) != nil {
|
if err := h.svc.CreateBucket(bucket); err != nil {
|
||||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
writeMappedS3Error(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
@@ -138,17 +146,37 @@ func (h *Handler) handlePutBucket(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (h *Handler) handleDeleteBucket(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) handleDeleteBucket(w http.ResponseWriter, r *http.Request) {
|
||||||
bucket := chi.URLParam(r, "bucket")
|
bucket := chi.URLParam(r, "bucket")
|
||||||
if h.svc.DeleteBucket(bucket) != nil {
|
if err := h.svc.DeleteBucket(bucket); err != nil {
|
||||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
writeMappedS3Error(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusAccepted)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) handleDeleteObject(w http.ResponseWriter, r *http.Request) {
|
||||||
|
bucket := chi.URLParam(r, "bucket")
|
||||||
|
key := chi.URLParam(r, "*")
|
||||||
|
if key == "" {
|
||||||
|
writeS3Error(w, r, s3ErrInvalidObjectKey, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := h.svc.DeleteObject(bucket, key)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, metadata.ErrObjectNotFound) {
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeMappedS3Error(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) handleHeadBucket(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) handleHeadBucket(w http.ResponseWriter, r *http.Request) {
|
||||||
bucket := chi.URLParam(r, "bucket")
|
bucket := chi.URLParam(r, "bucket")
|
||||||
if h.svc.HeadBucket(bucket) != nil {
|
if err := h.svc.HeadBucket(bucket); err != nil {
|
||||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
writeMappedS3Error(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
@@ -157,7 +185,7 @@ func (h *Handler) handleHeadBucket(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (h *Handler) handleGetBuckets(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) handleGetBuckets(w http.ResponseWriter, r *http.Request) {
|
||||||
buckets, err := h.svc.ListBuckets()
|
buckets, err := h.svc.ListBuckets()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
writeMappedS3Error(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/xml")
|
w.Header().Set("Content-Type", "application/xml")
|
||||||
@@ -178,20 +206,20 @@ func (h *Handler) handleGetBucket(w http.ResponseWriter, r *http.Request) {
|
|||||||
h.handleListObjectsV2(w, r, bucket, prefix)
|
h.handleListObjectsV2(w, r, bucket, prefix)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
http.Error(w, "NotImplemented", http.StatusNotImplemented)
|
writeS3Error(w, r, s3ErrNotImplemented, r.URL.Path)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) handleListObjectsV2(w http.ResponseWriter, r *http.Request, bucket, prefix string) {
|
func (h *Handler) handleListObjectsV2(w http.ResponseWriter, r *http.Request, bucket, prefix string) {
|
||||||
objects, err := h.svc.ListObjects(bucket, prefix)
|
objects, err := h.svc.ListObjects(bucket, prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
writeMappedS3Error(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
xmlResponse, err := utils.ConstructXMLResponseForObjectList(bucket, objects)
|
xmlResponse, err := utils.ConstructXMLResponseForObjectList(bucket, objects)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
writeMappedS3Error(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +228,6 @@ func (h *Handler) handleListObjectsV2(w http.ResponseWriter, r *http.Request, bu
|
|||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
_, err = w.Write([]byte(xmlResponse))
|
_, err = w.Write([]byte(xmlResponse))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,14 @@ var systemIndex = []byte("__SYSTEM_BUCKETS__")
|
|||||||
|
|
||||||
var validBucketName = regexp.MustCompile(`^[a-z0-9.-]{3,63}$`)
|
var validBucketName = regexp.MustCompile(`^[a-z0-9.-]{3,63}$`)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidBucketName = errors.New("invalid bucket name")
|
||||||
|
ErrBucketAlreadyExists = errors.New("bucket already exists")
|
||||||
|
ErrBucketNotFound = errors.New("bucket not found")
|
||||||
|
ErrBucketNotEmpty = errors.New("bucket not empty")
|
||||||
|
ErrObjectNotFound = errors.New("object not found")
|
||||||
|
)
|
||||||
|
|
||||||
func NewMetadataHandler(dbPath string) (*MetadataHandler, error) {
|
func NewMetadataHandler(dbPath string) (*MetadataHandler, error) {
|
||||||
db, err := bbolt.Open(dbPath, 0600, nil)
|
db, err := bbolt.Open(dbPath, 0600, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -41,7 +49,7 @@ func NewMetadataHandler(dbPath string) (*MetadataHandler, error) {
|
|||||||
|
|
||||||
func (h *MetadataHandler) CreateBucket(bucketName string) error {
|
func (h *MetadataHandler) CreateBucket(bucketName string) error {
|
||||||
if !validBucketName.MatchString(bucketName) {
|
if !validBucketName.MatchString(bucketName) {
|
||||||
return fmt.Errorf("invalid bucket name: %s", bucketName)
|
return fmt.Errorf("%w: %s", ErrInvalidBucketName, bucketName)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := h.db.Update(func(tx *bbolt.Tx) error {
|
err := h.db.Update(func(tx *bbolt.Tx) error {
|
||||||
@@ -50,7 +58,7 @@ func (h *MetadataHandler) CreateBucket(bucketName string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if indexBucket.Get([]byte(bucketName)) != nil {
|
if indexBucket.Get([]byte(bucketName)) != nil {
|
||||||
return fmt.Errorf("bucket %s already exists", bucketName)
|
return fmt.Errorf("%w: %s", ErrBucketAlreadyExists, bucketName)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.CreateBucketIfNotExists([]byte(bucketName))
|
_, err = tx.CreateBucketIfNotExists([]byte(bucketName))
|
||||||
@@ -73,7 +81,7 @@ func (h *MetadataHandler) CreateBucket(bucketName string) error {
|
|||||||
|
|
||||||
func (h *MetadataHandler) DeleteBucket(bucketName string) error {
|
func (h *MetadataHandler) DeleteBucket(bucketName string) error {
|
||||||
if !validBucketName.MatchString(bucketName) {
|
if !validBucketName.MatchString(bucketName) {
|
||||||
return fmt.Errorf("invalid bucket name: %s", bucketName)
|
return fmt.Errorf("%w: %s", ErrInvalidBucketName, bucketName)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := h.db.Update(func(tx *bbolt.Tx) error {
|
err := h.db.Update(func(tx *bbolt.Tx) error {
|
||||||
@@ -82,7 +90,14 @@ func (h *MetadataHandler) DeleteBucket(bucketName string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if indexBucket.Get([]byte(bucketName)) == nil {
|
if indexBucket.Get([]byte(bucketName)) == nil {
|
||||||
return fmt.Errorf("bucket %s not found", bucketName)
|
return fmt.Errorf("%w: %s", ErrBucketNotFound, bucketName)
|
||||||
|
}
|
||||||
|
metadataBucket := tx.Bucket([]byte(bucketName))
|
||||||
|
if metadataBucket == nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrBucketNotFound, bucketName)
|
||||||
|
}
|
||||||
|
if k, _ := metadataBucket.Cursor().First(); k != nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrBucketNotEmpty, bucketName)
|
||||||
}
|
}
|
||||||
if err := tx.DeleteBucket([]byte(bucketName)); err != nil && !errors.Is(err, bbolt.ErrBucketNotFound) {
|
if err := tx.DeleteBucket([]byte(bucketName)); err != nil && !errors.Is(err, bbolt.ErrBucketNotFound) {
|
||||||
return fmt.Errorf("error deleting metadata bucket %s: %w", bucketName, err)
|
return fmt.Errorf("error deleting metadata bucket %s: %w", bucketName, err)
|
||||||
@@ -127,7 +142,7 @@ func (h *MetadataHandler) GetBucketManifest(bucketName string) (*models.BucketMa
|
|||||||
}
|
}
|
||||||
data := systemIndexBucket.Get([]byte(bucketName))
|
data := systemIndexBucket.Get([]byte(bucketName))
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return fmt.Errorf("bucket manifest not found for bucket %s", bucketName)
|
return fmt.Errorf("%w: %s", ErrBucketNotFound, bucketName)
|
||||||
}
|
}
|
||||||
err := json.Unmarshal(data, &manifest)
|
err := json.Unmarshal(data, &manifest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -157,7 +172,7 @@ func (h *MetadataHandler) PutManifest(manifest *models.ObjectManifest) error {
|
|||||||
}
|
}
|
||||||
metadataBucket := tx.Bucket([]byte(bucket))
|
metadataBucket := tx.Bucket([]byte(bucket))
|
||||||
if metadataBucket == nil {
|
if metadataBucket == nil {
|
||||||
return fmt.Errorf("metadata bucket %s not found; create it first", bucket)
|
return fmt.Errorf("%w: %s", ErrBucketNotFound, bucket)
|
||||||
}
|
}
|
||||||
return metadataBucket.Put([]byte(key), data)
|
return metadataBucket.Put([]byte(key), data)
|
||||||
})
|
})
|
||||||
@@ -173,12 +188,12 @@ func (h *MetadataHandler) GetManifest(bucket, key string) (*models.ObjectManifes
|
|||||||
err := h.db.View(func(tx *bbolt.Tx) error {
|
err := h.db.View(func(tx *bbolt.Tx) error {
|
||||||
metadataBucket := tx.Bucket([]byte(bucket))
|
metadataBucket := tx.Bucket([]byte(bucket))
|
||||||
if metadataBucket == nil {
|
if metadataBucket == nil {
|
||||||
return fmt.Errorf("bucket %s not found", bucket)
|
return fmt.Errorf("%w: %s", ErrBucketNotFound, bucket)
|
||||||
}
|
}
|
||||||
data := metadataBucket.Get([]byte(key))
|
data := metadataBucket.Get([]byte(key))
|
||||||
if data == nil {
|
if data == nil {
|
||||||
|
|
||||||
return fmt.Errorf("manifest not found for bucket %s and key %s", bucket, key)
|
return fmt.Errorf("%w: %s/%s", ErrObjectNotFound, bucket, key)
|
||||||
}
|
}
|
||||||
err := json.Unmarshal(data, &manifest)
|
err := json.Unmarshal(data, &manifest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -203,11 +218,11 @@ func (h *MetadataHandler) ListObjects(bucket, prefix string) ([]*models.ObjectMa
|
|||||||
return errors.New("system index not found")
|
return errors.New("system index not found")
|
||||||
}
|
}
|
||||||
if systemIndexBucket.Get([]byte(bucket)) == nil {
|
if systemIndexBucket.Get([]byte(bucket)) == nil {
|
||||||
return fmt.Errorf("bucket %s not found", bucket)
|
return fmt.Errorf("%w: %s", ErrBucketNotFound, bucket)
|
||||||
}
|
}
|
||||||
_bucket := tx.Bucket([]byte(bucket))
|
_bucket := tx.Bucket([]byte(bucket))
|
||||||
if _bucket == nil {
|
if _bucket == nil {
|
||||||
return fmt.Errorf("bucket %s not found", bucket)
|
return fmt.Errorf("%w: %s", ErrBucketNotFound, bucket)
|
||||||
}
|
}
|
||||||
err := _bucket.ForEach(func(k, v []byte) error {
|
err := _bucket.ForEach(func(k, v []byte) error {
|
||||||
if prefix != "" && !strings.HasPrefix(string(k), prefix) {
|
if prefix != "" && !strings.HasPrefix(string(k), prefix) {
|
||||||
@@ -231,3 +246,22 @@ func (h *MetadataHandler) ListObjects(bucket, prefix string) ([]*models.ObjectMa
|
|||||||
}
|
}
|
||||||
return objects, nil
|
return objects, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *MetadataHandler) DeleteManifest(bucket, key string) error {
|
||||||
|
if _, err := h.GetManifest(bucket, key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err := h.db.Update(func(tx *bbolt.Tx) error {
|
||||||
|
metadataBucket := tx.Bucket([]byte(bucket))
|
||||||
|
if metadataBucket == nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrBucketNotFound, bucket)
|
||||||
|
}
|
||||||
|
return metadataBucket.Delete([]byte(key))
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,15 @@ type BucketManifest struct {
|
|||||||
PublicAccessBlock bool `json:"public_access_block"`
|
PublicAccessBlock bool `json:"public_access_block"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type S3ErrorResponse struct {
|
||||||
|
XMLName xml.Name `xml:"Error"`
|
||||||
|
Code string `xml:"Code"`
|
||||||
|
Message string `xml:"Message"`
|
||||||
|
Resource string `xml:"Resource,omitempty"`
|
||||||
|
RequestID string `xml:"RequestId,omitempty"`
|
||||||
|
HostID string `xml:"HostId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type ListBucketResult struct {
|
type ListBucketResult struct {
|
||||||
XMLName xml.Name `xml:"ListBucketResult"`
|
XMLName xml.Name `xml:"ListBucketResult"`
|
||||||
Xmlns string `xml:"xmlns,attr"`
|
Xmlns string `xml:"xmlns,attr"`
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ func (s *ObjectService) HeadObject(bucket, key string) (models.ObjectManifest, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *ObjectService) DeleteObject(bucket, key string) error {
|
func (s *ObjectService) DeleteObject(bucket, key string) error {
|
||||||
return nil
|
return s.metadataHandler.DeleteManifest(bucket, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ObjectService) ListObjects(bucket, prefix string) ([]*models.ObjectManifest, error) {
|
func (s *ObjectService) ListObjects(bucket, prefix string) ([]*models.ObjectManifest, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user