2023-05-25 21:23:04 +02:00
|
|
|
defmodule ChiyaWeb.Indie.MicropubHandler do
|
2023-06-11 08:57:22 +02:00
|
|
|
@behaviour PlugMicropub.HandlerBehaviour
|
2023-05-28 22:59:11 +02:00
|
|
|
require Logger
|
|
|
|
|
2023-05-31 21:32:34 +02:00
|
|
|
alias ChiyaWeb.Indie.Properties, as: Props
|
|
|
|
alias ChiyaWeb.Indie.Token
|
2023-05-28 22:59:11 +02:00
|
|
|
|
2023-06-11 22:22:17 +02:00
|
|
|
@default_properties [
|
|
|
|
"name",
|
|
|
|
"content",
|
|
|
|
"published_at",
|
|
|
|
"slug",
|
|
|
|
"channels",
|
|
|
|
"tags"
|
|
|
|
]
|
|
|
|
|
2023-05-25 21:23:04 +02:00
|
|
|
@impl true
|
|
|
|
def handle_create(type, properties, access_token) do
|
2023-06-09 20:07:59 +02:00
|
|
|
Logger.info("Handle create")
|
2023-06-09 23:23:45 +02:00
|
|
|
Logger.info("Properties: #{inspect(properties)}")
|
2023-06-09 20:07:59 +02:00
|
|
|
Logger.info("Type: #{type}")
|
2023-05-31 21:32:34 +02:00
|
|
|
|
2023-06-12 18:50:00 +02:00
|
|
|
settings = Chiya.Site.get_settings()
|
2023-06-19 22:23:55 +02:00
|
|
|
micropub_channel_id = settings.micropub_channel_id
|
2023-06-12 18:50:00 +02:00
|
|
|
|
2023-06-08 09:06:16 +02:00
|
|
|
with :ok <- verify_token(access_token),
|
2023-05-31 21:32:34 +02:00
|
|
|
{:ok, post_type} <- Props.get_post_type(properties),
|
2023-06-19 22:23:55 +02:00
|
|
|
{:ok, note_attrs} <- get_attrs(type, post_type, properties, micropub_channel_id),
|
2023-05-28 22:59:11 +02:00
|
|
|
{:ok, note} <- Chiya.Notes.create_note(note_attrs) do
|
2023-06-09 20:37:29 +02:00
|
|
|
Logger.info("Note created!")
|
2023-06-09 20:07:59 +02:00
|
|
|
{:ok, :created, Chiya.Notes.Note.note_url(note)}
|
2023-05-28 22:59:11 +02:00
|
|
|
else
|
|
|
|
error ->
|
|
|
|
Logger.error("Error occurred while creating note from micropub:")
|
2023-06-09 20:07:59 +02:00
|
|
|
Logger.error(inspect(error))
|
2023-05-28 22:59:11 +02:00
|
|
|
|
2023-06-09 20:07:59 +02:00
|
|
|
{:error, :invalid_request}
|
2023-05-28 22:59:11 +02:00
|
|
|
end
|
2023-05-25 21:23:04 +02:00
|
|
|
end
|
|
|
|
|
2023-05-31 21:42:55 +02:00
|
|
|
@impl true
|
|
|
|
def handle_update(_, _, _, _, _) do
|
|
|
|
{:error, :insufficient_scope}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def handle_delete(_url, _access_token) do
|
|
|
|
{:error, :insufficient_scope}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def handle_undelete(_url, _access_token) do
|
|
|
|
{:error, :insufficient_scope}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
2023-06-11 22:22:17 +02:00
|
|
|
def handle_media(_files, _access_token) do
|
2023-05-31 21:42:55 +02:00
|
|
|
{:error, :insufficient_scope}
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
2023-06-11 22:22:17 +02:00
|
|
|
def handle_source_query(url, filter_properties, access_token) do
|
|
|
|
filter_properties =
|
|
|
|
if Enum.empty?(filter_properties),
|
|
|
|
do: @default_properties,
|
|
|
|
else: filter_properties
|
|
|
|
|
|
|
|
with :ok <- verify_token(access_token),
|
|
|
|
{:ok, slug} <- Chiya.Notes.Note.note_slug(url),
|
|
|
|
note <- Chiya.Notes.get_public_note_by_slug_preloaded!(slug) do
|
|
|
|
filtered_note =
|
|
|
|
Map.filter(note, fn {key, _val} ->
|
|
|
|
Enum.member?(filter_properties, to_string(key))
|
|
|
|
end)
|
|
|
|
|
|
|
|
{:ok, filtered_note}
|
|
|
|
else
|
|
|
|
_ -> {:error, :insufficient_scope}
|
|
|
|
end
|
2023-05-31 21:42:55 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
2023-06-11 22:22:17 +02:00
|
|
|
def handle_config_query(access_token) do
|
|
|
|
case verify_token(access_token) do
|
|
|
|
:ok ->
|
|
|
|
channels = Chiya.Channels.list_channels()
|
2023-06-08 09:06:16 +02:00
|
|
|
|
2023-06-11 22:22:17 +02:00
|
|
|
{:ok,
|
2023-06-08 09:21:33 +02:00
|
|
|
%{
|
2023-06-11 22:22:17 +02:00
|
|
|
"destination" => [],
|
|
|
|
"post-types" => [
|
|
|
|
%{
|
|
|
|
"type" => "note",
|
|
|
|
"name" => "Note"
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"channels" =>
|
|
|
|
Enum.map(channels, fn c ->
|
|
|
|
%{
|
|
|
|
"uid" => c.slug,
|
|
|
|
"name" => c.name
|
|
|
|
}
|
|
|
|
end)
|
|
|
|
}}
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
{:error, :insufficient_scope}
|
|
|
|
end
|
2023-05-31 21:42:55 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
2023-06-11 08:57:22 +02:00
|
|
|
def handle_syndicate_to_query(access_token) do
|
|
|
|
case verify_token(access_token) do
|
|
|
|
:ok -> {:ok, %{"syndicate-to" => []}}
|
|
|
|
_ -> {:error, :insufficient_scope}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def handle_category_query(access_token) do
|
|
|
|
case verify_token(access_token) do
|
2023-06-11 22:22:17 +02:00
|
|
|
:ok ->
|
2023-06-11 08:57:22 +02:00
|
|
|
tags = Enum.map(Chiya.Tags.list_tags(), fn t -> t.name end)
|
|
|
|
{:ok, %{"categories" => tags}}
|
2023-06-11 22:22:17 +02:00
|
|
|
|
|
|
|
_ ->
|
|
|
|
{:error, :insufficient_scope}
|
2023-06-11 08:57:22 +02:00
|
|
|
end
|
2023-06-08 09:06:16 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
defp verify_token(access_token) do
|
2023-06-11 08:57:22 +02:00
|
|
|
Enum.reduce_while([&verify_app_token/1, &verify_micropub_token/1], nil, fn fun, _result ->
|
2023-06-08 09:06:16 +02:00
|
|
|
case fun.(access_token) do
|
|
|
|
:ok -> {:halt, :ok}
|
|
|
|
error -> {:cont, error}
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp verify_micropub_token(access_token) do
|
|
|
|
Token.verify(access_token, "create", get_hostname())
|
|
|
|
end
|
|
|
|
|
|
|
|
defp verify_app_token(access_token) do
|
|
|
|
token = Chiya.Accounts.get_app_token("obsidian", "app")
|
|
|
|
|
|
|
|
if not is_nil(token) do
|
|
|
|
token_string =
|
|
|
|
token.token
|
|
|
|
|> :crypto.bytes_to_integer()
|
|
|
|
|> to_string()
|
|
|
|
|
|
|
|
if token_string == access_token do
|
|
|
|
:ok
|
|
|
|
else
|
|
|
|
{:error, :insufficient_scope, "Could not verify app token"}
|
|
|
|
end
|
|
|
|
else
|
|
|
|
{:error, :insufficient_scope, "Could not verify app token"}
|
|
|
|
end
|
2023-05-31 21:42:55 +02:00
|
|
|
end
|
2023-05-25 21:23:04 +02:00
|
|
|
|
2023-06-19 22:23:55 +02:00
|
|
|
defp get_attrs(type, post_type, properties, default_channel_id) do
|
2023-05-31 22:15:39 +02:00
|
|
|
Logger.info("Creating a #{type}/#{post_type}..")
|
|
|
|
|
2023-06-19 22:23:55 +02:00
|
|
|
channel = Chiya.Channels.get_channel(default_channel_id)
|
|
|
|
|
2023-05-31 21:32:34 +02:00
|
|
|
case post_type do
|
2023-06-19 22:23:55 +02:00
|
|
|
:note -> get_note_attrs(properties, channel)
|
2023-05-31 21:32:34 +02:00
|
|
|
_ -> {:error, :insufficient_scope}
|
|
|
|
end
|
2023-05-28 22:59:11 +02:00
|
|
|
end
|
|
|
|
|
2023-06-12 18:50:00 +02:00
|
|
|
defp get_note_attrs(p, default_channel) do
|
2023-05-31 21:32:34 +02:00
|
|
|
content = Props.get_content(p)
|
2023-06-20 23:08:55 +02:00
|
|
|
name = Props.get_title(p) || Chiya.Notes.Note.note_title(content)
|
2023-05-31 21:32:34 +02:00
|
|
|
tags = Props.get_tags(p) |> Enum.join(",")
|
|
|
|
|
2023-06-01 23:22:16 +02:00
|
|
|
published_at =
|
|
|
|
if Props.is_published?(p),
|
|
|
|
do: NaiveDateTime.local_now(),
|
|
|
|
else: nil
|
|
|
|
|
2023-06-19 22:23:55 +02:00
|
|
|
attrs = %{
|
|
|
|
content: content,
|
|
|
|
name: name,
|
|
|
|
tags_string: tags,
|
|
|
|
published_at: published_at
|
|
|
|
}
|
|
|
|
|
|
|
|
attrs =
|
|
|
|
if default_channel,
|
|
|
|
do: Map.put(attrs, :channel, default_channel),
|
|
|
|
else: attrs
|
2023-06-12 18:50:00 +02:00
|
|
|
|
2023-06-19 22:23:55 +02:00
|
|
|
{:ok, attrs}
|
2023-05-25 21:23:04 +02:00
|
|
|
end
|
2023-05-28 22:59:11 +02:00
|
|
|
|
|
|
|
defp get_hostname(),
|
|
|
|
do: URI.parse(ChiyaWeb.Endpoint.url()).host
|
2023-05-25 21:23:04 +02:00
|
|
|
end
|