1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Mix.Tasks.Pleroma.Instance do
11 @shortdoc "Manages Pleroma instance"
12 @moduledoc File.read!("docs/administration/CLI_tasks/instance.md")
14 def run(["gen" | rest]) do
23 instance_name: :string,
25 notify_email: :string,
32 db_configurable: :string,
37 strip_uploads_location: :string,
38 read_uploads_description: :string,
39 anonymize_uploads: :string,
40 dedupe_uploads: :string
49 [config_path, psql_path] = [
50 Keyword.get(options, :output, "config/generated_config.exs"),
51 Keyword.get(options, :output_psql, "config/setup_db.psql")
54 will_overwrite = Enum.filter(paths, &File.exists?/1)
55 proceed? = Enum.empty?(will_overwrite) or Keyword.get(options, :force, false)
63 "What domain will your instance use? (e.g pleroma.soykaf.com)"
72 "What is the name of your instance? (e.g. The Corndog Emporium)",
76 email = get_option(options, :admin_email, "What is your admin email address?")
82 "What email address do you want to use for sending email notifications?",
90 "Do you want search engines to index your site? (y/n)",
98 "Do you want to store the configuration in the database (allows controlling it from admin-fe)? (y/n)",
102 dbhost = get_option(options, :dbhost, "What is the hostname of your database?", "localhost")
104 dbname = get_option(options, :dbname, "What is the name of your database?", "pleroma")
110 "What is the user used to connect to your database?",
118 "What is the password used to connect to your database?",
119 :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64),
127 "Would you like to use RUM indices?",
135 "What port will the app listen to (leave it if you are using the default setup with nginx)?",
143 "What ip will the app listen to (leave it if you are using the default setup with nginx)?",
151 "What directory should media uploads go in (when using the local uploader)?",
152 Config.get([Pleroma.Uploaders.Local, :uploads])
160 "What directory should custom public files be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)?",
161 Config.get([:instance, :static_dir])
165 {strip_uploads_location_message, strip_uploads_location_default} =
166 if Pleroma.Utils.command_available?("exiftool") do
167 {"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as installed. (y/n)",
170 {"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as not installed, please install it if you answer yes. (y/n)",
174 strip_uploads_location =
177 :strip_uploads_location,
178 strip_uploads_location_message,
179 strip_uploads_location_default
182 {read_uploads_description_message, read_uploads_description_default} =
183 if Pleroma.Utils.command_available?("exiftool") do
184 {"Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as installed. (y/n)",
187 {"Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as not installed, please install it if you answer yes. (y/n)",
191 read_uploads_description =
194 :read_uploads_description,
195 read_uploads_description_message,
196 read_uploads_description_default
203 "Do you want to anonymize the filenames of uploads? (y/n)",
211 "Do you want to deduplicate uploaded files? (y/n)",
215 Config.put([:instance, :static_dir], static_dir)
217 secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
218 jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
219 signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
220 lv_signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
221 {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
222 template_dir = Application.app_dir(:pleroma, "priv") <> "/templates"
226 template_dir <> "/sample_config.eex",
230 notify_email: notify_email,
237 jwt_secret: jwt_secret,
238 signing_salt: signing_salt,
239 lv_signing_salt: lv_signing_salt,
240 web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
241 web_push_private_key: Base.url_encode64(web_push_private_key, padding: false),
242 db_configurable?: db_configurable?,
243 static_dir: static_dir,
244 uploads_dir: uploads_dir,
245 rum_enabled: rum_enabled,
246 listen_ip: listen_ip,
247 listen_port: listen_port,
250 strip_location: strip_uploads_location,
251 read_description: read_uploads_description,
252 anonymize: anonymize_uploads,
253 dedupe: dedupe_uploads
259 template_dir <> "/sample_psql.eex",
263 rum_enabled: rum_enabled
266 config_dir = Path.dirname(config_path)
267 psql_dir = Path.dirname(psql_path)
269 # Note: Distros requiring group read (0o750) on those directories should
270 # pre-create the directories.
271 [config_dir, psql_dir, static_dir, uploads_dir]
272 |> Enum.reject(&File.exists?/1)
273 |> Enum.each(fn dir ->
275 File.chmod!(dir, 0o700)
278 shell_info("Writing config to #{config_path}.")
280 # Sadly no fchmod(2) equivalent in Elixir…
281 File.touch!(config_path)
282 File.chmod!(config_path, 0o640)
283 File.write(config_path, result_config)
284 shell_info("Writing the postgres script to #{psql_path}.")
285 File.write(psql_path, result_psql)
287 write_robots_txt(static_dir, indexable, template_dir)
290 "\n All files successfully written! Refer to the installation instructions for your platform for next steps."
293 if db_configurable? do
295 " Please transfer your config to the database after running database migrations. Refer to \"Transfering the config to/from the database\" section of the docs for more information."
300 "The task would have overwritten the following files:\n" <>
301 Enum.map_join(will_overwrite, &"- #{&1}\n") <> "Rerun with `--force` to overwrite them."
306 defp write_robots_txt(static_dir, indexable, template_dir) do
309 template_dir <> "/robots_txt.eex",
313 robots_txt_path = Path.join(static_dir, "robots.txt")
315 if File.exists?(robots_txt_path) do
316 File.cp!(robots_txt_path, "#{robots_txt_path}.bak")
317 shell_info("Backing up existing robots.txt to #{robots_txt_path}.bak")
320 File.write(robots_txt_path, robots_txt)
321 shell_info("Writing #{robots_txt_path}.")
324 defp upload_filters(filters) when is_map(filters) do
326 if filters.strip_location do
327 [Pleroma.Upload.Filter.Exiftool.StripLocation]
333 if filters.read_description do
334 enabled_filters ++ [Pleroma.Upload.Filter.Exiftool.ReadDescription]
340 if filters.anonymize do
341 enabled_filters ++ [Pleroma.Upload.Filter.AnonymizeFilename]
348 enabled_filters ++ [Pleroma.Upload.Filter.Dedupe]
356 defp upload_filters(_), do: []