Merge branch 'frontend-admin-api' into 'develop'
Add an API to manage frontends Closes #2238 See merge request pleroma/pleroma!3108
This commit is contained in:
commit
6b32e1de23
10 changed files with 537 additions and 106 deletions
|
@ -28,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Pleroma API: Add `idempotency_key` to the chat message entity that can be used for optimistic message sending.
|
- Pleroma API: Add `idempotency_key` to the chat message entity that can be used for optimistic message sending.
|
||||||
- Pleroma API: (`GET /api/v1/pleroma/federation_status`) Add a way to get a list of unreachable instances.
|
- Pleroma API: (`GET /api/v1/pleroma/federation_status`) Add a way to get a list of unreachable instances.
|
||||||
- Mastodon API: User and conversation mutes can now auto-expire if `expires_in` parameter was given while adding the mute.
|
- Mastodon API: User and conversation mutes can now auto-expire if `expires_in` parameter was given while adding the mute.
|
||||||
|
- Admin API: An endpoint to manage frontends
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
|
@ -1499,3 +1499,66 @@ Returns the content of the document
|
||||||
"url": "https://example.com/instance/panel.html"
|
"url": "https://example.com/instance/panel.html"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `GET /api/pleroma/admin/frontends
|
||||||
|
|
||||||
|
### List available frontends
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"build_url": "https://git.pleroma.social/pleroma/fedi-fe/-/jobs/artifacts/${ref}/download?job=build",
|
||||||
|
"git": "https://git.pleroma.social/pleroma/fedi-fe",
|
||||||
|
"installed": true,
|
||||||
|
"name": "fedi-fe",
|
||||||
|
"ref": "master"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"build_url": "https://git.pleroma.social/lambadalambda/kenoma/-/jobs/artifacts/${ref}/download?job=build",
|
||||||
|
"git": "https://git.pleroma.social/lambadalambda/kenoma",
|
||||||
|
"installed": false,
|
||||||
|
"name": "kenoma",
|
||||||
|
"ref": "master"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## `POST /api/pleroma/admin/frontends/install`
|
||||||
|
|
||||||
|
### Install a frontend
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `name`: frontend name, required
|
||||||
|
- `ref`: frontend ref
|
||||||
|
- `file`: path to a frontend zip file
|
||||||
|
- `build_url`: build URL
|
||||||
|
- `build_dir`: build directory
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"build_url": "https://git.pleroma.social/pleroma/fedi-fe/-/jobs/artifacts/${ref}/download?job=build",
|
||||||
|
"git": "https://git.pleroma.social/pleroma/fedi-fe",
|
||||||
|
"installed": true,
|
||||||
|
"name": "fedi-fe",
|
||||||
|
"ref": "master"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"build_url": "https://git.pleroma.social/lambadalambda/kenoma/-/jobs/artifacts/${ref}/download?job=build",
|
||||||
|
"git": "https://git.pleroma.social/lambadalambda/kenoma",
|
||||||
|
"installed": false,
|
||||||
|
"name": "kenoma",
|
||||||
|
"ref": "master"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "Could not install frontend"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -17,8 +17,6 @@ defmodule Mix.Tasks.Pleroma.Frontend do
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(["install", frontend | args]) do
|
def run(["install", frontend | args]) do
|
||||||
log_level = Logger.level()
|
|
||||||
Logger.configure(level: :warn)
|
|
||||||
start_pleroma()
|
start_pleroma()
|
||||||
|
|
||||||
{options, [], []} =
|
{options, [], []} =
|
||||||
|
@ -33,109 +31,6 @@ defmodule Mix.Tasks.Pleroma.Frontend do
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
instance_static_dir =
|
Pleroma.Frontend.install(frontend, options)
|
||||||
with nil <- options[:static_dir] do
|
|
||||||
Pleroma.Config.get!([:instance, :static_dir])
|
|
||||||
end
|
|
||||||
|
|
||||||
cmd_frontend_info = %{
|
|
||||||
"name" => frontend,
|
|
||||||
"ref" => options[:ref],
|
|
||||||
"build_url" => options[:build_url],
|
|
||||||
"build_dir" => options[:build_dir]
|
|
||||||
}
|
|
||||||
|
|
||||||
config_frontend_info = Pleroma.Config.get([:frontends, :available, frontend], %{})
|
|
||||||
|
|
||||||
frontend_info =
|
|
||||||
Map.merge(config_frontend_info, cmd_frontend_info, fn _key, config, cmd ->
|
|
||||||
# This only overrides things that are actually set
|
|
||||||
cmd || config
|
|
||||||
end)
|
|
||||||
|
|
||||||
ref = frontend_info["ref"]
|
|
||||||
|
|
||||||
unless ref do
|
|
||||||
raise "No ref given or configured"
|
|
||||||
end
|
|
||||||
|
|
||||||
dest =
|
|
||||||
Path.join([
|
|
||||||
instance_static_dir,
|
|
||||||
"frontends",
|
|
||||||
frontend,
|
|
||||||
ref
|
|
||||||
])
|
|
||||||
|
|
||||||
fe_label = "#{frontend} (#{ref})"
|
|
||||||
|
|
||||||
tmp_dir = Path.join([instance_static_dir, "frontends", "tmp"])
|
|
||||||
|
|
||||||
with {_, :ok} <-
|
|
||||||
{:download_or_unzip, download_or_unzip(frontend_info, tmp_dir, options[:file])},
|
|
||||||
shell_info("Installing #{fe_label} to #{dest}"),
|
|
||||||
:ok <- install_frontend(frontend_info, tmp_dir, dest) do
|
|
||||||
File.rm_rf!(tmp_dir)
|
|
||||||
shell_info("Frontend #{fe_label} installed to #{dest}")
|
|
||||||
|
|
||||||
Logger.configure(level: log_level)
|
|
||||||
else
|
|
||||||
{:download_or_unzip, _} ->
|
|
||||||
shell_info("Could not download or unzip the frontend")
|
|
||||||
|
|
||||||
_e ->
|
|
||||||
shell_info("Could not install the frontend")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp download_or_unzip(frontend_info, temp_dir, file) do
|
|
||||||
if file do
|
|
||||||
with {:ok, zip} <- File.read(Path.expand(file)) do
|
|
||||||
unzip(zip, temp_dir)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
download_build(frontend_info, temp_dir)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def unzip(zip, dest) do
|
|
||||||
with {:ok, unzipped} <- :zip.unzip(zip, [:memory]) do
|
|
||||||
File.rm_rf!(dest)
|
|
||||||
File.mkdir_p!(dest)
|
|
||||||
|
|
||||||
Enum.each(unzipped, fn {filename, data} ->
|
|
||||||
path = filename
|
|
||||||
|
|
||||||
new_file_path = Path.join(dest, path)
|
|
||||||
|
|
||||||
new_file_path
|
|
||||||
|> Path.dirname()
|
|
||||||
|> File.mkdir_p!()
|
|
||||||
|
|
||||||
File.write!(new_file_path, data)
|
|
||||||
end)
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp download_build(frontend_info, dest) do
|
|
||||||
shell_info("Downloading pre-built bundle for #{frontend_info["name"]}")
|
|
||||||
url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"])
|
|
||||||
|
|
||||||
with {:ok, %{status: 200, body: zip_body}} <-
|
|
||||||
Pleroma.HTTP.get(url, [], pool: :media, recv_timeout: 120_000) do
|
|
||||||
unzip(zip_body, dest)
|
|
||||||
else
|
|
||||||
e -> {:error, e}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp install_frontend(frontend_info, source, dest) do
|
|
||||||
from = frontend_info["build_dir"] || "dist"
|
|
||||||
File.rm_rf!(dest)
|
|
||||||
File.mkdir_p!(dest)
|
|
||||||
File.cp_r!(Path.join([source, from]), dest)
|
|
||||||
:ok
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
110
lib/pleroma/frontend.ex
Normal file
110
lib/pleroma/frontend.ex
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Frontend do
|
||||||
|
alias Pleroma.Config
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
def install(name, opts \\ []) do
|
||||||
|
frontend_info = %{
|
||||||
|
"ref" => opts[:ref],
|
||||||
|
"build_url" => opts[:build_url],
|
||||||
|
"build_dir" => opts[:build_dir]
|
||||||
|
}
|
||||||
|
|
||||||
|
frontend_info =
|
||||||
|
[:frontends, :available, name]
|
||||||
|
|> Config.get(%{})
|
||||||
|
|> Map.merge(frontend_info, fn _key, config, cmd ->
|
||||||
|
# This only overrides things that are actually set
|
||||||
|
cmd || config
|
||||||
|
end)
|
||||||
|
|
||||||
|
ref = frontend_info["ref"]
|
||||||
|
|
||||||
|
unless ref do
|
||||||
|
raise "No ref given or configured"
|
||||||
|
end
|
||||||
|
|
||||||
|
dest = Path.join([dir(), name, ref])
|
||||||
|
|
||||||
|
label = "#{name} (#{ref})"
|
||||||
|
tmp_dir = Path.join(dir(), "tmp")
|
||||||
|
|
||||||
|
with {_, :ok} <-
|
||||||
|
{:download_or_unzip, download_or_unzip(frontend_info, tmp_dir, opts[:file])},
|
||||||
|
Logger.info("Installing #{label} to #{dest}"),
|
||||||
|
:ok <- install_frontend(frontend_info, tmp_dir, dest) do
|
||||||
|
File.rm_rf!(tmp_dir)
|
||||||
|
Logger.info("Frontend #{label} installed to #{dest}")
|
||||||
|
else
|
||||||
|
{:download_or_unzip, _} ->
|
||||||
|
Logger.info("Could not download or unzip the frontend")
|
||||||
|
{:error, "Could not download or unzip the frontend"}
|
||||||
|
|
||||||
|
_e ->
|
||||||
|
Logger.info("Could not install the frontend")
|
||||||
|
{:error, "Could not install the frontend"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def dir(opts \\ []) do
|
||||||
|
if is_nil(opts[:static_dir]) do
|
||||||
|
Pleroma.Config.get!([:instance, :static_dir])
|
||||||
|
else
|
||||||
|
opts[:static_dir]
|
||||||
|
end
|
||||||
|
|> Path.join("frontends")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp download_or_unzip(frontend_info, temp_dir, nil),
|
||||||
|
do: download_build(frontend_info, temp_dir)
|
||||||
|
|
||||||
|
defp download_or_unzip(_frontend_info, temp_dir, file) do
|
||||||
|
with {:ok, zip} <- File.read(Path.expand(file)) do
|
||||||
|
unzip(zip, temp_dir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unzip(zip, dest) do
|
||||||
|
with {:ok, unzipped} <- :zip.unzip(zip, [:memory]) do
|
||||||
|
File.rm_rf!(dest)
|
||||||
|
File.mkdir_p!(dest)
|
||||||
|
|
||||||
|
Enum.each(unzipped, fn {filename, data} ->
|
||||||
|
path = filename
|
||||||
|
|
||||||
|
new_file_path = Path.join(dest, path)
|
||||||
|
|
||||||
|
new_file_path
|
||||||
|
|> Path.dirname()
|
||||||
|
|> File.mkdir_p!()
|
||||||
|
|
||||||
|
File.write!(new_file_path, data)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp download_build(frontend_info, dest) do
|
||||||
|
Logger.info("Downloading pre-built bundle for #{frontend_info["name"]}")
|
||||||
|
url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"])
|
||||||
|
|
||||||
|
with {:ok, %{status: 200, body: zip_body}} <-
|
||||||
|
Pleroma.HTTP.get(url, [], pool: :media, recv_timeout: 120_000) do
|
||||||
|
unzip(zip_body, dest)
|
||||||
|
else
|
||||||
|
{:error, e} -> {:error, e}
|
||||||
|
e -> {:error, e}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp install_frontend(frontend_info, source, dest) do
|
||||||
|
from = frontend_info["build_dir"] || "dist"
|
||||||
|
File.rm_rf!(dest)
|
||||||
|
File.mkdir_p!(dest)
|
||||||
|
File.cp_r!(Path.join([source, from]), dest)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
40
lib/pleroma/web/admin_api/controllers/frontend_controller.ex
Normal file
40
lib/pleroma/web/admin_api/controllers/frontend_controller.ex
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.FrontendController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action == :install)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["read"], admin: true} when action == :index)
|
||||||
|
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.FrontendOperation
|
||||||
|
|
||||||
|
def index(conn, _params) do
|
||||||
|
installed = installed()
|
||||||
|
|
||||||
|
frontends =
|
||||||
|
[:frontends, :available]
|
||||||
|
|> Config.get([])
|
||||||
|
|> Enum.map(fn {name, desc} ->
|
||||||
|
Map.put(desc, "installed", name in installed)
|
||||||
|
end)
|
||||||
|
|
||||||
|
render(conn, "index.json", frontends: frontends)
|
||||||
|
end
|
||||||
|
|
||||||
|
def install(%{body_params: params} = conn, _params) do
|
||||||
|
with :ok <- Pleroma.Frontend.install(params.name, Map.delete(params, :name)) do
|
||||||
|
index(conn, %{})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp installed do
|
||||||
|
File.ls!(Pleroma.Frontend.dir())
|
||||||
|
end
|
||||||
|
end
|
21
lib/pleroma/web/admin_api/views/frontend_view.ex
Normal file
21
lib/pleroma/web/admin_api/views/frontend_view.ex
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.FrontendView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
def render("index.json", %{frontends: frontends}) do
|
||||||
|
render_many(frontends, __MODULE__, "show.json")
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("show.json", %{frontend: frontend}) do
|
||||||
|
%{
|
||||||
|
name: frontend["name"],
|
||||||
|
git: frontend["git"],
|
||||||
|
build_url: frontend["build_url"],
|
||||||
|
ref: frontend["ref"],
|
||||||
|
installed: frontend["installed"]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,85 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.Admin.FrontendOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Reports"],
|
||||||
|
summary: "Get a list of available frontends",
|
||||||
|
operationId: "AdminAPI.FrontendController.index",
|
||||||
|
security: [%{"oAuth" => ["read"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Response", "application/json", list_of_frontends()),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def install_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "Reports"],
|
||||||
|
summary: "Install a frontend",
|
||||||
|
operationId: "AdminAPI.FrontendController.install",
|
||||||
|
security: [%{"oAuth" => ["read"]}],
|
||||||
|
requestBody: request_body("Parameters", install_request(), required: true),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Response", "application/json", list_of_frontends()),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError),
|
||||||
|
400 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp list_of_frontends do
|
||||||
|
%Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
name: %Schema{type: :string},
|
||||||
|
git: %Schema{type: :string, format: :uri, nullable: true},
|
||||||
|
build_url: %Schema{type: :string, format: :uri, nullable: true},
|
||||||
|
ref: %Schema{type: :string},
|
||||||
|
installed: %Schema{type: :boolean}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp install_request do
|
||||||
|
%Schema{
|
||||||
|
title: "FrontendInstallRequest",
|
||||||
|
type: :object,
|
||||||
|
required: [:name],
|
||||||
|
properties: %{
|
||||||
|
name: %Schema{
|
||||||
|
type: :string
|
||||||
|
},
|
||||||
|
ref: %Schema{
|
||||||
|
type: :string
|
||||||
|
},
|
||||||
|
file: %Schema{
|
||||||
|
type: :string
|
||||||
|
},
|
||||||
|
build_url: %Schema{
|
||||||
|
type: :string
|
||||||
|
},
|
||||||
|
build_dir: %Schema{
|
||||||
|
type: :string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -244,6 +244,9 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/chats/:id/messages", ChatController, :messages)
|
get("/chats/:id/messages", ChatController, :messages)
|
||||||
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
||||||
|
|
||||||
|
get("/frontends", FrontendController, :index)
|
||||||
|
post("/frontends/install", FrontendController, :install)
|
||||||
|
|
||||||
post("/backups", AdminAPIController, :create_backup)
|
post("/backups", AdminAPIController, :create_backup)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
72
test/pleroma/frontend_test.exs
Normal file
72
test/pleroma/frontend_test.exs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.FrontendTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
alias Pleroma.Frontend
|
||||||
|
|
||||||
|
@dir "test/frontend_static_test"
|
||||||
|
|
||||||
|
setup do
|
||||||
|
File.mkdir_p!(@dir)
|
||||||
|
clear_config([:instance, :static_dir], @dir)
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
File.rm_rf(@dir)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it downloads and unzips a known frontend" do
|
||||||
|
clear_config([:frontends, :available], %{
|
||||||
|
"pleroma" => %{
|
||||||
|
"ref" => "fantasy",
|
||||||
|
"name" => "pleroma",
|
||||||
|
"build_url" => "http://gensokyo.2hu/builds/${ref}"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/builds/fantasy"} ->
|
||||||
|
%Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend_dist.zip")}
|
||||||
|
end)
|
||||||
|
|
||||||
|
Frontend.install("pleroma")
|
||||||
|
|
||||||
|
assert File.exists?(Path.join([@dir, "frontends", "pleroma", "fantasy", "test.txt"]))
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it also works given a file" do
|
||||||
|
clear_config([:frontends, :available], %{
|
||||||
|
"pleroma" => %{
|
||||||
|
"ref" => "fantasy",
|
||||||
|
"name" => "pleroma",
|
||||||
|
"build_dir" => ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
folder = Path.join([@dir, "frontends", "pleroma", "fantasy"])
|
||||||
|
previously_existing = Path.join([folder, "temp"])
|
||||||
|
File.mkdir_p!(folder)
|
||||||
|
File.write!(previously_existing, "yey")
|
||||||
|
assert File.exists?(previously_existing)
|
||||||
|
|
||||||
|
Frontend.install("pleroma", file: "test/fixtures/tesla_mock/frontend.zip")
|
||||||
|
|
||||||
|
assert File.exists?(Path.join([folder, "test.txt"]))
|
||||||
|
refute File.exists?(previously_existing)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it downloads and unzips unknown frontends" do
|
||||||
|
Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/madeup.zip"} ->
|
||||||
|
%Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend.zip")}
|
||||||
|
end)
|
||||||
|
|
||||||
|
Frontend.install("unknown",
|
||||||
|
ref: "baka",
|
||||||
|
build_url: "http://gensokyo.2hu/madeup.zip",
|
||||||
|
build_dir: ""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert File.exists?(Path.join([@dir, "frontends", "unknown", "baka", "test.txt"]))
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,141 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.FrontendControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
|
||||||
|
@dir "test/frontend_static_test"
|
||||||
|
|
||||||
|
setup do
|
||||||
|
clear_config([:instance, :static_dir], @dir)
|
||||||
|
File.mkdir_p!(Pleroma.Frontend.dir())
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
File.rm_rf(@dir)
|
||||||
|
end)
|
||||||
|
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
token = insert(:oauth_admin_token, user: admin)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|
||||||
|
{:ok, %{admin: admin, token: token, conn: conn}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/frontends" do
|
||||||
|
test "it lists available frontends", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/frontends")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert Enum.map(response, & &1["name"]) ==
|
||||||
|
Enum.map(Config.get([:frontends, :available]), fn {_, map} -> map["name"] end)
|
||||||
|
|
||||||
|
refute Enum.any?(response, fn frontend -> frontend["installed"] == true end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/frontends/install" do
|
||||||
|
test "from available frontends", %{conn: conn} do
|
||||||
|
clear_config([:frontends, :available], %{
|
||||||
|
"pleroma" => %{
|
||||||
|
"ref" => "fantasy",
|
||||||
|
"name" => "pleroma",
|
||||||
|
"build_url" => "http://gensokyo.2hu/builds/${ref}"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/builds/fantasy"} ->
|
||||||
|
%Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend_dist.zip")}
|
||||||
|
end)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/frontends/install", %{name: "pleroma"})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert File.exists?(Path.join([@dir, "frontends", "pleroma", "fantasy", "test.txt"]))
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/frontends")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert response == [
|
||||||
|
%{
|
||||||
|
"build_url" => "http://gensokyo.2hu/builds/${ref}",
|
||||||
|
"git" => nil,
|
||||||
|
"installed" => true,
|
||||||
|
"name" => "pleroma",
|
||||||
|
"ref" => "fantasy"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "from a file", %{conn: conn} do
|
||||||
|
clear_config([:frontends, :available], %{
|
||||||
|
"pleroma" => %{
|
||||||
|
"ref" => "fantasy",
|
||||||
|
"name" => "pleroma",
|
||||||
|
"build_dir" => ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/frontends/install", %{
|
||||||
|
name: "pleroma",
|
||||||
|
file: "test/fixtures/tesla_mock/frontend.zip"
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert File.exists?(Path.join([@dir, "frontends", "pleroma", "fantasy", "test.txt"]))
|
||||||
|
end
|
||||||
|
|
||||||
|
test "from an URL", %{conn: conn} do
|
||||||
|
Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/madeup.zip"} ->
|
||||||
|
%Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend.zip")}
|
||||||
|
end)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/frontends/install", %{
|
||||||
|
name: "unknown",
|
||||||
|
ref: "baka",
|
||||||
|
build_url: "http://gensokyo.2hu/madeup.zip",
|
||||||
|
build_dir: ""
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert File.exists?(Path.join([@dir, "frontends", "unknown", "baka", "test.txt"]))
|
||||||
|
end
|
||||||
|
|
||||||
|
test "failing returns an error", %{conn: conn} do
|
||||||
|
Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/madeup.zip"} ->
|
||||||
|
%Tesla.Env{status: 404, body: ""}
|
||||||
|
end)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/frontends/install", %{
|
||||||
|
name: "unknown",
|
||||||
|
ref: "baka",
|
||||||
|
build_url: "http://gensokyo.2hu/madeup.zip",
|
||||||
|
build_dir: ""
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
|
|
||||||
|
assert result == %{"error" => "Could not download or unzip the frontend"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue