defmodule PlugMicropub do @moduledoc """ A Plug for building a Micropub server. To use: """ require Logger use Plug.Router alias PlugMicropub.{Parser, Handler, Response} plug :match plug :dispatch @default_scopes [ "create", "media" ] # Plug Callbacks @doc false def init(opts) do hostname = Keyword.get(opts, :hostname) || raise ArgumentError, "Micropub Plug requires :hostname option" handler = Keyword.get(opts, :handler) || raise ArgumentError, "Micropub Plug requires :handler option" token_endpoint = Keyword.get(opts, :token_endpoint) || raise ArgumentError, "Micropub Plug requires :token_endpoint option" json_encoder = Keyword.get(opts, :json_encoder) || raise ArgumentError, "Micropub Plug requires :json_encoder option" user_agent = Keyword.get(opts, :user_agent) || raise ArgumentError, "Micropub Plug requires :user_agent option" scopes = Keyword.get(opts, :scopes) || @default_scopes token_handler = Keyword.get(opts, :token_handler) || PlugMicropub.Token [ hostname: hostname, handler: handler, token_handler: token_handler, json_encoder: json_encoder, scopes: scopes, token_endpoint: token_endpoint, user_agent: user_agent ] end @doc false def call(conn, opts) do conn = put_private(conn, :plug_micropub, opts) super(conn, opts) end # Routes post "/" do token_endpoint = get_config(conn, :token_endpoint) supported_scopes = get_config(conn, :scopes) hostname = get_config(conn, :hostname) user_agent = get_config(conn, :user_agent) token_handler = get_config(conn, :token_handler) with {:ok, access_token, conn} <- Parser.get_access_token(conn), {:ok, action, conn} <- Parser.get_action(conn), :ok <- token_handler.verify( access_token, token_endpoint, Atom.to_string(action), supported_scopes, hostname, user_agent ) do Handler.handle_action(action, access_token, conn) else error -> Response.send_error(conn, error) end end get "/" do token_endpoint = get_config(conn, :token_endpoint) supported_scopes = get_config(conn, :scopes) hostname = get_config(conn, :hostname) user_agent = get_config(conn, :user_agent) token_handler = get_config(conn, :token_handler) with {:ok, access_token, conn} <- Parser.get_access_token(conn), {:ok, query} <- Parser.get_query(conn), :ok <- token_handler.verify( access_token, token_endpoint, "source", supported_scopes, hostname, user_agent ) do Handler.handle_query(query, access_token, conn) else error -> Response.send_error(conn, error) end end post "/media" do handler = get_config(conn, :handler) token_endpoint = get_config(conn, :token_endpoint) supported_scopes = get_config(conn, :scopes) hostname = get_config(conn, :hostname) user_agent = get_config(conn, :user_agent) token_handler = get_config(conn, :token_handler) with {:ok, access_token, conn} <- Parser.get_access_token(conn), {:ok, file} <- Parser.get_file(conn), {:ok, url} <- handler.handle_media(file, access_token), :ok <- token_handler.verify( access_token, token_endpoint, "media", supported_scopes, hostname, user_agent ) do conn |> put_resp_header("location", url) |> send_resp(:created, "") else error -> Response.send_error(conn, error) end end match _ do Response.send_error(conn, {:error, :invalid_request, "Request did not match any route."}) end defp get_config(conn, name) do conn.private[:plug_micropub][name] end end