add delete avatar and call it from deleteMe controller

This commit is contained in:
Umputun
2018-07-25 12:48:57 -04:00
parent 5c9d0d6119
commit 1c97e0d31f
7 changed files with 66 additions and 2 deletions
+11
View File
@@ -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})
+1
View File
@@ -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
+13 -1
View File
@@ -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)
})
}
+15
View File
@@ -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 {
+7
View File
@@ -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
+16
View File
@@ -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)
+3 -1
View File
@@ -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