farmbot_os/farmbot_os/lib/farmbot_os/configurator/router.ex

393 lines
10 KiB
Elixir

defmodule FarmbotOS.Configurator.Router do
@moduledoc "Routes web connections for configuring farmbot os"
require FarmbotCore.Logger
require FarmbotTelemetry
import Phoenix.HTML
use Plug.Router
use Plug.Debugger, otp_app: :farmbot
plug(Plug.Logger)
plug(Plug.Static, from: {:farmbot, "priv/static"}, at: "/")
plug(Plug.Parsers, parsers: [:urlencoded, :multipart])
plug(Plug.Session,
store: :ets,
key: "_farmbot_session",
table: :configurator_session
)
plug(:fetch_session)
plug(:match)
plug(:dispatch)
@data_layer Application.get_env(:farmbot, FarmbotOS.Configurator)[:data_layer]
@network_layer Application.get_env(:farmbot, FarmbotOS.Configurator)[
:network_layer
]
@telemetry_layer Application.get_env(:farmbot, FarmbotOS.Configurator)[
:telemetry_layer
]
# Trigger for captive portal for various operating systems
get("/gen_204", do: redir(conn, "/"))
get("/generate_204", do: redir(conn, "/"))
get("/hotspot-detect.html", do: redir(conn, "/"))
get("/library/test/success.html", do: redir(conn, "/"))
get("/connecttest.txt", do: redir(conn, "/"))
get("/redirect", do: redir(conn, "/"))
get("/check_network_status.txt", do: redir(conn, "/"))
get("/success.txt", do: redir(conn, "/"))
get "/scheduler_debugger" do
render_page(conn, "scheduler_debugger")
end
get "/logger" do
render_page(conn, "logger")
end
get "/api/telemetry/cpu_usage" do
render_json(conn, cpu_usage())
end
get "/" do
FarmbotTelemetry.event(:configurator, :configuration_start)
case load_last_reset_reason() do
reason when is_binary(reason) ->
if String.contains?(reason, "CeleryScript request.") do
redir(conn, "/network")
else
render_page(conn, "index",
version: version(),
last_reset_reason: reason
)
end
nil ->
redir(conn, "/network")
end
end
get("/setup", do: redir(conn, "/"))
# NETWORKCONFIG
get "/network" do
interfaces = list_interfaces()
case interfaces do
[{<<"w", _::binary>> = ifname, _}] ->
conn
|> put_session("iftype", "wireless")
|> put_session("ifname", ifname)
|> redir("/config_wireless")
[{ifname, _}] ->
conn
|> put_session("iftype", "wired")
|> put_session("ifname", ifname)
|> redir("/config_wired")
_ ->
render_page(conn, "network",
interfaces: interfaces,
post_action: "select_interface"
)
end
end
post "select_interface" do
{:ok, _, conn} = read_body(conn)
ifname = conn.body_params["interface"] |> remove_empty_string()
case ifname do
nil ->
redir(conn, "/network")
<<"w", _::binary>> ->
conn
|> put_session("iftype", "wireless")
|> put_session("ifname", ifname)
|> redir("/config_wireless")
_ ->
conn
|> put_session("iftype", "wired")
|> put_session("ifname", ifname)
|> redir("/config_wired")
end
end
get "/config_wired" do
ifname = get_session(conn, "ifname")
render_page(conn, "config_wired",
ifname: ifname,
advanced_network: advanced_network()
)
end
get "/config_wireless" do
ifname = get_session(conn, "ifname")
render_page(conn, "/config_wireless_step_1",
ifname: ifname,
ssids: scan(ifname),
post_action: "config_wireless_step_1"
)
end
post "config_wireless_step_1" do
ifname = get_session(conn, "ifname")
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 ->
redir(conn, "/config_wireless")
security == nil ->
redir(conn, "/config_wireless")
security == "WPA-PSK" ->
render_page(conn, "/config_wireless_step_2_PSK", opts)
security == "WPA2-PSK" ->
render_page(conn, "/config_wireless_step_2_PSK", opts)
security == "WPA-EAP" ->
render_page(conn, "/config_wireless_step_2_EAP", opts)
security == "NONE" ->
render_page(conn, "/config_wireless_step_2_NONE", opts)
true ->
render_page(conn, "/config_wireless_step_2_other", opts)
end
end
post "/config_network" do
# Global configuration data
dns_name = conn.params["dns_name"] |> remove_empty_string()
ntp1 = conn.params["ntp_server_1"] |> remove_empty_string()
ntp2 = conn.params["ntp_server_2"] |> remove_empty_string()
ssh_key = conn.params["ssh_key"] |> remove_empty_string()
# Network Interface configuration data
ssid = conn.params["ssid"] |> remove_empty_string()
security = conn.params["security"] |> remove_empty_string()
psk = conn.params["psk"] |> remove_empty_string()
identity = conn.params["identity"] |> remove_empty_string()
password = conn.params["password"] |> 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()
reg_domain = conn.params["regulatory_domain"] |> remove_empty_string()
conn
|> put_session("net_config_dns_name", dns_name)
|> put_session("net_config_ntp1", ntp1)
|> put_session("net_config_ntp2", ntp2)
|> put_session("net_config_ssh_key", ssh_key)
|> put_session("net_config_ssid", ssid)
|> put_session("net_config_security", security)
|> put_session("net_config_psk", psk)
|> put_session("net_config_identity", identity)
|> put_session("net_config_password", password)
|> put_session("net_config_domain", domain)
|> put_session("net_config_name_servers", name_servers)
|> put_session("net_config_ipv4_method", ipv4_method)
|> put_session("net_config_ipv4_address", ipv4_address)
|> put_session("net_config_ipv4_gateway", ipv4_gateway)
|> put_session("net_config_ipv4_subnet_mask", ipv4_subnet_mask)
|> put_session("net_config_reg_domain", reg_domain)
|> redir("/credentials")
end
# /NETWORKCONFIG
get "/credentials" do
email = get_session(conn, "auth_config_email") || load_email() || ""
pass = get_session(conn, "auth_config_password") || load_password() || ""
server = get_session(conn, "auth_config_server") || load_server() || ""
render_page(conn, "credentials",
server: server,
email: email,
password: pass
)
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
FarmbotCore.Logger.info(1, "server valid: #{server}")
conn
|> put_session("auth_config_email", email)
|> put_session("auth_config_password", pass)
|> put_session("auth_config_server", server)
|> redir("/finish")
else
conn
|> put_session("__error", "Server is not a valid URI")
|> redir("/credentials")
end
_ ->
conn
|> put_session(
"__error",
"Email, Server, or Password are missing or invalid"
)
|> redir("/credentials")
end
end
get "/finish" do
FarmbotCore.Logger.debug(1, "Configuration complete")
case get_session(conn) do
%{
"ifname" => _,
"auth_config_email" => _,
"auth_config_password" => _,
"auth_config_server" => _
} ->
save_config(get_session(conn))
render_page(conn, "finish")
_ ->
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(Keyword.merge([version: version()], info),
engine: Phoenix.HTML.Engine
)
|> (fn {:safe, contents} -> send_resp(conn, 200, contents) end).()
rescue
e ->
IO.warn("render error", __STACKTRACE__)
send_resp(
conn,
500,
"Failed to render page: #{page} error: #{Exception.message(e)}"
)
end
defp render_json(conn, data) do
conn = put_resp_header(conn, "content-type", "application/json")
case FarmbotCore.JSON.encode(data) do
{:ok, json} ->
send_resp(conn, 200, json)
_ ->
send_resp(
conn,
501,
FarmbotCore.JSON.encode!(%{error: "failed to render json"})
)
end
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
_ ->
FarmbotCore.Logger.error(1, "#{inspect(uri)} is not valid")
nil
end
end
defp load_last_reset_reason do
@data_layer.load_last_reset_reason()
end
defp load_email do
@data_layer.load_email()
end
defp load_password do
@data_layer.load_password()
end
def load_server do
@data_layer.load_server()
end
defp save_config(conf) do
@data_layer.save_config(conf)
end
defp list_interfaces() do
@network_layer.list_interfaces()
end
defp scan(interface) do
@network_layer.scan(interface)
end
## Telemetry layer calls
defp cpu_usage() do
@telemetry_layer.cpu_usage()
end
defp version, do: FarmbotCore.Project.version()
end