Skip to content

Commit 40adb5c

Browse files
committed
Add DELETE /settings/avatar route to delete the stored avatar file
1 parent d3114c8 commit 40adb5c

6 files changed

Lines changed: 77 additions & 11 deletions

File tree

docs/settings.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,25 @@ PUT /settings/avatar HTTP/1.1
10961096
Host: alice.cozy.example.net
10971097
Authorization: Bearer token
10981098
Content-Type: image/jpeg
1099+
...
1100+
```
1101+
1102+
#### Response
1103+
1104+
```http
1105+
HTTP/1.1 204 No Content
1106+
```
10991107

1108+
### DELETE /settings/avatar
1109+
1110+
This route can be used to delete the avatar for an instance.
1111+
1112+
#### Request
1113+
1114+
```http
1115+
DELETE /settings/avatar HTTP/1.1
1116+
Host: alice.cozy.example.net
1117+
Authorization: Bearer token
11001118
...
11011119
```
11021120

model/vfs/vfs.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,10 @@ type DiskThresholder interface {
265265
// Avatarer defines an interface to define an avatar filesystem.
266266
type Avatarer interface {
267267
CreateAvatar(contentType string) (io.WriteCloser, error)
268+
// DeleteAvatar deletes the avatar file if it exists.
269+
// It does not return an error if the file does not exist,
270+
// but does if there was a problem deleting it.
271+
DeleteAvatar() error
268272
ServeAvatarContent(w http.ResponseWriter, req *http.Request) error
269273
}
270274

model/vfs/vfsafero/avatar.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ func (a *avatarFS) CreateAvatar(contentType string) (io.WriteCloser, error) {
5151
return u, nil
5252
}
5353

54+
func (a *avatarFS) DeleteAvatar() error {
55+
if exists, err := a.AvatarExists(); err != nil {
56+
return err
57+
} else if !exists {
58+
return nil
59+
}
60+
if err := a.fs.Remove(AvatarFilename); err != nil {
61+
return err
62+
}
63+
return nil
64+
}
65+
5466
func (a *avatarFS) AvatarExists() (bool, error) {
5567
infos, err := a.fs.Stat(AvatarFilename)
5668
if os.IsNotExist(err) {

model/vfs/vfsswift/avatar_v3.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ func (a *avatarV3) CreateAvatar(contentType string) (io.WriteCloser, error) {
3333
return a.c.ObjectCreate(a.ctx, a.container, "avatar", true, "", contentType, nil)
3434
}
3535

36+
func (a *avatarV3) DeleteAvatar() error {
37+
err := a.c.ObjectDelete(a.ctx, a.container, "avatar")
38+
if err == swift.ObjectNotFound {
39+
return nil
40+
}
41+
return err
42+
}
43+
3644
func (a *avatarV3) ServeAvatarContent(w http.ResponseWriter, req *http.Request) error {
3745
f, o, err := a.c.ObjectOpen(a.ctx, a.container, "avatar", false, nil)
3846
if err != nil {

web/settings/settings.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,18 @@ func (h *HTTPHandler) UploadAvatar(c echo.Context) error {
279279
return c.NoContent(http.StatusNoContent)
280280
}
281281

282+
func (h *HTTPHandler) DeleteAvatar(c echo.Context) error {
283+
inst := middlewares.GetInstance(c)
284+
if err := middlewares.AllowWholeType(c, http.MethodPut, consts.Settings); err != nil {
285+
return err
286+
}
287+
err := inst.AvatarFS().DeleteAvatar()
288+
if err != nil {
289+
return jsonapi.InternalServerError(err)
290+
}
291+
return c.NoContent(http.StatusNoContent)
292+
}
293+
282294
// Register all the `/settings` routes to the given router.
283295
func (h *HTTPHandler) Register(router *echo.Group) {
284296
router.GET("/disk-usage", h.diskUsage)
@@ -308,6 +320,7 @@ func (h *HTTPHandler) Register(router *echo.Group) {
308320
router.DELETE("/instance/moved_from", h.clearMovedFrom)
309321

310322
router.PUT("/avatar", h.UploadAvatar)
323+
router.DELETE("/avatar", h.DeleteAvatar)
311324

312325
router.GET("/flags", h.getFlags)
313326

web/settings/settings_test.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,19 +1083,30 @@ func TestSettings(t *testing.T) {
10831083
Header("location").IsEqual(testInstance.DefaultRedirection().String())
10841084
})
10851085

1086-
t.Run("PutAvatar", func(t *testing.T) {
1087-
e := testutils.CreateTestClient(t, ts.URL)
1088-
sessCookie := session.CookieName(testInstance)
1086+
t.Run("Avatar", func(t *testing.T) {
1087+
t.Run("Put", func(t *testing.T) {
1088+
e := testutils.CreateTestClient(t, ts.URL)
1089+
sessCookie := session.CookieName(testInstance)
10891090

1090-
// Create a sample avatar image
1091-
avatarContent := "fake image content"
1091+
// Create a sample avatar image
1092+
avatarContent := "fake image content"
10921093

1093-
e.PUT("/settings/avatar").
1094-
WithCookie(sessCookie, "connected").
1095-
WithHeader("Authorization", "Bearer "+token).
1096-
WithHeader("Content-Type", "image/png").
1097-
WithBytes([]byte(avatarContent)).
1098-
Expect().Status(204)
1094+
e.PUT("/settings/avatar").
1095+
WithCookie(sessCookie, "connected").
1096+
WithHeader("Authorization", "Bearer "+token).
1097+
WithHeader("Content-Type", "image/png").
1098+
WithBytes([]byte(avatarContent)).
1099+
Expect().Status(204)
1100+
})
1101+
1102+
t.Run("Delete", func(t *testing.T) {
1103+
e := testutils.CreateTestClient(t, ts.URL)
1104+
sessCookie := session.CookieName(testInstance)
1105+
e.DELETE("/settings/avatar").
1106+
WithCookie(sessCookie, "connected").
1107+
WithHeader("Authorization", "Bearer "+token).
1108+
Expect().Status(204)
1109+
})
10991110
})
11001111
}
11011112

0 commit comments

Comments
 (0)