From 45e3fc7b68bd7b313a11209b7685f5a1aa7d0f2a Mon Sep 17 00:00:00 2001 From: Inhji Date: Mon, 17 Jul 2023 20:22:58 +0200 Subject: [PATCH] refactor micropub_handler --- lib/chiya_web/indie/micropub.ex | 117 +++++++++++++++++++++++ lib/chiya_web/indie/micropub_handler.ex | 120 +++--------------------- test/chiya_web/indie/micropub_test.exs | 18 ++++ 3 files changed, 147 insertions(+), 108 deletions(-) create mode 100644 lib/chiya_web/indie/micropub.ex create mode 100644 test/chiya_web/indie/micropub_test.exs diff --git a/lib/chiya_web/indie/micropub.ex b/lib/chiya_web/indie/micropub.ex new file mode 100644 index 0000000..5c87ddc --- /dev/null +++ b/lib/chiya_web/indie/micropub.ex @@ -0,0 +1,117 @@ +defmodule ChiyaWeb.Indie.Micropub do + require Logger + + alias ChiyaWeb.Indie.Properties, as: Props + alias ChiyaWeb.Indie.Token + + def create_note(type, properties, channel_id) do + with {:ok, post_type} <- Props.get_post_type(properties), + {:ok, note_attrs} <- get_attrs(type, post_type, properties, channel_id), + {:ok, note} <- Chiya.Notes.create_note(note_attrs) do + Logger.info("Note created!") + + # TODO: Make separate function for this + note_attrs + |> Props.get_photos() + |> Enum.map(fn photo -> + Chiya.Notes.create_note_image(%{ + note_id: note.id, + path: photo.path + }) + end) + |> Enum.each(fn result -> + Logger.info("Photo created!") + Logger.info(inspect(result)) + end) + + {:ok, :created, Chiya.Notes.Note.note_url(note)} + else + error -> + Logger.error("Error occurred while creating note from micropub:") + Logger.error(inspect(error)) + + {:error, :invalid_request} + end + end + + def verify_token(access_token) do + Enum.reduce_while( + [ + &verify_app_token/1, + &verify_micropub_token/1 + ], + nil, + fn fun, _result -> + case fun.(access_token) do + :ok -> {:halt, :ok} + error -> {:cont, error} + end + end + ) + end + + defp get_attrs(type, post_type, properties, default_channel_id) do + Logger.info("Creating a #{type}/#{post_type}..") + + channel = + if default_channel_id, + do: Chiya.Channels.get_channel(default_channel_id), + else: nil + + case post_type do + :note -> get_note_attrs(properties, channel) + _ -> {:error, :insufficient_scope} + 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 + end + + defp get_note_attrs(p, default_channel) do + content = Props.get_content(p) + name = Props.get_title(p) || Chiya.Notes.Note.note_title(content) + tags = Props.get_tags(p) |> Enum.join(",") + + published_at = + if Props.is_published?(p), + do: NaiveDateTime.local_now(), + else: nil + + 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 + + {:ok, attrs} + end + + defp get_hostname(), + do: URI.parse(ChiyaWeb.Endpoint.url()).host +end diff --git a/lib/chiya_web/indie/micropub_handler.ex b/lib/chiya_web/indie/micropub_handler.ex index 47fc4c1..3dc09e7 100644 --- a/lib/chiya_web/indie/micropub_handler.ex +++ b/lib/chiya_web/indie/micropub_handler.ex @@ -6,8 +6,7 @@ defmodule ChiyaWeb.Indie.MicropubHandler do endpoint: ChiyaWeb.Endpoint, router: ChiyaWeb.Router - alias ChiyaWeb.Indie.Properties, as: Props - alias ChiyaWeb.Indie.Token + alias ChiyaWeb.Indie.Micropub @default_properties [ "name", @@ -25,35 +24,11 @@ defmodule ChiyaWeb.Indie.MicropubHandler do Logger.info("Type: #{type}") settings = Chiya.Site.get_settings() - micropub_channel_id = settings.micropub_channel_id + channel_id = settings.micropub_channel_id - with :ok <- verify_token(access_token), - {:ok, post_type} <- Props.get_post_type(properties), - {:ok, note_attrs} <- get_attrs(type, post_type, properties, micropub_channel_id), - {:ok, note} <- Chiya.Notes.create_note(note_attrs) do - Logger.info("Note created!") - - # TODO: Make separate function for this - note_attrs - |> Props.get_photos() - |> Enum.map(fn photo -> - Chiya.Notes.create_note_image(%{ - note_id: note.id, - path: photo.path - }) - end) - |> Enum.each(fn result -> - Logger.info("Photo created!") - Logger.info(inspect(result)) - end) - - {:ok, :created, Chiya.Notes.Note.note_url(note)} - else - error -> - Logger.error("Error occurred while creating note from micropub:") - Logger.error(inspect(error)) - - {:error, :invalid_request} + case Micropub.verify_token(access_token) do + :ok -> Micropub.create_note(type, properties, channel_id) + _ -> {:error, :invalid_request} end end @@ -74,9 +49,9 @@ defmodule ChiyaWeb.Indie.MicropubHandler do @impl true def handle_media(file, access_token) do - with :ok <- verify_token(access_token), + with :ok <- Micropub.verify_token(access_token), {:ok, image} <- Chiya.Notes.create_note_image_temp(%{path: file.path}) do - url = ChiyaWeb.Uploaders.UserImageTemp.url({image.path, image}, :original) + url = ChiyaWeb.Uploaders.NoteImageTemp.url({image.path, image}, :original) {:ok, url} else _ -> @@ -91,7 +66,7 @@ defmodule ChiyaWeb.Indie.MicropubHandler do do: @default_properties, else: filter_properties - with :ok <- verify_token(access_token), + with :ok <- Micropub.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 = @@ -107,7 +82,7 @@ defmodule ChiyaWeb.Indie.MicropubHandler do @impl true def handle_config_query(access_token) do - case verify_token(access_token) do + case Micropub.verify_token(access_token) do :ok -> channels = Chiya.Channels.list_channels() @@ -138,7 +113,7 @@ defmodule ChiyaWeb.Indie.MicropubHandler do @impl true def handle_syndicate_to_query(access_token) do - case verify_token(access_token) do + case Micropub.verify_token(access_token) do :ok -> {:ok, %{"syndicate-to" => []}} _ -> {:error, :insufficient_scope} end @@ -146,7 +121,7 @@ defmodule ChiyaWeb.Indie.MicropubHandler do @impl true def handle_category_query(access_token) do - case verify_token(access_token) do + case Micropub.verify_token(access_token) do :ok -> tags = Enum.map(Chiya.Tags.list_tags(), fn t -> t.name end) {:ok, %{"categories" => tags}} @@ -154,76 +129,5 @@ defmodule ChiyaWeb.Indie.MicropubHandler do _ -> {:error, :insufficient_scope} end - end - - defp verify_token(access_token) do - Enum.reduce_while([&verify_app_token/1, &verify_micropub_token/1], nil, fn fun, _result -> - 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 - end - - defp get_attrs(type, post_type, properties, default_channel_id) do - Logger.info("Creating a #{type}/#{post_type}..") - - channel = Chiya.Channels.get_channel(default_channel_id) - - case post_type do - :note -> get_note_attrs(properties, channel) - _ -> {:error, :insufficient_scope} - end - end - - defp get_note_attrs(p, default_channel) do - content = Props.get_content(p) - name = Props.get_title(p) || Chiya.Notes.Note.note_title(content) - tags = Props.get_tags(p) |> Enum.join(",") - - published_at = - if Props.is_published?(p), - do: NaiveDateTime.local_now(), - else: nil - - 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 - - {:ok, attrs} - end - - defp get_hostname(), - do: URI.parse(ChiyaWeb.Endpoint.url()).host + end end diff --git a/test/chiya_web/indie/micropub_test.exs b/test/chiya_web/indie/micropub_test.exs new file mode 100644 index 0000000..1d41a64 --- /dev/null +++ b/test/chiya_web/indie/micropub_test.exs @@ -0,0 +1,18 @@ +defmodule ChiyaWeb.MicropubTest do + use Chiya.DataCase + + alias ChiyaWeb.Indie.Micropub + + @valid_props %{ + "content" => ["this is a test"] + } + + describe "create_note/3" do + test "creates a note with valid attributes" do + assert {:ok, :created, url} = + Micropub.create_note("entry", @valid_props, nil) + + assert url =~ "this-is-a-test" + end + end +end