diff --git a/lib/nulla/activitypub.ex b/lib/nulla/activitypub.ex index ec6332e..cbf5cfe 100644 --- a/lib/nulla/activitypub.ex +++ b/lib/nulla/activitypub.ex @@ -316,4 +316,44 @@ defmodule Nulla.ActivityPub do last: "https://#{domain}/@#{username}/outbox?min_id=0&page=true" ) end + + @spec outbox(String.t(), Integer.t(), Integer.t(), String.t(), List.t()) :: + Jason.OrderedObject.t() + def outbox(domain, username, max_id, min_id, items) do + Jason.OrderedObject.new( + "@context": [ + "https://www.w3.org/ns/activitystreams", + Jason.OrderedObject.new( + sensitive: "as:sensitive", + Hashtag: "as:Hashtag" + ) + ], + id: "https://#{domain}/@#{username}/outbox?page=true", + type: "OrderedCollectionPage", + next: "https://#{domain}/@#{username}/outbox?max_id=#{max_id}&page=true", + prev: "https://#{domain}/@#{username}/outbox?min_id=#{min_id}&page=true", + partOf: "https://#{domain}/@#{username}/outbox", + orderedItems: items + ) + end + + @spec render_activity(String.t(), Note.t()) :: Jason.OrderedObject.t() + def render_activity(domain, note) do + Jason.OrderedObject.new( + id: "https://#{domain}/@#{note.user.username}/#{note.id}/activity", + type: "Create", + actor: "https://#{domain}/@#{note.user.username}", + published: note.inserted_at |> DateTime.to_iso8601(), + to: ["https://www.w3.org/ns/activitystreams#Public"], + object: + Jason.OrderedObject.new( + id: "https://#{domain}/@#{note.user.username}/#{note.id}", + type: "Note", + content: note.content, + published: note.inserted_at |> DateTime.to_iso8601(), + attributedTo: "https://#{domain}/@#{note.user.username}", + to: ["https://www.w3.org/ns/activitystreams#Public"] + ) + ) + end end diff --git a/lib/nulla/models/note.ex b/lib/nulla/models/note.ex index 83e1eb5..7a7a20f 100644 --- a/lib/nulla/models/note.ex +++ b/lib/nulla/models/note.ex @@ -32,7 +32,23 @@ defmodule Nulla.Models.Note do def get_note!(id), do: Repo.get!(Note, id) - def get_all_notes!(user_id), do: Repo.all(from n in Note, where: n.user_id == ^user_id) + def get_latest_notes(user_id, limit \\ 20) do + from(n in Note, + where: n.user_id == ^user_id, + order_by: [desc: n.inserted_at], + limit: ^limit + ) + |> Repo.all() + end + + def get_before_notes(user_id, max_id, limit \\ 20) do + from(n in Note, + where: n.user_id == ^user_id and n.id < ^max_id, + order_by: [desc: n.inserted_at], + limit: ^limit + ) + |> Nulla.Repo.all() + end def get_total_notes_count(user_id) do from(n in Note, where: n.user_id == ^user_id) diff --git a/lib/nulla_web/controllers/outbox_controller.ex b/lib/nulla_web/controllers/outbox_controller.ex index 55d13da..fd9c8c4 100644 --- a/lib/nulla_web/controllers/outbox_controller.ex +++ b/lib/nulla_web/controllers/outbox_controller.ex @@ -5,14 +5,51 @@ defmodule NullaWeb.OutboxController do alias Nulla.Models.Note alias Nulla.Models.InstanceSettings - def show(conn, %{"username" => username}) do - instance_settings = InstanceSettings.get_instance_settings!() - domain = instance_settings.domain - user = User.get_user_by_username!(username) - total = Note.get_total_notes_count(user.id) + def show(conn, %{"username" => username} = params) do + case Map.get(params, "page") do + "true" -> + instance_settings = InstanceSettings.get_instance_settings!() + domain = instance_settings.domain + user = User.get_user_by_username!(username) + max_id = params["max_id"] && String.to_integer(params["max_id"]) - conn - |> put_resp_content_type("application/activity+json") - |> send_resp(200, Jason.encode!(ActivityPub.outbox(domain, username, total))) + notes = + if max_id do + Note.get_before_notes(user.id, max_id) + else + Note.get_latest_notes(user.id) + end + + items = Enum.map(notes, &ActivityPub.render_activity(&1, domain)) + + next_max_id = + case List.last(notes) do + nil -> 0 + last -> last.id + end + + min_id = + case List.first(notes) do + nil -> 0 + first -> first.id + end + + conn + |> put_resp_content_type("application/activity+json") + |> send_resp( + 200, + Jason.encode!(ActivityPub.outbox(domain, username, next_max_id, min_id || 0, items)) + ) + + _ -> + instance_settings = InstanceSettings.get_instance_settings!() + domain = instance_settings.domain + user = User.get_user_by_username!(username) + total = Note.get_total_notes_count(user.id) + + conn + |> put_resp_content_type("application/activity+json") + |> send_resp(200, Jason.encode!(ActivityPub.outbox(domain, username, total))) + end end end diff --git a/lib/nulla_web/controllers/user_controller.ex b/lib/nulla_web/controllers/user_controller.ex index d85d580..9263318 100644 --- a/lib/nulla_web/controllers/user_controller.ex +++ b/lib/nulla_web/controllers/user_controller.ex @@ -11,7 +11,7 @@ defmodule NullaWeb.UserController do instance_settings = InstanceSettings.get_instance_settings!() domain = instance_settings.domain user = User.get_user_by_username!(username) - notes = Note.get_all_notes!(user.id) + notes = Note.get_notes(user.id) if accept in ["application/activity+json", "application/ld+json"] do conn