420fea12c6faf2aa89189f4411bc3c2721d5911d
[anni] / lib / pleroma / web / pleroma_api / controllers / emoji_pack_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
6   use Pleroma.Web, :controller
7
8   alias Pleroma.Emoji.Pack
9
10   plug(Pleroma.Web.ApiSpec.CastAndValidate)
11
12   plug(
13     Pleroma.Web.Plugs.OAuthScopesPlug,
14     %{scopes: ["admin:write"]}
15     when action in [
16            :import_from_filesystem,
17            :remote,
18            :download,
19            :create,
20            :update,
21            :delete
22          ]
23   )
24
25   plug(:skip_auth when action in [:index, :archive, :show])
26
27   defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaEmojiPackOperation
28
29   def remote(conn, params) do
30     with {:ok, packs} <-
31            Pack.list_remote(url: params.url, page_size: params.page_size, page: params.page) do
32       json(conn, packs)
33     else
34       {:error, :not_shareable} ->
35         conn
36         |> put_status(:internal_server_error)
37         |> json(%{error: "The requested instance does not support sharing emoji packs"})
38     end
39   end
40
41   def index(conn, params) do
42     emoji_path =
43       [:instance, :static_dir]
44       |> Pleroma.Config.get!()
45       |> Path.join("emoji")
46
47     with {:ok, packs, count} <- Pack.list_local(page: params.page, page_size: params.page_size) do
48       json(conn, %{packs: packs, count: count})
49     else
50       {:error, :create_dir, e} ->
51         conn
52         |> put_status(:internal_server_error)
53         |> json(%{error: "Failed to create the emoji pack directory at #{emoji_path}: #{e}"})
54
55       {:error, :ls, e} ->
56         conn
57         |> put_status(:internal_server_error)
58         |> json(%{
59           error: "Failed to get the contents of the emoji pack directory at #{emoji_path}: #{e}"
60         })
61     end
62   end
63
64   def show(conn, %{name: name, page: page, page_size: page_size}) do
65     name = String.trim(name)
66
67     with {:ok, pack} <- Pack.show(name: name, page: page, page_size: page_size) do
68       json(conn, pack)
69     else
70       {:error, :enoent} ->
71         conn
72         |> put_status(:not_found)
73         |> json(%{error: "Pack #{name} does not exist"})
74
75       {:error, :empty_values} ->
76         conn
77         |> put_status(:bad_request)
78         |> json(%{error: "pack name cannot be empty"})
79
80       {:error, error} ->
81         error_message =
82           add_posix_error(
83             "Failed to get the contents of the `#{name}` pack.",
84             error
85           )
86
87         conn
88         |> put_status(:internal_server_error)
89         |> json(%{error: error_message})
90     end
91   end
92
93   def archive(conn, %{name: name}) do
94     with {:ok, archive} <- Pack.get_archive(name) do
95       send_download(conn, {:binary, archive}, filename: "#{name}.zip")
96     else
97       {:error, :cant_download} ->
98         conn
99         |> put_status(:forbidden)
100         |> json(%{
101           error:
102             "Pack #{name} cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing"
103         })
104
105       {:error, :enoent} ->
106         conn
107         |> put_status(:not_found)
108         |> json(%{error: "Pack #{name} does not exist"})
109     end
110   end
111
112   def download(%{body_params: %{url: url, name: name} = params} = conn, _) do
113     with {:ok, _pack} <- Pack.download(name, url, params[:as]) do
114       json(conn, "ok")
115     else
116       {:error, :not_shareable} ->
117         conn
118         |> put_status(:internal_server_error)
119         |> json(%{error: "The requested instance does not support sharing emoji packs"})
120
121       {:error, :invalid_checksum} ->
122         conn
123         |> put_status(:internal_server_error)
124         |> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"})
125
126       {:error, error} ->
127         conn
128         |> put_status(:internal_server_error)
129         |> json(%{error: error})
130     end
131   end
132
133   def create(conn, %{name: name}) do
134     name = String.trim(name)
135
136     with {:ok, _pack} <- Pack.create(name) do
137       json(conn, "ok")
138     else
139       {:error, :eexist} ->
140         conn
141         |> put_status(:conflict)
142         |> json(%{error: "A pack named \"#{name}\" already exists"})
143
144       {:error, :empty_values} ->
145         conn
146         |> put_status(:bad_request)
147         |> json(%{error: "pack name cannot be empty"})
148
149       {:error, error} ->
150         error_message =
151           add_posix_error(
152             "Unexpected error occurred while creating pack.",
153             error
154           )
155
156         conn
157         |> put_status(:internal_server_error)
158         |> json(%{error: error_message})
159     end
160   end
161
162   def delete(conn, %{name: name}) do
163     name = String.trim(name)
164
165     with {:ok, deleted} when deleted != [] <- Pack.delete(name) do
166       json(conn, "ok")
167     else
168       {:ok, []} ->
169         conn
170         |> put_status(:not_found)
171         |> json(%{error: "Pack #{name} does not exist"})
172
173       {:error, :empty_values} ->
174         conn
175         |> put_status(:bad_request)
176         |> json(%{error: "pack name cannot be empty"})
177
178       {:error, error, _} ->
179         error_message = add_posix_error("Couldn't delete the `#{name}` pack", error)
180
181         conn
182         |> put_status(:internal_server_error)
183         |> json(%{error: error_message})
184     end
185   end
186
187   def update(%{body_params: %{metadata: metadata}} = conn, %{name: name}) do
188     with {:ok, pack} <- Pack.update_metadata(name, metadata) do
189       json(conn, pack.pack)
190     else
191       {:error, :incomplete} ->
192         conn
193         |> put_status(:bad_request)
194         |> json(%{error: "The fallback archive does not have all files specified in pack.json"})
195
196       {:error, error} ->
197         error_message =
198           add_posix_error(
199             "Unexpected error occurred while updating pack metadata.",
200             error
201           )
202
203         conn
204         |> put_status(:internal_server_error)
205         |> json(%{error: error_message})
206     end
207   end
208
209   def import_from_filesystem(conn, _params) do
210     with {:ok, names} <- Pack.import_from_filesystem() do
211       json(conn, names)
212     else
213       {:error, :no_read_write} ->
214         conn
215         |> put_status(:internal_server_error)
216         |> json(%{error: "Error: emoji pack directory must be writable"})
217
218       {:error, _} ->
219         conn
220         |> put_status(:internal_server_error)
221         |> json(%{error: "Error accessing emoji pack directory"})
222     end
223   end
224
225   defp add_posix_error(msg, error) do
226     [msg, Pleroma.Utils.posix_error_message(error)]
227     |> Enum.join(" ")
228     |> String.trim()
229   end
230 end