2023-07-17 20:22:58 +02:00
|
|
|
defmodule ChiyaWeb.Indie.Micropub do
|
|
|
|
require Logger
|
|
|
|
|
|
|
|
alias ChiyaWeb.Indie.Properties, as: Props
|
|
|
|
alias ChiyaWeb.Indie.Token
|
|
|
|
|
2023-07-17 22:23:58 +02:00
|
|
|
def create_note(type, properties) do
|
|
|
|
settings = Chiya.Site.get_settings()
|
|
|
|
|
2023-08-02 20:48:41 +02:00
|
|
|
with {:ok, note_attrs} <- get_create_attrs(type, properties, settings),
|
2023-07-17 20:22:58 +02:00
|
|
|
{:ok, note} <- Chiya.Notes.create_note(note_attrs) do
|
2023-07-19 23:10:44 +02:00
|
|
|
create_photos(note, properties)
|
2023-07-17 20:22:58 +02:00
|
|
|
|
2023-07-24 18:39:39 +02:00
|
|
|
Logger.info("Note created!")
|
2023-07-17 20:22:58 +02:00
|
|
|
{: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
|
|
|
|
|
2023-07-24 22:31:10 +02:00
|
|
|
def update_note(note, replace, add, delete) do
|
2023-08-22 00:02:09 +02:00
|
|
|
Logger.info("Updating note..")
|
|
|
|
|
2023-07-24 22:31:10 +02:00
|
|
|
with {:ok, note_attrs} <- get_update_attrs(replace, add, delete),
|
2023-07-24 18:39:39 +02:00
|
|
|
{:ok, note} <- Chiya.Notes.update_note(note, note_attrs) do
|
|
|
|
Logger.info("Note updated!")
|
|
|
|
{:ok, note}
|
|
|
|
else
|
|
|
|
error ->
|
|
|
|
Logger.error("Error occurred while creating note from micropub:")
|
|
|
|
Logger.error(inspect(error))
|
|
|
|
|
|
|
|
{:error, :invalid_request}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-08-21 23:54:29 +02:00
|
|
|
def find_note(note_url) do
|
2023-08-22 00:02:09 +02:00
|
|
|
Logger.info("Looking up note by url #{note_url}")
|
|
|
|
|
2023-08-22 00:07:23 +02:00
|
|
|
case Chiya.Notes.Note.note_slug(note_url) do
|
|
|
|
{:ok, slug} ->
|
|
|
|
Logger.info("Found note with slug #{slug}, fetching note.")
|
|
|
|
note = Chiya.Notes.get_note_by_slug_preloaded(slug)
|
|
|
|
|
|
|
|
if is_nil(note) do
|
|
|
|
Logger.error("Note with #{note_url} was not found.")
|
|
|
|
{:error, :invalid_request}
|
|
|
|
else
|
|
|
|
Logger.info("Note found!")
|
|
|
|
{:ok, note}
|
|
|
|
end
|
2023-08-22 00:02:09 +02:00
|
|
|
|
2023-08-22 00:07:23 +02:00
|
|
|
_ ->
|
2023-08-22 00:02:09 +02:00
|
|
|
Logger.error("Note with #{note_url} was not found.")
|
|
|
|
{:error, :invalid_request}
|
2023-08-21 23:54:29 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-07-19 23:10:44 +02:00
|
|
|
defp create_photos(note, properties) do
|
|
|
|
properties
|
|
|
|
|> Props.get_photos()
|
|
|
|
|> Enum.with_index()
|
|
|
|
|> Enum.each(fn {photo, index} ->
|
|
|
|
featured = index == 0
|
|
|
|
|
|
|
|
Chiya.Notes.create_note_image(%{
|
|
|
|
note_id: note.id,
|
|
|
|
path: photo.path,
|
|
|
|
featured: featured
|
|
|
|
})
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2023-07-17 20:22:58 +02:00
|
|
|
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
|
|
|
|
|
2023-08-02 20:48:41 +02:00
|
|
|
defp get_create_attrs(type, properties, settings) do
|
2023-07-19 23:10:44 +02:00
|
|
|
{:ok, post_type} = Props.get_post_type(properties)
|
2023-07-17 20:22:58 +02:00
|
|
|
Logger.info("Creating a #{type}/#{post_type}..")
|
|
|
|
|
|
|
|
case post_type do
|
2023-08-02 20:48:41 +02:00
|
|
|
:note -> get_note_attrs(properties, settings.micropub_channel_id)
|
|
|
|
:bookmark -> get_bookmark_attrs(properties, settings.bookmark_channel_id)
|
2023-07-17 20:22:58 +02:00
|
|
|
_ -> {:error, :insufficient_scope}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-07-24 22:31:10 +02:00
|
|
|
defp get_update_attrs(replace, add, _delete) do
|
|
|
|
replace_attrs = put_update_attrs(replace)
|
|
|
|
add_attrs = put_update_attrs(add)
|
|
|
|
|
|
|
|
attrs =
|
|
|
|
%{}
|
|
|
|
|> Enum.into(replace_attrs)
|
|
|
|
|> Enum.into(add_attrs)
|
|
|
|
|
2023-08-21 19:54:30 +02:00
|
|
|
Logger.info("Update attributes: #{inspect(attrs)}")
|
|
|
|
|
2023-07-24 22:31:10 +02:00
|
|
|
{:ok, attrs}
|
|
|
|
end
|
|
|
|
|
|
|
|
defp put_update_attrs(source) do
|
|
|
|
name = Props.get_title(source)
|
|
|
|
attrs = %{}
|
|
|
|
|
|
|
|
attrs =
|
|
|
|
if name do
|
|
|
|
Map.put(attrs, :name, name)
|
|
|
|
else
|
|
|
|
attrs
|
|
|
|
end
|
|
|
|
|
|
|
|
content = Props.get_content(source)
|
|
|
|
|
|
|
|
attrs =
|
|
|
|
if content do
|
|
|
|
Map.put(attrs, :content, content)
|
|
|
|
else
|
|
|
|
attrs
|
|
|
|
end
|
|
|
|
|
|
|
|
tags = Props.get_tags(source)
|
|
|
|
|
|
|
|
attrs =
|
2023-09-13 23:09:19 +02:00
|
|
|
if Enum.empty?(tags) do
|
|
|
|
attrs
|
|
|
|
else
|
2023-07-24 22:31:10 +02:00
|
|
|
tags_string = Enum.join(tags, ",")
|
|
|
|
Map.put(attrs, :tags_string, tags_string)
|
|
|
|
end
|
|
|
|
|
|
|
|
attrs
|
|
|
|
end
|
|
|
|
|
2023-07-17 20:22:58 +02:00
|
|
|
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")
|
|
|
|
|
2023-09-13 23:09:19 +02:00
|
|
|
if is_nil(token) do
|
|
|
|
{:error, :insufficient_scope, "Could not verify app token"}
|
|
|
|
else
|
2023-07-17 20:22:58 +02:00
|
|
|
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
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-08-02 20:48:41 +02:00
|
|
|
defp get_note_attrs(properties, channel_id) do
|
2023-07-19 23:10:44 +02:00
|
|
|
attrs =
|
|
|
|
properties
|
|
|
|
|> get_base_attrs()
|
2023-08-02 20:48:41 +02:00
|
|
|
|> get_channel(channel_id)
|
2023-07-19 23:10:44 +02:00
|
|
|
|
2023-08-21 19:54:30 +02:00
|
|
|
Logger.info("Note attributes: #{inspect(attrs)}")
|
|
|
|
|
2023-07-19 23:10:44 +02:00
|
|
|
{:ok, attrs}
|
|
|
|
end
|
|
|
|
|
2023-08-02 20:48:41 +02:00
|
|
|
defp get_bookmark_attrs(properties, channel_id) do
|
2023-07-19 23:10:44 +02:00
|
|
|
url = Props.get_bookmarked_url(properties)
|
|
|
|
|
|
|
|
attrs =
|
|
|
|
properties
|
|
|
|
|> get_base_attrs()
|
2023-08-02 20:48:41 +02:00
|
|
|
|> get_channel(channel_id)
|
2023-07-19 23:10:44 +02:00
|
|
|
|> Map.put_new(:url, url)
|
|
|
|
|> Map.put_new(:kind, :bookmark)
|
|
|
|
|
2023-08-21 19:54:30 +02:00
|
|
|
Logger.info("Bookmark attributes: #{inspect(attrs)}")
|
|
|
|
|
2023-07-19 23:10:44 +02:00
|
|
|
{:ok, attrs}
|
|
|
|
end
|
|
|
|
|
|
|
|
defp get_base_attrs(properties) do
|
|
|
|
content = Props.get_content(properties)
|
|
|
|
name = Props.get_title(properties) || Chiya.Notes.Note.note_title(content)
|
|
|
|
tags = Props.get_tags(properties) |> Enum.join(",")
|
2023-07-17 20:22:58 +02:00
|
|
|
|
|
|
|
published_at =
|
2023-07-19 23:10:44 +02:00
|
|
|
if Props.is_published?(properties),
|
2023-07-17 20:22:58 +02:00
|
|
|
do: NaiveDateTime.local_now(),
|
|
|
|
else: nil
|
|
|
|
|
|
|
|
attrs = %{
|
|
|
|
content: content,
|
|
|
|
name: name,
|
|
|
|
tags_string: tags,
|
|
|
|
published_at: published_at
|
|
|
|
}
|
|
|
|
|
2023-08-21 19:54:30 +02:00
|
|
|
Logger.info("Base attributes: #{inspect(attrs)}")
|
|
|
|
|
2023-07-19 23:10:44 +02:00
|
|
|
attrs
|
|
|
|
end
|
2023-07-17 20:22:58 +02:00
|
|
|
|
2023-08-02 20:48:41 +02:00
|
|
|
def get_channel(attrs, channel_id) do
|
|
|
|
if channel_id,
|
|
|
|
do: Map.put(attrs, :channels, [Chiya.Channels.get_channel(channel_id)]),
|
2023-07-19 23:10:44 +02:00
|
|
|
else: attrs
|
2023-07-17 20:22:58 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
defp get_hostname(),
|
|
|
|
do: URI.parse(ChiyaWeb.Endpoint.url()).host
|
|
|
|
end
|