Merge branch 'develop' into feature/reports-groups-and-multiple-state-update
[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 File.read!("docs/administration/CLI_tasks/instance.md")
11
12 def run(["gen" | rest]) do
13 {options, [], []} =
14 OptionParser.parse(
15 rest,
16 strict: [
17 force: :boolean,
18 output: :string,
19 output_psql: :string,
20 domain: :string,
21 instance_name: :string,
22 admin_email: :string,
23 notify_email: :string,
24 dbhost: :string,
25 dbname: :string,
26 dbuser: :string,
27 dbpass: :string,
28 rum: :string,
29 indexable: :string,
30 db_configurable: :string,
31 uploads_dir: :string,
32 static_dir: :string,
33 listen_ip: :string,
34 listen_port: :string
35 ],
36 aliases: [
37 o: :output,
38 f: :force
39 ]
40 )
41
42 paths =
43 [config_path, psql_path] = [
44 Keyword.get(options, :output, "config/generated_config.exs"),
45 Keyword.get(options, :output_psql, "config/setup_db.psql")
46 ]
47
48 will_overwrite = Enum.filter(paths, &File.exists?/1)
49 proceed? = Enum.empty?(will_overwrite) or Keyword.get(options, :force, false)
50
51 if proceed? do
52 [domain, port | _] =
53 String.split(
54 get_option(
55 options,
56 :domain,
57 "What domain will your instance use? (e.g pleroma.soykaf.com)"
58 ),
59 ":"
60 ) ++ [443]
61
62 name =
63 get_option(
64 options,
65 :instance_name,
66 "What is the name of your instance? (e.g. Pleroma/Soykaf)"
67 )
68
69 email = get_option(options, :admin_email, "What is your admin email address?")
70
71 notify_email =
72 get_option(
73 options,
74 :notify_email,
75 "What email address do you want to use for sending email notifications?",
76 email
77 )
78
79 indexable =
80 get_option(
81 options,
82 :indexable,
83 "Do you want search engines to index your site? (y/n)",
84 "y"
85 ) === "y"
86
87 db_configurable? =
88 get_option(
89 options,
90 :db_configurable,
91 "Do you want to store the configuration in the database (allows controlling it from admin-fe)? (y/n)",
92 "n"
93 ) === "y"
94
95 dbhost = get_option(options, :dbhost, "What is the hostname of your database?", "localhost")
96
97 dbname = get_option(options, :dbname, "What is the name of your database?", "pleroma")
98
99 dbuser =
100 get_option(
101 options,
102 :dbuser,
103 "What is the user used to connect to your database?",
104 "pleroma"
105 )
106
107 dbpass =
108 get_option(
109 options,
110 :dbpass,
111 "What is the password used to connect to your database?",
112 :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64),
113 "autogenerated"
114 )
115
116 rum_enabled =
117 get_option(
118 options,
119 :rum,
120 "Would you like to use RUM indices?",
121 "n"
122 ) === "y"
123
124 listen_port =
125 get_option(
126 options,
127 :listen_port,
128 "What port will the app listen to (leave it if you are using the default setup with nginx)?",
129 4000
130 )
131
132 listen_ip =
133 get_option(
134 options,
135 :listen_ip,
136 "What ip will the app listen to (leave it if you are using the default setup with nginx)?",
137 "127.0.0.1"
138 )
139
140 uploads_dir =
141 get_option(
142 options,
143 :uploads_dir,
144 "What directory should media uploads go in (when using the local uploader)?",
145 Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads])
146 )
147
148 static_dir =
149 get_option(
150 options,
151 :static_dir,
152 "What directory should custom public files be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)?",
153 Pleroma.Config.get([:instance, :static_dir])
154 )
155
156 secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
157 jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
158 signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
159 {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
160 template_dir = Application.app_dir(:pleroma, "priv") <> "/templates"
161
162 result_config =
163 EEx.eval_file(
164 template_dir <> "/sample_config.eex",
165 domain: domain,
166 port: port,
167 email: email,
168 notify_email: notify_email,
169 name: name,
170 dbhost: dbhost,
171 dbname: dbname,
172 dbuser: dbuser,
173 dbpass: dbpass,
174 secret: secret,
175 jwt_secret: jwt_secret,
176 signing_salt: signing_salt,
177 web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
178 web_push_private_key: Base.url_encode64(web_push_private_key, padding: false),
179 db_configurable?: db_configurable?,
180 static_dir: static_dir,
181 uploads_dir: uploads_dir,
182 rum_enabled: rum_enabled,
183 listen_ip: listen_ip,
184 listen_port: listen_port
185 )
186
187 result_psql =
188 EEx.eval_file(
189 template_dir <> "/sample_psql.eex",
190 dbname: dbname,
191 dbuser: dbuser,
192 dbpass: dbpass,
193 rum_enabled: rum_enabled
194 )
195
196 shell_info("Writing config to #{config_path}.")
197
198 File.write(config_path, result_config)
199 shell_info("Writing the postgres script to #{psql_path}.")
200 File.write(psql_path, result_psql)
201
202 write_robots_txt(indexable, template_dir)
203
204 shell_info(
205 "\n All files successfully written! Refer to the installation instructions for your platform for next steps"
206 )
207 else
208 shell_error(
209 "The task would have overwritten the following files:\n" <>
210 (Enum.map(paths, &"- #{&1}\n") |> Enum.join("")) <>
211 "Rerun with `--force` to overwrite them."
212 )
213 end
214 end
215
216 defp write_robots_txt(indexable, template_dir) do
217 robots_txt =
218 EEx.eval_file(
219 template_dir <> "/robots_txt.eex",
220 indexable: indexable
221 )
222
223 static_dir = Pleroma.Config.get([:instance, :static_dir], "instance/static/")
224
225 unless File.exists?(static_dir) do
226 File.mkdir_p!(static_dir)
227 end
228
229 robots_txt_path = Path.join(static_dir, "robots.txt")
230
231 if File.exists?(robots_txt_path) do
232 File.cp!(robots_txt_path, "#{robots_txt_path}.bak")
233 shell_info("Backing up existing robots.txt to #{robots_txt_path}.bak")
234 end
235
236 File.write(robots_txt_path, robots_txt)
237 shell_info("Writing #{robots_txt_path}.")
238 end
239 end