plug_indie/lib/plug_micropub.ex
2024-11-29 10:42:27 +01:00

151 lines
3.9 KiB
Elixir

defmodule PlugIndie do
@moduledoc """
A Plug for building a Micropub server.
To use:
"""
require Logger
use Plug.Router
alias PlugIndie.{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) || PlugIndie.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