Backup chats and chat messages

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-03-10 23:52:27 +01:00
parent 139057f346
commit 3c21a33656
4 changed files with 120 additions and 3 deletions

View file

@ -0,0 +1 @@
Backup chats and chat messages

View file

@ -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)

View file

@ -14,6 +14,7 @@ defmodule Pleroma.User.Backup do
alias Pleroma.Activity
alias Pleroma.Bookmark
alias Pleroma.Chat
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.User.Backup.State
@ -196,7 +197,14 @@ defp wait_backup(backup, current_processed, task) do
end
end
@files ['actor.json', 'outbox.json', 'likes.json', 'bookmarks.json']
@files [
'actor.json',
'outbox.json',
'likes.json',
'bookmarks.json',
'chats.json',
'chat_messages.json'
]
@spec export(Pleroma.User.Backup.t(), pid()) :: {:ok, String.t()} | :error
def export(%__MODULE__{} = backup, caller_pid) do
backup = Repo.preload(backup, :user)
@ -207,6 +215,8 @@ def export(%__MODULE__{} = backup, caller_pid) do
:ok <- statuses(dir, backup.user, caller_pid),
:ok <- likes(dir, backup.user, caller_pid),
:ok <- bookmarks(dir, backup.user, caller_pid),
:ok <- chats(dir, backup.user, caller_pid),
:ok <- chat_messages(dir, backup.user, caller_pid),
{:ok, zip_path} <- :zip.create(backup.file_name, @files, cwd: dir),
{:ok, _} <- File.rm_rf(dir) do
{:ok, zip_path}
@ -357,6 +367,56 @@ defp statuses(dir, user, caller_pid) do
caller_pid
)
end
defp chats(dir, user, caller_pid) 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,
caller_pid
)
end
def chat_messages(dir, %{id: user_id}, caller_pid) 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,
caller_pid
)
end
end
defmodule Pleroma.User.Backup.ProcessorAPI do

View file

@ -12,9 +12,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.User.Backup.ProcessorMock
alias Pleroma.Web.CommonAPI
@ -165,7 +167,10 @@ test "it removes outdated backups after creating a fresh one" 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"})
%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"})
@ -182,6 +187,11 @@ test "it creates a zip archive with user data" do
Bookmark.create(user.id, status2.id)
Bookmark.create(user.id, status3.id)
{: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} = user |> Backup.new() |> Repo.insert()
assert {:ok, path} = Backup.export(backup, self())
assert {:ok, zipfile} = :zip.zip_open(String.to_charlist(path), [:memory])
@ -261,6 +271,52 @@ test "it creates a zip archive with user data" do
"type" => "OrderedCollection"
} = Jason.decode!(json)
assert {:ok, {'chats.json', json}} = :zip.zip_get('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, {'chat_messages.json', json}} = :zip.zip_get('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!(path)
end