diff --git a/lib/nulla/activitypub.ex b/lib/nulla/activitypub.ex index 0f7dd73..40410dd 100644 --- a/lib/nulla/activitypub.ex +++ b/lib/nulla/activitypub.ex @@ -122,10 +122,10 @@ defmodule Nulla.ActivityPub do def following(actor, total) do Jason.OrderedObject.new( "@context": "https://www.w3.org/ns/activitystreams", - id: "https://#{actor.domain}/users/#{actor.preferredUsername}/following", + id: "#{actor.ap_id}/following", type: "OrderedCollection", totalItems: total, - first: "https://#{actor.domain}/users/#{actor.preferredUsername}/following?page=1" + first: "#{actor.ap_id}/following?page=1" ) end @@ -134,12 +134,12 @@ defmodule Nulla.ActivityPub do def following(actor, total, following_list, page, limit) when is_integer(page) and page > 0 do data = [ "@context": "https://www.w3.org/ns/activitystreams", - id: "https://#{actor.domain}/@#{actor.preferredUsername}/following?page=#{page}", + id: "#{actor.ap_id}/following?page=#{page}", type: "OrderedCollectionPage", totalItems: total, - next: "https://#{actor.domain}/users/#{actor.preferredUsername}/following?page=#{page + 1}", - prev: "https://#{actor.domain}/users/#{actor.preferredUsername}/following?page=#{page - 1}", - partOf: "https://#{actor.domain}/users/#{actor.preferredUsername}/following", + next: "#{actor.ap_id}/following?page=#{page + 1}", + prev: "#{actor.ap_id}/following?page=#{page - 1}", + partOf: "#{actor.ap_id}/following", orderedItems: following_list ] @@ -166,10 +166,10 @@ defmodule Nulla.ActivityPub do def followers(actor, total) do Jason.OrderedObject.new( "@context": "https://www.w3.org/ns/activitystreams", - id: "https://#{actor.domain}/users/#{actor.preferredUsername}/followers", + id: "#{actor.ap_id}/followers", type: "OrderedCollection", totalItems: total, - first: "https://#{actor.domain}/users/#{actor.preferredUsername}/followers?page=1" + first: "#{actor.ap_id}/followers?page=1" ) end @@ -179,12 +179,12 @@ defmodule Nulla.ActivityPub do when is_integer(page) and page > 0 do data = [ "@context": "https://www.w3.org/ns/activitystreams", - id: "https://#{actor.domain}/users#{actor.preferredUsername}/followers?page=#{page}", + id: "#{actor.ap_id}/followers?page=#{page}", type: "OrderedCollectionPage", totalItems: total, - next: "https://#{actor.domain}/users/#{actor.preferredUsername}/followers?page=#{page + 1}", - prev: "https://#{actor.domain}/users/#{actor.preferredUsername}/followers?page=#{page - 1}", - partOf: "https://#{actor.domain}/users/#{actor.preferredUsername}/followers", + next: "#{actor.ap_id}/followers?page=#{page + 1}", + prev: "#{actor.ap_id}/followers?page=#{page - 1}", + partOf: "#{actor.ap_id}/followers", orderedItems: followers_list ] diff --git a/lib/nulla_web/controllers/follow_controller.ex b/lib/nulla_web/controllers/follow_controller.ex index e6448c5..ca5afee 100644 --- a/lib/nulla_web/controllers/follow_controller.ex +++ b/lib/nulla_web/controllers/follow_controller.ex @@ -8,7 +8,7 @@ defmodule NullaWeb.FollowController do def following(conn, %{"username" => username, "page" => page_param}) do instance_settings = InstanceSettings.get_instance_settings!() domain = instance_settings.domain - limit = instance_settings.limit + limit = instance_settings.api_limit actor = Actor.get_actor(username, domain) total = Relation.count_following(actor.id) @@ -39,7 +39,7 @@ defmodule NullaWeb.FollowController do def followers(conn, %{"username" => username, "page" => page_param}) do instance_settings = InstanceSettings.get_instance_settings!() domain = instance_settings.domain - limit = instance_settings.limit + limit = instance_settings.api_limit actor = Actor.get_actor(username, domain) total = Relation.count_followers(actor.id) diff --git a/priv/repo/migrations/20250615131158_create_users.exs b/priv/repo/migrations/20250615131158_create_users.exs index 3ff4dba..9ee5345 100644 --- a/priv/repo/migrations/20250615131158_create_users.exs +++ b/priv/repo/migrations/20250615131158_create_users.exs @@ -6,7 +6,7 @@ defmodule Nulla.Repo.Migrations.CreateUsers do add :id, :bigint, primary_key: true add :email, :string add :password, :string - add :privateKeyPem, :string + add :privateKeyPem, :text add :last_active_at, :utc_datetime timestamps(type: :utc_datetime) diff --git a/test/nulla_web/controllers/follow_controller_test.exs b/test/nulla_web/controllers/follow_controller_test.exs new file mode 100644 index 0000000..900c112 --- /dev/null +++ b/test/nulla_web/controllers/follow_controller_test.exs @@ -0,0 +1,123 @@ +defmodule NullaWeb.FollowControllerTest do + use NullaWeb.ConnCase + alias Nulla.KeyGen + alias Nulla.Models.User + alias Nulla.Models.Actor + + setup do + {publicKeyPem, privateKeyPem} = KeyGen.gen() + + {:ok, actor} = + Actor.create_actor(%{ + domain: "localhost", + ap_id: "http://localhost/users/test", + type: "Person", + following: "http://localhost/users/test/following", + followers: "http://localhost/users/test/followers", + inbox: "http://localhost/users/test/inbox", + outbox: "http://localhost/users/test/outbox", + featured: "http://localhost/users/test/collections/featured", + featuredTags: "http://localhost/users/test/collections/tags", + preferredUsername: "test", + name: "Test", + summary: "Test User", + url: "http://localhost/@test", + manuallyApprovesFollowers: false, + discoverable: true, + indexable: true, + published: DateTime.utc_now(), + memorial: false, + publicKey: + Jason.OrderedObject.new( + id: "http://localhost/users/test#main-key", + owner: "http://localhost/users/test", + publicKeyPem: publicKeyPem + ), + endpoints: Jason.OrderedObject.new(sharedInbox: "http://localhost/inbox") + }) + + User.create_user(%{ + id: actor.id, + email: "test@localhost", + password: "password", + privateKeyPem: privateKeyPem, + last_active_at: DateTime.utc_now() + }) + + :ok + end + + describe "GET /users/username/following" do + test "returns ActivityPub JSON of following", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/following") + + assert response = json_response(conn, 200) + + assert response["id"] == "http://localhost/users/test/following" + assert response["type"] == "OrderedCollection" + assert response["totalItems"] == 0 + assert response["first"] == "http://localhost/users/test/following?page=1" + end + + test "returns ActivityPub JSON of following with page", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/following?page=1") + + assert response = json_response(conn, 200) + + assert response["id"] == "http://localhost/users/test/following?page=1" + assert response["type"] == "OrderedCollectionPage" + assert response["totalItems"] == 0 + assert response["partOf"] == "http://localhost/users/test/following" + assert is_list(response["orderedItems"]) + end + + test "returns first page with invalid value", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/following?page=abc") + + assert conn.status == 200 + end + end + + describe "GET /users/username/followers" do + test "returns ActivityPub JSON of followers", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/followers") + + assert response = json_response(conn, 200) + + assert response["id"] == "http://localhost/users/test/followers" + assert response["type"] == "OrderedCollection" + assert response["totalItems"] == 0 + assert response["first"] == "http://localhost/users/test/followers?page=1" + end + + test "returns ActivityPub JSON of followers with page", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/followers?page=1") + + assert response = json_response(conn, 200) + + assert response["id"] == "http://localhost/users/test/followers?page=1" + assert response["type"] == "OrderedCollectionPage" + assert response["totalItems"] == 0 + assert response["partOf"] == "http://localhost/users/test/followers" + assert is_list(response["orderedItems"]) + end + + test "returns first page with invalid value", %{conn: conn} do + conn = + conn + |> get(~p"/users/test/followers?page=abc") + + assert conn.status == 200 + end + end +end diff --git a/test/nulla_web/controllers/nodeinfo_controller_test.exs b/test/nulla_web/controllers/nodeinfo_controller_test.exs index 085b5a0..d52add7 100644 --- a/test/nulla_web/controllers/nodeinfo_controller_test.exs +++ b/test/nulla_web/controllers/nodeinfo_controller_test.exs @@ -1,42 +1,46 @@ defmodule NullaWeb.NodeinfoControllerTest do use NullaWeb.ConnCase + alias Nulla.KeyGen alias Nulla.Models.User alias Nulla.Models.Actor setup do - {:ok, actor} = Actor.create_actor(%{ - domain: "localhost", - ap_id: "http://localhost/users/test", - type: "Person", - following: "http://localhost/users/test/following", - followers: "http://localhost/users/test/followers", - inbox: "http://localhost/users/test/inbox", - outbox: "http://localhost/users/test/outbox", - featured: "http://localhost/users/test/collections/featured", - featuredTags: "http://localhost/users/test/collections/tags", - preferredUsername: "test", - name: "Test", - summary: "Test User", - url: "http://localhost/@test", - manuallyApprovesFollowers: false, - discoverable: true, - indexable: true, - published: DateTime.utc_now(), - memorial: false, - publicKey: - Jason.OrderedObject.new( - id: "http://localhost/users/test#main-key", - owner: "http://localhost/users/test", - publicKeyPem: "PUBLIC KEY" - ), - endpoints: Jason.OrderedObject.new(sharedInbox: "http://localhost/inbox") - }) + {publicKeyPem, privateKeyPem} = KeyGen.gen() + + {:ok, actor} = + Actor.create_actor(%{ + domain: "localhost", + ap_id: "http://localhost/users/test", + type: "Person", + following: "http://localhost/users/test/following", + followers: "http://localhost/users/test/followers", + inbox: "http://localhost/users/test/inbox", + outbox: "http://localhost/users/test/outbox", + featured: "http://localhost/users/test/collections/featured", + featuredTags: "http://localhost/users/test/collections/tags", + preferredUsername: "test", + name: "Test", + summary: "Test User", + url: "http://localhost/@test", + manuallyApprovesFollowers: false, + discoverable: true, + indexable: true, + published: DateTime.utc_now(), + memorial: false, + publicKey: + Jason.OrderedObject.new( + id: "http://localhost/users/test#main-key", + owner: "http://localhost/users/test", + publicKeyPem: publicKeyPem + ), + endpoints: Jason.OrderedObject.new(sharedInbox: "http://localhost/inbox") + }) User.create_user(%{ id: actor.id, email: "test@localhost", password: "password", - privateKeyPem: "PRIVATE KEY", + privateKeyPem: privateKeyPem, last_active_at: DateTime.utc_now() }) @@ -59,7 +63,9 @@ defmodule NullaWeb.NodeinfoControllerTest do assert is_binary(link["href"]) end) end + end + describe "GET /nodeinfo/2.0" do test "returns Nodeinfo JSON show", %{conn: conn} do conn = conn