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:
Thomas Citharel
2020-11-26 11:41:13 +01:00
parent c19e326bd8
commit c9457fe0d3
78 changed files with 1405 additions and 700 deletions

View File

@@ -7,6 +7,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Discussion do
alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions.{Comment, Discussion}
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.GraphQL.API.Comments
alias Mobilizon.Storage.Page
alias Mobilizon.Users.User
import Mobilizon.Web.Gettext
@@ -94,17 +95,13 @@ defmodule Mobilizon.GraphQL.Resolvers.Discussion do
with {:actor, %Actor{id: creator_id} = _actor} <- {:actor, Users.get_actor_for_user(user)},
{:member, true} <- {:member, Actors.is_member?(creator_id, group_id)},
{:ok, _activity, %Discussion{} = discussion} <-
ActivityPub.create(
:discussion,
%{
title: title,
text: text,
actor_id: group_id,
creator_id: creator_id,
attributed_to_id: group_id
},
true
) do
Comments.create_discussion(%{
title: title,
text: text,
actor_id: group_id,
creator_id: creator_id,
attributed_to_id: group_id
}) do
{:ok, discussion}
else
{:member, false} ->
@@ -134,19 +131,15 @@ defmodule Mobilizon.GraphQL.Resolvers.Discussion do
{:no_discussion, Discussions.get_discussion(discussion_id)},
{:member, true} <- {:member, Actors.is_member?(creator_id, actor_id)},
{:ok, _activity, %Discussion{} = discussion} <-
ActivityPub.create(
:discussion,
%{
text: text,
discussion_id: discussion_id,
actor_id: creator_id,
attributed_to_id: actor_id,
in_reply_to_comment_id: last_comment_id,
origin_comment_id:
origin_comment_id || previous_in_reply_to_comment_id || last_comment_id
},
true
) do
Comments.create_discussion(%{
text: text,
discussion_id: discussion_id,
actor_id: creator_id,
attributed_to_id: actor_id,
in_reply_to_comment_id: last_comment_id,
origin_comment_id:
origin_comment_id || previous_in_reply_to_comment_id || last_comment_id
}) do
{:ok, discussion}
end
end

View File

