An IndieWeb engine for a self-hostable website. https://koype.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

syndication.ex 3.6KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. # Koype: a IndieWeb-focused, single-tenant website engine for people.
  2. #
  3. # Copyright © 2019 Jacky Alciné <jacky.is@black.af>
  4. #
  5. # This file belongs to the Koype project.
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU Affero General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU Affero General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Affero General Public License
  18. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  19. defmodule IndieWeb.Syndication do
  20. require Logger
  21. import Ecto.Query, only: [where: 2, order_by: 2, from: 2]
  22. @spec use_count_of(any(), atom() | binary()) :: integer()
  23. def use_count_of(target, status \\ :all)
  24. def use_count_of(target, :all) do
  25. from(r in Koype.Repo.Syndication.Result, select: r.target_id, where: r.target_id == ^target.id)
  26. |> Koype.Repo.count()
  27. end
  28. def use_count_of(target, status) do
  29. from(r in Koype.Repo.Syndication.Result,
  30. select: r.target_id,
  31. where: r.target_id == ^target.id and r.status == ^status
  32. )
  33. |> Koype.Repo.count()
  34. end
  35. @spec targets(atom()) :: list()
  36. def targets(state \\ :enabled) do
  37. enabled = state == :enabled
  38. Koype.Repo.Syndication.Target
  39. |> where(enabled: ^enabled)
  40. |> order_by(desc: :updated_at)
  41. |> Koype.Repo.all()
  42. end
  43. def syndicate(nil, _), do: {:error, :no_syndication_target_specified}
  44. def syndicate(_, nil), do: {:error, :no_syndication_content_specified}
  45. def syndicate(%Koype.Repo.Syndication.Target{} = target, %Koype.Repo.Entry{} = entry) do
  46. Koype.Job.create(Koype.Job.Micropub.Syndicate, target: target, source: entry)
  47. end
  48. # TODO: This approach to syndication has an expectation around Webmentions.
  49. @spec syndicate(any(), any()) :: {:ok, any()} | {:error, any()}
  50. def syndicate!(%Koype.Repo.Syndication.Target{} = target, %Koype.Repo.Entry{} = entry) do
  51. entry_uri = Koype.Repo.Entry.get_uri(entry)
  52. with(
  53. {:ok, syndication_result} <- Koype.Repo.Syndication.Result.create(%{target_id: target.id, status: "sent"}),
  54. {:ok, model} <-
  55. entry
  56. |> Ecto.build_assoc(:syndication_results, %{syndication_result_id: syndication_result.id, entry_id: entry.id})
  57. |> Koype.Repo.insert(),
  58. result <- Koype.Repo.preload(model, :syndication_result),
  59. {:ok, resp} <- Koype.Webmention.send!(source: entry_uri, target: target.endpoint),
  60. {:ok, updated_result} <- Koype.Repo.Syndication.Result.update(result.syndication_result, %{status: "processed"})
  61. ) do
  62. Logger.info("Sent request to syndication target #{target.endpoint} / '#{target.name}' for #{entry_uri}")
  63. # TODO: Check the response type.
  64. case Jason.decode(resp.body) do
  65. {:ok, %{"url" => url}} ->
  66. Koype.Repo.Syndication.Result.update(updated_result, %{status: "completed", result: url})
  67. error ->
  68. Koype.Repo.Syndication.Result.update(updated_result, %{status: "completed"})
  69. error
  70. end
  71. else
  72. {:ok, %Koype.Http.Response{body: body} = resp} ->
  73. Logger.info(fn -> "Server failed to accept syndication webmention sent to #{target.endpoint}: #{body}" end)
  74. {:error, resp}
  75. {:error, issue} = err ->
  76. Logger.warn("Failed to send syndication webmention to #{target.endpoint}: #{inspect(issue)}")
  77. err
  78. end
  79. end
  80. end