Poubelle par utilisateur (backend) : dossier virtuel .poubelle, restauration, suppression définitive, purge automatique (trashDays)
This commit is contained in:
1 parent
8c29eb35e7
commit
8a1417055b
9 files changed
+634
-37
No files matched your search
+31
-6
@@ -12,7 +12,9 @@
|
||||
// POST /api/move {"from":"x","to":"y"} renommage / déplacement
|
||||
// POST /api/move-many {"paths":[...],"dir":"d"} déplacement multiple
|
||||
// POST /api/copy {"from":"x","to":"y"} duplication d'un schéma
|
||||
// DELETE /api/entry?path=x[&recursive=true] mise à la corbeille
|
||||
// DELETE /api/entry?path=x[&recursive=true] mise à la poubelle (suppression définitive
|
||||
// dans .poubelle ; path=.poubelle : la vide)
|
||||
// POST /api/trash/restore {"paths":[...]} restauration depuis la poubelle
|
||||
// GET /api/thumb?path=x / PUT (image/png) miniature d'un schéma
|
||||
// GET /api/history?path=x versions d'un schéma
|
||||
// GET /api/history/version?path=x&id=v contenu d'une version
|
||||
@@ -24,13 +26,15 @@
|
||||
// POST /api/shares/revoke-all révocation de tous les liens
|
||||
// GET /api/public/{jeton} schéma partagé (sans authentification)
|
||||
// GET /api/library / PUT /api/library bibliothèque Excalidraw
|
||||
// GET /api/settings / PUT {"title","channel"} paramètres (titre, logo, canal)
|
||||
// GET /api/settings / PUT {"title","channel","trashDays"} paramètres (titre, logo, canal,
|
||||
// purge de la poubelle après N jours, 0 : jamais)
|
||||
// GET /api/logo / PUT (image/png) / DELETE logo 64x64
|
||||
// GET /api/update dernière version d'Excalidraw sur npm
|
||||
//
|
||||
// Les chemins sont vus par l'utilisateur connecté (storage.Space) : l'arborescence
|
||||
// commune, plus ".perso/..." (son espace personnel) et ".favoris" (ses favoris,
|
||||
// liens symboliques vers les originaux ; DELETE .favoris/x retire le lien).
|
||||
// commune, plus ".perso/..." (son espace personnel), ".favoris" (ses favoris,
|
||||
// liens symboliques vers les originaux ; DELETE .favoris/x retire le lien) et
|
||||
// ".poubelle" (éléments supprimés, de l'arborescence commune et de son espace).
|
||||
package api
|
||||
|
||||
import (
|
||||
@@ -87,6 +91,7 @@ func (h *Handler) Register(mux *http.ServeMux) {
|
||||
mux.HandleFunc("POST /api/move-many", h.moveMany)
|
||||
mux.HandleFunc("POST /api/copy", h.copy)
|
||||
mux.HandleFunc("DELETE /api/entry", h.delete)
|
||||
mux.HandleFunc("POST /api/trash/restore", h.restoreTrash)
|
||||
mux.HandleFunc("GET /api/thumb", h.readThumb)
|
||||
mux.HandleFunc("PUT /api/thumb", h.writeThumb)
|
||||
mux.HandleFunc("GET /api/history", h.history)
|
||||
@@ -381,6 +386,18 @@ func (h *Handler) moveMany(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, res)
|
||||
}
|
||||
|
||||
func (h *Handler) restoreTrash(w http.ResponseWriter, r *http.Request) {
|
||||
u, ok := h.space(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
req, ok := h.decodeMeta(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, u.RestoreTrash(req.Paths))
|
||||
}
|
||||
|
||||
func (h *Handler) readThumb(w http.ResponseWriter, r *http.Request) {
|
||||
u, ok := h.space(w, r)
|
||||
if !ok {
|
||||
@@ -648,8 +665,9 @@ func (h *Handler) readSettings(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// settingsReq : seuls les champs présents sont modifiés.
|
||||
type settingsReq struct {
|
||||
Title *string `json:"title"`
|
||||
Channel *string `json:"channel"`
|
||||
Title *string `json:"title"`
|
||||
Channel *string `json:"channel"`
|
||||
TrashDays *int `json:"trashDays"`
|
||||
}
|
||||
|
||||
func (h *Handler) writeSettings(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -664,6 +682,13 @@ func (h *Handler) writeSettings(w http.ResponseWriter, r *http.Request) {
|
||||
if err == nil && req.Channel != nil {
|
||||
st, err = h.Store.WriteChannel(*req.Channel)
|
||||
}
|
||||
if err == nil && req.TrashDays != nil {
|
||||
if st, err = h.Store.WriteTrashDays(*req.TrashDays); err == nil {
|
||||
if perr := h.Spaces.PurgeTrash(); perr != nil {
|
||||
slog.Warn("poubelle : purge incomplète", "err", perr)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
fail(w, err)
|
||||
return
|
||||
|
||||
@@ -67,7 +67,7 @@ func TestFavoritesPerUser(t *testing.T) {
|
||||
}
|
||||
var root []storage.Entry
|
||||
call(t, srv, "bob@x.fr", "GET", "/api/list?path=", "", &root)
|
||||
if len(root) != 3 || root[0].Special != "favoris" || root[2].Favorite {
|
||||
if len(root) != 4 || root[0].Special != "favoris" || root[2].Special != "poubelle" || root[3].Favorite {
|
||||
t.Fatalf("racine de bob = %+v", root)
|
||||
}
|
||||
if c := call(t, srv, "bob@x.fr", "GET", "/api/file?path=.perso/mine.excalidraw", "", nil); c != 404 {
|
||||
@@ -87,3 +87,41 @@ func TestFavoritesPerUser(t *testing.T) {
|
||||
t.Fatalf("écriture dans Favoris : %d", c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrash(t *testing.T) {
|
||||
srv := newServer(t)
|
||||
call(t, srv, "alice@x.fr", "PUT", "/api/file?path=d/a.excalidraw", doc, nil)
|
||||
if c := call(t, srv, "alice@x.fr", "DELETE", "/api/entry?path=d/a.excalidraw", "", nil); c != 204 {
|
||||
t.Fatalf("suppression : %d", c)
|
||||
}
|
||||
var items []storage.Entry
|
||||
call(t, srv, "bob@x.fr", "GET", "/api/list?path=.poubelle", "", &items)
|
||||
if len(items) != 1 || items[0].Origin != "d/a.excalidraw" || items[0].By != "alice@x.fr" {
|
||||
t.Fatalf("poubelle vue par bob = %+v", items)
|
||||
}
|
||||
var res []storage.MoveResult
|
||||
if c := call(t, srv, "bob@x.fr", "POST", "/api/trash/restore", `{"paths":["`+items[0].Path+`"]}`, &res); c != 200 ||
|
||||
len(res) != 1 || res[0].To != "d/a.excalidraw" {
|
||||
t.Fatalf("restauration : %d %+v", c, res)
|
||||
}
|
||||
if c := call(t, srv, "alice@x.fr", "GET", "/api/file?path=d/a.excalidraw", "", nil); c != 200 {
|
||||
t.Fatalf("lecture après restauration : %d", c)
|
||||
}
|
||||
|
||||
// Réglage de la purge, puis vidage.
|
||||
var st storage.Settings
|
||||
if c := call(t, srv, "alice@x.fr", "PUT", "/api/settings", `{"trashDays":7}`, &st); c != 200 || st.TrashDays != 7 {
|
||||
t.Fatalf("trashDays : %d %+v", c, st)
|
||||
}
|
||||
if c := call(t, srv, "alice@x.fr", "PUT", "/api/settings", `{"trashDays":-3}`, nil); c != 400 {
|
||||
t.Fatalf("trashDays négatif : %d", c)
|
||||
}
|
||||
call(t, srv, "alice@x.fr", "DELETE", "/api/entry?path=d/a.excalidraw", "", nil)
|
||||
if c := call(t, srv, "alice@x.fr", "DELETE", "/api/entry?path=.poubelle", "", nil); c != 204 {
|
||||
t.Fatalf("vidage : %d", c)
|
||||
}
|
||||
call(t, srv, "alice@x.fr", "GET", "/api/list?path=.poubelle", "", &items)
|
||||
if len(items) != 0 {
|
||||
t.Fatalf("poubelle après vidage = %+v", items)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user