ActivityPub helpers and object route

This commit is contained in:
Shadowfacts 2019-09-28 21:57:11 -04:00
parent 3818e713cd
commit 70370fb824
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
8 changed files with 127 additions and 4 deletions

View File

@ -25,6 +25,10 @@ config :logger, :console,
# Use Jason for JSON parsing in Phoenix # Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason config :phoenix, :json_library, Jason
config :mime, :types, %{
"application/activity+json" => ["activity+json"]
}
# Import environment specific config. This must remain at the bottom # Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above. # of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs" import_config "#{Mix.env()}.exs"

View File

@ -16,6 +16,7 @@ config :clacks, Clacks.Repo,
# watchers to your application. For example, we use it # watchers to your application. For example, we use it
# with webpack to recompile .js and .css sources. # with webpack to recompile .js and .css sources.
config :clacks, ClacksWeb.Endpoint, config :clacks, ClacksWeb.Endpoint,
url: [scheme: "http", port: 4000],
http: [port: 4000], http: [port: 4000],
debug_errors: true, debug_errors: true,
code_reloader: true, code_reloader: true,

View File

@ -10,7 +10,7 @@ use Mix.Config
# which you should run after static files are built and # which you should run after static files are built and
# before starting your production server. # before starting your production server.
config :clacks, ClacksWeb.Endpoint, config :clacks, ClacksWeb.Endpoint,
url: [host: "example.com", port: 80], url: [scheme: "http", host: "example.com", port: 80],
cache_static_manifest: "priv/static/cache_manifest.json" cache_static_manifest: "priv/static/cache_manifest.json"
# Do not print debug messages in production # Do not print debug messages in production

View File

@ -1,6 +1,5 @@
defmodule Clacks.Activity do defmodule Clacks.Activity do
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset import Ecto.Changeset
@type t() :: %__MODULE__{} @type t() :: %__MODULE__{}

85
lib/clacks/activitypub.ex Normal file
View File

@ -0,0 +1,85 @@
defmodule Clacks.ActivityPub do
@context ["https://www.w3.org/ns/activitystreams"]
@public "https://www.w3.org/ns/activitystreams#Public"
@spec note(
actor :: String.t(),
html :: String.t(),
context :: String.t(),
id :: String.t() | nil,
published :: DateTime.t(),
to :: [String.t()],
cc :: [String.t()]
) :: map()
def note(
actor,
html,
context,
id \\ nil,
published \\ DateTime.utc_now(),
to \\ [@public],
cc \\ []
) do
id = id || object_id(Ecto.UUID.generate())
%{
"@context" => @context,
"id" => id,
"url" => id,
"type" => "Note",
"actor" => actor,
"attributedTo" => actor,
"to" => to,
"cc" => cc,
"content" => html,
"conversation" => context,
"context" => context,
"published" => published |> DateTime.to_iso8601()
}
end
@spec create(
object :: map(),
id :: String.t() | nil,
actor :: String.t() | nil,
to :: [String.t()] | nil,
cc :: [String.t()] | nil
) :: map()
def create(object, id \\ nil, actor \\ nil, to \\ nil, cc \\ nil) do
%{
"@context" => @context,
"id" => id || activity_id(Ecto.UUID.generate()),
"actor" => actor || object["actor"],
"type" => "Create",
"object" => object,
"to" => to || object["to"],
"cc" => cc || object["cc"]
}
end
@spec object_id(id :: String.t()) :: String.t()
def object_id(id) do
url = Application.get_env(:clacks, ClacksWeb.Endpoint)[:url]
%URI{
scheme: url[:scheme],
host: url[:host],
port: url[:port],
path: Path.join("/objects", id)
}
|> URI.to_string()
end
@spec activity_id(id :: String.t()) :: String.t()
def activity_id(id) do
url = Application.get_env(:clacks, ClacksWeb.Endpoint)[:url]
%URI{
scheme: url[:scheme],
host: url[:host],
port: url[:port],
path: Path.join("/activities", id)
}
|> URI.to_string()
end
end

View File

@ -1,5 +1,6 @@
defmodule Clacks.Object do defmodule Clacks.Object do
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset
@type t() :: %__MODULE__{} @type t() :: %__MODULE__{}
@ -8,4 +9,10 @@ defmodule Clacks.Object do
timestamps() timestamps()
end end
def changeset(%__MODULE__{} = schema, attrs) do
schema
|> cast(attrs, [:data])
|> validate_required([:data])
end
end end

View File

@ -0,0 +1,21 @@
defmodule ClacksWeb.ObjectsController do
use ClacksWeb, :controller
alias Clacks.{Repo, Object, ActivityPub}
import Ecto.Query
def get(conn, %{"id" => id}) do
object_id = current_url(conn)
query = from(o in Object, where: fragment("?->>'id'", o.data) == ^object_id)
case Repo.one(query) do
nil ->
conn
|> put_status(404)
object ->
conn
|> put_resp_header("content-type", "application/activity+json")
|> json(object.data)
end
end
end

View File

@ -9,8 +9,8 @@ defmodule ClacksWeb.Router do
plug :put_secure_browser_headers plug :put_secure_browser_headers
end end
pipeline :api do pipeline :activitypub do
plug :accepts, ["json"] plug :accepts, ["activity+json", "html"]
end end
scope "/", ClacksWeb do scope "/", ClacksWeb do
@ -19,6 +19,12 @@ defmodule ClacksWeb.Router do
get "/", PageController, :index get "/", PageController, :index
end end
scope "/", ClacksWeb do
pipe_through :activitypub
get "/objects/:id", ObjectsController, :get
end
# Other scopes may use custom stacks. # Other scopes may use custom stacks.
# scope "/api", ClacksWeb do # scope "/api", ClacksWeb do
# pipe_through :api # pipe_through :api