Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into frontend-bundles...
authorlain <lain@soykaf.club>
Tue, 4 Aug 2020 13:10:36 +0000 (15:10 +0200)
committerlain <lain@soykaf.club>
Tue, 4 Aug 2020 13:10:36 +0000 (15:10 +0200)
CHANGELOG.md
config/config.exs
config/description.exs
docs/administration/CLI_tasks/frontend.md [new file with mode: 0644]
docs/configuration/cheatsheet.md
lib/mix/tasks/pleroma/frontend.ex [new file with mode: 0644]
lib/pleroma/plugs/frontend_static.ex
lib/pleroma/web/endpoint.ex
test/plugs/frontend_static_test.exs

index de017e30aab54b6bf99a59117d97f3aba5c3c817..262a014155b8cfd4a4a0f47e4d9fc95ede08e571 100644 (file)
@@ -49,6 +49,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ### Added
 
+- Frontends: Add mix task to install frontends.
+- Frontends: Add configurable frontends for primary and admin fe.
 - Configuration: Added a blacklist for email servers.
 - Chats: Added `accepts_chat_messages` field to user, exposed in APIs and federation.
 - Chats: Added support for federated chats. For details, see the docs.
index 933a899ab77100b1c608d97f8b64ed64414df8eb..317aa0f04eff32e3e61f9c1ad5a6d1c650764b20 100644 (file)
@@ -664,7 +664,50 @@ config :pleroma, :static_fe, enabled: false
 # With no frontend configuration, the bundled files from the `static` directory will
 # be used.
 #
-# config :pleroma, :frontends, primary: %{"name" => "pleroma", "ref" => "develop"}
+# config :pleroma, :frontends, 
+# primary: %{"name" => "pleroma-fe", "ref" => "develop"},
+# admin: %{"name" => "admin-fe", "ref" => "stable"},
+# available: %{...}
+
+config :pleroma, :frontends,
+  available: %{
+    "kenoma" => %{
+      "name" => "kenoma",
+      "git" => "https://git.pleroma.social/lambadalambda/kenoma",
+      "build_url" =>
+        "https://git.pleroma.social/lambadalambda/kenoma/-/jobs/artifacts/${ref}/download?job=build",
+      "ref" => "master"
+    },
+    "pleroma-fe" => %{
+      "name" => "pleroma-fe",
+      "git" => "https://git.pleroma.social/pleroma/pleroma-fe",
+      "build_url" =>
+        "https://git.pleroma.social/pleroma/pleroma-fe/-/jobs/artifacts/${ref}/download?job=build",
+      "ref" => "develop"
+    },
+    "fedi-fe" => %{
+      "name" => "fedi-fe",
+      "git" => "https://git.pleroma.social/pleroma/fedi-fe",
+      "build_url" =>
+        "https://git.pleroma.social/pleroma/fedi-fe/-/jobs/artifacts/${ref}/download?job=build",
+      "ref" => "master"
+    },
+    "admin-fe" => %{
+      "name" => "admin-fe",
+      "git" => "https://git.pleroma.social/pleroma/admin-fe",
+      "build_url" =>
+        "https://git.pleroma.social/pleroma/admin-fe/-/jobs/artifacts/${ref}/download?job=build",
+      "ref" => "develop"
+    },
+    "soapbox-fe" => %{
+      "name" => "soapbox-fe",
+      "git" => "https://gitlab.com/soapbox-pub/soapbox-fe",
+      "build_url" =>
+        "https://gitlab.com/soapbox-pub/soapbox-fe/-/jobs/artifacts/${ref}/download?job=build-production",
+      "ref" => "v1.0.0",
+      "build_dir" => "static"
+    }
+  }
 
 config :pleroma, :web_cache_ttl,
   activity_pub: nil,