@@ -96,8 +96,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
# TODO Move me to somewhere cleaner
defp save_attached_pictures(args) do
Enum.reduce([:avatar, :banner], args, fn key, args ->
if Map.has_key?(args, key) && !is_nil(args[key][:picture]) do
pic = args[key][:picture]
if Map.has_key?(args, key) && !is_nil(args[key][:media]) do
pic = args[key][:media]
with {:ok, %{name: name, url: url, content_type: content_type, size: _size}} <-
Upload.store(pic.file, type: key, description: pic.alt) do

View File

@@ -1,50 +1,47 @@
defmodule Mobilizon.GraphQL.Resolvers.Picture do
defmodule Mobilizon.GraphQL.Resolvers.Media do
@moduledoc """
Handles the picture-related GraphQL calls
Handles the media-related GraphQL calls
"""
alias Mobilizon.Actors.Actor
alias Mobilizon.{Media, Users}
alias Mobilizon.Media.Picture
alias Mobilizon.{Medias, Users}
alias Mobilizon.Medias.Media
alias Mobilizon.Users.User
import Mobilizon.Web.Gettext
@doc """
Get picture for an event
Get media for an event
See Mobilizon.Web.Resolvers.Event.create_event/3
"""
def picture(%{picture_id: picture_id} = _parent, _args, _resolution) do
with {:ok, picture} <- do_fetch_picture(picture_id), do: {:ok, picture}
def media(%{picture_id: media_id} = _parent, _args, _resolution) do
with {:ok, media} <- do_fetch_media(media_id), do: {:ok, media}
end
def picture(%{picture: picture} = _parent, _args, _resolution), do: {:ok, picture}
def picture(_parent, %{id: picture_id}, _resolution), do: do_fetch_picture(picture_id)
def picture(_parent, _args, _resolution), do: {:ok, nil}
def media(%{picture: media} = _parent, _args, _resolution), do: {:ok, media}
def media(_parent, %{id: media_id}, _resolution), do: do_fetch_media(media_id)
def media(_parent, _args, _resolution), do: {:ok, nil}
@spec do_fetch_picture(nil) :: {:error, nil}
defp do_fetch_picture(nil), do: {:error, nil}
def medias(%{media: medias}, _args, _resolution) do
{:ok, Enum.map(medias, &transform_media/1)}
end
@spec do_fetch_picture(String.t()) :: {:ok, Picture.t()} | {:error, :not_found}
defp do_fetch_picture(picture_id) do
case Media.get_picture(picture_id) do
%Picture{id: id, file: file} ->
{:ok,
%{
name: file.name,
url: file.url,
id: id,
content_type: file.content_type,
size: file.size
}}
@spec do_fetch_media(nil) :: {:error, nil}
defp do_fetch_media(nil), do: {:error, nil}
@spec do_fetch_media(String.t()) :: {:ok, Media.t()} | {:error, :not_found}
defp do_fetch_media(media_id) do
case Medias.get_media(media_id) do
%Media{} = media ->
{:ok, transform_media(media)}
nil ->
{:error, :not_found}
end
end
@spec upload_picture(map, map, map) :: {:ok, Picture.t()} | {:error, any}
def upload_picture(
@spec upload_media(map, map, map) :: {:ok, Media.t()} | {:error, any}
def upload_media(
_parent,
%{file: %Plug.Upload{} = file} = args,
%{context: %{current_user: %User{} = user}}
@@ -57,16 +54,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Picture do
|> Map.put(:url, url)
|> Map.put(:size, size)
|> Map.put(:content_type, content_type),
{:ok, picture = %Picture{}} <-
Media.create_picture(%{"file" => args, "actor_id" => actor_id}) do
{:ok,
%{
name: picture.file.name,
url: picture.file.url,
id: picture.id,
content_type: picture.file.content_type,
size: picture.file.size
}}
{:ok, media = %Media{}} <-
Medias.create_media(%{"file" => args, "actor_id" => actor_id}) do
{:ok, transform_media(media)}
else
{:error, :mime_type_not_allowed} ->
{:error, dgettext("errors", "File doesn't have an allowed MIME type.")}
@@ -76,28 +66,28 @@ defmodule Mobilizon.GraphQL.Resolvers.Picture do
end
end
def upload_picture(_parent, _args, _resolution), do: {:error, :unauthenticated}
def upload_media(_parent, _args, _resolution), do: {:error, :unauthenticated}
@doc """
Remove a picture that the user owns
Remove a media that the user owns
"""
@spec remove_picture(map(), map(), map()) ::
{:ok, Picture.t()}
@spec remove_media(map(), map(), map()) ::
{:ok, Media.t()}
| {:error, :unauthorized}
| {:error, :unauthenticated}
| {:error, :not_found}
def remove_picture(_parent, %{id: picture_id}, %{context: %{current_user: %User{} = user}}) do
with {:picture, %Picture{actor_id: actor_id} = picture} <-
{:picture, Media.get_picture(picture_id)},
def remove_media(_parent, %{id: media_id}, %{context: %{current_user: %User{} = user}}) do
with {:media, %Media{actor_id: actor_id} = media} <-
{:media, Medias.get_media(media_id)},
{:is_owned, %Actor{} = _actor} <- User.owns_actor(user, actor_id) do
Media.delete_picture(picture)
Medias.delete_media(media)
else
{:picture, nil} -> {:error, :not_found}
{:media, nil} -> {:error, :not_found}
{:is_owned, _} -> {:error, :unauthorized}
end
end
def remove_picture(_parent, _args, _resolution), do: {:error, :unauthenticated}
def remove_media(_parent, _args, _resolution), do: {:error, :unauthenticated}
@doc """
Return the total media size for an actor
@@ -108,7 +98,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Picture do
context: %{current_user: %User{} = user}
}) do
if can_get_actor_size?(user, actor_id) do
{:ok, Media.media_size_for_actor(actor_id)}
{:ok, Medias.media_size_for_actor(actor_id)}
else
{:error, :unauthorized}
end
@@ -125,7 +115,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Picture do
context: %{current_user: %User{} = logged_user}
}) do
if can_get_user_size?(logged_user, user_id) do
{:ok, Media.media_size_for_user(user_id)}
{:ok, Medias.media_size_for_user(user_id)}
else
{:error, :unauthorized}
end
@@ -133,6 +123,17 @@ defmodule Mobilizon.GraphQL.Resolvers.Picture do
def user_size(_parent, _args, _resolution), do: {:error, :unauthenticated}
@spec transform_media(Media.t()) :: map()
defp transform_media(%Media{id: id, file: file}) do
%{
name: file.name,
url: file.url,
id: id,
content_type: file.content_type,
size: file.size
}
end
@spec can_get_user_size?(User.t(), integer()) :: boolean()
defp can_get_actor_size?(%User{role: role} = user, actor_id) do
role in [:moderator, :administrator] || owns_actor?(User.owns_actor(user, actor_id))

View File

@@ -13,6 +13,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do
import Mobilizon.Web.Gettext
alias Mobilizon.Federation.ActivityPub
require Logger
alias Mobilizon.Web.{MediaProxy, Upload}
@@ -137,6 +138,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do
%{id: id} = args,
%{context: %{current_user: user}} = _resolution
) do
require Logger
args = Map.put(args, :user_id, user.id)
with {:find_actor, %Actor{} = actor} <-
@@ -198,11 +200,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do
defp save_attached_pictures(args) do
Enum.reduce([:avatar, :banner], args, fn key, args ->
if Map.has_key?(args, key) && !is_nil(args[key][:picture]) do
pic = args[key][:picture]
if Map.has_key?(args, key) && !is_nil(args[key][:media]) do
media = args[key][:media]
with {:ok, %{name: name, url: url, content_type: content_type, size: _size}} <-
Upload.store(pic.file, type: key, description: pic.alt) do
Upload.store(media.file, type: key, description: media.alt) do
Map.put(args, key, %{"name" => name, "url" => url, "mediaType" => content_type})
end
else

View File

@@ -116,6 +116,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Post do
Map.update(args, :picture, nil, fn picture ->
process_picture(picture, group)
end),
args <- extract_pictures_from_post_body(args, actor_id),
{:ok, _, %Post{} = post} <-
ActivityPub.create(
:post,
@@ -156,6 +157,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Post do
Map.update(args, :picture, nil, fn picture ->
process_picture(picture, group)
end),
args <- extract_pictures_from_post_body(args, actor_id),
{:member, true} <- {:member, Actors.is_member?(actor_id, group_id)},
{:ok, _, %Post{} = post} <-
ActivityPub.update(post, args, true, %{"actor" => actor_url}) do
@@ -210,15 +212,23 @@ defmodule Mobilizon.GraphQL.Resolvers.Post do
end
defp process_picture(nil, _), do: nil
defp process_picture(%{picture_id: _picture_id} = args, _), do: args
defp process_picture(%{media_id: _picture_id} = args, _), do: args
defp process_picture(%{picture: picture}, %Actor{id: actor_id}) do
defp process_picture(%{media: media}, %Actor{id: actor_id}) do
%{
file:
picture
media
|> Map.get(:file)
|> Utils.make_picture_data(description: Map.get(picture, :name)),
|> Utils.make_media_data(description: Map.get(media, :name)),
actor_id: actor_id
}
end
@spec extract_pictures_from_post_body(map(), String.t()) :: map()
defp extract_pictures_from_post_body(%{body: body} = args, actor_id) do
pictures = Mobilizon.GraphQL.API.Utils.extract_pictures_from_body(body, actor_id)
Map.put(args, :media, pictures)
end
defp extract_pictures_from_post_body(args, _actor_id), do: args
end

View File

@@ -529,7 +529,7 @@ defmodule Mobilizon.GraphQL.Resolvers.User do
context: %{current_user: %User{id: logged_in_user_id}}
})
when user_id == logged_in_user_id do
%{elements: elements, total: total} = Mobilizon.Media.pictures_for_user(user_id, page, limit)
%{elements: elements, total: total} = Mobilizon.Medias.medias_for_user(user_id, page, limit)
{:ok,
%{