Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
[akkoma] / lib / mix / tasks / pleroma / instance.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Mix.Tasks.Pleroma.Instance do
6 use Mix.Task
7 import Mix.Pleroma
8
9 @shortdoc "Manages Pleroma instance"
10 @moduledoc """
11 Manages Pleroma instance.
12
13 ## Generate a new instance config.
14
15 mix pleroma.instance gen [OPTION...]
16
17 If any options are left unspecified, you will be prompted interactively
18
19 ## Options
20
21 - `-f`, `--force` - overwrite any output files
22 - `-o PATH`, `--output PATH` - the output file for the generated configuration
23 - `--output-psql PATH` - the output file for the generated PostgreSQL setup
24 - `--domain DOMAIN` - the domain of your instance
25 - `--instance-name INSTANCE_NAME` - the name of your instance
26 - `--admin-email ADMIN_EMAIL` - the email address of the instance admin
27 - `--notify-email NOTIFY_EMAIL` - email address for notifications
28 - `--dbhost HOSTNAME` - the hostname of the PostgreSQL database to use
29 - `--dbname DBNAME` - the name of the database to use
30 - `--dbuser DBUSER` - the user (aka role) to use for the database connection
31 - `--dbpass DBPASS` - the password to use for the database connection
32 - `--rum Y/N` - Whether to enable RUM indexes
33 - `--indexable Y/N` - Allow/disallow indexing site by search engines
34 - `--db-configurable Y/N` - Allow/disallow configuring instance from admin part
35 - `--uploads-dir` - the directory uploads go in when using a local uploader
36 - `--static-dir` - the directory custom public files should be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)
37 - `--listen-ip` - the ip the app should listen to, defaults to 127.0.0.1
38 - `--listen-port` - the port the app should listen to, defaults to 4000
39 """
40
41 def run(["gen" | rest]) do
42 {options, [], []} =
43 OptionParser.parse(
44 rest,
45 strict: [
46 force: :boolean,
47 output: :string,
48 output_psql: :string,
49 domain: :string,
50 instance_name: :string,
51 admin_email: :string,
52 notify_email: :string,
53 dbhost: :string,
54 dbname: :string,
55 dbuser: :string,
56 dbpass: :string,
57 rum: :string,
58 indexable: :string,
59 db_configurable: :string,
60 uploads_dir: :string,
61 static_dir: :string,
62 listen_ip: :string,
63 listen_port: :string
64 ],
65 aliases: [
66 o: :output,
67 f: :force
68 ]
69 )
70
71 paths =
72 [config_path, psql_path] = [
73 Keyword.get(options, :output, "config/generated_config.exs"),
74 Keyword.get(options, :output_psql, "config/setup_db.psql")
75 ]
76
77 will_overwrite = Enum.filter(paths, &File.exists?/1)
78 proceed? = Enum.empty?(will_overwrite) or Keyword.get(options, :force, false)
79
80 if proceed? do
81 [domain, port | _] =
82 String.split(
83 get_option(
84 options,
85 :domain,
86 "What domain will your instance use? (e.g pleroma.soykaf.com)"
87 ),
88 ":"
89 ) ++ [443]
90
91 name =
92 get_option(
93 options,
94 :instance_name,
95 "What is the name of your instance? (e.g. Pleroma/Soykaf)"
96 )
97
98 email = get_option(options, :admin_email, "What is your admin email address?")
99
100 notify_email =
101 get_option(
102 options,
103 :notify_email,
104 "What email address do you want to use for sending email notifications?",
105 email
106 )
107
108 indexable =
109 get_option(
110 options,
111 :indexable,
112 "Do you want search engines to index your site? (y/n)",
113 "y"
114 ) === "y"
115
116 db_configurable? =
117 get_option(
118 options,
119 :db_configurable,
120 "Do you want to store the configuration in the database (allows controlling it from admin-fe)? (y/n)",
121 "n"
122 ) === "y"
123
124 dbhost = get_option(options, :dbhost, "What is the hostname of your database?", "localhost")
125
126 dbname = get_option(options, :dbname, "What is the name of your database?", "pleroma")
127
128 dbuser =
129 get_option(
130 options,
131 :dbuser,
132 "What is the user used to connect to your database?",
133 "pleroma"
134 )
135
136 dbpass =
137 get_option(
138 options,
139 :dbpass,
140 "What is the password used to connect to your database?",
141 :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64),
142 "autogenerated"
143 )
144
145 rum_enabled =
146 get_option(
147 options,
148 :rum,
149 "Would you like to use RUM indices?",
150 "n"
151 ) === "y"
152
153 listen_port =
154 get_option(
155 options,
156 :listen_port,
157 "What port will the app listen to (leave it if you are using the default setup with nginx)?",
158 4000
159 )
160
161 listen_ip =
162 get_option(
163 options,
164 :listen_ip,
165 "What ip will the app listen to (leave it if you are using the default setup with nginx)?",
166 "127.0.0.1"
167 )
168
169 uploads_dir =
170 get_option(
171 options,
172 :uploads_dir,
173 "What directory should media uploads go in (when using the local uploader)?",
174 Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads])
175 )
176
177 static_dir =
178 get_option(
179 options,
180 :static_dir,
181 "What directory should custom public files be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)?",
182 Pleroma.Config.get([:instance, :static_dir])
183 )
184
185 secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
186 jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
187 signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
188 {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
189 template_dir = Application.app_dir(:pleroma, "priv") <> "/templates"
190
191 result_config =
192 EEx.eval_file(
193 template_dir <> "/sample_config.eex",
194 domain: domain,
195 port: port,
196 email: email,
197 notify_email: notify_email,
198 name: name,
199 dbhost: dbhost,
200 dbname: dbname,
201 dbuser: dbuser,
202 dbpass: dbpass,
203 secret: secret,
204 jwt_secret: jwt_secret,
205 signing_salt: signing_salt,
206 web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
207 web_push_private_key: Base.url_encode64(web_push_private_key, padding: false),
208 db_configurable?: db_configurable?,
209 static_dir: static_dir,
210 uploads_dir: uploads_dir,
211 rum_enabled: rum_enabled,
212 listen_ip: listen_ip,
213 listen_port: listen_port
214 )
215
216 result_psql =
217 EEx.eval_file(
218 template_dir <> "/sample_psql.eex",
219 dbname: dbname,
220 dbuser: dbuser,
221 dbpass: dbpass,
222 rum_enabled: rum_enabled
223 )
224
225 shell_info("Writing config to #{config_path}.")
226
227 File.write(config_path, result_config)
228 shell_info("Writing the postgres script to #{psql_path}.")
229 File.write(psql_path, result_psql)
230
231 write_robots_txt(indexable, template_dir)
232
233 shell_info(
234 "\n All files successfully written! Refer to the installation instructions for your platform for next steps"
235 )
236 else
237 shell_error(
238 "The task would have overwritten the following files:\n" <>
239 (Enum.map(paths, &"- #{&1}\n") |> Enum.join("")) <>
240 "Rerun with `--force` to overwrite them."
241 )
242 end
243 end
244
245 defp write_robots_txt(indexable, template_dir) do
246 robots_txt =
247 EEx.eval_file(
248 template_dir <> "/robots_txt.eex",
249 indexable: indexable
250 )
251
252 static_dir = Pleroma.Config.get([:instance, :static_dir], "instance/static/")
253
254 unless File.exists?(static_dir) do
255 File.mkdir_p!(static_dir)
256 end
257
258 robots_txt_path = Path.join(static_dir, "robots.txt")
259
260 if File.exists?(robots_txt_path) do
261 File.cp!(robots_txt_path, "#{robots_txt_path}.bak")
262 shell_info("Backing up existing robots.txt to #{robots_txt_path}.bak")
263 end
264
265 File.write(robots_txt_path, robots_txt)
266 shell_info("Writing #{robots_txt_path}.")
267 end
268 end