feat: Kanban touch drag, admins delete backups, bigger share message
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m5s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m5s
- Kanban on phones/tablets: hold a card, drag it onto a card or column, release; the page scrolls at the edges
- Admin settings: database backups can be deleted (DELETE /api/v1/admin/backups/{name})
- Share message: large Liste/Aufgabe buttons, close button, tap outside closes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
da258fdce2
commit
f76b0f34ff
14 files changed
+371
-57
No files matched your search
@@ -110,6 +110,16 @@ func (s *Server) handleAdminRunBackup(w http.ResponseWriter, r *http.Request) {
|
||||
s.handleAdminListBackups(w, r)
|
||||
}
|
||||
|
||||
// handleAdminDeleteBackup removes one database snapshot (admins only; users cannot delete their own
|
||||
// backups, those expire with the retention period).
|
||||
func (s *Server) handleAdminDeleteBackup(w http.ResponseWriter, r *http.Request) {
|
||||
if err := storage.DeleteBackup(filepath.Join(s.backupRoot(), "system"), chi.URLParam(r, "name")); err != nil {
|
||||
writeError(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
s.handleAdminListBackups(w, r)
|
||||
}
|
||||
|
||||
func (s *Server) handleAdminDownloadBackup(w http.ResponseWriter, r *http.Request) {
|
||||
serveBackupFile(w, r, filepath.Join(s.backupRoot(), "system"), chi.URLParam(r, "name"), "application/gzip")
|
||||
}
|
||||
|
||||
@@ -89,3 +89,41 @@ func mustTime(day string) (t time.Time) {
|
||||
t, _ = time.Parse("2006-01-02", day)
|
||||
return t
|
||||
}
|
||||
|
||||
// Admins can delete database snapshots; members cannot, and only real backup names are accepted
|
||||
func TestAdminDeletesBackups(t *testing.T) {
|
||||
srv, store, token := setupTestServer(t)
|
||||
router := srv.Router()
|
||||
call := func(method, path, tok string) *httptest.ResponseRecorder {
|
||||
req := httptest.NewRequest(method, path, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
rec := httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, req)
|
||||
return rec
|
||||
}
|
||||
var files []storage.BackupFile
|
||||
_ = json.Unmarshal(call("POST", "/api/v1/admin/backups", token).Body.Bytes(), &files)
|
||||
if len(files) != 1 {
|
||||
t.Fatalf("expected one snapshot, got %+v", files)
|
||||
}
|
||||
name := files[0].Name
|
||||
|
||||
_, memberToken := newTestUser(t, store, "member@example.com")
|
||||
if rec := call("DELETE", "/api/v1/admin/backups/"+name, memberToken); rec.Code != http.StatusForbidden {
|
||||
t.Fatalf("members must not delete backups, got %d", rec.Code)
|
||||
}
|
||||
if rec := call("DELETE", "/api/v1/admin/backups/..%2F..%2Fmindsift.db", token); rec.Code != http.StatusNotFound {
|
||||
t.Fatalf("path tricks must be refused, got %d", rec.Code)
|
||||
}
|
||||
rec := call("DELETE", "/api/v1/admin/backups/"+name, token)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("delete: %d %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
_ = json.Unmarshal(rec.Body.Bytes(), &files)
|
||||
if len(files) != 0 {
|
||||
t.Fatalf("snapshot should be gone, got %+v", files)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(srv.backupRoot(), "system", name)); !os.IsNotExist(err) {
|
||||
t.Fatalf("file must be removed from disk")
|
||||
}
|
||||
}
|
||||
@@ -303,6 +303,7 @@ func (s *Server) Router() http.Handler {
|
||||
r.Get("/api/v1/admin/backups", s.handleAdminListBackups)
|
||||
r.Post("/api/v1/admin/backups", s.notInDemo(s.handleAdminRunBackup))
|
||||
r.Get("/api/v1/admin/backups/{name}", s.handleAdminDownloadBackup)
|
||||
r.Delete("/api/v1/admin/backups/{name}", s.notInDemo(s.handleAdminDeleteBackup))
|
||||
r.Put("/api/v1/admin/settings", s.notInDemo(s.handleAdminUpdateSettings))
|
||||
r.Get("/api/v1/admin/settings/sharing-impact", s.handleAdminSharingImpact)
|
||||
r.Get("/api/v1/admin/voice", s.handleAdminVoiceStatus)
|
||||
|
||||
Reference in new issue
Block a user