Add actors/fetching
This commit is contained in:
parent
70370fb824
commit
ae9da65e1b
|
@ -32,3 +32,5 @@ npm-debug.log
|
|||
# we ignore priv/static. You may want to comment
|
||||
# this depending on your deployment strategy.
|
||||
/priv/static/
|
||||
|
||||
config/dev.secret.exs
|
||||
|
|
|
@ -75,3 +75,5 @@ config :phoenix, :stacktrace_depth, 20
|
|||
|
||||
# Initialize plugs at runtime for faster development compilation
|
||||
config :phoenix, :plug_init_mode, :runtime
|
||||
|
||||
import_config "dev.secret.exs"
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
defmodule Clacks.ActivityPub.Fetcher do
|
||||
require Logger
|
||||
|
||||
@spec fetch_actor(id :: String.t()) :: map() | nil
|
||||
def fetch_actor(id) do
|
||||
%{host: id_host} = URI.parse(id)
|
||||
|
||||
with %{"type" => type, "id" => remote_id} = actor <- fetch(id),
|
||||
"person" <- String.downcase(type),
|
||||
%{host: actor_host} when actor_host == id_host <- URI.parse(remote_id) do
|
||||
actor
|
||||
else
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
@spec fetch_object(id :: String.t()) :: map() | nil
|
||||
def fetch_object(id) do
|
||||
%{host: id_host} = URI.parse(id)
|
||||
|
||||
with %{"actor" => remote_actor} = object <- fetch(id),
|
||||
%{host: actor_host} when actor_host == id_host <- URI.parse(remote_actor) do
|
||||
object
|
||||
else
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
@spec fetch(uri :: String.t()) :: map() | nil
|
||||
defp fetch(uri) do
|
||||
Logger.debug("Attempting to fetch AP object at #{uri}")
|
||||
|
||||
headers = [Accept: "application/activity+json, application/ld+json"]
|
||||
opts = [hackney: Application.get_env(:clacks, :hackney_opts, [])]
|
||||
|
||||
with {:ok, %HTTPoison.Response{status_code: status_code, body: body}}
|
||||
when status_code in 200..299 <-
|
||||
HTTPoison.get(uri, headers, opts),
|
||||
{:ok, data} <- Jason.decode(body) do
|
||||
data
|
||||
else
|
||||
{:ok, %HTTPoison.Response{}} ->
|
||||
nil
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.warn("Couldn't fetch AP object at #{uri}: #{inspect(reason)}")
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,71 @@
|
|||
defmodule Clacks.Actor do
|
||||
require Logger
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
alias Clacks.Repo
|
||||
|
||||
@type t() :: %__MODULE__{}
|
||||
|
||||
@primary_key {:id, FlakeId.Ecto.Type, autogenerate: true}
|
||||
|
||||
schema "actors" do
|
||||
field :ap_id, :string
|
||||
field :nickname, :string
|
||||
field :local, :boolean
|
||||
field :data, :map
|
||||
|
||||
belongs_to :user, Clacks.User
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
def changeset(%__MODULE__{} = schema, attrs) do
|
||||
schema
|
||||
|> cast(attrs, [:ap_id, :nickname, :local, :data])
|
||||
|> validate_required([:ap_id, :nickname, :local, :data])
|
||||
end
|
||||
|
||||
@spec get_by_ap_id(ap_id :: String.t(), force_refetch :: boolean()) :: t() | nil
|
||||
def get_by_ap_id(ap_id, force_refetch \\ false) when is_binary(ap_id) do
|
||||
if force_refetch do
|
||||
fetch(ap_id)
|
||||
else
|
||||
get_cached_by_ap_id(ap_id) || fetch(ap_id)
|
||||
end
|
||||
end
|
||||
|
||||
@spec get_cached_by_ap_id(ap_id :: String.t()) :: t() | nil
|
||||
def get_cached_by_ap_id(ap_id) when is_binary(ap_id) do
|
||||
Repo.one(from a in __MODULE__, where: a.ap_id == ^ap_id)
|
||||
end
|
||||
|
||||
@spec fetch(ap_id :: String.t()) :: t() | nil
|
||||
def fetch(ap_id) when is_binary(ap_id) do
|
||||
case Clacks.ActivityPub.Fetcher.fetch_actor(ap_id) do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
data ->
|
||||
ap_id = data["id"]
|
||||
existing = get_cached_by_ap_id(ap_id) |> Repo.preload(:user)
|
||||
|
||||
changeset =
|
||||
changeset(existing || %__MODULE__{}, %{
|
||||
ap_id: ap_id,
|
||||
nickname: data["preferredUsername"] <> "@" <> URI.parse(ap_id).host,
|
||||
local: false,
|
||||
data: data
|
||||
})
|
||||
|
||||
case Repo.insert_or_update(changeset) do
|
||||
{:ok, actor} ->
|
||||
actor
|
||||
|
||||
{:error, changeset} ->
|
||||
Logger.error("Couldn't store remote actor #{ap_id}: #{inspect(changeset)}")
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
defmodule Clacks.Keys do
|
||||
def generate_rsa_pem() do
|
||||
key = :public_key.generate_key({:rsa, 2048, 65_537})
|
||||
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
|
||||
pem = :public_key.pem_encode([entry]) |> String.trim_trailing()
|
||||
{:ok, pem}
|
||||
end
|
||||
|
||||
def keys_from_pem(pem) do
|
||||
with [private_key_code] <- :public_key.pem_decode(pem),
|
||||
private_key <- :public_key.pem_entry_decode(private_key_code),
|
||||
{:RSAPrivateKey, _, modulus, exponent, _, _, _, _, _, _, _} <- private_key do
|
||||
{:ok, private_key, {:RSAPublicKey, modulus, exponent}}
|
||||
else
|
||||
error ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
defmodule Clacks.User do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@type t() :: %__MODULE__{}
|
||||
|
||||
schema "users" do
|
||||
field :username, :string
|
||||
field :private_key, :string
|
||||
|
||||
has_one :actor, Clacks.Actor
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
def changeset(%__MODULE__{} = schema, attrs) do
|
||||
schema
|
||||
|> cast(attrs, [:username, :private_key])
|
||||
|> validate_required([:username, :private_key])
|
||||
end
|
||||
end
|
6
mix.exs
6
mix.exs
|
@ -43,7 +43,11 @@ defmodule Clacks.MixProject do
|
|||
{:gettext, "~> 0.11"},
|
||||
{:jason, "~> 1.0"},
|
||||
{:plug_cowboy, "~> 2.0"},
|
||||
{:flake_id, "~> 0.1.0"}
|
||||
{:flake_id, "~> 0.1.0"},
|
||||
{:http_signatures,
|
||||
git: "https://git.pleroma.social/pleroma/http_signatures.git",
|
||||
ref: "293d77bb6f4a67ac8bde1428735c3b42f22cbb30"},
|
||||
{:httpoison, "~> 1.5.1"}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
10
mix.lock
10
mix.lock
|
@ -1,5 +1,6 @@
|
|||
%{
|
||||
"base62": {:hex, :base62, "1.2.1", "4866763e08555a7b3917064e9eef9194c41667276c51b59de2bc42c6ea65f806", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
|
||||
"cowboy": {:hex, :cowboy, "2.6.3", "99aa50e94e685557cad82e704457336a453d4abcb77839ad22dbe71f311fcc06", [:rebar3], [{:cowlib, "~> 2.7.3", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm"},
|
||||
|
@ -11,8 +12,15 @@
|
|||
"file_system": {:hex, :file_system, "0.2.7", "e6f7f155970975789f26e77b8b8d8ab084c59844d8ecfaf58cbda31c494d14aa", [:mix], [], "hexpm"},
|
||||
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"},
|
||||
"hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"http_signatures": {:git, "https://git.pleroma.social/pleroma/http_signatures.git", "293d77bb6f4a67ac8bde1428735c3b42f22cbb30", [ref: "293d77bb6f4a67ac8bde1428735c3b42f22cbb30"]},
|
||||
"httpoison": {:hex, :httpoison, "1.5.1", "0f55b5b673b03c5c327dac7015a67cb571b99b631acc0bc1b0b98dcd6b9f2104", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
|
||||
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
|
||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
|
||||
"phoenix": {:hex, :phoenix, "1.4.10", "619e4a545505f562cd294df52294372d012823f4fd9d34a6657a8b242898c255", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
@ -23,5 +31,7 @@
|
|||
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
|
||||
"postgrex": {:hex, :postgrex, "0.15.1", "23ce3417de70f4c0e9e7419ad85bdabcc6860a6925fe2c6f3b1b5b1e8e47bf2f", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
defmodule Clacks.Repo.Migrations.CreateActors do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:actors, primary_key: false) do
|
||||
add :id, :uuid, primary_key: true
|
||||
|
||||
add :ap_id, :string
|
||||
add :nickname, :string
|
||||
add :local, :boolean
|
||||
add :data, :jsonb
|
||||
|
||||
timestamps()
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
defmodule Clacks.Repo.Migrations.CreateUsers do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:users) do
|
||||
add :username, :string
|
||||
add :private_key, :text
|
||||
|
||||
timestamps()
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
defmodule Clacks.Repo.Migrations.ActorsAddUserId do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:actors) do
|
||||
add :user_id, references(:users)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
defmodule Clacks.Repo.Migrations.ActorsAddApIdUniqueIndex do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create unique_index(:actors, [:ap_id], name: :actors_unique_ap_id_index)
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue