Introduce relay
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
56467301a1
commit
c51115bdbe
@ -16,6 +16,7 @@ config :mobilizon, :instance,
|
||||
hostname: System.get_env("MOBILIZON_INSTANCE_HOST") || "localhost",
|
||||
registrations_open: System.get_env("MOBILIZON_INSTANCE_REGISTRATIONS_OPEN") || false,
|
||||
repository: Mix.Project.config()[:source_url],
|
||||
allow_relay: true,
|
||||
remote_limit: 100_000,
|
||||
upload_limit: 16_000_000,
|
||||
avatar_upload_limit: 2_000_000,
|
||||
@ -109,6 +110,9 @@ config :auto_linker,
|
||||
config :phoenix, :format_encoders, json: Jason, "activity-json": Jason
|
||||
config :phoenix, :json_library, Jason
|
||||
|
||||
config :http_signatures,
|
||||
adapter: Mobilizon.Service.HTTPSignatures.Signature
|
||||
|
||||
config :mobilizon, Mobilizon.Service.Geospatial.Nominatim,
|
||||
endpoint:
|
||||
System.get_env("GEOSPATIAL_NOMINATIM_ENDPOINT") || "https://nominatim.openstreetmap.org",
|
||||
|
@ -11,7 +11,9 @@ config :mobilizon, MobilizonWeb.Endpoint,
|
||||
port: System.get_env("MOBILIZON_INSTANCE_PORT") || 4000
|
||||
],
|
||||
url: [
|
||||
host: System.get_env("MOBILIZON_INSTANCE_HOST") || "mobilizon.local"
|
||||
host: System.get_env("MOBILIZON_INSTANCE_HOST") || "mobilizon.local",
|
||||
port: 80,
|
||||
scheme: "http"
|
||||
],
|
||||
debug_errors: true,
|
||||
code_reloader: true,
|
||||
|
@ -56,6 +56,7 @@
|
||||
"chai": "^4.2.0",
|
||||
"dotenv-webpack": "^1.7.0",
|
||||
"eslint": "^6.0.1",
|
||||
"graphql-cli": "^3.0.12",
|
||||
"node-sass": "^4.11.0",
|
||||
"patch-package": "^6.1.2",
|
||||
"sass-loader": "^7.1.0",
|
||||
|
@ -98,6 +98,7 @@ export default class App extends Vue {
|
||||
@import "~buefy/src/scss/components/tag";
|
||||
@import "~buefy/src/scss/components/taginput";
|
||||
@import "~buefy/src/scss/components/upload";
|
||||
@import "~buefy/src/scss/components/radio";
|
||||
|
||||
.router-enter-active,
|
||||
.router-leave-active {
|
||||
|
@ -145,7 +145,8 @@ export const CREATE_EVENT = gql`
|
||||
$beginsOn: DateTime!,
|
||||
$picture: PictureInput,
|
||||
$tags: [String],
|
||||
$physicalAddress: AddressInput!
|
||||
$physicalAddress: AddressInput,
|
||||
$visibility: EventVisibility
|
||||
) {
|
||||
createEvent(
|
||||
title: $title,
|
||||
@ -155,7 +156,8 @@ export const CREATE_EVENT = gql`
|
||||
category: $category,
|
||||
picture: $picture,
|
||||
tags: $tags,
|
||||
physicalAddress: $physicalAddress
|
||||
physicalAddress: $physicalAddress,
|
||||
visibility: $visibility
|
||||
) {
|
||||
id,
|
||||
uuid,
|
||||
|
@ -6,6 +6,11 @@
|
||||
<div v-if="$apollo.loading">Loading...</div>
|
||||
<div class="columns is-centered" v-else>
|
||||
<form class="column is-two-thirds-desktop" @submit="createEvent">
|
||||
<h2 class="subtitle">
|
||||
<translate>
|
||||
General informations
|
||||
</translate>
|
||||
</h2>
|
||||
<picture-upload v-model="pictureFile" />
|
||||
|
||||
<b-field :label="$gettext('Title')">
|
||||
@ -34,7 +39,28 @@
|
||||
</b-select>
|
||||
</b-field>
|
||||
|
||||
<button class="button is-primary">
|
||||
<h2 class="subtitle">
|
||||
<translate>
|
||||
Visibility
|
||||
</translate>
|
||||
</h2>
|
||||
<label class="label">{{ $gettext('Event visibility') }}</label>
|
||||
<div class="field">
|
||||
<b-radio v-model="event.visibility"
|
||||
name="name"
|
||||
:native-value="EventVisibility.PUBLIC">
|
||||
<translate>Visible everywhere on the web (public)</translate>
|
||||
</b-radio>
|
||||
</div>
|
||||
<div class="field">
|
||||
<b-radio v-model="event.visibility"
|
||||
name="name"
|
||||
:native-value="EventVisibility.PRIVATE">
|
||||
<translate>Only accessible through link and search (private)</translate>
|
||||
</b-radio>
|
||||
</div>
|
||||
|
||||
<button class="button is-primary">
|
||||
<translate>Create my event</translate>
|
||||
</button>
|
||||
</form>
|
||||
@ -60,6 +86,7 @@ import TagInput from '@/components/Event/TagInput.vue';
|
||||
import { TAGS } from '@/graphql/tags';
|
||||
import { ITag } from '@/types/tag.model';
|
||||
import AddressAutoComplete from '@/components/Event/AddressAutoComplete.vue';
|
||||
import { EventVisibility } from '@/types/event.model';
|
||||
|
||||
@Component({
|
||||
components: { AddressAutoComplete, TagInput, DateTimePicker, PictureUpload, Editor },
|
||||
@ -79,6 +106,7 @@ export default class CreateEvent extends Vue {
|
||||
categories: string[] = Object.keys(Category);
|
||||
event: IEvent = new EventModel();
|
||||
pictureFile: File | null = null;
|
||||
EventVisibility = EventVisibility;
|
||||
|
||||
created() {
|
||||
const now = new Date();
|
||||
@ -181,4 +209,4 @@ export default class CreateEvent extends Vue {
|
||||
// }
|
||||
// }
|
||||
}
|
||||
</script>
|
||||
</script>
|
1928
js/yarn.lock
1928
js/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,11 @@ defmodule Mix.Tasks.Mobilizon.Common do
|
||||
end
|
||||
end
|
||||
|
||||
def start_mobilizon do
|
||||
Application.put_env(:phoenix, :serve_endpoints, false, persistent: true)
|
||||
{:ok, _} = Application.ensure_all_started(:mobilizon)
|
||||
end
|
||||
|
||||
def escape_sh_path(path) do
|
||||
~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(')
|
||||
end
|
||||
|
65
lib/mix/tasks/mobilizon/relay.ex
Normal file
65
lib/mix/tasks/mobilizon/relay.ex
Normal file
@ -0,0 +1,65 @@
|
||||
# Portions of this file are derived from Pleroma:
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# Upstream: https://git.pleroma.social/pleroma/pleroma/blob/develop/lib/mix/tasks/pleroma/relay.ex
|
||||
|
||||
defmodule Mix.Tasks.Mobilizon.Relay do
|
||||
use Mix.Task
|
||||
alias Mobilizon.Service.ActivityPub.Relay
|
||||
alias Mix.Tasks.Mobilizon.Common
|
||||
|
||||
@shortdoc "Manages remote relays"
|
||||
@moduledoc """
|
||||
Manages remote relays
|
||||
|
||||
## Follow a remote relay
|
||||
|
||||
``mix mobilizon.relay follow <relay_url>``
|
||||
|
||||
Example: ``mix mobilizon.relay follow https://example.org/relay``
|
||||
|
||||
## Unfollow a remote relay
|
||||
|
||||
``mix mobilizon.relay unfollow <relay_url>``
|
||||
|
||||
Example: ``mix mobilizon.relay unfollow https://example.org/relay``
|
||||
"""
|
||||
def run(["follow", target]) do
|
||||
Common.start_mobilizon()
|
||||
|
||||
case Relay.follow(target) do
|
||||
{:ok, _activity} ->
|
||||
# put this task to sleep to allow the genserver to push out the messages
|
||||
:timer.sleep(500)
|
||||
|
||||
{:error, e} ->
|
||||
IO.puts(:stderr, "Error while following #{target}: #{inspect(e)}")
|
||||
end
|
||||
end
|
||||
|
||||
def run(["unfollow", target]) do
|
||||
Common.start_mobilizon()
|
||||
|
||||
case Relay.unfollow(target) do
|
||||
{:ok, _activity} ->
|
||||
# put this task to sleep to allow the genserver to push out the messages
|
||||
:timer.sleep(500)
|
||||
|
||||
{:error, e} ->
|
||||
IO.puts(:stderr, "Error while unfollowing #{target}: #{inspect(e)}")
|
||||
end
|
||||
end
|
||||
|
||||
def run(["accept", target]) do
|
||||
Common.start_mobilizon()
|
||||
|
||||
case Relay.accept(target) do
|
||||
{:ok, _activity} ->
|
||||
# put this task to sleep to allow the genserver to push out the messages
|
||||
:timer.sleep(500)
|
||||
|
||||
{:error, e} ->
|
||||
IO.puts(:stderr, "Error while accept #{target} follow: #{inspect(e)}")
|
||||
end
|
||||
end
|
||||
end
|
@ -163,7 +163,6 @@ defmodule Mobilizon.Actors.Actor do
|
||||
])
|
||||
|> validate_required([
|
||||
:url,
|
||||
:outbox_url,
|
||||
:inbox_url,
|
||||
:type,
|
||||
:domain,
|
||||
@ -184,6 +183,44 @@ defmodule Mobilizon.Actors.Actor do
|
||||
changes
|
||||
end
|
||||
|
||||
def relay_creation(%{url: url, preferred_username: preferred_username} = _params) do
|
||||
key = :public_key.generate_key({:rsa, 2048, 65_537})
|
||||
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
|
||||
pem = [entry] |> :public_key.pem_encode() |> String.trim_trailing()
|
||||
|
||||
vars = %{
|
||||
"name" => Mobilizon.CommonConfig.get([:instance, :name], "Mobilizon"),
|
||||
"summary" =>
|
||||
Mobilizon.CommonConfig.get(
|
||||
[:instance, :description],
|
||||
"An internal service actor for this Mobilizon instance"
|
||||
),
|
||||
"url" => url,
|
||||
"keys" => pem,
|
||||
"preferred_username" => preferred_username,
|
||||
"domain" => nil,
|
||||
"inbox_url" => "#{MobilizonWeb.Endpoint.url()}/inbox",
|
||||
"followers_url" => "#{url}/followers",
|
||||
"following_url" => "#{url}/following",
|
||||
"shared_inbox_url" => "#{MobilizonWeb.Endpoint.url()}/inbox",
|
||||
"type" => :Application
|
||||
}
|
||||
|
||||
cast(%Actor{}, vars, [
|
||||
:type,
|
||||
:name,
|
||||
:summary,
|
||||
:url,
|
||||
:keys,
|
||||
:preferred_username,
|
||||
:domain,
|
||||
:inbox_url,
|
||||
:followers_url,
|
||||
:following_url,
|
||||
:shared_inbox_url
|
||||
])
|
||||
end
|
||||
|
||||
@doc """
|
||||
Changeset for group creation
|
||||
"""
|
||||
@ -240,6 +277,14 @@ defmodule Mobilizon.Actors.Actor do
|
||||
:outbox_url,
|
||||
build_url(username, :outbox)
|
||||
)
|
||||
|> put_change(
|
||||
:followers_url,
|
||||
build_url(username, :followers)
|
||||
)
|
||||
|> put_change(
|
||||
:following_url,
|
||||
build_url(username, :following)
|
||||
)
|
||||
|> put_change(
|
||||
:inbox_url,
|
||||
build_url(username, :inbox)
|
||||
@ -325,18 +370,30 @@ defmodule Mobilizon.Actors.Actor do
|
||||
%{total: Task.await(total), elements: Task.await(elements)}
|
||||
end
|
||||
|
||||
@spec get_full_followers(struct()) :: list()
|
||||
def get_full_followers(%Actor{id: actor_id} = _actor) do
|
||||
Repo.all(
|
||||
from(
|
||||
a in Actor,
|
||||
join: f in Follower,
|
||||
on: a.id == f.actor_id,
|
||||
where: f.target_actor_id == ^actor_id
|
||||
)
|
||||
defp get_full_followers_query(%Actor{id: actor_id} = _actor) do
|
||||
from(
|
||||
a in Actor,
|
||||
join: f in Follower,
|
||||
on: a.id == f.actor_id,
|
||||
where: f.target_actor_id == ^actor_id
|
||||
)
|
||||
end
|
||||
|
||||
@spec get_full_followers(struct()) :: list()
|
||||
def get_full_followers(%Actor{} = actor) do
|
||||
actor
|
||||
|> get_full_followers_query()
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@spec get_full_external_followers(struct()) :: list()
|
||||
def get_full_external_followers(%Actor{} = actor) do
|
||||
actor
|
||||
|> get_full_followers_query()
|
||||
|> where([a], not is_nil(a.domain))
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get followings from an actor
|
||||
|
||||
@ -404,18 +461,19 @@ defmodule Mobilizon.Actors.Actor do
|
||||
Make an actor follow another
|
||||
"""
|
||||
@spec follow(struct(), struct(), boolean()) :: Follower.t() | {:error, String.t()}
|
||||
def follow(%Actor{} = followed, %Actor{} = follower, approved \\ true) do
|
||||
def follow(%Actor{} = followed, %Actor{} = follower, url \\ nil, approved \\ true) do
|
||||
with {:suspended, false} <- {:suspended, followed.suspended},
|
||||
# Check if followed has blocked follower
|
||||
{:already_following, false} <- {:already_following, following?(follower, followed)} do
|
||||
do_follow(follower, followed, approved)
|
||||
do_follow(follower, followed, approved, url)
|
||||
else
|
||||
{:already_following, %Follower{}} ->
|
||||
{:error,
|
||||
{:error, :already_following,
|
||||
"Could not follow actor: you are already following #{followed.preferred_username}"}
|
||||
|
||||
{:suspended, _} ->
|
||||
{:error, "Could not follow actor: #{followed.preferred_username} has been suspended"}
|
||||
{:error, :suspended,
|
||||
"Could not follow actor: #{followed.preferred_username} has been suspended"}
|
||||
end
|
||||
end
|
||||
|
||||
@ -433,13 +491,20 @@ defmodule Mobilizon.Actors.Actor do
|
||||
end
|
||||
end
|
||||
|
||||
@spec do_follow(struct(), struct(), boolean) ::
|
||||
@spec do_follow(struct(), struct(), boolean(), String.t()) ::
|
||||
{:ok, Follower.t()} | {:error, Ecto.Changeset.t()}
|
||||
defp do_follow(%Actor{} = follower, %Actor{} = followed, approved) do
|
||||
defp do_follow(%Actor{} = follower, %Actor{} = followed, approved, url) do
|
||||
Logger.info(
|
||||
"Making #{follower.preferred_username} follow #{followed.preferred_username} (approved: #{
|
||||
approved
|
||||
})"
|
||||
)
|
||||
|
||||
Actors.create_follower(%{
|
||||
"actor_id" => follower.id,
|
||||
"target_actor_id" => followed.id,
|
||||
"approved" => approved
|
||||
"approved" => approved,
|
||||
"url" => url
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -297,7 +297,7 @@ defmodule Mobilizon.Actors do
|
||||
{:ok, actor}
|
||||
|
||||
err ->
|
||||
Logger.error(inspect(err))
|
||||
Logger.debug(inspect(err))
|
||||
{:error, err}
|
||||
end
|
||||
end
|
||||
@ -475,16 +475,16 @@ defmodule Mobilizon.Actors do
|
||||
@spec get_or_fetch_by_url(String.t(), bool()) :: {:ok, Actor.t()} | {:error, String.t()}
|
||||
def get_or_fetch_by_url(url, preload \\ false) do
|
||||
case get_actor_by_url(url, preload) do
|
||||
{:ok, actor} ->
|
||||
{:ok, %Actor{} = actor} ->
|
||||
{:ok, actor}
|
||||
|
||||
_ ->
|
||||
case ActivityPub.make_actor_from_url(url, preload) do
|
||||
{:ok, actor} ->
|
||||
{:ok, %Actor{} = actor} ->
|
||||
{:ok, actor}
|
||||
|
||||
_ ->
|
||||
Logger.error("Could not fetch by AP id")
|
||||
Logger.warn("Could not fetch by AP id")
|
||||
{:error, "Could not fetch by AP id"}
|
||||
end
|
||||
end
|
||||
@ -655,6 +655,18 @@ defmodule Mobilizon.Actors do
|
||||
end
|
||||
end
|
||||
|
||||
def get_or_create_service_actor_by_url(url, preferred_username \\ "relay") do
|
||||
case get_actor_by_url(url) do
|
||||
{:ok, %Actor{} = actor} ->
|
||||
{:ok, actor}
|
||||
|
||||
_ ->
|
||||
%{url: url, preferred_username: preferred_username}
|
||||
|> Actor.relay_creation()
|
||||
|> Repo.insert()
|
||||
end
|
||||
end
|
||||
|
||||
alias Mobilizon.Actors.Member
|
||||
|
||||
@doc """
|
||||
@ -895,7 +907,7 @@ defmodule Mobilizon.Actors do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get a follower by the followed actor and following actor
|
||||
Get a follow by the followed actor and following actor
|
||||
"""
|
||||
@spec get_follower(Actor.t(), Actor.t()) :: Follower.t()
|
||||
def get_follower(%Actor{id: followed_id}, %Actor{id: follower_id}) do
|
||||
@ -904,6 +916,19 @@ defmodule Mobilizon.Actors do
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get a follow by the followed actor and following actor
|
||||
"""
|
||||
@spec get_follow_by_url(String.t()) :: Follower.t()
|
||||
def get_follow_by_url(url) do
|
||||
Repo.one(
|
||||
from(f in Follower,
|
||||
where: f.url == ^url,
|
||||
preload: [:actor, :target_actor]
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a follower.
|
||||
|
||||
@ -1009,7 +1034,7 @@ defmodule Mobilizon.Actors do
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("Error while removing an upload file")
|
||||
Logger.error(inspect(error))
|
||||
Logger.debug(inspect(error))
|
||||
{:ok, actor}
|
||||
end
|
||||
end
|
||||
|
@ -7,9 +7,11 @@ defmodule Mobilizon.Actors.Follower do
|
||||
alias Mobilizon.Actors.Follower
|
||||
alias Mobilizon.Actors.Actor
|
||||
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
|
||||
schema "followers" do
|
||||
field(:approved, :boolean, default: false)
|
||||
field(:score, :integer, default: 1000)
|
||||
field(:url, :string)
|
||||
belongs_to(:target_actor, Actor)
|
||||
belongs_to(:actor, Actor)
|
||||
end
|
||||
@ -17,8 +19,34 @@ defmodule Mobilizon.Actors.Follower do
|
||||
@doc false
|
||||
def changeset(%Follower{} = member, attrs) do
|
||||
member
|
||||
|> cast(attrs, [:score, :approved, :target_actor_id, :actor_id])
|
||||
|> validate_required([:score, :approved, :target_actor_id, :actor_id])
|
||||
|> cast(attrs, [:url, :approved, :target_actor_id, :actor_id])
|
||||
|> generate_url()
|
||||
|> validate_required([:url, :approved, :target_actor_id, :actor_id])
|
||||
|> unique_constraint(:target_actor_id, name: :followers_actor_target_actor_unique_index)
|
||||
end
|
||||
|
||||
# If there's a blank URL that's because we're doing the first insert
|
||||
defp generate_url(%Ecto.Changeset{data: %Follower{url: nil}} = changeset) do
|
||||
case fetch_change(changeset, :url) do
|
||||
{:ok, _url} -> changeset
|
||||
:error -> do_generate_url(changeset)
|
||||
end
|
||||
end
|
||||
|
||||
# Most time just go with the given URL
|
||||
defp generate_url(%Ecto.Changeset{} = changeset), do: changeset
|
||||
|
||||
defp do_generate_url(%Ecto.Changeset{} = changeset) do
|
||||
uuid = Ecto.UUID.generate()
|
||||
|
||||
changeset
|
||||
|> put_change(
|
||||
:url,
|
||||
"#{MobilizonWeb.Endpoint.url()}/follow/#{uuid}"
|
||||
)
|
||||
|> put_change(
|
||||
:id,
|
||||
uuid
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -32,7 +32,8 @@ defmodule Mobilizon.Events do
|
||||
:tracks,
|
||||
:tags,
|
||||
:participants,
|
||||
:physical_address
|
||||
:physical_address,
|
||||
:picture
|
||||
]
|
||||
)
|
||||
|> paginate(page, limit)
|
||||
@ -248,7 +249,8 @@ defmodule Mobilizon.Events do
|
||||
:tracks,
|
||||
:tags,
|
||||
:participants,
|
||||
:physical_address
|
||||
:physical_address,
|
||||
:picture
|
||||
]
|
||||
)
|
||||
)
|
||||
|
@ -9,6 +9,8 @@ defmodule MobilizonWeb.API.Events do
|
||||
alias Mobilizon.Service.ActivityPub.Utils, as: ActivityPubUtils
|
||||
alias MobilizonWeb.API.Utils
|
||||
|
||||
@visibility %{"PUBLIC" => :public, "PRIVATE" => :private}
|
||||
|
||||
@doc """
|
||||
Create an event
|
||||
"""
|
||||
|
51
lib/mobilizon_web/api/follows.ex
Normal file
51
lib/mobilizon_web/api/follows.ex
Normal file
@ -0,0 +1,51 @@
|
||||
defmodule MobilizonWeb.API.Follows do
|
||||
@moduledoc """
|
||||
Common API for following, unfollowing, accepting and rejecting stuff.
|
||||
"""
|
||||
|
||||
alias Mobilizon.Actors
|
||||
alias Mobilizon.Actors.{Actor, Follower}
|
||||
alias Mobilizon.Service.ActivityPub
|
||||
require Logger
|
||||
|
||||
def follow(%Actor{} = follower, %Actor{} = followed) do
|
||||
case ActivityPub.follow(follower, followed) do
|
||||
{:ok, activity, _} ->
|
||||
{:ok, activity}
|
||||
|
||||
e ->
|
||||
Logger.warn("Error while following actor: #{inspect(e)}")
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
def unfollow(%Actor{} = follower, %Actor{} = followed) do
|
||||
case ActivityPub.unfollow(follower, followed) do
|
||||
{:ok, activity, _} ->
|
||||
{:ok, activity}
|
||||
|
||||
e ->
|
||||
Logger.warn("Error while unfollowing actor: #{inspect(e)}")
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
def accept(%Actor{} = follower, %Actor{} = followed) do
|
||||
with %Follower{approved: false, id: follow_id, url: follow_url} = follow <-
|
||||
Actor.following?(follower, followed),
|
||||
activity_follow_url <- "#{MobilizonWeb.Endpoint.url()}/accept/follow/#{follow_id}",
|
||||
data <-
|
||||
ActivityPub.Utils.make_follow_data(followed, follower, follow_url),
|
||||
{:ok, activity, _} <-
|
||||
ActivityPub.accept(
|
||||
%{to: [follower.url], actor: followed.url, object: data},
|
||||
activity_follow_url
|
||||
),
|
||||
{:ok, %Follower{approved: true}} <- Actors.update_follower(follow, %{"approved" => true}) do
|
||||
{:ok, activity}
|
||||
else
|
||||
%Follower{approved: true} ->
|
||||
{:error, "Follow already accepted"}
|
||||
end
|
||||
end
|
||||
end
|
@ -9,7 +9,7 @@ defmodule MobilizonWeb.API.Utils do
|
||||
Determines the full audience based on mentions for a public audience
|
||||
|
||||
Audience is:
|
||||
* `to` : the mentionned actors, the eventual actor we're replying to and the public
|
||||
* `to` : the mentioned actors, the eventual actor we're replying to and the public
|
||||
* `cc` : the actor's followers
|
||||
"""
|
||||
@spec get_to_and_cc(Actor.t(), list(), map(), String.t()) :: {list(), list()}
|
||||
@ -72,7 +72,9 @@ defmodule MobilizonWeb.API.Utils do
|
||||
end
|
||||
end
|
||||
|
||||
def get_to_and_cc(_user, mentions, _inReplyTo, {:list, _}), do: {mentions, []}
|
||||
def get_to_and_cc(_actor, mentions, _inReplyTo, {:list, _}) do
|
||||
{mentions, []}
|
||||
end
|
||||
|
||||
# def get_addressed_users(_, to) when is_list(to) do
|
||||
# Actors.get(to)
|
||||
@ -138,7 +140,7 @@ defmodule MobilizonWeb.API.Utils do
|
||||
make_content_html(
|
||||
content,
|
||||
tags,
|
||||
"text/plain"
|
||||
"text/html"
|
||||
),
|
||||
mentioned_users <- for({_, mentioned_user} <- mentions, do: mentioned_user.url),
|
||||
addressed_users <- get_addressed_users(mentioned_users, nil),
|
||||
|
@ -14,6 +14,19 @@ defmodule MobilizonWeb.ActivityPubController do
|
||||
|
||||
action_fallback(:errors)
|
||||
|
||||
plug(:relay_active? when action in [:relay])
|
||||
|
||||
def relay_active?(conn, _) do
|
||||
if Mobilizon.CommonConfig.get([:instance, :allow_relay]) do
|
||||
conn
|
||||
else
|
||||
conn
|
||||
|> put_status(404)
|
||||
|> json("Not found")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
def following(conn, %{"name" => name, "page" => page}) do
|
||||
with {page, ""} <- Integer.parse(page),
|
||||
%Actor{} = actor <- Actors.get_local_actor_by_name_with_everything(name) do
|
||||
@ -67,6 +80,7 @@ defmodule MobilizonWeb.ActivityPubController do
|
||||
|
||||
# TODO: Ensure that this inbox is a recipient of the message
|
||||
def inbox(%{assigns: %{valid_signature: true}} = conn, params) do
|
||||
Logger.debug("Got something with valid signature inside inbox")
|
||||
Federator.enqueue(:incoming_ap_doc, params)
|
||||
json(conn, "ok")
|
||||
end
|
||||
@ -90,19 +104,35 @@ defmodule MobilizonWeb.ActivityPubController do
|
||||
"Signature validation error for: #{params["actor"]}, make sure you are forwarding the HTTP Host header!"
|
||||
)
|
||||
|
||||
Logger.error(inspect(conn.req_headers))
|
||||
Logger.debug(inspect(conn.req_headers))
|
||||
end
|
||||
|
||||
json(conn, "error")
|
||||
end
|
||||
|
||||
def relay(conn, _params) do
|
||||
with {status, actor} <-
|
||||
Cachex.fetch(
|
||||
:activity_pub,
|
||||
"relay_actor",
|
||||
&Mobilizon.Service.ActivityPub.Relay.get_actor/0
|
||||
),
|
||||
true <- status in [:ok, :commit] do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> json(ActorView.render("actor.json", %{actor: actor}))
|
||||
end
|
||||
end
|
||||
|
||||
def errors(conn, {:error, :not_found}) do
|
||||
conn
|
||||
|> put_status(404)
|
||||
|> json("Not found")
|
||||
end
|
||||
|
||||
def errors(conn, _e) do
|
||||
def errors(conn, e) do
|
||||
Logger.debug(inspect(e))
|
||||
|
||||
conn
|
||||
|> put_status(500)
|
||||
|> json("Unknown Error")
|
||||
|
@ -10,7 +10,6 @@ defmodule MobilizonWeb.HTTPSignaturePlug do
|
||||
Plug to check HTTP Signatures on every incoming request
|
||||
"""
|
||||
|
||||
alias Mobilizon.Service.HTTPSignatures
|
||||
import Plug.Conn
|
||||
require Logger
|
||||
|
||||
@ -23,32 +22,30 @@ defmodule MobilizonWeb.HTTPSignaturePlug do
|
||||
end
|
||||
|
||||
def call(conn, _opts) do
|
||||
actor = conn.params["actor"]
|
||||
|
||||
Logger.debug(fn ->
|
||||
"Checking sig for #{actor}"
|
||||
end)
|
||||
|
||||
[signature | _] = get_req_header(conn, "signature")
|
||||
|
||||
cond do
|
||||
String.contains?(signature, actor) ->
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header(
|
||||
"(request-target)",
|
||||
String.downcase("#{conn.method}") <> " #{conn.request_path}"
|
||||
)
|
||||
|
||||
assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn))
|
||||
|
||||
signature ->
|
||||
Logger.debug("Signature not from actor")
|
||||
assign(conn, :valid_signature, false)
|
||||
|
||||
true ->
|
||||
Logger.debug("No signature header!")
|
||||
if signature do
|
||||
# set (request-target) header to the appropriate value
|
||||
# we also replace the digest header with the one we computed
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header(
|
||||
"(request-target)",
|
||||
String.downcase("#{conn.method}") <> " #{conn.request_path}"
|
||||
)
|
||||
|
||||
conn =
|
||||
if conn.assigns[:digest] do
|
||||
conn
|
||||
|> put_req_header("digest", conn.assigns[:digest])
|
||||
else
|
||||
conn
|
||||
end
|
||||
|
||||
assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn))
|
||||
else
|
||||
Logger.debug("No signature header!")
|
||||
conn
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -18,6 +18,10 @@ defmodule MobilizonWeb.Router do
|
||||
plug(MobilizonWeb.HTTPSignaturePlug)
|
||||
end
|
||||
|
||||
pipeline :relay do
|
||||
plug(:accepts, ["activity-json", "json"])
|
||||
end
|
||||
|
||||
pipeline :activity_pub do
|
||||
plug(:accepts, ["activity-json"])
|
||||
end
|
||||
@ -97,6 +101,13 @@ defmodule MobilizonWeb.Router do
|
||||
post("/inbox", ActivityPubController, :inbox)
|
||||
end
|
||||
|
||||
scope "/relay", MobilizonWeb do
|
||||
pipe_through(:relay)
|
||||
|
||||
get("/", ActivityPubController, :relay)
|
||||
post("/inbox", ActivityPubController, :inbox)
|
||||
end
|
||||
|
||||
scope "/proxy/", MobilizonWeb do
|
||||
pipe_through(:remote_media)
|
||||
|
||||
|
@ -12,12 +12,12 @@ defmodule MobilizonWeb.ActivityPub.ActorView do
|
||||
public_key = Mobilizon.Service.ActivityPub.Utils.pem_to_public_key_pem(actor.keys)
|
||||
|
||||
%{
|
||||
"id" => Actor.build_url(actor.preferred_username, :page),
|
||||
"type" => "Person",
|
||||
"following" => Actor.build_url(actor.preferred_username, :following),
|
||||
"followers" => Actor.build_url(actor.preferred_username, :followers),
|
||||
"inbox" => Actor.build_url(actor.preferred_username, :inbox),
|
||||
"outbox" => Actor.build_url(actor.preferred_username, :outbox),
|
||||
"id" => actor.url,
|
||||
"type" => to_string(actor.type),
|
||||
"following" => actor.following_url,
|
||||
"followers" => actor.followers_url,
|
||||
"inbox" => actor.inbox_url,
|
||||
"outbox" => actor.outbox_url,
|
||||
"preferredUsername" => actor.preferred_username,
|
||||
"name" => actor.name,
|
||||
"summary" => actor.summary,
|
||||
|
@ -31,8 +31,8 @@ defmodule MobilizonWeb.ErrorView do
|
||||
# template is found, let's render it as 500
|
||||
def template_not_found(template, assigns) do
|
||||
require Logger
|
||||
Logger.error("Template not found")
|
||||
Logger.error(inspect(template))
|
||||
Logger.warn("Template not found")
|
||||
Logger.debug(inspect(template))
|
||||
render("500.html", assigns)
|
||||
end
|
||||
end
|
||||
|
@ -46,24 +46,8 @@ defmodule MobilizonWeb.PageView do
|
||||
end
|
||||
|
||||
def render("event.activity-json", %{conn: %{assigns: %{object: event}}}) do
|
||||
event = Mobilizon.Service.ActivityPub.Converters.Event.model_to_as(event)
|
||||
{:ok, html, []} = Earmark.as_html(event["summary"])
|
||||
|
||||
%{
|
||||
"type" => "Event",
|
||||
"attributedTo" => event["actor"],
|
||||
"id" => event["id"],
|
||||
"name" => event["title"],
|
||||
"category" => event["category"],
|
||||
"content" => html,
|
||||
"source" => %{
|
||||
"content" => event["summary"],
|
||||
"mediaType" => "text/markdown"
|
||||
},
|
||||
"mediaType" => "text/html",
|
||||
"published" => event["publish_at"],
|
||||
"updated" => event["updated_at"]
|
||||
}
|
||||
event
|
||||
|> Mobilizon.Service.ActivityPub.Converters.Event.model_to_as()
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
|
@ -21,10 +21,11 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
alias Mobilizon.Actors.Follower
|
||||
|
||||
alias Mobilizon.Service.Federator
|
||||
alias Mobilizon.Service.HTTPSignatures
|
||||
alias Mobilizon.Service.HTTPSignatures.Signature
|
||||
|
||||
require Logger
|
||||
import Mobilizon.Service.ActivityPub.Utils
|
||||
import Mobilizon.Service.ActivityPub.Visibility
|
||||
|
||||
@doc """
|
||||
Get recipients for an activity or object
|
||||
@ -42,10 +43,6 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
def insert(map, local \\ true) when is_map(map) do
|
||||
with map <- lazy_put_activity_defaults(map),
|
||||
{:ok, object} <- insert_full_object(map) do
|
||||
object_id = if is_map(map["object"]), do: map["object"]["id"], else: map["id"]
|
||||
|
||||
map = if local, do: Map.put(map, "id", "#{object_id}/activity"), else: map
|
||||
|
||||
activity = %Activity{
|
||||
data: map,
|
||||
local: local,
|
||||
@ -94,7 +91,7 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
{:ok, _activity, %{url: object_url} = _object} <- Transmogrifier.handle_incoming(params) do
|
||||
case data["type"] do
|
||||
"Event" ->
|
||||
{:ok, Events.get_event_by_url!(object_url)}
|
||||
{:ok, Events.get_event_full_by_url!(object_url)}
|
||||
|
||||
"Note" ->
|
||||
{:ok, Events.get_comment_full_from_url!(object_url)}
|
||||
@ -107,15 +104,17 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
end
|
||||
else
|
||||
{:existing_event, %Event{url: event_url}} ->
|
||||
{:ok, Events.get_event_by_url!(event_url)}
|
||||
{:ok, Events.get_event_full_by_url!(event_url)}
|
||||
|
||||
{:existing_comment, %Comment{url: comment_url}} ->
|
||||
{:ok, Events.get_comment_full_from_url!(comment_url)}
|
||||
|
||||
{:existing_actor, %Actor{url: actor_url}} ->
|
||||
{:existing_actor, {:ok, %Actor{url: actor_url}}} ->
|
||||
{:ok, Actors.get_actor_by_url!(actor_url, true)}
|
||||
|
||||
e ->
|
||||
require Logger
|
||||
Logger.error(inspect(e))
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
@ -137,24 +136,40 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
%{to: to, actor: actor, published: published, object: object},
|
||||
additional
|
||||
),
|
||||
:ok <- Logger.debug(inspect(create_data)),
|
||||
{:ok, activity, object} <- insert(create_data, local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
# {:ok, actor} <- Actors.increase_event_count(actor) do
|
||||
{:ok, activity, object}
|
||||
else
|
||||
err ->
|
||||
Logger.error("Something went wrong")
|
||||
Logger.error(inspect(err))
|
||||
Logger.error("Something went wrong while creating an activity")
|
||||
Logger.debug(inspect(err))
|
||||
err
|
||||
end
|
||||
end
|
||||
|
||||
def accept(%{to: to, actor: actor, object: object} = params) do
|
||||
def accept(%{to: to, actor: actor, object: object} = params, activity_follow_id \\ nil) do
|
||||
# only accept false as false value
|
||||
local = !(params[:local] == false)
|
||||
|
||||
with data <- %{"to" => to, "type" => "Accept", "actor" => actor, "object" => object},
|
||||
with data <- %{
|
||||
"to" => to,
|
||||
"type" => "Accept",
|
||||
"actor" => actor,
|
||||
"object" => object,
|
||||
"id" => activity_follow_id || get_url(object) <> "/activity"
|
||||
},
|
||||
{:ok, activity, object} <- insert(data, local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity, object}
|
||||
end
|
||||
end
|
||||
|
||||
def reject(%{to: to, actor: actor, object: object} = params) do
|
||||
# only accept false as false value
|
||||
local = !(params[:local] == false)
|
||||
|
||||
with data <- %{"to" => to, "type" => "Reject", "actor" => actor.url, "object" => object},
|
||||
{:ok, activity, object} <- insert(data, local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity, object}
|
||||
@ -168,6 +183,7 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
with data <- %{
|
||||
"to" => to,
|
||||
"cc" => cc,
|
||||
"id" => object["url"],
|
||||
"type" => "Update",
|
||||
"actor" => actor,
|
||||
"object" => object
|
||||
@ -215,55 +231,56 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
# end
|
||||
# end
|
||||
|
||||
# def announce(
|
||||
# %Actor{} = actor,
|
||||
# object,
|
||||
# activity_id \\ nil,
|
||||
# local \\ true
|
||||
# ) do
|
||||
# #with true <- is_public?(object),
|
||||
# with announce_data <- make_announce_data(actor, object, activity_id),
|
||||
# {:ok, activity, object} <- insert(announce_data, local),
|
||||
# # {:ok, object} <- add_announce_to_object(activity, object),
|
||||
# :ok <- maybe_federate(activity) do
|
||||
# {:ok, activity, object}
|
||||
# else
|
||||
# error -> {:error, error}
|
||||
# end
|
||||
# end
|
||||
def announce(
|
||||
%Actor{} = actor,
|
||||
object,
|
||||
activity_id \\ nil,
|
||||
local \\ true,
|
||||
public \\ true
|
||||
) do
|
||||
with true <- is_public?(object),
|
||||
announce_data <- make_announce_data(actor, object, activity_id, public),
|
||||
{:ok, activity, object} <- insert(announce_data, local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity, object}
|
||||
else
|
||||
error ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
# def unannounce(
|
||||
# %Actor{} = actor,
|
||||
# object,
|
||||
# activity_id \\ nil,
|
||||
# local \\ true
|
||||
# ) do
|
||||
# with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
|
||||
# unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
|
||||
# {:ok, unannounce_activity, _object} <- insert(unannounce_data, local),
|
||||
# :ok <- maybe_federate(unannounce_activity),
|
||||
# {:ok, _activity} <- Repo.delete(announce_activity),
|
||||
# {:ok, object} <- remove_announce_from_object(announce_activity, object) do
|
||||
# {:ok, unannounce_activity, object}
|
||||
# else
|
||||
# _e -> {:ok, object}
|
||||
# end
|
||||
# end
|
||||
def unannounce(
|
||||
%Actor{} = actor,
|
||||
object,
|
||||
activity_id \\ nil,
|
||||
cancelled_activity_id \\ nil,
|
||||
local \\ true
|
||||
) do
|
||||
with announce_activity <- make_announce_data(actor, object, cancelled_activity_id),
|
||||
unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
|
||||
{:ok, unannounce_activity, _object} <- insert(unannounce_data, local),
|
||||
:ok <- maybe_federate(unannounce_activity) do
|
||||
{:ok, unannounce_activity, object}
|
||||
else
|
||||
_e -> {:ok, object}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Make an actor follow another
|
||||
"""
|
||||
def follow(%Actor{} = follower, %Actor{} = followed, activity_id \\ nil, local \\ true) do
|
||||
with {:ok, %Follower{id: follow_id}} <- Actor.follow(followed, follower, true),
|
||||
with {:ok, %Follower{url: follow_url}} <-
|
||||
Actor.follow(followed, follower, activity_id, false),
|
||||
activity_follow_id <-
|
||||
activity_id || "#{MobilizonWeb.Endpoint.url()}/follow/#{follow_id}/activity",
|
||||
activity_id || follow_url,
|
||||
data <- make_follow_data(followed, follower, activity_follow_id),
|
||||
{:ok, activity, object} <- insert(data, local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity, object}
|
||||
else
|
||||
{err, _} when err in [:already_following, :suspended] ->
|
||||
{:error, err}
|
||||
{:error, err, msg} when err in [:already_following, :suspended] ->
|
||||
{:error, msg}
|
||||
end
|
||||
end
|
||||
|
||||
@ -271,18 +288,26 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
Make an actor unfollow another
|
||||
"""
|
||||
@spec unfollow(Actor.t(), Actor.t(), String.t(), boolean()) :: {:ok, map()} | any()
|
||||
def unfollow(%Actor{} = followed, %Actor{} = follower, activity_id \\ nil, local \\ true) do
|
||||
def unfollow(%Actor{} = follower, %Actor{} = followed, activity_id \\ nil, local \\ true) do
|
||||
with {:ok, %Follower{id: follow_id}} <- Actor.unfollow(followed, follower),
|
||||
# We recreate the follow activity
|
||||
data <- make_follow_data(followed, follower, follow_id),
|
||||
data <-
|
||||
make_follow_data(
|
||||
followed,
|
||||
follower,
|
||||
"#{MobilizonWeb.Endpoint.url()}/follow/#{follow_id}/activity"
|
||||
),
|
||||
{:ok, follow_activity, _object} <- insert(data, local),
|
||||
unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
|
||||
activity_unfollow_id <-
|
||||
activity_id || "#{MobilizonWeb.Endpoint.url()}/unfollow/#{follow_id}/activity",
|
||||
unfollow_data <-
|
||||
make_unfollow_data(follower, followed, follow_activity, activity_unfollow_id),
|
||||
{:ok, activity, object} <- insert(unfollow_data, local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity, object}
|
||||
else
|
||||
err ->
|
||||
Logger.error(inspect(err))
|
||||
Logger.debug("Error while unfollowing an actor #{inspect(err)}")
|
||||
err
|
||||
end
|
||||
end
|
||||
@ -294,7 +319,8 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
"type" => "Delete",
|
||||
"actor" => actor.url,
|
||||
"object" => url,
|
||||
"to" => [actor.url <> "/followers", "https://www.w3.org/ns/activitystreams#Public"]
|
||||
"to" => [actor.url <> "/followers", "https://www.w3.org/ns/activitystreams#Public"],
|
||||
"id" => url <> "/delete"
|
||||
}
|
||||
|
||||
with {:ok, _} <- Events.delete_event(event),
|
||||
@ -309,6 +335,7 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
"type" => "Delete",
|
||||
"actor" => actor.url,
|
||||
"object" => url,
|
||||
"id" => url <> "/delete",
|
||||
"to" => [actor.url <> "/followers", "https://www.w3.org/ns/activitystreams#Public"]
|
||||
}
|
||||
|
||||
@ -324,6 +351,7 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
"type" => "Delete",
|
||||
"actor" => url,
|
||||
"object" => url,
|
||||
"id" => url <> "/delete",
|
||||
"to" => [url <> "/followers", "https://www.w3.org/ns/activitystreams#Public"]
|
||||
}
|
||||
|
||||
@ -366,11 +394,11 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
|
||||
# Request returned 410
|
||||
{:error, :actor_deleted} ->
|
||||
Logger.info("Actor was deleted")
|
||||
{:error, :actor_deleted}
|
||||
|
||||
e ->
|
||||
Logger.error("Failed to make actor from url")
|
||||
Logger.error(inspect(e))
|
||||
Logger.warn("Failed to make actor from url")
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
@ -414,10 +442,18 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
"""
|
||||
def publish(actor, activity) do
|
||||
Logger.debug("Publishing an activity")
|
||||
Logger.debug(inspect(activity))
|
||||
|
||||
public = is_public?(activity)
|
||||
|
||||
if public && Mobilizon.CommonConfig.get([:instance, :allow_relay]) do
|
||||
Logger.info(fn -> "Relaying #{activity.data["id"]} out" end)
|
||||
Mobilizon.Service.ActivityPub.Relay.publish(activity)
|
||||
end
|
||||
|
||||
followers =
|
||||
if actor.followers_url in activity.recipients do
|
||||
Actor.get_full_followers(actor) |> Enum.filter(fn follower -> is_nil(follower.domain) end)
|
||||
Actor.get_full_external_followers(actor)
|
||||
else
|
||||
[]
|
||||
end
|
||||
@ -448,15 +484,16 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
Logger.info("Federating #{id} to #{inbox}")
|
||||
%URI{host: host, path: path} = URI.parse(inbox)
|
||||
|
||||
digest = HTTPSignatures.build_digest(json)
|
||||
date = HTTPSignatures.generate_date_header()
|
||||
request_target = HTTPSignatures.generate_request_target("POST", path)
|
||||
digest = Signature.build_digest(json)
|
||||
date = Signature.generate_date_header()
|
||||
# request_target = Signature.generate_request_target("POST", path)
|
||||
|
||||
signature =
|
||||
HTTPSignatures.sign(actor, %{
|
||||
Signature.sign(actor, %{
|
||||
host: host,
|
||||
"content-length": byte_size(json),
|
||||
"(request-target)": request_target,
|
||||
# TODO : Look me up in depth why Pleroma handles this inside lib/mobilizon_web/http_signature.ex
|
||||
# "(request-target)": request_target,
|
||||
digest: digest,
|
||||
date: date
|
||||
})
|
||||
@ -478,20 +515,27 @@ defmodule Mobilizon.Service.ActivityPub do
|
||||
@spec fetch_and_prepare_actor_from_url(String.t()) :: {:ok, struct()} | {:error, atom()} | any()
|
||||
defp fetch_and_prepare_actor_from_url(url) do
|
||||
Logger.debug("Fetching and preparing actor from url")
|
||||
Logger.debug(inspect(url))
|
||||
|
||||
with {:ok, %HTTPoison.Response{status_code: 200, body: body}} <-
|
||||
HTTPoison.get(url, [Accept: "application/activity+json"], follow_redirect: true),
|
||||
{:ok, data} <- Jason.decode(body) do
|
||||
actor_data_from_actor_object(data)
|
||||
else
|
||||
# Actor is gone, probably deleted
|
||||
{:ok, %HTTPoison.Response{status_code: 410}} ->
|
||||
{:error, :actor_deleted}
|
||||
res =
|
||||
with %HTTPoison.Response{status_code: 200, body: body} <-
|
||||
HTTPoison.get!(url, [Accept: "application/activity+json"], follow_redirect: true),
|
||||
:ok <- Logger.debug("response okay, now decoding json"),
|
||||
{:ok, data} <- Jason.decode(body) do
|
||||
Logger.debug("Got activity+json response at actor's endpoint, now converting data")
|
||||
actor_data_from_actor_object(data)
|
||||
else
|
||||
# Actor is gone, probably deleted
|
||||
{:ok, %HTTPoison.Response{status_code: 410}} ->
|
||||
Logger.info("Response HTTP 410")
|
||||
{:error, :actor_deleted}
|
||||
|
||||
e ->
|
||||
Logger.error("Could not decode actor at fetch #{url}, #{inspect(e)}")
|
||||
e
|
||||
end
|
||||
e ->
|
||||
Logger.warn("Could not decode actor at fetch #{url}, #{inspect(e)}")
|
||||
{:error, e}
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
@ -7,3 +7,10 @@ defmodule Mobilizon.Service.ActivityPub.Converter do
|
||||
@callback as_to_model_data(map()) :: map()
|
||||
@callback model_to_as(struct()) :: map()
|
||||
end
|
||||
|
||||
defprotocol Mobilizon.Service.ActivityPub.Convertible do
|
||||
@type activitystreams :: map()
|
||||
|
||||
@spec model_to_as(t) :: activitystreams
|
||||
def model_to_as(convertible)
|
||||
end
|
||||
|
@ -45,3 +45,9 @@ defmodule Mobilizon.Service.ActivityPub.Converters.Actor do
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
defimpl Mobilizon.Service.ActivityPub.Convertible, for: Mobilizon.Actors.Actor do
|
||||
alias Mobilizon.Service.ActivityPub.Converters.Actor, as: ActorConverter
|
||||
|
||||
defdelegate model_to_as(actor), to: |