1915aacd9b2973092eebadd8adb708bf3d432371
[akkoma] / lib / mix / tasks / pleroma / instance.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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 alias Pleroma.Config
10
11 @shortdoc "Manages Pleroma instance"
12 @moduledoc File.read!("docs/administration/CLI_tasks/instance.md")
13
14 def run(["gen" | rest]) do
15 {options, [], []} =
16 OptionParser.parse(
17 rest,
18 strict: [
19 force: :boolean,
20 output: :string,
21 output_psql: :string,
22 domain: :string,
23 instance_name: :string,
24 admin_email: :string,
25 notify_email: :string,
26 dbhost: :string,
27 dbname: :string,
28 dbuser: :string,
29 dbpass: :string,
30 rum: :string,
31 indexable: :string,
32 db_configurable: :string,
33 uploads_dir: :string,
34 static_dir: :string,
35 listen_ip: :string,
36 listen_port: :string,
37 strip_uploads: :string,
38 anonymize_uploads: :string,
39 dedupe_uploads: :string,
40 skip_release_env: :boolean,
41 release_env_file: :string
42 ],
43 aliases: [
44 o: :output,
45 f: :force
46 ]
47 )
48
49 paths =
50 [config_path, psql_path] = [
51 Keyword.get(options, :output, "config/generated_config.exs"),
52 Keyword.get(options, :output_psql, "config/setup_db.psql")
53 ]
54
55 will_overwrite = Enum.filter(paths, &File.exists?/1)
56 proceed? = Enum.empty?(will_overwrite) or Keyword.get(options, :force, false)
57
58 if proceed? do
59 [domain, port | _] =
60 String.split(
61 get_option(
62 options,
63 :domain,
64 "What domain will your instance use? (e.g pleroma.soykaf.com)"
65 ),
66 ":"
67 ) ++ [443]
68
69 name =
70 get_option(
71 options,
72 :instance_name,
73 "What is the name of your instance? (e.g. The Corndog Emporium)",
74 domain
75 )
76
77 email = get_option(options, :admin_email, "What is your admin email address?")
78
79 notify_email =
80 get_option(
81 options,
82 :notify_email,
83 "What email address do you want to use for sending email notifications?",
84 email
85 )
86
87 indexable =
88 get_option(
89 options,
90 :indexable,
91 "Do you want search engines to index your site? (y/n)",
92 "y"
93 ) === "y"
94
95 db_configurable? =
96 get_option(
97 options,
98 :db_configurable,
99 "Do you want to store the configuration in the database (allows controlling it from admin-fe)? (y/n)",
100 "n"
101 ) === "y"
102
103 dbhost = get_option(options, :dbhost, "What is the hostname of your database?", "localhost")
104
105 dbname = get_option(options, :dbname, "What is the name of your database?", "pleroma")
106
107 dbuser =
108 get_option(
109 options,
110 :dbuser,
111 "What is the user used to connect to your database?",
112 "pleroma"
113 )
114
115 dbpass =
116 get_option(
117 options,
118 :dbpass,
119 "What is the password used to connect to your database?",
120 :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64),
121 "autogenerated"
122 )
123
124 rum_enabled =
125 get_option(
126 options,
127 :rum,
128 "Would you like to use RUM indices?",
129 "n"
130 ) === "y"
131
132 listen_port =
133 get_option(
134 options,
135 :listen_port,
136 "What port will the app listen to (leave it if you are using the default setup with nginx)?",
137 4000
138 )
139
140 listen_ip =
141 get_option(
142 options,
143 :listen_ip,
144 "What ip will the app listen to (leave it if you are using the default setup with nginx)?",
145 "127.0.0.1"
146 )
147
148 uploads_dir =
149 get_option(
150 options,
151 :uploads_dir,
152 "What directory should media uploads go in (when using the local uploader)?",
153 Config.get([Pleroma.Uploaders.Local, :uploads])
154 )
155 |> Path.expand()
156
157 static_dir =
158 get_option(
159 options,
160 :static_dir,
161 "What directory should custom public files be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)?",
162 Config.get([:instance, :static_dir])
163 )
164 |> Path.expand()
165
166 strip_uploads =
167 get_option(
168 options,
169 :strip_uploads,
170 "Do you want to strip location (GPS) data from uploaded images? (y/n)",
171 "y"
172 ) === "y"
173
174 anonymize_uploads =
175 get_option(
176 options,
177 :anonymize_uploads,
178 "Do you want to anonymize the filenames of uploads? (y/n)",
179 "n"
180 ) === "y"
181
182 dedupe_uploads =
183 get_option(
184 options,
185 :dedupe_uploads,
186 "Do you want to deduplicate uploaded files? (y/n)",
187 "n"
188 ) === "y"
189
190 Config.put([:instance, :static_dir], static_dir)
191
192 secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
193 jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
194 signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
195 {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
196 template_dir = Application.app_dir(:pleroma, "priv") <> "/templates"
197
198 result_config =
199 EEx.eval_file(
200 template_dir <> "/sample_config.eex",
201 domain: domain,
202 port: port,
203 email: email,
204 notify_email: notify_email,
205 name: name,
206 dbhost: dbhost,
207 dbname: dbname,
208 dbuser: dbuser,
209 dbpass: dbpass,
210 secret: secret,
211 jwt_secret: jwt_secret,
212 signing_salt: signing_salt,
213 web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
214 web_push_private_key: Base.url_encode64(web_push_private_key, padding: false),
215 db_configurable?: db_configurable?,
216 static_dir: static_dir,
217 uploads_dir: uploads_dir,
218 rum_enabled: rum_enabled,
219 listen_ip: listen_ip,
220 listen_port: listen_port,
221 upload_filters:
222 upload_filters(%{
223 strip: strip_uploads,
224 anonymize: anonymize_uploads,
225 dedupe: dedupe_uploads
226 })
227 )
228
229 result_psql =
230 EEx.eval_file(
231 template_dir <> "/sample_psql.eex",
232 dbname: dbname,
233 dbuser: dbuser,
234 dbpass: dbpass,
235 rum_enabled: rum_enabled
236 )
237
238 shell_info("Writing config to #{config_path}.")
239
240 File.write(config_path, result_config)
241 shell_info("Writing the postgres script to #{psql_path}.")
242 File.write(psql_path, result_psql)
243
244 write_robots_txt(static_dir, indexable, template_dir)
245
246 if Keyword.get(options, :skip_release_env, false) do
247 shell_info("""
248 Release environment file is skip. Please generate the release env file before start.
249 `MIX_ENV=#{Mix.env()} mix pleroma.release_env gen`
250 """)
251 else
252 shell_info("Generation the environment file:")
253
254 release_env_args =
255 with path when not is_nil(path) <- Keyword.get(options, :release_env_file) do
256 ["gen", "--path", path]
257 else
258 _ -> ["gen"]
259 end
260
261 Mix.Tasks.Pleroma.ReleaseEnv.run(release_env_args)
262 end
263
264 shell_info(
265 "\n All files successfully written! Refer to the installation instructions for your platform for next steps."
266 )
267
268 if db_configurable? do
269 shell_info(
270 " 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."
271 )
272 end
273 else
274 shell_error(
275 "The task would have overwritten the following files:\n" <>
276 (Enum.map(paths, &"- #{&1}\n") |> Enum.join("")) <>
277 "Rerun with `--force` to overwrite them."
278 )
279 end
280 end
281
282 defp write_robots_txt(static_dir, indexable, template_dir) do
283 robots_txt =
284 EEx.eval_file(
285 template_dir <> "/robots_txt.eex",
286 indexable: indexable
287 )
288
289 unless File.exists?(static_dir) do
290 File.mkdir_p!(static_dir)
291 end
292
293 robots_txt_path = Path.join(static_dir, "robots.txt")
294
295 if File.exists?(robots_txt_path) do
296 File.cp!(robots_txt_path, "#{robots_txt_path}.bak")
297 shell_info("Backing up existing robots.txt to #{robots_txt_path}.bak")
298 end
299
300 File.write(robots_txt_path, robots_txt)
301 shell_info("Writing #{robots_txt_path}.")
302 end
303
304 defp upload_filters(filters) when is_map(filters) do
305 enabled_filters =
306 if filters.strip do
307 [Pleroma.Upload.Filter.ExifTool]
308 else
309 []
310 end
311
312 enabled_filters =
313 if filters.anonymize do
314 enabled_filters ++ [Pleroma.Upload.Filter.AnonymizeFilename]
315 else
316 enabled_filters
317 end
318
319 enabled_filters =
320 if filters.dedupe do
321 enabled_filters ++ [Pleroma.Upload.Filter.Dedupe]
322 else
323 enabled_filters
324 end
325
326 enabled_filters
327 end
328
329 defp upload_filters(_), do: []
330 end