296 lines
11 KiB
Elixir
296 lines
11 KiB
Elixir
defmodule Farmbot.Target.Bootstrap.Configurator.Router do
|
|
@moduledoc "Routes web connections."
|
|
|
|
use Plug.Router
|
|
use Plug.Debugger, otp_app: :farmbot
|
|
plug(Plug.Static, from: {:farmbot, "priv/static"}, at: "/")
|
|
plug(Plug.Logger, log: :debug)
|
|
plug(Plug.Parsers, parsers: [:urlencoded, :multipart])
|
|
plug(:match)
|
|
plug(:dispatch)
|
|
|
|
use Farmbot.Logger
|
|
import Phoenix.HTML
|
|
alias Farmbot.System.ConfigStorage
|
|
import ConfigStorage, only: [
|
|
get_config_value: 3,
|
|
update_config_value: 4
|
|
]
|
|
|
|
defmodule MissingField do
|
|
defexception [:message, :field, :redir]
|
|
end
|
|
|
|
@version Farmbot.Project.version()
|
|
@data_path Application.get_env(:farmbot, :data_path)
|
|
|
|
get "/" do
|
|
last_reset_reason_file = Path.join(@data_path, "last_shutdown_reason")
|
|
case File.read(last_reset_reason_file) do
|
|
{:ok, reason} when is_binary(reason) ->
|
|
if String.contains?(reason, "CeleryScript request.") do
|
|
render_page(conn, "index", [version: @version, last_reset_reason: nil])
|
|
else
|
|
render_page(conn, "index", [version: @version, last_reset_reason: Phoenix.HTML.raw(reason)])
|
|
end
|
|
{:error, _} ->
|
|
render_page(conn, "index", [version: @version, last_reset_reason: nil])
|
|
end
|
|
end
|
|
|
|
get "/logs" do
|
|
file = Path.join(@data_path, "debug_logs.sqlite3")
|
|
case File.read(file) do
|
|
{:ok, data} ->
|
|
conn
|
|
|> put_resp_content_type("application/octet-stream")
|
|
|> put_resp_header("Content-Disposition", "inline; filename=\"#{@version}-logs.sqlite3\"")
|
|
|> send_resp(200, data)
|
|
{:error, posix} ->
|
|
send_resp(conn, 404, "Error downloading file: #{posix}")
|
|
end
|
|
end
|
|
|
|
get "/setup", do: redir(conn, "/")
|
|
|
|
#NETWORKCONFIG
|
|
get "/network" do
|
|
interfaces = Farmbot.Target.Network.get_interfaces()
|
|
render_page(conn, "network", [interfaces: interfaces, post_action: "select_interface"])
|
|
end
|
|
|
|
post "select_interface" do
|
|
{:ok, _, conn} = read_body(conn)
|
|
interface = conn.body_params["interface"] |> remove_empty_string()
|
|
case interface do
|
|
nil -> redir(conn, "/network")
|
|
<<"w", _ ::binary >> = wireless -> redir(conn, "/config_wireless?ifname=#{wireless}")
|
|
wired -> redir(conn, "/config_wired?ifname=#{wired}")
|
|
end
|
|
end
|
|
|
|
get "/config_wired" do
|
|
try do
|
|
ifname = conn.params["ifname"] || raise(MissingField, field: "ifname", message: "ifname not provided", redir: "/network")
|
|
render_page(conn, "config_wired", [ifname: ifname, advanced_network: advanced_network()])
|
|
rescue
|
|
e in MissingField ->
|
|
Logger.error 1, Exception.message(e)
|
|
redir(conn, e.redir)
|
|
end
|
|
end
|
|
|
|
get "/config_wireless" do
|
|
try do
|
|
ifname = conn.params["ifname"] || raise(MissingField, field: "ifname", message: "ifname not provided", redir: "/network")
|
|
opts = [ifname: ifname, ssids: Farmbot.Target.Network.scan(ifname), post_action: "config_wireless_step_1"]
|
|
render_page(conn, "/config_wireless_step_1", opts)
|
|
rescue
|
|
e in MissingField -> redir(conn, e.redir)
|
|
end
|
|
end
|
|
|
|
post "config_wireless_step_1" do
|
|
try do
|
|
ifname = conn.params["ifname"] |> remove_empty_string() || raise(MissingField, field: "ifname", message: "ifname not provided", redir: "/network")
|
|
ssid = conn.params["ssid"] |> remove_empty_string()
|
|
security = conn.params["security"] |> remove_empty_string()
|
|
manualssid = conn.params["manualssid"] |> remove_empty_string()
|
|
opts = [ssid: ssid, ifname: ifname, security: security, advanced_network: advanced_network(), post_action: "config_network"]
|
|
cond do
|
|
manualssid != nil -> render_page(conn, "/config_wireless_step_2_custom", Keyword.put(opts, :ssid, manualssid))
|
|
ssid == nil -> raise(MissingField, field: "ssid", message: "ssid not provided", redir: "/config_wireless?ifname=#{ifname}")
|
|
security == nil -> raise(MissingField, field: "security", message: "security not provided", redir: "/config_wireless?ifname=#{ifname}")
|
|
security == "WPA-PSK" -> render_page(conn, "/config_wireless_step_2_PSK", opts)
|
|
security == "NONE" -> render_page(conn, "/config_wireless_step_2_NONE", opts)
|
|
true -> render_page(conn, "/config_wireless_step_2_other", opts)
|
|
end
|
|
rescue
|
|
e in MissingField ->
|
|
Logger.error 1, Exception.message(e)
|
|
redir(conn, e.redir)
|
|
end
|
|
end
|
|
|
|
post "/config_network" do
|
|
try do
|
|
ifname = conn.params["ifname"] || raise(MissingField, field: "ifname", message: "ifname not provided", redir: "/network")
|
|
ssid = conn.params["ssid"] |> remove_empty_string()
|
|
security = conn.params["security"] |> remove_empty_string()
|
|
psk = conn.params["psk"] |> remove_empty_string()
|
|
domain = conn.params["domain"] |> remove_empty_string()
|
|
name_servers = conn.params["name_servers"] |> remove_empty_string()
|
|
ipv4_method = conn.params["ipv4_method"] |> remove_empty_string()
|
|
ipv4_address = conn.params["ipv4_address"] |> remove_empty_string()
|
|
ipv4_gateway = conn.params["ipv4_gateway"] |> remove_empty_string()
|
|
ipv4_subnet_mask = conn.params["ipv4_subnet_mask"] |> remove_empty_string()
|
|
|
|
dns_name = conn.params["dns_name"] |> remove_empty_string()
|
|
ntp_server_1 = conn.params["ntp_server_1"] |> remove_empty_string()
|
|
ntp_server_2 = conn.params["ntp_server_2"] |> remove_empty_string()
|
|
|
|
if dns_name do
|
|
update_config_value(:string, "settings", "default_dns_name", dns_name)
|
|
end
|
|
|
|
if ntp_server_1 do
|
|
update_config_value(:string, "settings", "default_ntp_server_1", ntp_server_1)
|
|
end
|
|
|
|
|
|
if ntp_server_2 do
|
|
update_config_value(:string, "settings", "default_ntp_server_2", ntp_server_2)
|
|
end
|
|
|
|
ConfigStorage.input_network_config!(%{
|
|
name: ifname,
|
|
ssid: ssid, security: security, psk: psk,
|
|
type: if(ssid, do: "wireless", else: "wired"),
|
|
domain: domain,
|
|
name_servers: name_servers,
|
|
ipv4_method: ipv4_method,
|
|
ipv4_address: ipv4_address,
|
|
ipv4_gateway: ipv4_gateway,
|
|
ipv4_subnet_mask: ipv4_subnet_mask
|
|
})
|
|
redir(conn, "/firmware")
|
|
rescue
|
|
e in MissingField ->
|
|
Logger.error 1, Exception.message(e)
|
|
redir(conn, e.redir)
|
|
end
|
|
end
|
|
#/NETWORKCONFIG
|
|
|
|
get "/credentials" do
|
|
email = get_config_value(:string, "authorization", "email") || ""
|
|
pass = get_config_value(:string, "authorization", "password") || ""
|
|
server = get_config_value(:string, "authorization", "server") || ""
|
|
first_boot = get_config_value(:bool, "settings", "first_boot")
|
|
update_config_value(:string, "authorization", "token", nil)
|
|
render_page(conn, "credentials", server: server, email: email, password: pass, first_boot: first_boot)
|
|
end
|
|
|
|
get "/firmware" do
|
|
render_page(conn, "firmware")
|
|
end
|
|
|
|
post "/configure_firmware" do
|
|
{:ok, _, conn} = read_body(conn)
|
|
|
|
case conn.body_params do
|
|
%{"firmware_hardware" => hw} when hw in ["arduino", "farmduino", "farmduino_k14"] ->
|
|
update_config_value(:string, "settings", "firmware_hardware", hw)
|
|
|
|
if Application.get_env(:farmbot, :behaviour)[:firmware_handler] == Farmbot.Firmware.UartHandler do
|
|
Logger.warn 1, "Updating #{hw} firmware."
|
|
# /shrug?
|
|
Farmbot.Firmware.UartHandler.Update.force_update_firmware(hw)
|
|
end
|
|
|
|
redir(conn, "/credentials")
|
|
|
|
%{"firmware_hardware" => "custom"} ->
|
|
update_config_value(:string, "settings", "firmware_hardware", "custom")
|
|
redir(conn, "/credentials")
|
|
|
|
_ ->
|
|
send_resp(conn, 500, "Bad firmware_hardware!")
|
|
end
|
|
end
|
|
|
|
post "/configure_credentials" do
|
|
{:ok, _, conn} = read_body(conn)
|
|
|
|
case conn.body_params do
|
|
%{"email" => email, "password" => pass, "server" => server} ->
|
|
if server = test_uri(server) do
|
|
IO.puts "server valid: #{server}"
|
|
else
|
|
send_resp(conn, 500, "server field invalid")
|
|
end
|
|
update_config_value(:string, "authorization", "email", email)
|
|
update_config_value(:string, "authorization", "password", pass)
|
|
update_config_value(:string, "authorization", "server", server)
|
|
update_config_value(:string, "authorization", "token", nil)
|
|
redir(conn, "/finish")
|
|
|
|
_ ->
|
|
send_resp(conn, 500, "invalid request.")
|
|
end
|
|
end
|
|
|
|
get "/finish" do
|
|
email = get_config_value(:string, "authorization", "email")
|
|
pass = get_config_value(:string, "authorization", "password")
|
|
server = get_config_value(:string, "authorization", "server")
|
|
network = !(Enum.empty?(ConfigStorage.get_all_network_configs()))
|
|
if email && pass && server && network do
|
|
conn = render_page(conn, "finish")
|
|
spawn fn() ->
|
|
try do
|
|
alias Farmbot.Target.Bootstrap.Configurator
|
|
Logger.success 2, "Configuration finished."
|
|
Process.sleep(2500) # Allow the page to render and send.
|
|
:ok = GenServer.stop(Configurator.CaptivePortal, :normal)
|
|
# :ok = Supervisor.terminate_child(Configurator, Configurator.CaptivePortal)
|
|
:ok = Supervisor.stop(Configurator)
|
|
Process.sleep(2500) # Good luck.
|
|
rescue
|
|
e ->
|
|
Logger.warn 1, "Falied to close captive portal. Good luck. " <>
|
|
Exception.message(e)
|
|
end
|
|
end
|
|
conn
|
|
else
|
|
Logger.warn 3, "Not configured yet. Restarting configuration."
|
|
redir(conn, "/")
|
|
end
|
|
end
|
|
|
|
match(_, do: send_resp(conn, 404, "Page not found"))
|
|
|
|
defp redir(conn, loc) do
|
|
conn
|
|
|> put_resp_header("location", loc)
|
|
|> send_resp(302, loc)
|
|
end
|
|
|
|
defp render_page(conn, page, info \\ []) do
|
|
page
|
|
|> template_file()
|
|
|> EEx.eval_file(info, [engine: Phoenix.HTML.Engine])
|
|
|> (fn {:safe, contents} -> send_resp(conn, 200, contents) end).()
|
|
rescue
|
|
e -> send_resp(conn, 500, "Failed to render page: #{page} inspect: #{Exception.message(e)}")
|
|
end
|
|
|
|
defp template_file(file) do
|
|
"#{:code.priv_dir(:farmbot)}/static/templates/#{file}.html.eex"
|
|
end
|
|
|
|
defp remove_empty_string(""), do: nil
|
|
defp remove_empty_string(str), do: str
|
|
|
|
defp advanced_network do
|
|
template_file("advanced_network")
|
|
|> EEx.eval_file([])
|
|
|> raw()
|
|
end
|
|
|
|
defp test_uri(nil), do: nil
|
|
|
|
defp test_uri(uri) do
|
|
case URI.parse(uri) do
|
|
%URI{host: host, port: port, scheme: scheme}
|
|
when scheme in ["https", "http"]
|
|
and is_binary(host)
|
|
and is_integer(port) -> uri
|
|
_ ->
|
|
IO.puts "#{inspect uri} is not valid"
|
|
nil
|
|
end
|
|
end
|
|
end
|