Change structure
This commit is contained in:
parent
01c2c57933
commit
ddfb58c041
16 changed files with 153 additions and 66 deletions
21
lib/nulla_web/controllers/activitypub/actor_controller.ex
Normal file
21
lib/nulla_web/controllers/activitypub/actor_controller.ex
Normal file
|
@ -0,0 +1,21 @@
|
|||
defmodule NullaWeb.ActivityPub.ActorController do
|
||||
use NullaWeb, :controller
|
||||
alias Nulla.ActivityPub
|
||||
alias Nulla.Models.Actor
|
||||
|
||||
def show(conn, %{"username" => username}) do
|
||||
domain = NullaWeb.Endpoint.host()
|
||||
|
||||
case Actor.get_actor(preferredUsername: username, domain: domain) do
|
||||
nil ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "Not Found"})
|
||||
|
||||
%Actor{} = actor ->
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> send_resp(200, Jason.encode!(ActivityPub.actor(actor)))
|
||||
end
|
||||
end
|
||||
end
|
67
lib/nulla_web/controllers/activitypub/follow_controller.ex
Normal file
67
lib/nulla_web/controllers/activitypub/follow_controller.ex
Normal file
|
@ -0,0 +1,67 @@
|
|||
defmodule NullaWeb.ActivityPub.FollowController do
|
||||
use NullaWeb, :controller
|
||||
alias Nulla.ActivityPub
|
||||
alias Nulla.Models.Actor
|
||||
alias Nulla.Models.Relation
|
||||
alias Nulla.Models.InstanceSettings
|
||||
|
||||
def following(conn, %{"username" => username, "page" => page_param}) do
|
||||
instance_settings = InstanceSettings.get_instance_settings!()
|
||||
domain = NullaWeb.Endpoint.host()
|
||||
limit = instance_settings.api_limit
|
||||
actor = Actor.get_actor(preferredUsername: username, domain: domain)
|
||||
total = Relation.count_following(actor.id)
|
||||
|
||||
page =
|
||||
case Integer.parse(page_param) do
|
||||
{int, _} when int > 0 -> int
|
||||
_ -> 1
|
||||
end
|
||||
|
||||
following_list = Enum.map(Relation.get_following(actor.id, page, limit), & &1.ap_id)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(ActivityPub.following(actor, total, following_list, page, limit))
|
||||
end
|
||||
|
||||
def following(conn, %{"username" => username}) do
|
||||
domain = NullaWeb.Endpoint.host()
|
||||
actor = Actor.get_actor(preferredUsername: username, domain: domain)
|
||||
total = Relation.count_following(actor.id)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(ActivityPub.following(actor, total))
|
||||
end
|
||||
|
||||
def followers(conn, %{"username" => username, "page" => page_param}) do
|
||||
instance_settings = InstanceSettings.get_instance_settings!()
|
||||
domain = NullaWeb.Endpoint.host()
|
||||
limit = instance_settings.api_limit
|
||||
actor = Actor.get_actor(preferredUsername: username, domain: domain)
|
||||
total = Relation.count_followers(actor.id)
|
||||
|
||||
page =
|
||||
case Integer.parse(page_param) do
|
||||
{int, _} when int > 0 -> int
|
||||
_ -> 1
|
||||
end
|
||||
|
||||
followers_list = Enum.map(Relation.get_followers(actor.id, page, limit), & &1.ap_id)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(ActivityPub.followers(actor, total, followers_list, page, limit))
|
||||
end
|
||||
|
||||
def followers(conn, %{"username" => username}) do
|
||||
domain = NullaWeb.Endpoint.host()
|
||||
actor = Actor.get_actor(preferredUsername: username, domain: domain)
|
||||
total = Relation.count_followers(actor.id)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(ActivityPub.followers(actor, total))
|
||||
end
|
||||
end
|
18
lib/nulla_web/controllers/activitypub/hostmeta_controller.ex
Normal file
18
lib/nulla_web/controllers/activitypub/hostmeta_controller.ex
Normal file
|
@ -0,0 +1,18 @@
|
|||
defmodule NullaWeb.ActivityPub.HostmetaController do
|
||||
use NullaWeb, :controller
|
||||
|
||||
def index(conn, _params) do
|
||||
domain = NullaWeb.Endpoint.host()
|
||||
|
||||
xml = """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
||||
<Link rel="lrdd" type="application/xrd+xml" template="https://#{domain}/.well-known/webfinger?resource={uri}"/>
|
||||
</XRD>
|
||||
"""
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("application/xrd+xml")
|
||||
|> send_resp(200, xml)
|
||||
end
|
||||
end
|
215
lib/nulla_web/controllers/activitypub/inbox_controller.ex
Normal file
215
lib/nulla_web/controllers/activitypub/inbox_controller.ex
Normal file
|
@ -0,0 +1,215 @@
|
|||
defmodule NullaWeb.ActivityPub.InboxController do
|
||||
use NullaWeb, :controller
|
||||
alias Nulla.Snowflake
|
||||
alias Nulla.HTTPSignature
|
||||
alias Nulla.Sender
|
||||
alias Nulla.Utils
|
||||
alias Nulla.Models.User
|
||||
alias Nulla.Models.Actor
|
||||
alias Nulla.Models.Relation
|
||||
alias Nulla.Models.Activity
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _create_id,
|
||||
"type" => "Create",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _read_id,
|
||||
"type" => "Read",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _update_id,
|
||||
"type" => "Update",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _delete_id,
|
||||
"type" => "Delete",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _add_id,
|
||||
"type" => "Add",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _view_id,
|
||||
"type" => "View",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _move_id,
|
||||
"type" => "Move",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _undo_id,
|
||||
"type" => "Undo",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => follow_id,
|
||||
"type" => "Follow",
|
||||
"actor" => actor_uri,
|
||||
"object" => target_uri
|
||||
}) do
|
||||
accept_id = Snowflake.next_id()
|
||||
|
||||
with local_actor <- Actor.get_actor(ap_id: target_uri),
|
||||
{:ok, remote_actor_json} <- Utils.fetch_remote_actor(actor_uri),
|
||||
:ok <- HTTPSignature.verify(conn, remote_actor_json["publicKey"]["publicKeyPem"]),
|
||||
{:ok, remote_actor} <- Actor.get_or_create_actor(remote_actor_json),
|
||||
{:ok, follow_activity} <-
|
||||
Activity.create_activity(%{
|
||||
ap_id: follow_id,
|
||||
type: "Follow",
|
||||
actor: remote_actor.ap_id,
|
||||
object: target_uri
|
||||
}),
|
||||
{:ok, accept_activity} <-
|
||||
Activity.create_activity(%{
|
||||
id: accept_id,
|
||||
ap_id: "https://#{local_actor.domain}/activities/accept/#{accept_id}",
|
||||
type: "Accept",
|
||||
actor: local_actor.ap_id,
|
||||
object: Jason.encode!(follow_activity)
|
||||
}),
|
||||
{:ok, _relation} <-
|
||||
Relation.get_or_create_relation(local_actor.id, remote_actor.id, followed_by: true) do
|
||||
user = User.get_user(id: local_actor.id)
|
||||
|
||||
Sender.send_activity(
|
||||
:post,
|
||||
remote_actor.inbox,
|
||||
accept_activity,
|
||||
local_actor.publicKey["id"],
|
||||
user.privateKeyPem
|
||||
)
|
||||
|
||||
send_resp(conn, 200, "")
|
||||
else
|
||||
error ->
|
||||
IO.inspect(error, label: "Follow error")
|
||||
json(conn, %{"error" => "Failed to process Follow"})
|
||||
end
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _accept_id,
|
||||
"type" => "Accept",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _reject_id,
|
||||
"type" => "Reject",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _block_id,
|
||||
"type" => "Block",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _join_id,
|
||||
"type" => "Join",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _leave_id,
|
||||
"type" => "Leave",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _like_id,
|
||||
"type" => "Like",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _dislike_id,
|
||||
"type" => "Dislike",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _announce_id,
|
||||
"type" => "Announce",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, %{
|
||||
"id" => _question_id,
|
||||
"type" => "Question",
|
||||
"actor" => _actor_uri,
|
||||
"object" => _target_uri
|
||||
}) do
|
||||
send_resp(conn, 200, "")
|
||||
end
|
||||
|
||||
def inbox(conn, _params) do
|
||||
send_resp(conn, 400, "")
|
||||
end
|
||||
end
|
29
lib/nulla_web/controllers/activitypub/nodeinfo_controller.ex
Normal file
29
lib/nulla_web/controllers/activitypub/nodeinfo_controller.ex
Normal file
|
@ -0,0 +1,29 @@
|
|||
defmodule NullaWeb.ActivityPub.NodeinfoController do
|
||||
use NullaWeb, :controller
|
||||
alias Nulla.ActivityPub
|
||||
alias Nulla.Models.User
|
||||
alias Nulla.Models.InstanceSettings
|
||||
|
||||
def index(conn, _params) do
|
||||
domain = NullaWeb.Endpoint.host()
|
||||
|
||||
json(conn, ActivityPub.nodeinfo(domain))
|
||||
end
|
||||
|
||||
def show(conn, _params) do
|
||||
version = Application.spec(:nulla, :vsn) |> to_string()
|
||||
total = User.get_total_users_count()
|
||||
month = User.get_active_users_count(30)
|
||||
halfyear = User.get_active_users_count(180)
|
||||
|
||||
users = %{
|
||||
total: total,
|
||||
month: month,
|
||||
halfyear: halfyear
|
||||
}
|
||||
|
||||
instance_settings = InstanceSettings.get_instance_settings!()
|
||||
|
||||
json(conn, ActivityPub.nodeinfo(version, users, instance_settings))
|
||||
end
|
||||
end
|
44
lib/nulla_web/controllers/activitypub/note_controller.ex
Normal file
44
lib/nulla_web/controllers/activitypub/note_controller.ex
Normal file
|
@ -0,0 +1,44 @@
|
|||
defmodule NullaWeb.ActivityPub.NoteController do
|
||||
use NullaWeb, :controller
|
||||
alias Nulla.Repo
|
||||
alias Nulla.ActivityPub
|
||||
alias Nulla.Models.Note
|
||||
|
||||
def show(conn, %{"username" => username, "id" => id}) do
|
||||
case Integer.parse(id) do
|
||||
{int_id, ""} ->
|
||||
note = Note.get_note(id: int_id) |> Repo.preload([:actor, :media_attachments])
|
||||
|
||||
cond do
|
||||
is_nil(note) ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "Not Found"})
|
||||
|> halt()
|
||||
|
||||
username != note.actor.preferredUsername ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "Not Found"})
|
||||
|> halt()
|
||||
|
||||
true ->
|
||||
format = Phoenix.Controller.get_format(conn)
|
||||
|
||||
if format == "activity+json" do
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> json(ActivityPub.note(note))
|
||||
else
|
||||
render(conn, :show, note: note, layout: false)
|
||||
end
|
||||
end
|
||||
|
||||
_ ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "Not Found"})
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
end
|
51
lib/nulla_web/controllers/activitypub/outbox_controller.ex
Normal file
51
lib/nulla_web/controllers/activitypub/outbox_controller.ex
Normal file
|
@ -0,0 +1,51 @@
|
|||
defmodule NullaWeb.ActivityPub.OutboxController do
|
||||
use NullaWeb, :controller
|
||||
alias Nulla.ActivityPub
|
||||
alias Nulla.Models.Actor
|
||||
alias Nulla.Models.Note
|
||||
|
||||
def outbox(conn, %{"username" => username} = params) do
|
||||
domain = NullaWeb.Endpoint.host()
|
||||
actor = Actor.get_actor(preferredUsername: username, domain: domain)
|
||||
|
||||
case Map.get(params, "page") do
|
||||
"true" ->
|
||||
max_id = params["max_id"] && String.to_integer(params["max_id"])
|
||||
|
||||
notes =
|
||||
if max_id do
|
||||
Note.get_before_notes(actor.id, max_id)
|
||||
else
|
||||
Note.get_latest_notes(actor.id)
|
||||
end
|
||||
|
||||
items = Enum.map(notes, &ActivityPub.activity_note(&1))
|
||||
|
||||
next_max_id =
|
||||
case List.last(notes) do
|
||||
nil -> 0
|
||||
last -> last.id
|
||||
end
|
||||
|
||||
min_id =
|
||||
case List.first(notes) do
|
||||
nil -> 0
|
||||
first -> first.id
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> send_resp(
|
||||
200,
|
||||
Jason.encode!(ActivityPub.outbox(actor, next_max_id, min_id || 0, items))
|
||||
)
|
||||
|
||||
_ ->
|
||||
total = Note.get_total_notes_count(actor.id)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("application/activity+json")
|
||||
|> send_resp(200, Jason.encode!(ActivityPub.outbox(actor, total)))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
defmodule NullaWeb.ActivityPub.WebfingerController do
|
||||
use NullaWeb, :controller
|
||||
alias Nulla.ActivityPub
|
||||
alias Nulla.Models.Actor
|
||||
|
||||
def index(conn, %{"resource" => resource}) do
|
||||
case Regex.run(~r/^acct:(.+)@(.+)$/, resource) do
|
||||
[_, preferredUsername, actor_domain] ->
|
||||
case Actor.get_actor(preferredUsername: preferredUsername, domain: actor_domain) do
|
||||
nil ->
|
||||
conn
|
||||
|> put_resp_content_type("text/plain")
|
||||
|> send_resp(404, "")
|
||||
|
||||
%Actor{} = actor ->
|
||||
domain = NullaWeb.Endpoint.host()
|
||||
|
||||
if actor_domain == domain do
|
||||
json(conn, ActivityPub.webfinger(actor))
|
||||
else
|
||||
conn
|
||||
|> put_resp_content_type("text/plain")
|
||||
|> send_resp(404, "")
|
||||
end
|
||||
end
|
||||
|
||||
_ ->
|
||||
conn
|
||||
|> put_resp_content_type("text/plain")
|
||||
|> send_resp(400, "")
|
||||
end
|
||||
end
|
||||
|
||||
def index(conn, _params) do
|
||||
conn
|
||||
|> put_resp_content_type("text/plain")
|
||||
|> send_resp(400, "")
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue