dotfiles/app/lib/prymn/apps/wordpress.ex
Nikos Papadakis 818b20f775 add functionality that creates wordpress sites
Reviewed-on: https://git.nikos.gg/prymn/prymn/pulls/9
Co-authored-by: Nikos Papadakis <nikos@papadakis.xyz>
Co-committed-by: Nikos Papadakis <nikos@papadakis.xyz>
2023-12-14 12:27:05 +00:00

199 lines
5.6 KiB
Elixir

defmodule Prymn.Apps.Wordpress do
use Ecto.Schema
import Ecto.Changeset
alias Prymn.Apps.App
alias Prymn.Agents
alias PrymnProto.Prymn.{ExecRequest, ExecResponse}
@primary_key false
embedded_schema do
field :path, :string
field :db_host, :string
field :db_name, :string
field :db_user, :string
field :db_pass, :string
field :admin_username, :string
field :admin_email, :string
end
def changeset(app, attrs) do
app
|> cast(attrs, [
:path,
:db_host,
:db_name,
:db_user,
:db_pass,
:admin_username,
:admin_email
])
|> validate_required([
:path,
:db_user,
:db_host,
:db_user,
:db_pass,
:admin_email,
:admin_username
])
end
@max_steps 6
# TODO: We need a mechanism to drive some messages to the pubsub, maybe using Health:
# Health.change_task(%Health.Task{
# key: "create_db_user",
# message: "Creating db user...",
# curr_step: 0,
# max_steps: 5
# })
# |> Health.update_and_broadcast()
def deploy(%App{type: "wordpress"} = app, agent, notify_fun) do
# TODO: Run sanity checks
# e.g Database does not exist, domain does not exist, etc.
deploy(app, agent, notify_fun, 1)
end
defp deploy(%App{wordpress: %__MODULE__{} = wp} = app, agent, notify_fun, 1 = step) do
# TODO: We need a mechanism to wait for the agent to connect before proceeding,
# this is executed faster than the connection (which happens on deploy_app in Worker)
Agents.exec(agent, %ExecRequest{
user: "root",
program: "mysql",
args: [
"-e",
# TODO: Sanitize the string to protect from injection
"create user '#{wp.db_user}'@'#{wp.db_host}' identified by '#{wp.db_pass}';"
]
})
|> then(&get_results/1)
|> case do
{_, _, exit_code} = data when exit_code != 0 ->
notify_fun.(:error, data, 1 / 5 * 100)
data ->
notify_fun.(:progress, data, step / @max_steps * 100)
deploy(app, agent, notify_fun, step + 1)
end
end
defp deploy(%App{wordpress: %__MODULE__{} = wp} = app, agent, notify_fun, 2 = step) do
Agents.exec(agent, %ExecRequest{
user: "root",
program: "mysql",
args: [
"-e",
# TODO: Sanitize the string to protect from injection
"grant all privileges on #{wp.db_name}.* to '#{wp.db_user}'@'#{wp.db_host}';"
]
})
|> then(&get_results/1)
|> case do
{_, _, exit_code} = data when exit_code != 0 ->
notify_fun.(:error, data, step / @max_steps * 100)
data ->
notify_fun.(:progress, data, step / @max_steps * 100)
deploy(app, agent, notify_fun, step + 1)
end
end
defp deploy(%App{wordpress: %__MODULE__{} = wp} = app, agent, notify_fun, 3 = step) do
Agents.exec(agent, %ExecRequest{
user: "vagrant",
program: "wp",
args: ["core", "download", "--path=#{wp.path}"]
})
|> then(&get_results/1)
|> case do
{_, _, exit_code} = data when exit_code != 0 ->
notify_fun.(:error, data, step / @max_steps * 100)
data ->
notify_fun.(:progress, data, step / @max_steps * 100)
deploy(app, agent, notify_fun, step + 1)
end
end
defp deploy(%App{wordpress: %__MODULE__{} = wp} = app, agent, notify_fun, 4 = step) do
Agents.exec(agent, %ExecRequest{
user: "vagrant",
program: "wp",
args: [
"config",
"create",
"--path=#{wp.path}",
"--dbhost=#{wp.db_host}",
"--dbname=#{wp.db_name}",
"--dbuser=#{wp.db_user}",
"--dbpass=#{wp.db_pass}"
]
})
|> then(&get_results/1)
|> case do
{_, _, exit_code} = data when exit_code != 0 ->
notify_fun.(:error, data, step / @max_steps * 100)
data ->
notify_fun.(:progress, data, step / @max_steps * 100)
deploy(app, agent, notify_fun, step + 1)
end
end
defp deploy(%App{wordpress: %__MODULE__{} = wp} = app, agent, notify_fun, 5 = step) do
Agents.exec(agent, %ExecRequest{
user: "vagrant",
program: "wp",
args: ["db", "create", "--path=#{wp.path}"]
})
|> then(&get_results/1)
|> case do
{_, _, exit_code} = data when exit_code != 0 ->
notify_fun.(:error, data, step / @max_steps * 100)
data ->
notify_fun.(:progress, data, step / @max_steps * 100)
deploy(app, agent, notify_fun, step + 1)
end
end
defp deploy(%App{name: name, wordpress: %__MODULE__{} = wp}, agent, notify_fun, 6 = step) do
Agents.exec(agent, %ExecRequest{
user: "vagrant",
program: "wp",
args: [
"core",
"install",
"--path=#{wp.path}",
"--url=http://site.test",
"--title=#{name}",
"--admin_user=#{wp.admin_username}",
"--admin_email=#{wp.admin_email}"
]
})
|> then(&get_results/1)
|> case do
{_, _, exit_code} = data when exit_code != 0 ->
notify_fun.(:error, data, step / @max_steps * 100)
data ->
notify_fun.(:complete, data, step / @max_steps * 100)
end
end
defp get_results(stream) do
Enum.reduce_while(stream, {"", "", nil}, fn
{:ok, %ExecResponse{out: {:exit_code, exit_code}}}, {stdout, stderr, _} ->
{:halt, {stdout, stderr, exit_code}}
{:ok, %ExecResponse{out: {:stdout, stdout}}}, {acc_stdout, stderr, exit_code} ->
{:cont, {acc_stdout <> stdout, stderr, exit_code}}
{:ok, %ExecResponse{out: {:stderr, stderr}}}, {stdout, acc_stderr, exit_code} ->
{:cont, {stdout, acc_stderr <> stderr, exit_code}}
end)
end
end