Add featured route

This commit is contained in:
Mirai Kumiko 2025-07-06 09:49:03 +02:00
parent b17e177335
commit f90a7133bc
Signed by: miraikumiko
GPG key ID: 3F178B1B5E0CB278
8 changed files with 118 additions and 3 deletions

View file

@ -1,4 +1,6 @@
defmodule Nulla.Notes do
import Ecto.Query
@moduledoc """
The Notes context.
"""
@ -21,6 +23,12 @@ defmodule Nulla.Notes do
Repo.all(Note)
end
def list_notes_by(by) when is_map(by) or is_list(by) do
Note
|> where(^by)
|> Repo.all()
end
@doc """
Gets a single note.

View file

@ -2,11 +2,13 @@ defmodule Nulla.Notes.Note do
use Ecto.Schema
import Ecto.Changeset
alias Nulla.Snowflake
alias Nulla.Actors
alias Nulla.Actors.Actor
alias Nulla.MediaAttachments.MediaAttachment
@primary_key {:id, :integer, autogenerate: false}
schema "notes" do
field :ap_id, :string
field :inReplyTo, :string
field :published, :utc_datetime
field :url, :string
@ -16,6 +18,7 @@ defmodule Nulla.Notes.Note do
field :sensitive, :boolean, default: false
field :content, :string
field :language, :string
field :featured, :boolean, default: false
belongs_to :actor, Actor
has_many :media_attachments, MediaAttachment
@ -27,6 +30,7 @@ defmodule Nulla.Notes.Note do
def changeset(note, attrs) do
note
|> cast(attrs, [
:ap_id,
:inReplyTo,
:published,
:url,
@ -36,10 +40,13 @@ defmodule Nulla.Notes.Note do
:sensitive,
:content,
:language,
:featured,
:actor_id
])
|> maybe_put_id()
|> maybe_put_ap_id()
|> validate_required([
:ap_id,
:published,
:url,
:visibility,
@ -47,17 +54,36 @@ defmodule Nulla.Notes.Note do
:cc,
:content,
:language,
:featured,
:actor_id
])
end
defp maybe_put_id(changeset) do
id_in_attrs = get_field(changeset, :id)
id = get_field(changeset, :id)
if is_nil(id_in_attrs) do
if is_nil(id) do
change(changeset, id: Snowflake.next_id())
else
changeset
end
end
defp maybe_put_ap_id(changeset) do
id = get_field(changeset, :id)
ap_id = get_field(changeset, :ap_id)
actor_id = get_field(changeset, :actor_id)
cond do
not is_nil(ap_id) ->
changeset
is_nil(actor_id) or is_nil(id) ->
changeset
true ->
actor = Actors.get_actor!(actor_id)
change(changeset, ap_id: "#{actor.ap_id}/notes/#{id}")
end
end
end

View file

@ -0,0 +1,24 @@
defmodule NullaWeb.ActivityPub.FeaturedController do
use NullaWeb, :controller
alias Nulla.Actors
alias Nulla.Notes
alias Nulla.Actors.Actor
alias NullaWeb.ActivityPub.FeaturedJSON
def index(conn, %{"username" => username}) do
domain = NullaWeb.Endpoint.host()
actor = Actors.get_actor_by(acct: "#{username}@#{domain}")
case actor do
%Actor{} = actor ->
notes = Notes.list_notes_by(actor_id: actor.id, featured: true)
conn
|> put_resp_content_type("application/activity+json")
|> send_resp(200, Jason.encode!(FeaturedJSON.index(actor, notes)))
_ ->
send_resp(conn, 404, "")
end
end
end

View file

@ -0,0 +1,51 @@
defmodule NullaWeb.ActivityPub.FeaturedJSON do
@doc """
Renders a featured.
"""
def index(actor, notes) do
Jason.OrderedObject.new(
"@context": "https://www.w3.org/ns/activitystreams",
id: actor.featured,
type: "OrderedCollection",
totalItems: length(notes),
orderedItems: Enum.map(notes, &data/1)
)
end
defp data(note) do
attachment =
case note.media_attachments do
[] ->
[]
attachments ->
[
attachment:
Enum.map(attachments, fn att ->
Jason.OrderedObject.new(
type: "Document",
mediaType: att.mime_type,
url: "https://#{note.actor.domain}/files/#{att.file}"
)
end)
]
end
Jason.OrderedObject.new(
"@context": "https://www.w3.org/ns/activitystreams",
id: note.ap_id,
type: "Note",
summary: nil,
inReplyTo: note.inReplyTo,
published: note.published,
url: note.url,
attributedTo: note.actor.ap_id,
to: note.to,
cc: note.cc,
sensitive: note.sensitive,
content: note.content,
contentMap: Jason.OrderedObject.new("#{note.language}": note.content),
attachment: attachment
)
end
end

View file

@ -32,7 +32,7 @@ defmodule NullaWeb.ActivityPub.NoteJSON do
"https://www.w3.org/ns/activitystreams",
Jason.OrderedObject.new(sensitive: "as:sensitive")
],
id: "#{note.actor.ap_id}/notes/#{note.id}",
id: note.ap_id,
type: "Note",
summary: nil,
inReplyTo: note.inReplyTo,

View file

@ -13,6 +13,9 @@ defmodule NullaWeb.ActivityPub.OutboxJSON do
)
end
@doc """
Renders outbox items.
"""
def show(actor, activities, max_id, min_id) do
Jason.OrderedObject.new(
"@context": [

View file

@ -85,6 +85,7 @@ defmodule NullaWeb.Router do
get "/followers", FollowController, :followers
post "/inbox", InboxController, :inbox
get "/outbox", OutboxController, :index
get "/collections/featured", FeaturedController, :index
get "/notes/:id", NoteController, :show
end
end

View file

@ -4,6 +4,7 @@ defmodule Nulla.Repo.Migrations.CreateNotes do
def change do
create table(:notes, primary_key: false) do
add :id, :bigint, primary_key: true
add :ap_id, :string
add :inReplyTo, :string
add :published, :utc_datetime
add :url, :string
@ -13,6 +14,7 @@ defmodule Nulla.Repo.Migrations.CreateNotes do
add :sensitive, :boolean, default: false, null: false
add :content, :string
add :language, :string
add :featured, :boolean, default: false, null: false
add :actor_id, references(:actors, on_delete: :delete_all)
timestamps(type: :utc_datetime)