diff --git a/server/internal/command/serve/cmd.go b/server/internal/command/serve/cmd.go index 87fe1912..a40f03b6 100644 --- a/server/internal/command/serve/cmd.go +++ b/server/internal/command/serve/cmd.go @@ -1,10 +1,6 @@ package serve import ( - "context" - "fmt" - "time" - "github.com/fvbock/endless" "github.com/shroff/phylum/server/internal/api/publink" apiv1 "github.com/shroff/phylum/server/internal/api/v1" @@ -27,12 +23,7 @@ func SetupCommand() *cobra.Command { webdav.SetupHandler(engine.Group(config.GetString("webdav_prefix"))) apiv1.Setup(engine.Group("/api/v1")) publink.Setup(engine.Group("/publink")) - t := time.Now().Add(-30 * 24 * time.Hour) - if d, err := fs.HardDeleteExpired(context.Background(), t); err != nil { - logrus.Fatal(err) - } else { - logrus.Info(fmt.Sprintf("Removed %d files deleted before %s", d, t.Format(time.RFC1123))) - } + fs.SetupTrashCompactor() server := endless.NewServer(config.GetString("listen"), engine) server.BeforeBegin = func(addr string) { diff --git a/server/internal/core/db/trash.sql.go b/server/internal/core/db/trash.sql.go index 898dff4e..da3246ad 100644 --- a/server/internal/core/db/trash.sql.go +++ b/server/internal/core/db/trash.sql.go @@ -95,35 +95,6 @@ func (q *Queries) HardDelete(ctx context.Context, arg HardDeleteParams) ([]HardD return items, nil } -const hardDeleteExpired = `-- name: HardDeleteExpired :many -DELETE FROM resources WHERE deleted < $1::TIMESTAMP RETURNING id, dir -` - -type HardDeleteExpiredRow struct { - ID uuid.UUID - Dir bool -} - -func (q *Queries) HardDeleteExpired(ctx context.Context, t pgtype.Timestamp) ([]HardDeleteExpiredRow, error) { - rows, err := q.db.Query(ctx, hardDeleteExpired, t) - if err != nil { - return nil, err - } - defer rows.Close() - var items []HardDeleteExpiredRow - for rows.Next() { - var i HardDeleteExpiredRow - if err := rows.Scan(&i.ID, &i.Dir); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - const restoreDeleted = `-- name: RestoreDeleted :many WITH RECURSIVE nodes(id, parent, ts) AS ( SELECT r.id, r.parent, r.deleted @@ -200,6 +171,35 @@ func (q *Queries) SoftDelete(ctx context.Context, arg SoftDeleteParams) error { return err } +const trashCompact = `-- name: TrashCompact :many +DELETE FROM resources WHERE deleted < $1::TIMESTAMP RETURNING id, dir +` + +type TrashCompactRow struct { + ID uuid.UUID + Dir bool +} + +func (q *Queries) TrashCompact(ctx context.Context, t pgtype.Timestamp) ([]TrashCompactRow, error) { + rows, err := q.db.Query(ctx, trashCompact, t) + if err != nil { + return nil, err + } + defer rows.Close() + var items []TrashCompactRow + for rows.Next() { + var i TrashCompactRow + if err := rows.Scan(&i.ID, &i.Dir); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const trashList = `-- name: TrashList :many SELECT id, name, parent, dir, created, modified, deleted, content_length, content_type, content_sha256, permissions, grants FROM resources WHERE CASE diff --git a/server/internal/core/fs/trash.go b/server/internal/core/fs/trash.go index 688d7c89..18d06b16 100644 --- a/server/internal/core/fs/trash.go +++ b/server/internal/core/fs/trash.go @@ -14,8 +14,11 @@ import ( "github.com/jackc/pgx/v5/pgtype" "github.com/shroff/phylum/server/internal/core/db" "github.com/shroff/phylum/server/internal/core/storage" + "github.com/sirupsen/logrus" ) +const trashDuration = 30 * 24 * time.Hour + func (f filesystem) TrashList(cursor string, n int) ([]Resource, string, error) { var username pgtype.Text if !f.fullAccess { @@ -162,9 +165,33 @@ func (r Resource) Restore(parentPathOrUUID string, name string, autoRename bool) return } -func HardDeleteExpired(ctx context.Context, t time.Time) (int, error) { +func SetupTrashCompactor() { + ticker := time.NewTimer(24 * time.Hour) + // ticker := time.NewTicker(time.Second) + go func() { + for { + <-ticker.C + TrashCompact() + } + }() + TrashCompact() +} + +func TrashCompact() { + t := time.Now().Add(-trashDuration) + if d, err := trashCompact(t); err != nil { + logrus.Error(err) + } else { + logrus.Info(fmt.Sprintf("Removed %d files deleted before %s", d, t.Format(time.RFC1123))) + } +} + +func trashCompact(t time.Time) (int, error) { d := db.Get() - rows, err := d.HardDeleteExpired(ctx, pgtype.Timestamp{Valid: true, Time: t.UTC()}) + rows, err := d.TrashCompact(context.Background(), pgtype.Timestamp{Valid: true, Time: t.UTC()}) + if err != nil { + return 0, err + } ids := make([]uuid.UUID, len(rows)) for _, d := range rows { if !d.Dir { diff --git a/server/sql/queries/trash.sql b/server/sql/queries/trash.sql index 98fdac3d..d95daf60 100644 --- a/server/sql/queries/trash.sql +++ b/server/sql/queries/trash.sql @@ -35,7 +35,7 @@ DELETE FROM resources WHERE id IN (SELECT id FROM nodes) RETURNING id, dir; --- name: HardDeleteExpired :many +-- name: TrashCompact :many DELETE FROM resources WHERE deleted < @t::TIMESTAMP RETURNING id, dir; -- name: RestoreDeleted :many