Attempt to fix incorrect federation of default instance avatars
[akkoma] / lib / pleroma / web / activity_pub / views / user_view.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.UserView do
6 use Pleroma.Web, :view
7
8 alias Pleroma.Repo
9 alias Pleroma.User
10 alias Pleroma.Web.ActivityPub.ActivityPub
11 alias Pleroma.Web.ActivityPub.Transmogrifier
12 alias Pleroma.Web.ActivityPub.Utils
13 alias Pleroma.Web.Endpoint
14 alias Pleroma.Web.Router.Helpers
15 alias Pleroma.Web.Salmon
16 alias Pleroma.Web.WebFinger
17
18 import Ecto.Query
19
20 def render("endpoints.json", %{user: %User{nickname: nil, local: true} = _user}) do
21 %{"sharedInbox" => Helpers.activity_pub_url(Endpoint, :inbox)}
22 end
23
24 def render("endpoints.json", %{user: %User{local: true} = _user}) do
25 %{
26 "oauthAuthorizationEndpoint" => Helpers.o_auth_url(Endpoint, :authorize),
27 "oauthRegistrationEndpoint" => Helpers.mastodon_api_url(Endpoint, :create_app),
28 "oauthTokenEndpoint" => Helpers.o_auth_url(Endpoint, :token_exchange),
29 "sharedInbox" => Helpers.activity_pub_url(Endpoint, :inbox)
30 }
31 end
32
33 def render("endpoints.json", _), do: %{}
34
35 # the instance itself is not a Person, but instead an Application
36 def render("user.json", %{user: %{nickname: nil} = user}) do
37 {:ok, user} = WebFinger.ensure_keys_present(user)
38 {:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
39 public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
40 public_key = :public_key.pem_encode([public_key])
41
42 endpoints = render("endpoints.json", %{user: user})
43
44 %{
45 "id" => user.ap_id,
46 "type" => "Application",
47 "following" => "#{user.ap_id}/following",
48 "followers" => "#{user.ap_id}/followers",
49 "inbox" => "#{user.ap_id}/inbox",
50 "name" => "Pleroma",
51 "summary" => "Virtual actor for Pleroma relay",
52 "url" => user.ap_id,
53 "manuallyApprovesFollowers" => false,
54 "publicKey" => %{
55 "id" => "#{user.ap_id}#main-key",
56 "owner" => user.ap_id,
57 "publicKeyPem" => public_key
58 },
59 "endpoints" => endpoints
60 }
61 |> Map.merge(Utils.make_json_ld_header())
62 end
63
64 def render("user.json", %{user: user}) do
65 {:ok, user} = WebFinger.ensure_keys_present(user)
66 {:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys)
67 public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
68 public_key = :public_key.pem_encode([public_key])
69
70 endpoints = render("endpoints.json", %{user: user})
71
72 %{
73 "id" => user.ap_id,
74 "type" => "Person",
75 "following" => "#{user.ap_id}/following",
76 "followers" => "#{user.ap_id}/followers",
77 "inbox" => "#{user.ap_id}/inbox",
78 "outbox" => "#{user.ap_id}/outbox",
79 "preferredUsername" => user.nickname,
80 "name" => user.name,
81 "summary" => user.bio,
82 "url" => user.ap_id,
83 "manuallyApprovesFollowers" => user.info.locked,
84 "publicKey" => %{
85 "id" => "#{user.ap_id}#main-key",
86 "owner" => user.ap_id,
87 "publicKeyPem" => public_key
88 },
89 "endpoints" => endpoints,
90 "image" => %{
91 "type" => "Image",
92 "url" => User.banner_url(user)
93 },
94 "tag" => user.info.source_data["tag"] || []
95 }
96 |> Map.merge(Utils.maybe_make_icon(user))
97 |> Map.merge(Utils.make_json_ld_header())
98 end
99
100 def render("following.json", %{user: user, page: page}) do
101 query = User.get_friends_query(user)
102 query = from(user in query, select: [:ap_id])
103 following = Repo.all(query)
104
105 total =
106 if !user.info.hide_follows do
107 length(following)
108 else
109 0
110 end
111
112 collection(following, "#{user.ap_id}/following", page, !user.info.hide_follows, total)
113 |> Map.merge(Utils.make_json_ld_header())
114 end
115
116 def render("following.json", %{user: user}) do
117 query = User.get_friends_query(user)
118 query = from(user in query, select: [:ap_id])
119 following = Repo.all(query)
120
121 total =
122 if !user.info.hide_follows do
123 length(following)
124 else
125 0
126 end
127
128 %{
129 "id" => "#{user.ap_id}/following",
130 "type" => "OrderedCollection",
131 "totalItems" => total,
132 "first" => collection(following, "#{user.ap_id}/following", 1, !user.info.hide_follows)
133 }
134 |> Map.merge(Utils.make_json_ld_header())
135 end
136
137 def render("followers.json", %{user: user, page: page}) do
138 query = User.get_followers_query(user)
139 query = from(user in query, select: [:ap_id])
140 followers = Repo.all(query)
141
142 total =
143 if !user.info.hide_followers do
144 length(followers)
145 else
146 0
147 end
148
149 collection(followers, "#{user.ap_id}/followers", page, !user.info.hide_followers, total)
150 |> Map.merge(Utils.make_json_ld_header())
151 end
152
153 def render("followers.json", %{user: user}) do
154 query = User.get_followers_query(user)
155 query = from(user in query, select: [:ap_id])
156 followers = Repo.all(query)
157
158 total =
159 if !user.info.hide_followers do
160 length(followers)
161 else
162 0
163 end
164
165 %{
166 "id" => "#{user.ap_id}/followers",
167 "type" => "OrderedCollection",
168 "totalItems" => total,
169 "first" =>
170 collection(followers, "#{user.ap_id}/followers", 1, !user.info.hide_followers, total)
171 }
172 |> Map.merge(Utils.make_json_ld_header())
173 end
174
175 def render("outbox.json", %{user: user, max_id: max_qid}) do
176 params = %{
177 "limit" => "10"
178 }
179
180 params =
181 if max_qid != nil do
182 Map.put(params, "max_id", max_qid)
183 else
184 params
185 end
186
187 activities = ActivityPub.fetch_user_activities(user, nil, params)
188
189 {max_id, min_id, collection} =
190 if length(activities) > 0 do
191 {
192 Enum.at(Enum.reverse(activities), 0).id,
193 Enum.at(activities, 0).id,
194 Enum.map(activities, fn act ->
195 {:ok, data} = Transmogrifier.prepare_outgoing(act.data)
196 data
197 end)
198 }
199 else
200 {
201 0,
202 0,
203 []
204 }
205 end
206
207 iri = "#{user.ap_id}/outbox"
208
209 page = %{
210 "id" => "#{iri}?max_id=#{max_id}",
211 "type" => "OrderedCollectionPage",
212 "partOf" => iri,
213 "orderedItems" => collection,
214 "next" => "#{iri}?max_id=#{min_id}"
215 }
216
217 if max_qid == nil do
218 %{
219 "id" => iri,
220 "type" => "OrderedCollection",
221 "first" => page
222 }
223 |> Map.merge(Utils.make_json_ld_header())
224 else
225 page |> Map.merge(Utils.make_json_ld_header())
226 end
227 end
228
229 def render("inbox.json", %{user: user, max_id: max_qid}) do
230 params = %{
231 "limit" => "10"
232 }
233
234 params =
235 if max_qid != nil do
236 Map.put(params, "max_id", max_qid)
237 else
238 params
239 end
240
241 activities = ActivityPub.fetch_activities([user.ap_id | user.following], params)
242
243 min_id = Enum.at(Enum.reverse(activities), 0).id
244 max_id = Enum.at(activities, 0).id
245
246 collection =
247 Enum.map(activities, fn act ->
248 {:ok, data} = Transmogrifier.prepare_outgoing(act.data)
249 data
250 end)
251
252 iri = "#{user.ap_id}/inbox"
253
254 page = %{
255 "id" => "#{iri}?max_id=#{max_id}",
256 "type" => "OrderedCollectionPage",
257 "partOf" => iri,
258 "orderedItems" => collection,
259 "next" => "#{iri}?max_id=#{min_id}"
260 }
261
262 if max_qid == nil do
263 %{
264 "id" => iri,
265 "type" => "OrderedCollection",
266 "first" => page
267 }
268 |> Map.merge(Utils.make_json_ld_header())
269 else
270 page |> Map.merge(Utils.make_json_ld_header())
271 end
272 end
273
274 def collection(collection, iri, page, show_items \\ true, total \\ nil) do
275 offset = (page - 1) * 10
276 items = Enum.slice(collection, offset, 10)
277 items = Enum.map(items, fn user -> user.ap_id end)
278 total = total || length(collection)
279
280 map = %{
281 "id" => "#{iri}?page=#{page}",
282 "type" => "OrderedCollectionPage",
283 "partOf" => iri,
284 "totalItems" => total,
285 "orderedItems" => if(show_items, do: items, else: [])
286 }
287
288 if offset < total do
289 Map.put(map, "next", "#{iri}?page=#{page + 1}")
290 else
291 map
292 end
293 end
294 end