index d823812fbd33d4fba0f2e56662d3880606455e87..b56ea3339dcbfc5ab48608e76a15af85dbf2c8ca 100644 (file)
@@ -3562,6 +3562,57 @@ config :pleroma, :config_description, [
             key: "ref",
             type: :string,
             description: "reference of the installed primary frontend to be used"
+          },
+          %{
+            key: "git",
+            type: :string,
+            description: "URL of the git repository of the frontend"
+          },
+          %{
+            key: "build_url",
+            type: :string,
+            description:
+              "Either an url to a zip file containing the frontend or a template to build it by inserting the `ref`. The string `${ref}` will be replaced by the configured `ref`.",
+            example: "https://some.url/builds/${ref}.zip"
+          },
+          %{
+            key: "build_dir",
+            type: :string,
+            description: "The directory inside the zip file "
+          }
+        ]
+      },
+      %{
+        key: :admin,
+        type: :map,
+        description: "Admin frontend",
+        children: [
+          %{
+            key: "name",
+            type: :string,
+            description: "Name of the installed Admin frontend"
+          },
+          %{
+            key: "ref",
+            type: :string,
+            description: "reference of the installed Admin frontend to be used"
+          },
+          %{
+            key: "git",
+            type: :string,
+            description: "URL of the git repository of the frontend"
+          },
+          %{
+            key: "build_url",
+            type: :string,
+            description:
+              "Either an url to a zip file containing the frontend or a template to build it by inserting the `ref`. The string `${ref}` will be replaced by the configured `ref`.",
+            example: "https://some.url/builds/${ref}.zip"
+          },
+          %{
+            key: "build_dir",
+            type: :string,
+            description: "The directory inside the zip file "
           }
         ]
       }
