diff --git a/api/v2/api.go b/api/v2/api.go index fd95fa6c7..eea211a4d 100644 --- a/api/v2/api.go +++ b/api/v2/api.go @@ -543,6 +543,7 @@ func (api *API) setupRoutes(debug bool) error { { pin.POST("/:hash", api.pinHashLocally) pin.POST("/:hash/extend", api.extendPin) + pin.DELETE("/remove/:hash", api.removePin) } // file upload routes file := public.Group("/file") diff --git a/api/v2/routes_rtfs.go b/api/v2/routes_rtfs.go index 5823ebe5f..561c4da8f 100644 --- a/api/v2/routes_rtfs.go +++ b/api/v2/routes_rtfs.go @@ -3,6 +3,7 @@ package v2 import ( "bytes" "errors" + "fmt" "html" "io" "io/ioutil" @@ -394,3 +395,31 @@ func (api *API) extendPin(c *gin.Context) { // return Respond(c, http.StatusOK, gin.H{"response": "pin time successfully extended"}) } + +func (api *API) removePin(c *gin.Context) { + username, err := GetAuthenticatedUserFromContext(c) + if err != nil { + api.LogError(c, err, eh.NoAPITokenError)(http.StatusBadRequest) + return + } + // dont let free users be able to unpin as this can be used as an + // attack vector to spam ipfs nodes with pins + if usg, err := api.usage.FindByUserName(username); err != nil { + api.LogError(c, err, eh.UserSearchError)(http.StatusBadRequest) + return + } else if usg.Tier == models.Free { + Fail(c, errors.New("free tier accounts are unable to remove pins, please upgrade your tier"), http.StatusBadRequest) + return + } + // validate hash + hash := c.Param("hash") + if _, err := gocid.Decode(hash); err != nil { + Fail(c, err) + return + } + if err := api.upm.RemovePin(username, hash, "public"); err != nil { + api.LogError(c, err, fmt.Sprint(eh.PinRemovalError+"hash: "+hash))(http.StatusBadRequest) + return + } + Respond(c, http.StatusOK, gin.H{"response": "pin successfuly removed with partial cost refunded"}) +} diff --git a/eh/errors.go b/eh/errors.go index 65dcd9ab2..a53256a95 100644 --- a/eh/errors.go +++ b/eh/errors.go @@ -134,4 +134,6 @@ const ( MaxHoldTimeError = "a hold time of this long would result in a longer maximum pin time than what your account allow, please reduce your hold time and try again" // HostNameNotFoundError is an error message when api server has not hostname HostNameNotFoundError = "an api host has not hostname, please set hostname" + // PinRemovalError is an error message when we failed to remove a pin + PinRemovalError = "failed to remove pin and refund partial cost" )