diff options
Diffstat (limited to 'lib/mix')
-rw-r--r-- | lib/mix/tasks/pleroma/frontend.ex | 243 | ||||
-rw-r--r-- | lib/mix/tasks/pleroma/instance.ex | 92 | ||||
-rw-r--r-- | lib/mix/tasks/pleroma/user.ex | 2 |
3 files changed, 333 insertions, 4 deletions
diff --git a/lib/mix/tasks/pleroma/frontend.ex b/lib/mix/tasks/pleroma/frontend.ex new file mode 100644 index 000000000..14d47aaba --- /dev/null +++ b/lib/mix/tasks/pleroma/frontend.ex @@ -0,0 +1,243 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.Frontend do + use Mix.Task + + import Mix.Pleroma + + @shortdoc "Manages bundled Pleroma frontends" + @moduledoc File.read!("docs/administration/CLI_tasks/frontend.md") + + @frontends %{ + "admin" => %{"project" => "pleroma/admin-fe"}, + "kenoma" => %{"project" => "lambadalambda/kenoma"}, + "mastodon" => %{"project" => "pleroma/mastofe"}, + "pleroma" => %{"project" => "pleroma/pleroma-fe"}, + "fedi" => %{"project" => "dockyard/fedi-fe"} + } + @known_frontends Map.keys(@frontends) + + @ref_local "__local__" + @ref_develop "__develop__" + @ref_stable "__stable__" + + @pleroma_gitlab_host "git.pleroma.social" + + def run(["install", "none" | _args]) do + shell_info("Skipping frontend installation because none was requested") + "none" + end + + def run(["install", unknown_fe | _args]) when unknown_fe not in @known_frontends do + shell_error( + "Frontend \"#{unknown_fe}\" is not known. Known frontends are: #{ + Enum.join(@known_frontends, ", ") + }" + ) + end + + def run(["install", frontend | args]) do + log_level = Logger.level() + Logger.configure(level: :warn) + {:ok, _} = Application.ensure_all_started(:pleroma) + + {options, [], []} = + OptionParser.parse( + args, + strict: [ + ref: :string, + path: :string, + develop: :boolean, + static_dir: :string + ] + ) + + instance_static_dir = + with nil <- options[:static_dir] do + Pleroma.Config.get!([:instance, :static_dir]) + end + + ref = + case options[:path] do + nil -> + ref0 = + cond do + options[:ref] -> options[:ref] + options[:develop] -> @ref_develop + true -> @ref_stable + end + + web_frontend_ref(frontend, ref0) + + path -> + local_path_frontend_ref(path) + end + + dest = + Path.join([ + instance_static_dir, + "frontends", + frontend, + ref + ]) + + fe_label = "#{frontend} (#{ref})" + + from = + with nil <- options[:path] do + Pleroma.Utils.command_required!("yarn") + + url = archive_url(frontend, ref) + + tmp_dir = Path.join(dest, "tmp/src") + + shell_info("Downloading #{fe_label} to #{tmp_dir}") + :ok = download_frontend(url, tmp_dir) + + shell_info("Building #{fe_label} (this will take some time)") + :ok = build_frontend(frontend, tmp_dir) + tmp_dir + end + + shell_info("Installing #{fe_label} to #{dest}") + + :ok = install_frontend(frontend, from, dest) + + shell_info("Frontend #{fe_label} installed to #{dest}") + Logger.configure(level: log_level) + ref + end + + defp download_frontend(url, dest) do + with {:ok, %{status: 200, body: zip_body}} <- + Pleroma.HTTP.get(url, [], timeout: 120_000, recv_timeout: 120_000), + {:ok, unzipped} <- :zip.unzip(zip_body, [:memory]) do + File.rm_rf!(dest) + File.mkdir_p!(dest) + + Enum.each(unzipped, fn {filename, data} -> + [_root | paths] = Path.split(filename) + path = Enum.join(paths, "/") + new_file_path = Path.join(dest, path) + + new_file_path + |> Path.dirname() + |> File.mkdir_p!() + + File.write!(new_file_path, data) + end) + else + {:ok, %{status: 404}} -> + {:error, "Bundle not found"} + + false -> + {:error, "Zip archive must contain \"dist\" folder"} + + error -> + {:error, error} + end + end + + defp build_frontend("admin", path) do + yarn = Pleroma.Config.get(:yarn, "yarn") + {_out, 0} = System.cmd(yarn, [], cd: path) + {_out, 0} = System.cmd(yarn, ["build:prod"], cd: path) + :ok + end + + defp build_frontend(_frontend, path) do + yarn = Pleroma.Config.get(:yarn, "yarn") + {_out, 0} = System.cmd(yarn, [], cd: path) + {_out, 0} = System.cmd(yarn, ["build"], cd: path) + :ok + end + + defp web_frontend_ref(frontend, @ref_develop) do + url = project_url(frontend) <> "/repository/branches" + + {:ok, %{status: 200, body: body}} = + Pleroma.HTTP.get(url, [], timeout: 120_000, recv_timeout: 120_000) + + json = Jason.decode!(body) + + %{"commit" => %{"short_id" => last_commit_ref}} = Enum.find(json, & &1["default"]) + + last_commit_ref + end + + # fallback to develop version if compatible stable ref is not defined in + # mix.exs for the given frontend + defp web_frontend_ref(frontend, @ref_stable) do + case Map.get(Pleroma.Application.frontends(), frontend) do + nil -> + web_frontend_ref(frontend, @ref_develop) + + ref -> + ref + end + end + + defp web_frontend_ref(_frontend, ref), do: ref + + defp project_url(frontend), + do: + "https://#{@pleroma_gitlab_host}/api/v4/projects/#{ + URI.encode_www_form(@frontends[frontend]["project"]) + }" + + defp archive_url(frontend, ref), + do: "https://#{@pleroma_gitlab_host}/#{@frontends[frontend]["project"]}/-/archive/#{ref}.zip" + + defp local_path_frontend_ref(path) do + path + |> Path.join("package.json") + |> File.read() + |> case do + {:ok, bin} -> + bin + |> Jason.decode!() + |> Map.get("version", @ref_local) + + _ -> + @ref_local + end + end + + defp post_install("mastodon", path) do + File.rename!("#{path}/assets/sw.js", "#{path}/sw.js") + + {:ok, files} = File.ls(path) + + Enum.each(files, fn file -> + with false <- file in ~w(packs sw.js) do + [path, file] + |> Path.join() + |> File.rm_rf!() + end + end) + end + + defp post_install(_frontend, _path) do + :ok + end + + defp install_frontend(frontend, source, dest) do + from = + case frontend do + "mastodon" -> + "public" + + "kenoma" -> + "build" + + _ -> + "dist" + end + + File.mkdir_p!(dest) + File.cp_r!(Path.join([source, from]), dest) + post_install(frontend, dest) + end +end diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index 91440b453..f57053a4c 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -33,7 +33,11 @@ defmodule Mix.Tasks.Pleroma.Instance do uploads_dir: :string, static_dir: :string, listen_ip: :string, - listen_port: :string + listen_port: :string, + fe_primary: :string, + fe_mastodon: :string, + fe_admin: :string, + fe_static: :string ], aliases: [ o: :output, @@ -158,7 +162,85 @@ defmodule Mix.Tasks.Pleroma.Instance do ) |> Path.expand() - Config.put([:instance, :static_dir], static_dir) + install_fe = + case Mix.env() do + :test -> + fn _, _ -> "42" end + + _ -> + fn frontend, callback -> + case Pleroma.Utils.command_available?("yarn") do + false when frontend != "none" -> + message = + "To install #{frontend} frontend, `yarn` command is required. Please install it before continue. ([C]ontinue/[A]bort)" + + case String.downcase(shell_prompt(message, "C")) do + abort when abort in ["a", "abort"] -> + "none" + + _continue -> + callback.(frontend, callback) + end + + _ -> + Mix.Tasks.Pleroma.Frontend.run([ + "install", + frontend, + "--static-dir", + static_dir + ]) + end + end + end + + fe_primary = + get_option( + options, + :fe_primary, + "Choose primary frontend for your instance (available: pleroma/kenoma/none)", + "pleroma" + ) + + fe_primary_ref = install_fe.(fe_primary, install_fe) + + enable_static_fe? = + get_option( + options, + :fe_static, + "Would you like to enable Static frontend (render profiles and posts using server-generated HTML that is viewable without using JavaScript)?", + "y" + ) === "y" + + install_mastodon_fe? = + get_option( + options, + :fe_mastodon, + "Would you like to install Mastodon frontend?", + "y" + ) === "y" + + fe_mastodon_ref = + case install_mastodon_fe? do + true -> + install_fe.("mastodon", install_fe) + + false -> + "none" + end + + install_admin_fe? = + get_option( + options, + :fe_admin, + "Would you like to install Admin frontend?", + "y" + ) === "y" + + fe_admin_ref = + case install_admin_fe? do + true -> install_fe.("admin", install_fe) + false -> "none" + end secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) @@ -188,7 +270,11 @@ defmodule Mix.Tasks.Pleroma.Instance do uploads_dir: uploads_dir, rum_enabled: rum_enabled, listen_ip: listen_ip, - listen_port: listen_port + listen_port: listen_port, + fe_primary: %{"name" => fe_primary, "ref" => fe_primary_ref}, + fe_mastodon: %{"name" => "mastodon", "ref" => fe_mastodon_ref}, + fe_admin: %{"name" => "admin", "ref" => fe_admin_ref}, + enable_static_fe?: enable_static_fe? ) result_psql = diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 01824aa18..ce7ba9b80 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -277,7 +277,7 @@ defmodule Mix.Tasks.Pleroma.User do shell_info("Generated user invite token " <> String.replace(invite.invite_type, "_", " ")) url = - Pleroma.Web.Router.Helpers.redirect_url( + Pleroma.Web.Router.Helpers.frontend_url( Pleroma.Web.Endpoint, :registration_page, invite.token |