From 1259d3202dc8293e9060ca8d4fd1638fb85d4296 Mon Sep 17 00:00:00 2001 From: Ro Date: Wed, 9 Oct 2024 22:45:28 +0000 Subject: [PATCH] feat: Handle follows --- .vscode/extensions.json | 13 ++++++++ lib/activity_pub.ex | 2 ++ lib/postland.ex | 6 ++++ lib/postland/activities.ex | 27 ++++++++++++++++ lib/postland/follow.ex | 29 +++++++++++++++++ lib/postland/follows.ex | 31 +++++++++++++++++++ .../controllers/inbox_controller.ex | 2 +- mix.lock | 2 +- .../migrations/20241009214840_add_follows.exs | 11 +++++++ 9 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 lib/activity_pub.ex create mode 100644 lib/postland/follow.ex create mode 100644 lib/postland/follows.ex create mode 100644 priv/repo/migrations/20241009214840_add_follows.exs diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..d1987c7 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,13 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [ + + ] +} \ No newline at end of file diff --git a/lib/activity_pub.ex b/lib/activity_pub.ex new file mode 100644 index 0000000..79d8e8d --- /dev/null +++ b/lib/activity_pub.ex @@ -0,0 +1,2 @@ +defmodule ActivityPub do +end diff --git a/lib/postland.ex b/lib/postland.ex index e5571fe..56a62fd 100644 --- a/lib/postland.ex +++ b/lib/postland.ex @@ -7,6 +7,8 @@ defmodule Postland do if it comes from the database, an external API or others. """ + use Phoenix.VerifiedRoutes, endpoint: PostlandWeb.Endpoint, router: PostlandWeb.Router + alias Postland.Accounts def temporary_password() do @@ -51,4 +53,8 @@ defmodule Postland do def setup?() do Accounts.solo_user() != nil end + + def my_actor_id do + url(~p"/actor") + end end diff --git a/lib/postland/activities.ex b/lib/postland/activities.ex index 0b216be..5dbbb0b 100644 --- a/lib/postland/activities.ex +++ b/lib/postland/activities.ex @@ -1,10 +1,37 @@ defmodule Postland.Activities do + use Phoenix.VerifiedRoutes, endpoint: PostlandWeb.Endpoint, router: PostlandWeb.Router + alias Postland.Activity alias Postland.Repo + def process_activity(params) do + case record_activity(params) do + {:ok, activity} -> + cause_effects(activity) + + other -> + other + end + end + def record_activity(params) do params |> Activity.changeset() |> Repo.insert() end + + def cause_effects(%Activity{actor_id: actor_id, type: "Follow", data: %{"object" => object}} = activity) do + if object == Postland.my_actor_id() do + case Postland.Follows.record_inbound_request(actor_id) do + {:ok, _follow} -> + {:ok, activity} + other -> + other + end + else + {:ok, activity} + end + end + + def cause_effects(activity), do: activity end diff --git a/lib/postland/follow.ex b/lib/postland/follow.ex new file mode 100644 index 0000000..da31a9d --- /dev/null +++ b/lib/postland/follow.ex @@ -0,0 +1,29 @@ +defmodule Postland.Follow do + use Ecto.Schema + + import Ecto.Changeset + + schema "follows" do + field :follower, :string, primary_key: true + field :followee, :string, primary_key: true + field :confirmed_at, :naive_datetime + end + + def changeset(follower, followee, confirmed \\ false) do + attrs = %{ + follower: follower, + followee: followee, + confirmed_at: (if confirmed, do: NaiveDateTime.utc_now()) + } + + %__MODULE__{} + |> cast(attrs, [:follower, :followee, :confirmed_at]) + |> validate_required(:followee) + |> validate_required(:follower) + end + + def confirm_changeset(request) do + request + |> cast(%{confirmed_at: NaiveDateTime.utc_now()}, [:confirmed_at]) + end +end diff --git a/lib/postland/follows.ex b/lib/postland/follows.ex new file mode 100644 index 0000000..19abe8a --- /dev/null +++ b/lib/postland/follows.ex @@ -0,0 +1,31 @@ +defmodule Postland.Follows do + import Ecto.Query, warn: false + + alias Postland.Follow + alias Postland.Repo + + def pending_inbound_requests() do + my_actor_id = Postland.my_actor_id() + + from(f in Follow, where: f.followee == ^my_actor_id, where: is_nil(f.confirmed_at)) + |> Repo.all() + end + + def record_outbound_request(to_actor_id) do + Postland.my_actor_id() + |> Follow.changeset(to_actor_id) + |> Repo.insert(conflict_target: [:followee, :follower], on_conflict: :nothing) + end + + def record_inbound_request(from_actor_id) do + from_actor_id + |> Follow.changeset(Postland.my_actor_id()) + |> Repo.insert(conflict_target: [:followee, :follower], on_conflict: :nothing) + end + + def confirm_request(request) do + request + |> Follow.confirm_changeset() + |> Repo.update() + end +end diff --git a/lib/postland_web/controllers/inbox_controller.ex b/lib/postland_web/controllers/inbox_controller.ex index f43525a..393cc74 100644 --- a/lib/postland_web/controllers/inbox_controller.ex +++ b/lib/postland_web/controllers/inbox_controller.ex @@ -8,7 +8,7 @@ defmodule PostlandWeb.InboxController do def post(conn, params) do if Headers.verify(conn.method, conn.request_path, conn.req_headers) do - case Activities.record_activity(params) do + case Activities.process_activity(params) do {:ok, _activity} -> render(conn, :ok) error -> diff --git a/mix.lock b/mix.lock index 6753c6c..e115060 100644 --- a/mix.lock +++ b/mix.lock @@ -18,7 +18,7 @@ "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"}, "floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"}, "gettext": {:hex, :gettext, "0.26.1", "38e14ea5dcf962d1fc9f361b63ea07c0ce715a8ef1f9e82d3dfb8e67e0416715", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "01ce56f188b9dc28780a52783d6529ad2bc7124f9744e571e1ee4ea88bf08734"}, - "heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized"]}, + "heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized", depth: 1]}, "hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, diff --git a/priv/repo/migrations/20241009214840_add_follows.exs b/priv/repo/migrations/20241009214840_add_follows.exs new file mode 100644 index 0000000..639c728 --- /dev/null +++ b/priv/repo/migrations/20241009214840_add_follows.exs @@ -0,0 +1,11 @@ +defmodule Postland.Repo.Migrations.AddActors do + use Ecto.Migration + + def change do + create table("follows", primary_key: false) do + add :follower, :string, primary_key: true + add :followee, :string, primary_key: true + add :confirmed_at, :naive_datetime + end + end +end