diff --git a/changelog.d/backups-chats.add b/changelog.d/backups-chats.add new file mode 100644 index 000000000..c48c589c4 --- /dev/null +++ b/changelog.d/backups-chats.add @@ -0,0 +1 @@ +Backup chats and chat messages \ No newline at end of file diff --git a/lib/pleroma/chat/message_reference.ex b/lib/pleroma/chat/message_reference.ex index ea65a4a35..af9e35255 100644 --- a/lib/pleroma/chat/message_reference.ex +++ b/lib/pleroma/chat/message_reference.ex @@ -17,7 +17,7 @@ defmodule Pleroma.Chat.MessageReference do import Ecto.Changeset import Ecto.Query - @primary_key {:id, FlakeId.Ecto.Type, autogenerate: true} + @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true} schema "chat_message_references" do belongs_to(:object, Object) diff --git a/lib/pleroma/user/backup.ex b/lib/pleroma/user/backup.ex index 244b08adb..afa195a56 100644 --- a/lib/pleroma/user/backup.ex +++ b/lib/pleroma/user/backup.ex @@ -14,6 +14,7 @@ defmodule Pleroma.User.Backup do alias Pleroma.Activity alias Pleroma.Bookmark + alias Pleroma.Chat alias Pleroma.Config alias Pleroma.Repo alias Pleroma.SafeZip @@ -185,7 +186,9 @@ defmodule Pleroma.User.Backup do "likes.json", "bookmarks.json", "followers.json", - "following.json" + "following.json", + "chats.json", + "chat_messages.json" ] @spec run(t()) :: {:ok, t()} | {:error, :failed} @@ -200,6 +203,8 @@ defmodule Pleroma.User.Backup do {_, :ok} <- {:bookmarks, bookmarks(backup.tempdir, backup.user)}, {_, :ok} <- {:followers, followers(backup.tempdir, backup.user)}, {_, :ok} <- {:following, following(backup.tempdir, backup.user)}, + {_, :ok} <- {:chats, chats(backup.tempdir, backup.user)}, + {_, :ok} <- {:chat_messages, chat_messages(backup.tempdir, backup.user)}, {_, {:ok, _zip_path}} <- {:zip, SafeZip.zip(tempfile, @files, backup.tempdir)}, {_, {:ok, %File.Stat{size: zip_size}}} <- {:filestat, File.stat(tempfile)}, @@ -252,7 +257,9 @@ defmodule Pleroma.User.Backup do "likes" => "likes.json", "outbox" => "outbox.json", "followers" => "followers.json", - "following" => "following.json" + "following" => "following.json", + "chats" => "chats.json", + "chat_messages" => "chat_messages.json" }) |> Jason.encode() do File.write(Path.join(dir, "actor.json"), json) @@ -358,4 +365,52 @@ defmodule Pleroma.User.Backup do User.get_friends_query(user) |> write(dir, "following", fn a -> {:ok, a.ap_id} end) end + + defp chats(dir, user) do + Chat.for_user_query(user.id) + |> write( + dir, + "chats", + fn chat -> + {:ok, + %{ + "type" => "Chat", + "id" => "#{Pleroma.Web.Endpoint.url()}/chats/#{chat.id}", + "actor" => user.ap_id, + "to" => [chat.recipient], + "published" => + chat.inserted_at |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_iso8601() + }} + end + ) + end + + defp chat_messages(dir, %{id: user_id}) do + chats_subquery = + from(c in Chat, + where: c.user_id == ^user_id, + select: c.id + ) + + from(cr in Chat.MessageReference, + where: cr.chat_id in subquery(chats_subquery), + preload: [:object] + ) + |> write( + dir, + "chat_messages", + fn reference -> + with {:ok, activity} <- Transmogrifier.prepare_outgoing(reference.object.data), + {:ok, activity} <- + {:ok, + Map.put( + activity, + "context", + "#{Pleroma.Web.Endpoint.url()}/chats/#{reference.chat_id}" + )} do + {:ok, Map.delete(activity, "@context")} + end + end + ) + end end diff --git a/test/pleroma/user/backup_test.exs b/test/pleroma/user/backup_test.exs index f4b92adf8..1557eb340 100644 --- a/test/pleroma/user/backup_test.exs +++ b/test/pleroma/user/backup_test.exs @@ -11,9 +11,11 @@ defmodule Pleroma.User.BackupTest do import Mox alias Pleroma.Bookmark + alias Pleroma.Chat alias Pleroma.Tests.ObanHelpers alias Pleroma.UnstubbedConfigMock, as: ConfigMock alias Pleroma.Uploaders.S3.ExAwsMock + alias Pleroma.User alias Pleroma.User.Backup alias Pleroma.Web.CommonAPI alias Pleroma.Workers.BackupWorker @@ -150,8 +152,10 @@ defmodule Pleroma.User.BackupTest do end test "it creates a zip archive with user data" do - user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"}) - %{ap_id: other_ap_id} = other_user = insert(:user) + %User{ap_id: ap_id} = + user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"}) + + %User{ap_id: other_ap_id} = other_user = insert(:user) {:ok, %{object: %{data: %{"id" => id1}}} = status1} = CommonAPI.post(user, %{status: "status1"}) @@ -170,6 +174,11 @@ defmodule Pleroma.User.BackupTest do CommonAPI.follow(other_user, user) + {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) + + {:ok, _message_1} = CommonAPI.post_chat_message(user, other_user, "hey") + {:ok, _message_2} = CommonAPI.post_chat_message(other_user, user, "ho") + assert {:ok, backup} = Backup.user(user) assert {:ok, run_backup} = Backup.run(backup) @@ -262,6 +271,52 @@ defmodule Pleroma.User.BackupTest do "type" => "OrderedCollection" } = Jason.decode!(json) + assert {:ok, {~c"chats.json", json}} = :zip.zip_get(~c"chats.json", zipfile) + + chat_id = "http://localhost:4001/chats/#{chat.id}" + + assert %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "chats.json", + "orderedItems" => [ + %{ + "type" => "Chat", + "id" => ^chat_id, + "actor" => ^ap_id, + "to" => [^other_ap_id] + } + ], + "totalItems" => 1, + "type" => "OrderedCollection" + } = Jason.decode!(json) + + assert {:ok, {~c"chat_messages.json", json}} = :zip.zip_get(~c"chat_messages.json", zipfile) + + chat_id = "http://localhost:4001/chats/#{chat.id}" + + assert %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "chat_messages.json", + "orderedItems" => [ + %{ + "type" => "ChatMessage", + "actor" => ^ap_id, + "to" => [^other_ap_id], + "context" => ^chat_id, + "content" => "hey" + }, + %{ + "type" => "ChatMessage", + "actor" => ^other_ap_id, + "to" => [^ap_id], + "context" => ^chat_id, + "content" => "ho" + } + ], + "totalItems" => 2, + "type" => "OrderedCollection" + } = Jason.decode!(json) + :zip.zip_close(zipfile) File.rm_rf!(run_backup.tempdir) end