diff --git a/docs/administration/CLI_tasks/frontend.md b/docs/administration/CLI_tasks/frontend.md
new file mode 100644 (file)
index 0000000..11c1c86
--- /dev/null
@@ -0,0 +1,53 @@
+# Managing frontends
+
+`mix pleroma.frontend install <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>]`
+
+Frontend can be installed either from local zip file, or automatically downloaded from the web.
+
+You can give all the options directly on the command like, but missing information will be filled out by looking at the data configured under `frontends.available` in the config files.
+
+Currently known `<frontend>` values are:
+- [admin-fe](https://git.pleroma.social/pleroma/admin-fe)
+- [kenoma](http://git.pleroma.social/lambadalambda/kenoma)
+- [pleroma-fe](http://git.pleroma.social/pleroma/pleroma-fe)
+- [fedi-fe](https://git.pleroma.social/pleroma/fedi-fe)
+- [soapbox-fe](https://gitlab.com/soapbox-pub/soapbox-fe)
+
+You can still install frontends that are not configured, see below.
+
+## Example installations for a known frontend
+
+For a frontend configured under the `available` key, it's enough to install it by name.
+
+```bash
+mix pleroma.frontend install pleroma
+```
+
+This will download the latest build for the the pre-configured `ref` and install it. It can then be configured as the one of the served frontends in the config file (see `primary` or `admin`).
+
+You can override any of the details. To install a pleroma build from a different url, you could do this:
+
+```bash
+mix pleroma.frontend install pleroma --ref 2huedition --build-url https://example.org/raymoo.zip
+```
+
+Similarly, you can also install from a local zip file.
+
+```bash
+mix pleroma.frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
+```
+
+The resulting frontend will always be installed into a folder of this template: `${instance_static}/frontends/${name}/${ref}`
+
+Careful: This folder will be completely replaced on installation
+
+## Example installation for an unknown frontend
+
+The installation process is the same, but you will have to give all the needed options on the commond line. For example:
+
+```bash
+mix pleroma.frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
+```
+
+If you don't have a zip file but just want to install a frontend from a local path, you can simply copy the files over a folder of this template: `${instance_static}/frontends/${name}/${ref}`
+
index f23cf4fe4cb62e7366ee95a01c59d69bd7943cdf..4645860b7ecc0cf4f4329ea207609f4c14a6fad2 100644 (file)
@@ -1064,11 +1064,11 @@ Control favicons for instances.
 
 Frontends in Pleroma are swappable - you can specify which one to use here.
 
-For now, you can set a frontend with the key `primary` and the options of `name` and `ref`. This will then make Pleroma serve the frontend from a folder constructed by concatenating the instance static path, `frontends` and the name and ref.
+You can set a frontends for the key `primary` and `admin` and the options of `name` and `ref`. This will then make Pleroma serve the frontend from a folder constructed by concatenating the instance static path, `frontends` and the name and ref.
 
-The key `primary` refers to the frontend that will be served by default for general requests. In the future, other frontends like the admin frontend will also be configurable here.
+The key `primary` refers to the frontend that will be served by default for general requests. The key `admin` refers to the frontend that will be served at the `/pleroma/admin` path.
 
-If you don't set anything here, the bundled frontend will be used.
+If you don't set anything here, the bundled frontends will be used.
 
 Example:
 
@@ -1077,6 +1077,10 @@ config :pleroma, :frontends,
   primary: %{
     "name" => "pleroma",
     "ref" => "stable"
+  },
+  admin: %{
+    "name" => "admin",
+    "ref" => "develop"
   }
 ```
 
diff --git a/lib/mix/tasks/pleroma/frontend.ex b/lib/mix/tasks/pleroma/frontend.ex
new file mode 100644 (file)
index 0000000..c385c35
--- /dev/null
@@ -0,0 +1,140 @@
+# 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")
+
+  def run(["install", "none" | _args]) do
+    shell_info("Skipping frontend installation because none was requested")
+    "none"
+  end
+
+  def run(["install", frontend | args]) do
+    log_level = Logger.level()
+    Logger.configure(level: :warn)
+    start_pleroma()
+
+    {options, [], []} =
+      OptionParser.parse(
+        args,
+        strict: [
+          ref: :string,
+          static_dir: :string,
+          build_url: :string,
+          build_dir: :string,
+          file: :string
+        ]
+      )
+
+    instance_static_dir =
+      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(dest, "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, [], timeout: 120_000, 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.mkdir_p!(dest)
+    File.cp_r!(Path.join([source, from]), dest)
+    :ok
+  end
+end
index f549ca75fa1677278845c532a55aa2c4b4e50ed6..11a0d538222e051ec99abba2f512fca3a04f3b5c 100644 (file)
@@ -30,6 +30,7 @@ defmodule Pleroma.Plugs.FrontendStatic do
     opts
     |> Keyword.put(:from, "__unconfigured_frontend_static_plug")
     |> Plug.Static.init()
+    |> Map.put(:frontend_type, opts[:frontend_type])
   end
 
   def call(conn, opts) do
index 527fb288d39766e101ead334d0571904e10dce46..8b153763d53ab61d674e1eb27b6d1e15f8d64541 100644 (file)
@@ -39,6 +39,18 @@ defmodule Pleroma.Web.Endpoint do
     }
   )
 
+  plug(Plug.Static.IndexHtml, at: "/pleroma/admin/")
+
+  plug(Pleroma.Plugs.FrontendStatic,
+    at: "/pleroma/admin",
+    frontend_type: :admin,
+    gzip: true,
+    cache_control_for_etags: @static_cache_control,
+    headers: %{
+      "cache-control" => @static_cache_control
+    }
+  )
+
   # Serve at "/" the static files from "priv/static" directory.
   #
   # You should set gzip to true if you are running phoenix.digest
@@ -56,8 +68,6 @@ defmodule Pleroma.Web.Endpoint do
     }
   )
 
-  plug(Plug.Static.IndexHtml, at: "/pleroma/admin/")
-
   plug(Plug.Static,
     at: "/pleroma/admin/",
     from: {:pleroma, "priv/static/adminfe/"}
index d11d91d78ff8f1bf0e10a8516c6140fcdba46928..6f49230481e80763475e0909ff973cc2c57ba69f 100644 (file)
@@ -3,6 +3,7 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.FrontendStaticPlugTest do
+  alias Pleroma.Plugs.FrontendStatic
   use Pleroma.Web.ConnCase
 
   @dir "test/tmp/instance_static"
@@ -14,6 +15,18 @@ defmodule Pleroma.Web.FrontendStaticPlugTest do
 
   setup do: clear_config([:instance, :static_dir], @dir)
 
+  test "init will give a static plug config + the frontend type" do
+    opts =
+      [
+        at: "/admin",
+        frontend_type: :admin
+      ]
+      |> FrontendStatic.init()
+
+    assert opts[:at] == ["admin"]
+    assert opts[:frontend_type] == :admin
+  end
+
   test "overrides existing static files", %{conn: conn} do
     name = "pelmora"
     ref = "uguu"
@@ -27,4 +40,18 @@ defmodule Pleroma.Web.FrontendStaticPlugTest do
     index = get(conn, "/")
     assert html_response(index, 200) == "from frontend plug"
   end
+
+  test "overrides existing static files for the `pleroma/admin` path", %{conn: conn} do
+    name = "pelmora"
+    ref = "uguu"
+
+    clear_config([:frontends, :admin], %{"name" => name, "ref" => ref})
+    path = "#{@dir}/frontends/#{name}/#{ref}"
+
+    File.mkdir_p!(path)
+    File.write!("#{path}/index.html", "from frontend plug")
+
+    index = get(conn, "/pleroma/admin/")
+    assert html_response(index, 200) == "from frontend plug"
+  end
 end