summaryrefslogtreecommitdiff
path: root/lib/mix/tasks/pleroma/frontend.ex
blob: 484af6da70bd3d77ba6aa3c91b0499e6490aa3d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# 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, [],
             adapter: [pool: :media, 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