add delete avatar and call it from deleteMe controller
This commit is contained in:
@@ -7,11 +7,13 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/umputun/remark/backend/app/rest/auth"
|
||||
"github.com/umputun/remark/backend/app/rest/proxy"
|
||||
|
||||
"github.com/umputun/remark/backend/app/migrator"
|
||||
"github.com/umputun/remark/backend/app/rest"
|
||||
@@ -27,6 +29,7 @@ type admin struct {
|
||||
cache cache.LoadingCache
|
||||
authenticator auth.Authenticator
|
||||
readOnlyAge int
|
||||
avatarProxy *proxy.Avatar
|
||||
}
|
||||
|
||||
func (a *admin) routes(middlewares ...func(http.Handler) http.Handler) chi.Router {
|
||||
@@ -118,6 +121,14 @@ func (a *admin) deleteMeRequestCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't delete user")
|
||||
return
|
||||
}
|
||||
|
||||
if claims.User.Picture != "" {
|
||||
if err := a.avatarProxy.Store.Remove(path.Base(claims.User.Picture)); err != nil {
|
||||
rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't delete user's avatar")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
a.cache.Flush(cache.Flusher(claims.SiteID).Scopes(claims.SiteID, claims.User.ID, "last"))
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, JSON{"user_id": claims.User.ID, "site_id": claims.SiteID})
|
||||
|
||||
@@ -115,6 +115,7 @@ func (s *Rest) routes() chi.Router {
|
||||
cache: s.Cache,
|
||||
authenticator: s.Authenticator,
|
||||
readOnlyAge: s.ReadOnlyAge,
|
||||
avatarProxy: s.AvatarProxy,
|
||||
}
|
||||
|
||||
ipFn := func(ip string) string { return store.HashValue(ip, s.DataService.Secret)[:12] } // logger uses it for anonymization
|
||||
|
||||
@@ -73,7 +73,7 @@ func (gf *GridFS) ID(avatar string) (id string) {
|
||||
return errors.Wrapf(e, "can't open avatar %s", avatar)
|
||||
}
|
||||
id = fh.MD5()
|
||||
return nil
|
||||
return errors.Wrapf(fh.Close(), "can't close avatar")
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] can't get file info '%s', %s", avatar, err)
|
||||
@@ -81,3 +81,15 @@ func (gf *GridFS) ID(avatar string) (id string) {
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// Remove avatar from gridfs
|
||||
func (gf *GridFS) Remove(avatar string) error {
|
||||
return gf.Connection.WithDB(func(dbase *mgo.Database) error {
|
||||
fh, e := dbase.GridFS("fs").Open(avatar)
|
||||
if e != nil {
|
||||
return errors.Wrapf(e, "can't get avatar %s", avatar)
|
||||
}
|
||||
_ = fh.Close()
|
||||
return dbase.GridFS("fs").Remove(avatar)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -34,6 +34,21 @@ func TestGridFS_PutAndGet(t *testing.T) {
|
||||
assert.Equal(t, "70c881d4a26984ddce795f6f71817c9cf4480e79", p.ID("aaaa"), "no data, encode avatar id")
|
||||
}
|
||||
|
||||
func TestGridFS_Remove(t *testing.T) {
|
||||
p, skip := prepGFStore(t)
|
||||
if skip {
|
||||
return
|
||||
}
|
||||
|
||||
assert.NotNil(t, p.Remove("no-such-thing.image"))
|
||||
|
||||
avatar, err := p.Put("user1", strings.NewReader("some picture bin data"))
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "b3daa77b4c04a9551b8781d03191fe098f325e67.image", avatar)
|
||||
assert.NoError(t, p.Remove("b3daa77b4c04a9551b8781d03191fe098f325e67.image"), "remove real one")
|
||||
assert.NotNil(t, p.Remove("b3daa77b4c04a9551b8781d03191fe098f325e67.image"), "already removed")
|
||||
}
|
||||
|
||||
func prepGFStore(t *testing.T) (Store, bool) {
|
||||
conn, err := mongo.MakeTestConnection(t)
|
||||
if err != nil {
|
||||
|
||||
@@ -88,6 +88,13 @@ func (fs *LocalFS) ID(avatar string) (id string) {
|
||||
return store.EncodeID(avatar + strconv.FormatInt(fi.ModTime().Unix(), 10))
|
||||
}
|
||||
|
||||
// Remove avatar file
|
||||
func (fs *LocalFS) Remove(avatar string) error {
|
||||
location := fs.location(strings.TrimSuffix(avatar, imgSfx))
|
||||
avFile := path.Join(location, avatar)
|
||||
return os.Remove(avFile)
|
||||
}
|
||||
|
||||
// get location (directory) for user id by adding partition to final path in order to keep files
|
||||
// in different subdirectories and avoid too many files in a single place.
|
||||
// the end result is a full path like this - /tmp/avatars.test/92
|
||||
|
||||
@@ -110,6 +110,22 @@ func TestAvatarStoreFS_ID(t *testing.T) {
|
||||
assert.Equal(t, "325d5b451f32c2f8e7f30a9fd65bff6a42954d9a", id) // store.EncodeID("b3daa77b4c04a9551b8781d03191fe098f325e67.image1500000000")
|
||||
}
|
||||
|
||||
func TestAvatarStoreFS_Remove(t *testing.T) {
|
||||
p := NewLocalFS("/tmp/avatars.test", 300)
|
||||
err := os.MkdirAll("/tmp/avatars.test/30", 0700)
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll("/tmp/avatars.test")
|
||||
|
||||
assert.NotNil(t, p.Remove("no-such-avatar"), "remove non-existing avatar")
|
||||
err = ioutil.WriteFile("/tmp/avatars.test/30/b3daa77b4c04a9551b8781d03191fe098f325e67.image", []byte("something"), 0666)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NoError(t, p.Remove("b3daa77b4c04a9551b8781d03191fe098f325e67.image"))
|
||||
_, err = os.Stat("/tmp/avatars.test/30/b3daa77b4c04a9551b8781d03191fe098f325e67.image")
|
||||
assert.NotNil(t, err, "removed for real")
|
||||
t.Log(err)
|
||||
}
|
||||
|
||||
func BenchmarkAvatarStoreFS_ID(b *testing.B) {
|
||||
p := NewLocalFS("/tmp/avatars.test", 300)
|
||||
os.MkdirAll("/tmp/avatars.test/30", 0700)
|
||||
|
||||
@@ -22,9 +22,11 @@ const imgSfx = ".image"
|
||||
|
||||
// Store defines interface to store and and load avatars
|
||||
type Store interface {
|
||||
Put(userID string, reader io.Reader) (avatarID string, err error) // save avatar data from the given reader and return base name
|
||||
Put(userID string, reader io.Reader) (avatarID string, err error) // save avatar data from the reader and return base name
|
||||
Get(avatarID string) (reader io.ReadCloser, size int, err error) // load avatar via reader
|
||||
ID(avatarID string) (id string) // unique id of stored avatar's data
|
||||
Remove(avatarID string) error // remove avatar data
|
||||
|
||||
}
|
||||
|
||||
// resize an image of supported format (PNG, JPG, GIF) to the size of "limit" px of the biggest side
|
||||
|
||||
Reference in New Issue
Block a user