More tests
This commit is contained in:
@@ -3,5 +3,5 @@ defmodule Eventos.Activity do
|
||||
Represents an activity
|
||||
"""
|
||||
|
||||
defstruct [:id, :data, :local, :actor, :recipients, :notifications]
|
||||
defstruct [:id, :data, :local, :actor, :recipients, :notifications, :type]
|
||||
end
|
||||
|
||||
@@ -173,23 +173,9 @@ defmodule Eventos.Actors.Actor do
|
||||
|> put_change(:local, true)
|
||||
end
|
||||
|
||||
def get_or_fetch_by_url(url) do
|
||||
if user = Actors.get_actor_by_url(url) do
|
||||
user
|
||||
else
|
||||
case ActivityPub.make_actor_from_url(url) do
|
||||
{:ok, user} ->
|
||||
user
|
||||
|
||||
_ ->
|
||||
{:error, "Could not fetch by AP id"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@spec get_public_key_for_url(String.t()) :: {:ok, String.t()}
|
||||
def get_public_key_for_url(url) do
|
||||
with %Actor{} = actor <- get_or_fetch_by_url(url) do
|
||||
with {:ok, %Actor{} = actor} <- Actors.get_or_fetch_by_url(url) do
|
||||
actor.keys
|
||||
|> Eventos.Service.ActivityPub.Utils.pem_to_public_key()
|
||||
else
|
||||
@@ -228,4 +214,44 @@ defmodule Eventos.Actors.Actor do
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def get_groups_member_of(%Actor{id: actor_id}) do
|
||||
Repo.all(
|
||||
from(
|
||||
a in Actor,
|
||||
join: m in Member,
|
||||
on: a.id == m.parent_id,
|
||||
where: m.actor_id == ^actor_id
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def get_members_for_group(%Actor{id: actor_id}) do
|
||||
Repo.all(
|
||||
from(
|
||||
a in Actor,
|
||||
join: m in Member,
|
||||
on: a.id == m.actor_id,
|
||||
where: m.parent_id == ^actor_id
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def follow(%Actor{} = follower, %Actor{} = followed) do
|
||||
# Check if actor is locked
|
||||
# Check if followed has blocked follower
|
||||
# Check if follower already follows followed
|
||||
cond do
|
||||
following?(follower, followed) ->
|
||||
{:error,
|
||||
"Could not follow actor: you are already following #{followed.preferred_username}"}
|
||||
|
||||
# true -> nil
|
||||
# Follow the person
|
||||
end
|
||||
end
|
||||
|
||||
def following?(%Actor{} = follower, %Actor{followers: followers}) do
|
||||
Enum.member?(followers, follower)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -265,13 +265,11 @@ defmodule Eventos.Actors do
|
||||
|
||||
def get_or_fetch_by_url(url) do
|
||||
if actor = get_actor_by_url(url) do
|
||||
actor
|
||||
{:ok, actor}
|
||||
else
|
||||
ap_try = ActivityPub.make_actor_from_url(url)
|
||||
|
||||
case ap_try do
|
||||
case ActivityPub.make_actor_from_url(url) do
|
||||
{:ok, actor} ->
|
||||
actor
|
||||
{:ok, actor}
|
||||
|
||||
_ ->
|
||||
{:error, "Could not fetch by AP id"}
|
||||
@@ -299,7 +297,7 @@ defmodule Eventos.Actors do
|
||||
@doc """
|
||||
Find actors by their name or displayed name
|
||||
"""
|
||||
def find_actors_by_username(username) do
|
||||
def find_actors_by_username_or_name(username) do
|
||||
Repo.all(
|
||||
from(
|
||||
a in Actor,
|
||||
@@ -320,7 +318,7 @@ defmodule Eventos.Actors do
|
||||
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
|
||||
def search(name) do
|
||||
# find already saved accounts
|
||||
case find_actors_by_username(name) do
|
||||
case find_actors_by_username_or_name(name) do
|
||||
[] ->
|
||||
# no accounts found, let's test if it's an username@domain.tld
|
||||
with true <- Regex.match?(@email_regex, name),
|
||||
@@ -457,6 +455,24 @@ defmodule Eventos.Actors do
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets an user by it's email
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_user_by_email(user, email)
|
||||
{:ok, %User{}}
|
||||
|
||||
iex> get_user_by_email(user, wrong_email)
|
||||
{:error, nil}
|
||||
"""
|
||||
def get_user_by_email(email) do
|
||||
case Repo.get_by(User, email: email) do
|
||||
nil -> {:error, nil}
|
||||
user -> {:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates a user.
|
||||
|
||||
@@ -548,10 +564,12 @@ defmodule Eventos.Actors do
|
||||
|
||||
"""
|
||||
def create_member(attrs \\ %{}) do
|
||||
%Member{}
|
||||
|> Member.changeset(attrs)
|
||||
|> Repo.insert!()
|
||||
|> Repo.preload([:actor, :parent])
|
||||
with {:ok, %Member{} = member} <-
|
||||
%Member{}
|
||||
|> Member.changeset(attrs)
|
||||
|> Repo.insert() do
|
||||
{:ok, Repo.preload(member, [:actor, :parent])}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
@@ -7,7 +7,6 @@ defmodule Eventos.Actors.Member do
|
||||
alias Eventos.Actors.Member
|
||||
alias Eventos.Actors.Actor
|
||||
|
||||
@primary_key false
|
||||
schema "members" do
|
||||
field(:approved, :boolean, default: true)
|
||||
# 0 : Member, 1 : Moderator, 2 : Admin
|
||||
@@ -23,5 +22,6 @@ defmodule Eventos.Actors.Member do
|
||||
member
|
||||
|> cast(attrs, [:role, :approved, :parent_id, :actor_id])
|
||||
|> validate_required([:parent_id, :actor_id])
|
||||
|> unique_constraint(:parent_id, name: :members_actor_parent_unique_index)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,16 +11,18 @@ defmodule Eventos.Actors.Service.ResetPassword do
|
||||
"""
|
||||
@spec check_reset_password_token(String.t(), String.t()) :: tuple
|
||||
def check_reset_password_token(password, token) do
|
||||
with %User{} = user <- Repo.get_by(User, reset_password_token: token) do
|
||||
Repo.update(
|
||||
User.password_reset_changeset(user, %{
|
||||
"password" => password,
|
||||
"reset_password_sent_at" => nil,
|
||||
"reset_password_token" => nil
|
||||
})
|
||||
)
|
||||
with %User{} = user <- Repo.get_by(User, reset_password_token: token),
|
||||
{:ok, %User{} = user} <-
|
||||
Repo.update(
|
||||
User.password_reset_changeset(user, %{
|
||||
"password" => password,
|
||||
"reset_password_sent_at" => nil,
|
||||
"reset_password_token" => nil
|
||||
})
|
||||
) do
|
||||
{:ok, Repo.preload(user, :actors)}
|
||||
else
|
||||
_err ->
|
||||
err ->
|
||||
{:error, :invalid_token}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,17 +21,23 @@ defmodule Eventos.Events.Comment do
|
||||
belongs_to(:in_reply_to_comment, Comment, foreign_key: :in_reply_to_comment_id)
|
||||
belongs_to(:origin_comment, Comment, foreign_key: :origin_comment_id)
|
||||
|
||||
timestamps()
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(comment, attrs) do
|
||||
uuid = Ecto.UUID.generate()
|
||||
|
||||
# TODO : really change me right away
|
||||
url =
|
||||
if Map.has_key?(attrs, "url"),
|
||||
do: attrs["url"],
|
||||
else: "#{EventosWeb.Endpoint.url()}/comments/#{uuid}"
|
||||
|
||||
comment
|
||||
|> cast(attrs, [:url, :text, :actor_id, :event_id, :in_reply_to_comment_id, :attributed_to_id])
|
||||
|> validate_required([:text, :actor_id])
|
||||
|> put_change(:uuid, uuid)
|
||||
|> put_change(:url, "#{EventosWeb.Endpoint.url()}/comments/#{uuid}")
|
||||
|> put_change(:url, url)
|
||||
|> validate_required([:text, :actor_id, :url])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -18,8 +18,11 @@ defmodule Eventos.Events.Event do
|
||||
field(:description, :string)
|
||||
field(:ends_on, Timex.Ecto.DateTimeWithTimezone)
|
||||
field(:title, :string)
|
||||
# ???
|
||||
field(:state, :integer, default: 0)
|
||||
# Event status: TENTATIVE 1, CONFIRMED 2, CANCELLED 3
|
||||
field(:status, :integer, default: 0)
|
||||
# If the event is public or private
|
||||
field(:public, :boolean, default: true)
|
||||
field(:thumbnail, :string)
|
||||
field(:large_image, :string)
|
||||
@@ -42,9 +45,7 @@ defmodule Eventos.Events.Event do
|
||||
|
||||
@doc false
|
||||
def changeset(%Event{} = event, attrs) do
|
||||
uuid = Ecto.UUID.generate()
|
||||
|
||||
# TODO : check what's the use here. Tests ?
|
||||
# TODO : Change all of this
|
||||
actor_url =
|
||||
if Map.has_key?(attrs, :organizer_actor) do
|
||||
attrs.organizer_actor.preferred_username
|
||||
@@ -52,6 +53,13 @@ defmodule Eventos.Events.Event do
|
||||
""
|
||||
end
|
||||
|
||||
uuid = Ecto.UUID.generate()
|
||||
|
||||
url =
|
||||
if Map.has_key?(attrs, "url"),
|
||||
do: attrs["url"],
|
||||
else: "#{EventosWeb.Endpoint.url()}/@#{actor_url}/#{uuid}"
|
||||
|
||||
event
|
||||
|> Ecto.Changeset.cast(attrs, [
|
||||
:title,
|
||||
@@ -74,7 +82,7 @@ defmodule Eventos.Events.Event do
|
||||
|> cast_assoc(:tags)
|
||||
|> cast_assoc(:physical_address)
|
||||
|> put_change(:uuid, uuid)
|
||||
|> put_change(:url, "#{EventosWeb.Endpoint.url()}/@#{actor_url}/#{uuid}")
|
||||
|> put_change(:url, url)
|
||||
|> validate_required([
|
||||
:title,
|
||||
:begins_on,
|
||||
|
||||
@@ -110,10 +110,17 @@ defmodule Eventos.Events do
|
||||
@doc """
|
||||
Gets an event by it's URL
|
||||
"""
|
||||
def get_event_by_url!(url) do
|
||||
def get_event_by_url(url) do
|
||||
Repo.get_by(Event, url: url)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets an event by it's URL
|
||||
"""
|
||||
def get_event_by_url!(url) do
|
||||
Repo.get_by!(Event, url: url)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets an event by it's UUID
|
||||
"""
|
||||
@@ -175,7 +182,10 @@ defmodule Eventos.Events do
|
||||
@doc """
|
||||
Find events by name
|
||||
"""
|
||||
def find_events_by_name(name) when name == "", do: []
|
||||
|
||||
def find_events_by_name(name) do
|
||||
name = String.trim(name)
|
||||
events = Repo.all(from(a in Event, where: ilike(a.title, ^like_sanitize(name))))
|
||||
Repo.preload(events, [:organizer_actor])
|
||||
end
|
||||
@@ -780,6 +790,32 @@ defmodule Eventos.Events do
|
||||
Repo.all(Comment)
|
||||
end
|
||||
|
||||
def get_comments_for_actor(%Actor{id: actor_id}, page \\ 1, limit \\ 10) do
|
||||
start = (page - 1) * limit
|
||||
|
||||
query =
|
||||
from(
|
||||
c in Comment,
|
||||
where: c.actor_id == ^actor_id,
|
||||
limit: ^limit,
|
||||
order_by: [desc: :id],
|
||||
offset: ^start,
|
||||
preload: [
|
||||
:actor,
|
||||
:in_reply_to_comment,
|
||||
:origin_comment,
|
||||
:event
|
||||
]
|
||||
)
|
||||
|
||||
comments = Repo.all(query)
|
||||
|
||||
count_comments =
|
||||
Repo.one(from(c in Comment, select: count(c.id), where: c.actor_id == ^actor_id))
|
||||
|
||||
{:ok, comments, count_comments}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a single comment.
|
||||
|
||||
@@ -798,6 +834,16 @@ defmodule Eventos.Events do
|
||||
|
||||
def get_comment_with_uuid!(uuid), do: Repo.get_by!(Comment, uuid: uuid)
|
||||
|
||||
def get_comment_from_url(url), do: Repo.get_by(Comment, url: url)
|
||||
|
||||
def get_comment_from_url!(url), do: Repo.get_by!(Comment, url: url)
|
||||
|
||||
def get_comment_full_from_url!(url) do
|
||||
with %Comment{} = comment <- Repo.get_by!(Comment, url: url) do
|
||||
Repo.preload(comment, :actor)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a comment.
|
||||
|
||||
|
||||
@@ -18,10 +18,16 @@ defmodule EventosWeb.ActivityPubController do
|
||||
end
|
||||
|
||||
def event(conn, %{"uuid" => uuid}) do
|
||||
with %Event{} = event <- Events.get_event_full_by_uuid(uuid) do
|
||||
with %Event{} = event <- Events.get_event_full_by_uuid(uuid),
|
||||
true <- event.public do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> json(ObjectView.render("event.json", %{event: event}))
|
||||
else
|
||||
false ->
|
||||
conn
|
||||
|> put_status(404)
|
||||
|> json("Not found")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -37,8 +37,12 @@ defmodule EventosWeb.ActorController do
|
||||
end
|
||||
|
||||
def show(conn, %{"name" => name}) do
|
||||
actor = Actors.get_actor_by_name_with_everything(name)
|
||||
render(conn, "show.json", actor: actor)
|
||||
with %Actor{} = actor <- Actors.get_actor_by_name_with_everything(name) do
|
||||
render(conn, "show.json", actor: actor)
|
||||
else
|
||||
nil ->
|
||||
send_resp(conn, :not_found, "")
|
||||
end
|
||||
end
|
||||
|
||||
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
|
||||
@@ -57,7 +61,7 @@ defmodule EventosWeb.ActorController do
|
||||
actor = Actors.get_local_actor_by_name(name)
|
||||
|
||||
with {:ok, %Actor{} = actor} <- Actors.update_actor(actor, actor_params) do
|
||||
render(conn, "show.json", actor: actor)
|
||||
render(conn, "show_basic.json", actor: actor)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -15,15 +15,13 @@ defmodule EventosWeb.GroupController do
|
||||
end
|
||||
|
||||
def create(conn, %{"group" => group_params}) do
|
||||
with {:ok, %Actor{} = group} <- Actors.create_group(group_params) do
|
||||
%Member{} =
|
||||
_member =
|
||||
Actors.create_member(%{
|
||||
"parent_id" => group.id,
|
||||
"actor_id" => Actors.get_local_actor_by_name(group_params["actor_admin"]).id,
|
||||
"role" => 2
|
||||
})
|
||||
|
||||
with {:ok, %Actor{} = group} <- Actors.create_group(group_params),
|
||||
{:ok, %Member{} = member} <-
|
||||
Actors.create_member(%{
|
||||
"parent_id" => group.id,
|
||||
"actor_id" => Actors.get_local_actor_by_name(group_params["actor_admin"]).id,
|
||||
"role" => 2
|
||||
}) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> put_resp_header("location", actor_path(conn, :show, group))
|
||||
@@ -34,7 +32,7 @@ defmodule EventosWeb.GroupController do
|
||||
def join(conn, %{"name" => group_name, "actor_name" => actor_name}) do
|
||||
with %Actor{} = group <- Actors.get_group_by_name(group_name),
|
||||
%Actor{} = actor <- Actors.get_local_actor_by_name(actor_name),
|
||||
%Member{} = member <-
|
||||
{:ok, %Member{} = member} <-
|
||||
Actors.create_member(%{"parent_id" => group.id, "actor_id" => actor.id}) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|
||||
@@ -42,11 +42,15 @@ defmodule EventosWeb.UserController do
|
||||
end
|
||||
end
|
||||
|
||||
@time_before_resend 3600
|
||||
def resend_confirmation(conn, %{"email" => email}) do
|
||||
with {:ok, %User{} = user} <- Actors.find_by_email(email),
|
||||
false <- is_nil(user.confirmation_token),
|
||||
true <-
|
||||
Timex.before?(Timex.shift(user.confirmation_sent_at, hours: 1), DateTime.utc_now()) do
|
||||
Timex.before?(
|
||||
Timex.shift(user.confirmation_sent_at, seconds: @time_before_resend),
|
||||
DateTime.utc_now()
|
||||
) do
|
||||
Activation.resend_confirmation_email(user)
|
||||
render(conn, "confirmation.json", %{user: user})
|
||||
else
|
||||
@@ -58,7 +62,10 @@ defmodule EventosWeb.UserController do
|
||||
_ ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{"error" => "Unable to resend the validation token"})
|
||||
|> json(%{
|
||||
"error" =>
|
||||
"Unable to resend the validation token. Please wait a while before you can ask for resending token"
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -67,7 +74,7 @@ defmodule EventosWeb.UserController do
|
||||
{:ok, _} <- ResetPassword.send_password_reset_email(user) do
|
||||
render(conn, "password_reset.json", %{user: user})
|
||||
else
|
||||
{:error, :not_found} ->
|
||||
{:error, nil} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{"errors" => "Unable to find an user with this email"})
|
||||
@@ -105,23 +112,23 @@ defmodule EventosWeb.UserController do
|
||||
render(conn, "show_simple.json", user: user)
|
||||
end
|
||||
|
||||
defp handle_changeset_errors(errors) do
|
||||
errors
|
||||
|> Enum.map(fn {field, detail} ->
|
||||
"#{field} " <> render_detail(detail)
|
||||
end)
|
||||
|> Enum.join()
|
||||
end
|
||||
# defp handle_changeset_errors(errors) do
|
||||
# errors
|
||||
# |> Enum.map(fn {field, detail} ->
|
||||
# "#{field} " <> render_detail(detail)
|
||||
# end)
|
||||
# |> Enum.join()
|
||||
# end
|
||||
|
||||
defp render_detail({message, values}) do
|
||||
Enum.reduce(values, message, fn {k, v}, acc ->
|
||||
String.replace(acc, "%{#{k}}", to_string(v))
|
||||
end)
|
||||
end
|
||||
# defp render_detail({message, values}) do
|
||||
# Enum.reduce(values, message, fn {k, v}, acc ->
|
||||
# String.replace(acc, "%{#{k}}", to_string(v))
|
||||
# end)
|
||||
# end
|
||||
|
||||
defp render_detail(message) do
|
||||
message
|
||||
end
|
||||
# defp render_detail(message) do
|
||||
# message
|
||||
# end
|
||||
|
||||
def update(conn, %{"id" => id, "user" => user_params}) do
|
||||
user = Actors.get_user!(id)
|
||||
|
||||
@@ -122,6 +122,7 @@ defmodule EventosWeb.Router do
|
||||
get("/@:name/following", ActivityPubController, :following)
|
||||
get("/@:name/followers", ActivityPubController, :followers)
|
||||
get("/events/:uuid", ActivityPubController, :event)
|
||||
get("/comments/:uuid", ActivityPubController, :event)
|
||||
post("/@:name/inbox", ActivityPubController, :inbox)
|
||||
post("/inbox", ActivityPubController, :inbox)
|
||||
end
|
||||
|
||||
@@ -134,10 +134,17 @@ defmodule EventosWeb.ActivityPub.ActorView do
|
||||
else
|
||||
"Announce"
|
||||
end,
|
||||
"actor" => activity.data.organizer_actor.url,
|
||||
"actor" => activity.actor,
|
||||
"published" => Timex.now(),
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"object" => render_one(activity.data, ObjectView, "event.json", as: :event)
|
||||
"object" =>
|
||||
case activity.type do
|
||||
:Event ->
|
||||
render_one(activity.data, ObjectView, "event.json", as: :event)
|
||||
|
||||
:Comment ->
|
||||
render_one(activity.data, ObjectView, "note.json", as: :note)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -32,6 +32,19 @@ defmodule EventosWeb.ActivityPub.ObjectView do
|
||||
Map.merge(event, @base)
|
||||
end
|
||||
|
||||
def render("note.json", %{note: note}) do
|
||||
event = %{
|
||||
"type" => "Note",
|
||||
"id" => note.url,
|
||||
"content" => note.text,
|
||||
"mediaType" => "text/markdown",
|
||||
"published" => Timex.format!(note.inserted_at, "{ISO:Extended}"),
|
||||
"updated" => Timex.format!(note.updated_at, "{ISO:Extended}")
|
||||
}
|
||||
|
||||
Map.merge(event, @base)
|
||||
end
|
||||
|
||||
def render("category.json", %{category: category}) do
|
||||
%{"title" => category.title}
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ defmodule Eventos.Service.ActivityPub do
|
||||
"""
|
||||
|
||||
alias Eventos.Events
|
||||
alias Eventos.Events.{Event, Category}
|
||||
alias Eventos.Events.{Event, Category, Comment}
|
||||
alias Eventos.Service.ActivityPub.Transmogrifier
|
||||
alias Eventos.Service.WebFinger
|
||||
alias Eventos.Activity
|
||||
@@ -44,36 +44,65 @@ defmodule Eventos.Service.ActivityPub do
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_event_from_url(url) do
|
||||
if object = Events.get_event_by_url!(url) do
|
||||
{:ok, object}
|
||||
def fetch_object_from_url(url, :event), do: fetch_event_from_url(url)
|
||||
def fetch_object_from_url(url, :note), do: fetch_note_from_url(url)
|
||||
|
||||
@spec fetch_object_from_url(String.t()) :: tuple()
|
||||
def fetch_object_from_url(url) do
|
||||
with true <- String.starts_with?(url, "http"),
|
||||
{:ok, %{body: body, status_code: code}} when code in 200..299 <-
|
||||
HTTPoison.get(
|
||||
url,
|
||||
[Accept: "application/activity+json"],
|
||||
follow_redirect: true,
|
||||
timeout: 10_000,
|
||||
recv_timeout: 20_000
|
||||
),
|
||||
{:ok, data} <- Jason.decode(body),
|
||||
nil <- Events.get_event_by_url(data["id"]),
|
||||
nil <- Events.get_comment_from_url(data["id"]),
|
||||
params <- %{
|
||||
"type" => "Create",
|
||||
"to" => data["to"],
|
||||
"cc" => data["cc"],
|
||||
"actor" => data["attributedTo"],
|
||||
"object" => data
|
||||
},
|
||||
{:ok, activity} <- Transmogrifier.handle_incoming(params) do
|
||||
case data["type"] do
|
||||
"Event" ->
|
||||
{:ok, Events.get_event_by_url!(activity.data["object"]["id"])}
|
||||
|
||||
"Note" ->
|
||||
{:ok, Events.get_comment_from_url!(activity.data["object"]["id"])}
|
||||
end
|
||||
else
|
||||
object = %Event{} -> {:ok, object}
|
||||
object = %Comment{} -> {:ok, object}
|
||||
e -> {:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
@spec fetch_object_from_url(String.t()) :: tuple()
|
||||
def fetch_event_from_url(url) do
|
||||
with nil <- Events.get_event_by_url(url) do
|
||||
Logger.info("Fetching #{url} via AP")
|
||||
fetch_object_from_url(url)
|
||||
else
|
||||
%Event{} = comment ->
|
||||
{:ok, comment}
|
||||
end
|
||||
end
|
||||
|
||||
@spec fetch_object_from_url(String.t()) :: tuple()
|
||||
def fetch_note_from_url(url) do
|
||||
with nil <- Events.get_comment_from_url(url) do
|
||||
Logger.info("Fetching #{url} via AP")
|
||||
|
||||
with true <- String.starts_with?(url, "http"),
|
||||
{:ok, %{body: body, status_code: code}} when code in 200..299 <-
|
||||
HTTPoison.get(
|
||||
url,
|
||||
[Accept: "application/activity+json"],
|
||||
follow_redirect: true,
|
||||
timeout: 10_000,
|
||||
recv_timeout: 20_000
|
||||
),
|
||||
{:ok, data} <- Jason.decode(body),
|
||||
nil <- Events.get_event_by_url!(data["id"]),
|
||||
params <- %{
|
||||
"type" => "Create",
|
||||
"to" => data["to"],
|
||||
"cc" => data["cc"],
|
||||
"actor" => data["attributedTo"],
|
||||
"object" => data
|
||||
},
|
||||
{:ok, activity} <- Transmogrifier.handle_incoming(params) do
|
||||
{:ok, Events.get_event_by_url!(activity.data["object"]["id"])}
|
||||
else
|
||||
object = %Event{} -> {:ok, object}
|
||||
e -> e
|
||||
end
|
||||
fetch_object_from_url(url)
|
||||
else
|
||||
%Comment{} = comment ->
|
||||
{:ok, comment}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -127,7 +156,7 @@ defmodule Eventos.Service.ActivityPub do
|
||||
end
|
||||
end
|
||||
|
||||
def follow(follower, followed, activity_id \\ nil, local \\ true) do
|
||||
def follow(%Actor{} = follower, %Actor{} = followed, activity_id \\ nil, local \\ true) do
|
||||
with data <- make_follow_data(follower, followed, activity_id),
|
||||
{:ok, activity} <- insert(data, local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
@@ -135,7 +164,9 @@ defmodule Eventos.Service.ActivityPub do
|
||||
end
|
||||
end
|
||||
|
||||
def delete(%Event{url: url, organizer_actor: actor} = event, local \\ true) do
|
||||
def delete(object, local \\ true)
|
||||
|
||||
def delete(%Event{url: url, organizer_actor: actor} = event, local) do
|
||||
data = %{
|
||||
"type" => "Delete",
|
||||
"actor" => actor.url,
|
||||
@@ -150,6 +181,21 @@ defmodule Eventos.Service.ActivityPub do
|
||||
end
|
||||
end
|
||||
|
||||
def delete(%Comment{url: url, actor: actor} = comment, local) do
|
||||
data = %{
|
||||
"type" => "Delete",
|
||||
"actor" => actor.url,
|
||||
"object" => url,
|
||||
"to" => [actor.url <> "/followers", "https://www.w3.org/ns/activitystreams#Public"]
|
||||
}
|
||||
|
||||
with Events.delete_comment(comment),
|
||||
{:ok, activity} <- insert(data, local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
def create_public_activities(%Actor{} = actor) do
|
||||
end
|
||||
|
||||
@@ -285,13 +331,22 @@ defmodule Eventos.Service.ActivityPub do
|
||||
case actor.type do
|
||||
:Person ->
|
||||
{:ok, events, total} = Events.get_events_for_actor(actor, page, limit)
|
||||
{:ok, comments, total} = Events.get_comments_for_actor(actor, page, limit)
|
||||
|
||||
activities =
|
||||
event_activities =
|
||||
Enum.map(events, fn event ->
|
||||
{:ok, activity} = event_to_activity(event)
|
||||
activity
|
||||
end)
|
||||
|
||||
comment_activities =
|
||||
Enum.map(comments, fn comment ->
|
||||
{:ok, activity} = comment_to_activity(comment)
|
||||
activity
|
||||
end)
|
||||
|
||||
activities = event_activities ++ comment_activities
|
||||
|
||||
{activities, total}
|
||||
|
||||
:Service ->
|
||||
@@ -322,6 +377,7 @@ defmodule Eventos.Service.ActivityPub do
|
||||
|
||||
defp event_to_activity(%Event{} = event, local \\ true) do
|
||||
activity = %Activity{
|
||||
type: :Event,
|
||||
data: event,
|
||||
local: local,
|
||||
actor: event.organizer_actor.url,
|
||||
@@ -333,6 +389,20 @@ defmodule Eventos.Service.ActivityPub do
|
||||
{:ok, activity}
|
||||
end
|
||||
|
||||
defp comment_to_activity(%Comment{} = comment, local \\ true) do
|
||||
activity = %Activity{
|
||||
type: :Comment,
|
||||
data: comment,
|
||||
local: local,
|
||||
actor: comment.actor.url,
|
||||
recipients: ["https://www.w3.org/ns/activitystreams#Public"]
|
||||
}
|
||||
|
||||
# Notification.create_notifications(activity)
|
||||
# stream_out(activity)
|
||||
{:ok, activity}
|
||||
end
|
||||
|
||||
defp ical_event_to_activity(%ExIcal.Event{} = ical_event, %Actor{} = actor, source) do
|
||||
# Logger.debug(inspect ical_event)
|
||||
# TODO : refactor me !
|
||||
|
||||
@@ -4,7 +4,7 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
|
||||
"""
|
||||
alias Eventos.Actors.Actor
|
||||
alias Eventos.Actors
|
||||
alias Eventos.Events.Event
|
||||
alias Eventos.Events.{Event, Comment}
|
||||
alias Eventos.Service.ActivityPub
|
||||
|
||||
import Ecto.Query
|
||||
@@ -77,9 +77,9 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
|
||||
# - tags
|
||||
# - emoji
|
||||
def handle_incoming(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do
|
||||
Logger.debug("Handle incoming to create notes")
|
||||
Logger.info("Handle incoming to create notes")
|
||||
|
||||
with %Actor{} = actor <- Actor.get_or_fetch_by_url(data["actor"]) do
|
||||
with {:ok, %Actor{} = actor} <- Actors.get_or_fetch_by_url(data["actor"]) do
|
||||
Logger.debug("found actor")
|
||||
object = fix_object(data["object"])
|
||||
|
||||
@@ -104,8 +104,8 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
|
||||
def handle_incoming(
|
||||
%{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data
|
||||
) do
|
||||
with %Actor{} = followed <- Actors.get_actor_by_url(followed),
|
||||
%Actor{} = follower <- Actors.get_or_fetch_by_url(follower),
|
||||
with {:ok, %Actor{} = followed} <- Actors.get_or_fetch_by_url(followed),
|
||||
{:ok, %Actor{} = follower} <- Actors.get_or_fetch_by_url(follower),
|
||||
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
|
||||
ActivityPub.accept(%{to: [follower.url], actor: followed.url, object: data, local: true})
|
||||
|
||||
@@ -133,7 +133,7 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
|
||||
def handle_incoming(
|
||||
%{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = data
|
||||
) do
|
||||
with %Actor{} = actor <- Actors.get_or_fetch_by_url(actor),
|
||||
with {:ok, %Actor{} = actor} <- Actors.get_or_fetch_by_url(actor),
|
||||
{:ok, object} <-
|
||||
get_obj_helper(object_id) || ActivityPub.fetch_event_from_url(object_id),
|
||||
{:ok, activity, object} <- ActivityPub.announce(actor, object, id, false) do
|
||||
@@ -268,6 +268,17 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
|
||||
{:ok, event}
|
||||
end
|
||||
|
||||
def prepare_outgoing(%Comment{} = comment) do
|
||||
comment =
|
||||
comment
|
||||
|> Map.from_struct()
|
||||
|> Map.drop([:__meta__])
|
||||
|> Map.put(:"@context", "https://www.w3.org/ns/activitystreams")
|
||||
|> prepare_object
|
||||
|
||||
{:ok, comment}
|
||||
end
|
||||
|
||||
#
|
||||
# def maybe_fix_object_url(data) do
|
||||
# if is_binary(data["object"]) and not String.starts_with?(data["object"], "http") do
|
||||
|
||||
@@ -132,20 +132,23 @@ defmodule Eventos.Service.ActivityPub.Utils do
|
||||
"""
|
||||
def insert_full_object(%{"object" => %{"type" => type} = object_data})
|
||||
when is_map(object_data) and type == "Note" do
|
||||
import Logger
|
||||
Logger.debug("insert full object")
|
||||
Logger.debug(inspect(object_data))
|
||||
actor = Actors.get_actor_by_url(object_data["actor"])
|
||||
with {:ok, %Actor{id: actor_id}} <- Actors.get_or_fetch_by_url(object_data["actor"]) do
|
||||
data = %{
|
||||
"text" => object_data["content"],
|
||||
"url" => object_data["id"],
|
||||
"actor_id" => actor_id,
|
||||
"in_reply_to_comment_id" => object_data["inReplyTo"]
|
||||
}
|
||||
|
||||
data = %{
|
||||
"text" => object_data["content"],
|
||||
"url" => object_data["id"],
|
||||
"actor_id" => actor.id,
|
||||
"in_reply_to_comment_id" => object_data["inReplyTo"]
|
||||
}
|
||||
require Logger
|
||||
Logger.info("comment data ready to be inserted")
|
||||
Logger.info(inspect(data))
|
||||
|
||||
with {:ok, _} <- Events.create_comment(data) do
|
||||
:ok
|
||||
with {:ok, comm} <- Events.create_comment(data) do
|
||||
Logger.info("comment inserted")
|
||||
Logger.info(inspect(comm))
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user