From 7824edbec0bb61dd7a8b7d468553d8d75e42c66b Mon Sep 17 00:00:00 2001 From: Ro Date: Fri, 11 Oct 2024 02:44:55 +0000 Subject: [PATCH] feat: Accept follow requests --- README.md | 17 +++++++- lib/activity_pub/headers.ex | 4 +- lib/postland/activities.ex | 1 - lib/postland/activity.ex | 4 +- lib/postland/follows.ex | 43 +++++++++++++++++++ ...011023003_add_timestamps_to_activities.exs | 9 ++++ 6 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 priv/repo/migrations/20241011023003_add_timestamps_to_activities.exs diff --git a/README.md b/README.md index aa6f0a0..b0fdf94 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,25 @@ # Postland +## Backend + - [ ] Posting - [ ] Timeline - [ ] Deleting posts - [x] Following - [ ] Unfollowing -- [ ] Being followed +- [x] Being followed +- [x] Accepting follows +- [ ] Liking +- [ ] Unliking + +## UX + +- [ ] Posting +- [ ] Timeline +- [ ] Deleting posts +- [x] Following +- [ ] Unfollowing +- [x] Being followed +- [ ] Accepting follows - [ ] Liking - [ ] Unliking diff --git a/lib/activity_pub/headers.ex b/lib/activity_pub/headers.ex index 5ab22d9..fd7ef0c 100644 --- a/lib/activity_pub/headers.ex +++ b/lib/activity_pub/headers.ex @@ -52,11 +52,9 @@ defmodule ActivityPub.Headers do end def verify(method, path, headers, actor_fetcher \\ &fetch_actor_key/1) do - dbg(headers) - {_key, signature_header} = Enum.find(headers, fn {key, _} -> key == "signature" end) - signature_kv = SignatureSplitter.split(signature_header) |> dbg() + signature_kv = SignatureSplitter.split(signature_header) key_id = find_value(signature_kv, "keyId") signature = signature_kv |> find_value("signature") |> Base.decode64!() signing_text_headers = signature_kv |> find_value("headers") |> String.split(" ") diff --git a/lib/postland/activities.ex b/lib/postland/activities.ex index 2929a79..e97b4b3 100644 --- a/lib/postland/activities.ex +++ b/lib/postland/activities.ex @@ -28,7 +28,6 @@ defmodule Postland.Activities do actor_id = case Regex.run(pattern, follow_id) do [_, encoded_actor_id] -> - dbg(encoded_actor_id) Base.url_decode64!(encoded_actor_id, padding: false) _other -> diff --git a/lib/postland/activity.ex b/lib/postland/activity.ex index 2da246a..08fcbff 100644 --- a/lib/postland/activity.ex +++ b/lib/postland/activity.ex @@ -12,13 +12,15 @@ defmodule Postland.Activity do def changeset(attrs) do attrs = %{ + "id" => Map.get(attrs, "id", Ecto.UUID.autogenerate()), "actor_id" => Map.get(attrs, "actor"), "type" => Map.get(attrs, "type"), "data" => attrs } %__MODULE__{} - |> cast(attrs, [:actor_id, :type, :data]) + |> cast(attrs, [:id, :actor_id, :type, :data]) + |> validate_required(:id) |> validate_required(:data) |> validate_required(:type) end diff --git a/lib/postland/follows.ex b/lib/postland/follows.ex index 8daa6d4..6903534 100644 --- a/lib/postland/follows.ex +++ b/lib/postland/follows.ex @@ -4,12 +4,55 @@ defmodule Postland.Follows do import Ecto.Query, warn: false alias Postland.Accounts + alias Postland.Activity alias Postland.Follow alias Postland.Repo alias ActivityPub.Headers alias Ecto.Multi + def record_and_send_acceptance(request) do + Multi.new() + |> Multi.run(:confirm_timestamp, fn _data, _repo -> confirm_request(request) end) + |> Multi.run(:send_acceptance, fn _data, _repo -> + send_acceptance(request) + end) + |> Repo.transaction() + end + + def send_acceptance(request) do + {:ok, actor} = ActivityPub.fetch_actor(request.follower) + inbox = Map.get(actor, "inbox") + + case get_follow_activity(request.follower) do + nil -> + {:error, "could not find Follow activity to Accept"} + + follow_activity -> + body = + %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Accept", + "actor" => Postland.my_actor_id(), + "object" => %{ + "actor" => request.follower, + "id" => Map.get(follow_activity.data, "id"), + "object" => Postland.my_actor_id(), + "type" => "Follow" + } + } + |> Jason.encode!() + + headers = Headers.signing_headers("POST", inbox, body, Postland.my_actor_id(), Accounts.solo_user().private_key) + + Req.post(inbox, headers: headers, body: body) + end + end + + def get_follow_activity(follower) do + from(a in Activity, where: a.type == "Follow", where: a.actor_id == ^follower, limit: 1, order_by: [desc: :inserted_at]) |> Repo.one() + end + def record_and_send_follow_request(to_actor_id) do Multi.new() |> Multi.run(:follow_record, fn _data, _repo -> record_outbound_request(to_actor_id) end) diff --git a/priv/repo/migrations/20241011023003_add_timestamps_to_activities.exs b/priv/repo/migrations/20241011023003_add_timestamps_to_activities.exs new file mode 100644 index 0000000..2b454d1 --- /dev/null +++ b/priv/repo/migrations/20241011023003_add_timestamps_to_activities.exs @@ -0,0 +1,9 @@ +defmodule Postland.Repo.Migrations.AddTimestampsToActivities do + use Ecto.Migration + + def change do + alter table("activities") do + timestamps() + end + end +end