Track usage of media files and add a job to clean them
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
60
lib/service/clean_orphan_media.ex
Normal file
60
lib/service/clean_orphan_media.ex
Normal file
@@ -0,0 +1,60 @@
|
||||
defmodule Mobilizon.Service.CleanOrphanMedia do
|
||||
@moduledoc """
|
||||
Service to clean orphan media
|
||||
"""
|
||||
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Medias
|
||||
alias Mobilizon.Medias.Media
|
||||
alias Mobilizon.Storage.Repo
|
||||
import Ecto.Query
|
||||
|
||||
@grace_period Mobilizon.Config.get([:instance, :orphan_upload_grace_period_hours], 48)
|
||||
|
||||
@doc """
|
||||
Clean orphan media
|
||||
|
||||
Remove media that is not attached to an entity, such as media uploads that were never used in entities.
|
||||
|
||||
Options:
|
||||
* `grace_period` how old in hours can the media be before it's taken into account for deletion
|
||||
* `dry_run` just return the media that would have been deleted, don't actually delete it
|
||||
"""
|
||||
@spec clean(Keyword.t()) :: {:ok, list(Media.t())} | {:error, String.t()}
|
||||
def clean(opts \\ []) do
|
||||
medias = find_media(opts)
|
||||
|
||||
if Keyword.get(opts, :dry_run, false) do
|
||||
{:ok, medias}
|
||||
else
|
||||
Enum.each(medias, fn media ->
|
||||
Medias.delete_media(media, ignore_file_not_found: true)
|
||||
end)
|
||||
|
||||
{:ok, medias}
|
||||
end
|
||||
end
|
||||
|
||||
@spec find_media(Keyword.t()) :: list(Media.t())
|
||||
defp find_media(opts) do
|
||||
grace_period = Keyword.get(opts, :grace_period, @grace_period)
|
||||
expiration_date = DateTime.add(DateTime.utc_now(), grace_period * -3600)
|
||||
|
||||
Media
|
||||
|> where([m], m.inserted_at < ^expiration_date)
|
||||
|> join(:inner, [m], a in Actor)
|
||||
|> where([_m, a], is_nil(a.domain))
|
||||
|> join(:left, [m], e in assoc(m, :events))
|
||||
|> join(:left, [m], ep in assoc(m, :event_picture))
|
||||
|> join(:left, [m], p in assoc(m, :posts))
|
||||
|> join(:left, [m], pp in assoc(m, :posts_picture))
|
||||
|> join(:left, [m], c in assoc(m, :comments))
|
||||
|> where([_m, _a, e], is_nil(e.id))
|
||||
|> where([_m, _a, _e, ep], is_nil(ep.id))
|
||||
|> where([_m, _a, _e, _ep, p], is_nil(p.id))
|
||||
|> where([_m, _a, _e, _ep, _p, pp], is_nil(pp.id))
|
||||
|> where([_m, _a, _e, _ep, _p, _pp, c], is_nil(c.id))
|
||||
|> distinct(true)
|
||||
|> Repo.all()
|
||||
end
|
||||
end
|
||||
31
lib/service/workers/clean_orphan_media_worker.ex
Normal file
31
lib/service/workers/clean_orphan_media_worker.ex
Normal file
@@ -0,0 +1,31 @@
|
||||
defmodule Mobilizon.Service.Workers.CleanOrphanMediaWorker do
|
||||
@moduledoc """
|
||||
Worker to clean orphan media
|
||||
"""
|
||||
|
||||
use Oban.Worker, queue: "background"
|
||||
alias Mobilizon.Service.CleanOrphanMedia
|
||||
|
||||
@grace_period Mobilizon.Config.get([:instance, :orphan_upload_grace_period_hours], 48)
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(%Job{}) do
|
||||
if Mobilizon.Config.get!([:instance, :remove_orphan_uploads]) and should_perform?() do
|
||||
CleanOrphanMedia.clean()
|
||||
end
|
||||
end
|
||||
|
||||
@spec should_perform? :: boolean()
|
||||
defp should_perform? do
|
||||
case Cachex.get(:key_value, "last_media_cleanup") do
|
||||
{:ok, %DateTime{} = last_media_cleanup} ->
|
||||
DateTime.compare(
|
||||
last_media_cleanup,
|
||||
DateTime.add(DateTime.utc_now(), @grace_period * -3600)
|
||||
) == :lt
|
||||
|
||||
_ ->
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user