farmbot_os/lib/farmbot/bot_state/transport/http/http.ex

114 lines
3.3 KiB
Elixir

defmodule Farmbot.BotState.Transport.HTTP do
@moduledoc """
RESTful API for accessing internal Farmbot state.
# Accessing the API
A developer should be able to access the REST API at
`http://<my_farmbot_id>:27347/api/v1/`.
The calls will require an authentication token.
See the [API docs](https://github.com/farmbot/Farmbot-Web-App#q-how-can-i-generate-an-api-token)
for information about generating a token. Access to the local api should be
the same as accessing the cloud API. You will need to have an HTTP header:
`Authorization`:`Bearer <long encrypted token>`
Each of the routes will be described below.
* GET `/api/v1/bot/state` - returns the bot's current state.
* POST `/api/v1/celery_script` - execute celery script node.
"""
use GenStage
alias Farmbot.BotState.Transport.HTTP.Router
alias Farmbot.System.ConfigStorage
@port 27_347
@doc "Subscribe to events."
def subscribe do
GenStage.call(__MODULE__, :subscribe)
end
@doc "Unsubscribe."
def unsubscribe do
GenStage.call(__MODULE__, :unsubscribe)
end
def public_key do
GenStage.call(__MODULE__, :public_key)
end
@doc false
def start_link do
GenStage.start_link(__MODULE__, [], [name: __MODULE__])
end
def stop(reason \\ :normal) do
if Process.whereis(__MODULE__) do
GenStage.stop(__MODULE__, reason)
else
:ok
end
end
def init([]) do
s = ConfigStorage.get_config_value(:string, "authorization", "server")
req = {'#{s}/api/public_key', []}
{:ok, {_, _, body}} = :httpc.request(:get, req, [], [body_format: :binary])
public_key = body |> JOSE.JWK.from_pem
# FIXME(Connor) The router should probably
# be put in an externally supervised module..
opts = [port: @port, acceptors: 2, dispatch: [cowboy_dispatch()]]
case Plug.Adapters.Cowboy.http Router, [], opts do
{:ok, web} ->
state = %{web: web, bot_state: nil, sockets: [], public_key: public_key}
Process.link(state.web)
{:consumer, state, [subscribe_to: [Farmbot.BotState, Farmbot.Logger]]}
{:error, {:already_started, web}} ->
state = %{web: web, bot_state: nil, sockets: [], public_key: public_key}
Process.link(state.web)
{:consumer, state, [subscribe_to: [Farmbot.BotState, Farmbot.Logger]]}
err -> err
end
end
def handle_events(events, {pid, _}, state) do
dispatch Process.info(pid)[:registered_name], events, state
end
def handle_call(:subscribe, {pid, _ref}, state) do
{:reply, :ok, [], %{state | sockets: [pid | state.sockets]}}
end
def handle_call(:unsubscribe, {pid, _ref}, state) do
{:reply, :ok, [], %{state | sockets: List.delete(state.sockets, pid)}}
end
def handle_call(:public_key, _, state) do
{:reply, {:ok, state.public_key}, [], state}
end
defp dispatch(Farmbot.BotState = dispatcher, events, state) do
bot_state = List.last(events)
for socket <- state.sockets do
send socket, {dispatcher, bot_state}
end
{:noreply, [], %{state | bot_state: bot_state}}
end
defp dispatch(Farmbot.Logger = dispatcher, logs, state) do
for socket <- state.sockets do
send socket, {dispatcher, logs}
end
{:noreply, [], state}
end
defp cowboy_dispatch do
{:_,
[
# {"/ws", SocketHandler, []},
{:_, Plug.Adapters.Cowboy.Handler, {Router, []}},
]
}
end
end