Browse Source

feat(job): Implement for Webmention and syndication.

jackyalcine 9 months ago
parent
commit
0e2dcfbbba
Signed by: Jacky Alciné <yo@jacky.wtf> GPG Key ID: 537A4F904B15268D

+ 1
- 0
.env.example View File

@@ -17,3 +17,4 @@ SESSION_SIGNING_SALT=ee8f193b0060c10d680a70250f8b5cb2c99af0c40e2503659e2cb7d397a
17 17
 ENV=dev
18 18
 UID=1000
19 19
 LOGGER_LEVEL=debug
20
+REDIS_URL=redis://cache:6379/toniq

+ 2
- 3
config/config.exs View File

@@ -1,8 +1,5 @@
1 1
 use Mix.Config
2 2
 
3
-config :sasl,
4
-  sasl_error_logger: false
5
-
6 3
 config :koype,
7 4
   domain: %{
8 5
     host: {:system, :string, "CANONICAL_HOST", "localhost"},
@@ -82,4 +79,6 @@ config :koype, :phoenix_swagger,
82 79
 
83 80
 config :liquid, error_mode: :strict
84 81
 
82
+config :toniq, redis_url: "redis://cache:6379/0"
83
+
85 84
 import_config "#{Mix.env()}.exs"

+ 1
- 1
config/test.exs View File

@@ -9,7 +9,7 @@ config :koype, Koype.Web.Endpoint,
9 9
   server: true,
10 10
   http: [port: {:system, :string, "TEST_PORT", 5001}]
11 11
 
12
-config :logger, level: :info
12
+config :logger, level: :debug
13 13
 
14 14
 config :exvcr,
15 15
   vcr_cassette_library_dir: "test/fixtures/vcr_cassettes",

+ 17
- 11
docker-compose.yml View File

@@ -51,6 +51,10 @@ services:
51 51
       - "9000:9000"
52 52
     networks:
53 53
       - network
54
+  cache:
55
+    image: "redis:5-alpine"
56
+    networks:
57
+      - network
54 58
   mc:
55 59
     image: "minio/mc"
56 60
     command: ls
@@ -71,25 +75,27 @@ services:
71 75
     environment:
72 76
       CANONICAL_HOST: ${CANONICAL_HOST}
73 77
       CANONICAL_SCHEME: ${CANONICAL_SCHEME}
74
-      GUARDIAN_SECRET_KEY: ${GUARDIAN_SECRET_KEY}
75
-      MIX_ENV: dev
76 78
       ENV: dev
77
-      PORT: ${PORT}
78
-      TEST_PORT: ${TEST_PORT}
79
-      TEST_HOST: ${TEST_HOST}
79
+      GUARDIAN_SECRET_KEY: ${GUARDIAN_SECRET_KEY}
80 80
       LOGGER_LEVEL: ${LOGGER_LEVEL}
81
-      SECRET_KEY_BASE: ${SECRET_KEY_BASE}
82
-      SESSION_SIGNING_SALT: ${SESSION_SIGNING_SALT}
81
+      MIX_ENV: dev
83 82
       OBJECT_STORAGE_ACCESS_KEY: ${OBJECT_STORAGE_ACCESS_KEY}
84
-      OBJECT_STORAGE_SECRET_KEY: ${OBJECT_STORAGE_SECRET_KEY}
85
-      OBJECT_STORAGE_HOST: ${OBJECT_STORAGE_HOST}
86
-      OBJECT_STORAGE_SCHEME: ${OBJECT_STORAGE_SCHEME}
83
+      OBJECT_STORAGE_ASSET_HOST: ${OBJECT_STORAGE_ASSET_HOST}
87 84
       OBJECT_STORAGE_BUCKET: ${OBJECT_STORAGE_BUCKET}
85
+      OBJECT_STORAGE_HOST: ${OBJECT_STORAGE_HOST}
88 86
       OBJECT_STORAGE_PORT: ${OBJECT_STORAGE_PORT}
89
-      OBJECT_STORAGE_ASSET_HOST: ${OBJECT_STORAGE_ASSET_HOST}
87
+      OBJECT_STORAGE_SCHEME: ${OBJECT_STORAGE_SCHEME}
88
+      OBJECT_STORAGE_SECRET_KEY: ${OBJECT_STORAGE_SECRET_KEY}
89
+      PORT: ${PORT}
90
+      REDIS_URL: ${REDIS_URL}
91
+      SECRET_KEY_BASE: ${SECRET_KEY_BASE}
92
+      SESSION_SIGNING_SALT: ${SESSION_SIGNING_SALT}
93
+      TEST_HOST: ${TEST_HOST}
94
+      TEST_PORT: ${TEST_PORT}
90 95
       UID: ${UID}
91 96
     links:
92 97
       - objectstorage
98
+      - cache
93 99
     ports:
94 100
       - "5000:5000"
95 101
     volumes:

+ 1
- 1
lib/application.ex View File

@@ -45,8 +45,8 @@ defmodule Koype.Application do
45 45
       :cowboy,
46 46
       :ex_aws,
47 47
       :hound,
48
-      :koype,
49 48
       :logger,
49
+      :koype,
50 50
       :phoenix
51 51
     ]
52 52
     |> Enum.each(&Confex.resolve_env!/1)

+ 6
- 3
lib/indieweb/syndication.ex View File

@@ -46,10 +46,12 @@ defmodule IndieWeb.Syndication do
46 46
     |> Koype.Repo.all()
47 47
   end
48 48
 
49
-  # TODO: Refactor to allow for any model, not just entries.
50
-  # TODO: Avoid using Webmention logic here.
51
-  @spec syndicate(any(), any()) :: {:ok, any()} | {:error, any()}
52 49
   def syndicate(%Koype.Repo.Syndication.Target{} = target, %Koype.Repo.Entry{} = entry) do
50
+    Koype.Job.create(Koype.Job.Micropub.Syndicate, target: target, source: entry)
51
+  end
52
+
53
+  @spec syndicate(any(), any()) :: {:ok, any()} | {:error, any()}
54
+  def syndicate!(%Koype.Repo.Syndication.Target{} = target, %Koype.Repo.Entry{} = entry) do
53 55
     entry_uri = Koype.Repo.Entry.get_uri(entry)
54 56
     body = %{source: entry_uri, target: target.endpoint}
55 57
 
@@ -77,6 +79,7 @@ defmodule IndieWeb.Syndication do
77 79
           Koype.Repo.Syndication.Result.update(updated_result, %{status: "completed", result: url})
78 80
 
79 81
         error ->
82
+          Koype.Repo.Syndication.Result.update(updated_result, %{status: "completed"})
80 83
           error
81 84
       end
82 85
     else

+ 18
- 13
lib/indieweb/webmention.ex View File

@@ -88,10 +88,14 @@ defmodule IndieWeb.Webmention do
88 88
 
89 89
   def send(args)
90 90
   # FIXME: Move into background worker (GenServer)
91
-  def send(source: source, target: target) do
91
+  def send([source: source, target: target] = args) do
92
+    Koype.Job.create(Koype.Job.Webmention.Send, args)
93
+  end
94
+
95
+  def send!([source: source, target: target] = args) do
92 96
     with(
93 97
       {:ok, webmention_endpoint} <- discover_webmention_endpoint(target),
94
-      :ok <- do_send(webmention_endpoint, source: source, target: target)
98
+      {:ok, resp} <- dispatch_http_request(webmention_endpoint, source: source, target: target)
95 99
     ) do
96 100
       Logger.debug("Webmention for #{source} to #{target} sent via #{webmention_endpoint}!")
97 101
       :ok
@@ -113,14 +117,18 @@ defmodule IndieWeb.Webmention do
113 117
     end
114 118
   end
115 119
 
116
-  def queue(args) do
120
+  def receive(args) do
121
+    Koype.Job.create(Koype.Job.Webmention.Receive, args)
122
+  end
123
+
124
+  def receive!(args) do
117 125
     with(
118 126
       :ok <- do_validate_scheme(args[:source]),
119 127
       :ok <- do_validate_scheme(args[:target]),
120 128
       :ok <- do_prevent_self_sending(args),
121 129
       {:ok, model} <- resolve_target(args[:target])
122 130
     ) do
123
-      Logger.info("Webmention info checks out. Beginning ingestion...")
131
+      Logger.debug("Webmention passed preliminary checks.")
124 132
       do_create_webmention_job(args ++ [model: model])
125 133
     else
126 134
       {:error, _} = error -> error
@@ -228,10 +236,9 @@ defmodule IndieWeb.Webmention do
228 236
     end
229 237
   end
230 238
 
231
-  # FIXME: Add retry logic on non {2,3}xx status codes.
232 239
   # TODO: Send path for callback information.
233 240
   # TODO: Save information about sending this Webmention.
234
-  defp do_send(endpoint, args) do
241
+  defp dispatch_http_request(endpoint, args) do
235 242
     body = %{source: args[:source], target: args[:target]}
236 243
 
237 244
     case Koype.Http.post(
@@ -241,19 +248,17 @@ defmodule IndieWeb.Webmention do
241 248
              "Content-Type" => "application/x-www-form-urlencoded"
242 249
            }
243 250
          ) do
244
-      {:ok, %Koype.Http.Response{code: code}} when code >= 200 and code < 300 ->
245
-        Logger.info(fn -> "Webmention sent to #{endpoint} for #{inspect(args)} successfully." end)
246
-        :ok
251
+      {:ok, %Koype.Http.Response{code: code, body: body, headers: headers}} when code >= 200 and code < 300 ->
252
+        Logger.debug(fn -> "Webmention sent to #{endpoint} for #{inspect(args)} successfully." end)
253
+        {:ok, body: body, headers: headers}
247 254
 
248 255
       {:ok, %Koype.Http.Response{body: body, code: code}} when code >= 400 and code < 599 ->
249
-        # TODO: Insert re-try logic here.
250 256
         Logger.info(fn -> "Server failed to accept webmention sent to #{endpoint} for #{inspect(args)}: #{body}" end)
251
-        {:error, body}
257
+        {:error, reason: :not_ok, raw: body}
252 258
 
253 259
       {:error, issue} = err ->
254
-        # TODO: Insert re-try logic here.
255 260
         Logger.warn("Failed to send webmention to #{endpoint} for #{inspect(args)}: #{inspect(issue)}")
256
-        err
261
+        {:error, reason: :unknown, raw: issue}
257 262
     end
258 263
   end
259 264
 end

+ 56
- 0
lib/job.ex View File

@@ -0,0 +1,56 @@
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 Koype.Job do
20
+  @moduledoc """
21
+  Facilities for running and handling jobs.
22
+
23
+  This module allows you to treat jobs as a resource in a CRUD-y fashion.
24
+
25
+  """
26
+  defstruct id: UUID.uuid1(), name: "", args: []
27
+  @type t :: %__MODULE__{id: binary(), name: binary(), args: keyword()}
28
+
29
+  require Logger
30
+
31
+  defprotocol Executor do
32
+    def execute(args \\ [])
33
+    def name(args \\ [])
34
+  end
35
+
36
+  defmodule Worker do
37
+    use Toniq.Worker, max_concurrency: 5
38
+
39
+    # TODO: Do exception handling around this.
40
+    def perform(kind: kind, args: args, job: job) do
41
+      Logger.info("Starting job #{job.name()}##{job.id}...")
42
+      kind.execute(args)
43
+      Logger.info("Completed job #{job.name()}##{job.id}.")
44
+    end
45
+  end
46
+
47
+  @spec create(Executor.t(), keyword()) :: {:ok, __MODULE__.t()}
48
+  def create(job_kind, args) do
49
+    job = %__MODULE__{args: args, name: job_kind.name(args)}
50
+    Logger.info("Queuing job #{job.name()}##{job.id}...")
51
+
52
+    task_args = [kind: job_kind, args: args, job: job]
53
+    Toniq.enqueue(Koype.Job.Worker, task_args)
54
+    job
55
+  end
56
+end

+ 31
- 0
lib/job/micropub.ex View File

@@ -0,0 +1,31 @@
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 Koype.Job.Micropub do
20
+  defmodule Syndicate do
21
+    @derive [Koype.Job]
22
+
23
+    def name(_), do: "micropub.syndicate"
24
+
25
+    def execute(target: target, source: entry) do
26
+      IndieWeb.Syndication.syndicate!(target, entry)
27
+    end
28
+
29
+    def execute(_), do: {:error, message: :invalid_args}
30
+  end
31
+end

+ 43
- 0
lib/job/webmention.ex View File

@@ -0,0 +1,43 @@
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 Koype.Job.Webmention do
20
+  defmodule Receive do
21
+    @derive [Koype.Job]
22
+
23
+    def name(_), do: "webmention.receive"
24
+
25
+    def execute(args) do
26
+      IndieWeb.Webmention.receive!(args)
27
+    end
28
+
29
+    def execute(_), do: {:error, message: :invalid_args}
30
+  end
31
+
32
+  defmodule Send do
33
+    @derive [Koype.Job]
34
+
35
+    def name(_), do: "webmention.send"
36
+
37
+    def execute(args) do
38
+      IndieWeb.Webmention.send!(args)
39
+    end
40
+
41
+    def execute(_), do: {:error, message: :invalid_args}
42
+  end
43
+end

+ 1
- 0
mix.exs View File

@@ -142,6 +142,7 @@ defmodule Koype.Mixfile do
142 142
       {:sobelow, "~> 0.7.1"},
143 143
       {:sqlite_ecto2, "~> 2.2.5"},
144 144
       {:sweet_xml, "~> 0.6"},
145
+      {:toniq, "~> 1.0"},
145 146
       {:totpex, "~> 0.1.2"},
146 147
       {:uuid, "~> 1.1"}
147 148
     ]

+ 3
- 0
mix.lock View File

@@ -23,6 +23,7 @@
23 23
   "double": {:hex, :double, "0.6.5", "a9c9a580901a09d3c811878889197f0e7ef4d8c22b9a3ed1aec139cac6d24559", [:mix], [], "hexpm"},
24 24
   "earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm"},
25 25
   "ecto": {:hex, :ecto, "2.2.9", "031d55df9bb430cb118e6f3026a87408d9ce9638737bda3871e5d727a3594aae", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
26
+  "eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm"},
26 27
   "erlex": {:hex, :erlex, "0.1.6", "c01c889363168d3fdd23f4211647d8a34c0f9a21ec726762312e08e083f3d47e", [:mix], [], "hexpm"},
27 28
   "esqlite": {:hex, :esqlite, "0.2.5", "cab6d87aeb5f33d848b9bb8a21129e9512ea608f930d4c63576942d8f7d72218", [:rebar3], [], "hexpm"},
28 29
   "eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm"},
@@ -41,6 +42,7 @@
41 42
   "excoveralls": {:hex, :excoveralls, "0.10.4", "b86230f0978bbc630c139af5066af7cd74fd16536f71bc047d1037091f9f63a9", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
42 43
   "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"},
43 44
   "explode": {:hex, :explode, "1.0.0", "e9b9acae7a7fe15027a8813b19b8cd424d8747903428ed4ae13c1460aaaa2934", [:mix], [{:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:poison, "~> 2.1 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
45
+  "exredis": {:hex, :exredis, "0.3.0", "2530d9d8d5a98c5bc80bf7a14e15440a88abd6457618682f10851e39ebdd518b", [:mix], [{:eredis, ">= 1.0.8", [hex: :eredis, repo: "hexpm", optional: false]}], "hexpm"},
44 46
   "exvcr": {:hex, :exvcr, "0.10.3", "1ae3b97560430acfa88ebc737c85b2b7a9dbacd8a2b26789a19718b51ae3522c", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
45 47
   "faker": {:hex, :faker, "0.11.2", "c2251639f7418f54159fabcacfa0949f467ae77e9046001a8f091455d46905dd", [:mix], [], "hexpm"},
46 48
   "file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [:mix], [], "hexpm"},
@@ -107,6 +109,7 @@
107 109
   "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
108 110
   "sweet_xml": {:hex, :sweet_xml, "0.6.5", "dd9cde443212b505d1b5f9758feb2000e66a14d3c449f04c572f3048c66e6697", [:mix], [], "hexpm"},
109 111
   "timex": {:hex, :timex, "3.4.2", "d74649c93ad0e12ce5b17cf5e11fbd1fb1b24a3d114643e86dba194b64439547", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
112
+  "toniq": {:hex, :toniq, "1.2.3", "e80ecafb0aaf131d74cdb7b39bb4de263585b012c59d263ec9f2769df30398a5", [:mix], [{:exredis, ">= 0.1.1", [hex: :exredis, repo: "hexpm", optional: false]}, {:uuid, "~> 1.0", [hex: :uuid, repo: "hexpm", optional: false]}], "hexpm"},
110 113
   "totpex": {:hex, :totpex, "0.1.3", "ae022eab70e8e230a0a65300b0d0dd67f510e0b7243c0d608a827c7b22ca6b51", [:mix], [], "hexpm"},
111 114
   "tzdata": {:hex, :tzdata, "0.5.19", "7962a3997bf06303b7d1772988ede22260f3dae1bf897408ebdac2b4435f4e6a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
112 115
   "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},

+ 1
- 1
priv/themes/default/_footer.html.liquid View File

@@ -24,7 +24,7 @@
24 24
       </li>
25 25
       {% if signed_in %}
26 26
         <li class="lh-copy mb1">
27
-          <a href="{{ route settings.view }}" class="moon-gray link">
27
+          <a href="{% route settings.view %}" class="moon-gray link">
28 28
             <i class="h1 w1 pa1 v-mid" data-feather="settings"></i>
29 29
             <span class="v-mid underline-hover">Settings</span>
30 30
           </a>

+ 1
- 0
priv/themes/default/entry/view/response.html.liquid View File

@@ -31,6 +31,7 @@
31 31
     </div>
32 32
     <form method="post" action="{% route indie_webmention.incoming %}" class="w-100 dark-gray mt2 flex flex-row flex-wrap measure-l">
33 33
       <input type=hidden name=target value="{{ entry.uri }}" />
34
+      <input type=hidden name=_format value="html" />
34 35
       <label class="lh-copy gray f6 pb2 db w-100 tl" for="source">Enter your response URI below!</label>
35 36
       <input name="source" placeholder="https://yoursite.here/response" type="url" class="mb2 ma0-ns code input-reset ba b--black-20 pa2 db w-100 w-75-ns center f6" id="manualWebmentionId">
36 37
       <button class="pointer input-reset pa2 w-100 center mt1 mt0-ns w-25-ns v-mid ba b--black bg-near-black near-white">

+ 4
- 10
web/controllers/indie/webmention_controller.ex View File

@@ -21,16 +21,10 @@ defmodule Koype.Web.Indie.WebmentionController do
21 21
 
22 22
   def incoming(conn, params)
23 23
 
24
-  # TODO: Render a different page depending on the format requested by the
25
-  # client.
24
+  # TODO: Render differently depending on format requested.
26 25
   def incoming(conn, %{"source" => source, "target" => target}) do
27
-    case IndieWeb.Webmention.queue(source: source, target: target) do
28
-      {:ok, _} ->
29
-        conn |> put_status(:accepted) |> text("ok")
30
-
31
-      {:error, err} ->
32
-        Explode.internal_server_error(conn, err)
33
-    end
26
+    IndieWeb.Webmention.receive(source: source, target: target)
27
+    conn |> put_status(:accepted) |> render(:ok)
34 28
   end
35 29
 
36 30
   def incoming(conn, _) do
@@ -38,7 +32,7 @@ defmodule Koype.Web.Indie.WebmentionController do
38 32
   end
39 33
 
40 34
   def manual_send(conn, %{"source" => source, "target" => target} = params) do
41
-    case IndieWeb.Webmention.send(source: source, target: target) do
35
+    case IndieWeb.Webmention.send!(source: source, target: target) do
42 36
       {:error, error} ->
43 37
         conn
44 38
         |> put_flash(:error, "Failed to queue sending of Webmention: #{error}")

+ 37
- 0
web/controllers/job_controller.ex View File

@@ -0,0 +1,37 @@
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 Koype.Web.JobController do
20
+  use Koype.Web, :controller
21
+
22
+  def index(conn, _params) do
23
+    render(conn, "index.html")
24
+  end
25
+
26
+  def change_state(conn, params)
27
+
28
+  def change_state(conn, %{"state" => "stop", "id" => job_id}) do
29
+  end
30
+
31
+  def change_state(conn, %{"state" => "retry", "id" => job_id}) do
32
+  end
33
+
34
+  def change_state(conn, _state) do
35
+    Explode.bad_request(conn)
36
+  end
37
+end

+ 8
- 2
web/router.ex View File

@@ -20,7 +20,7 @@ defmodule Koype.Web.Router do
20 20
   use Koype.Web, :router
21 21
 
22 22
   pipeline :browser do
23
-    plug(:accepts, ["html", "json"])
23
+    plug(:accepts, ["html", "json", "form"])
24 24
     plug(:fetch_session)
25 25
     plug(:fetch_flash)
26 26
     plug(:put_secure_browser_headers)
@@ -30,7 +30,9 @@ defmodule Koype.Web.Router do
30 30
 
31 31
   pipeline :api do
32 32
     plug(:fetch_session)
33
-    plug(:accepts, ["json", "form"])
33
+    plug(:fetch_flash)
34
+    plug(:put_secure_browser_headers)
35
+    plug(:accepts, ["json", "form", "html"])
34 36
   end
35 37
 
36 38
   pipeline(:owner, do: plug(Koype.Web.Plug.Guardian.Owner))
@@ -82,6 +84,10 @@ defmodule Koype.Web.Router do
82 84
 
83 85
     get("/settings/", SettingsController, :view)
84 86
     post("/settings/", SettingsController, :apply)
87
+
88
+    get("/jobs/", JobController, :index)
89
+    get("/job/:job_id", JobController, :view)
90
+    patch("/job/:job_id", JobController, :change_state)
85 91
   end
86 92
 
87 93
   scope "/~/indie", Koype.Web do

BIN
web/static/images/ouch/pluto-downloading.png View File


+ 5
- 0
web/templates/indie/webmention/ok.html.eex View File

@@ -0,0 +1,5 @@
1
+<section class="flex-auto pa2 flex-grow w-100 h-100 items-center justify-center self-center">
2
+  <h1 class="h2 dark-gray lh-title mt1 measure">Off We Go!</h1>
3
+  <p class="lh-copy gray f4 measure">Your Webmention was queued up! Let's see what I think of it.</p>
4
+  <p class="lh-copy gray f5 measure-narrow">Go to the <a href="<%= @conn.params["source"] %>" class="link underline blue">original page</a>.</p>
5
+</section>

+ 22
- 0
web/templates/job/index.html.eex View File

@@ -0,0 +1,22 @@
1
+<section class="mw8 mw-100-l w-100 w-90-l center">
2
+  <div class="center mw7 w-100">
3
+    <img src="<%= asset_path_for(:core, "/images/ouch/pluto-downloading.png") %>"
4
+        class="mw5 w-100 w-auto-l mr2-l h-auto fl-l db center mv0-l"
5
+        alt="Image of person floating in space with a rocket to the top right" />
6
+    <h2 class="f3 f2-l tc tl-l lh-title tracked-tight">Jobs</h2>
7
+    <p class="lh-copy f5 measure-wide gray">
8
+      View all of the concurrent tasks Koype's taking at the moment.
9
+    </p>
10
+  </div>
11
+  <table class="w-100 center mv2 mb4">
12
+    <thead>
13
+      <tr>
14
+        <th class="tc tl-l pv2 bb b--black-20">ID</th>
15
+        <th class="tc tl-l pv2 bb b--black-20">Kind</th>
16
+        <th class="tc tl-l pv2 bb b--black-20">Status</th>
17
+        <th class="tc tl-l pv2 bb b--black-20">Run Time</th>
18
+      </tr>
19
+    </thead>
20
+  </table>
21
+</section>
22
+<script async defer src="<%= asset_path_for(:core, "/js/jobs.js") %>"></script>

+ 1
- 1
web/templates/layout/app.html.eex View File

@@ -1,7 +1,7 @@
1 1
 <!DOCTYPE html>
2 2
 <html lang="en" data-nojs>
3 3
   <head>
4
-    <%= render Koype.Web.LayoutView, "_meta.html", Map.put(assigns, :view_module, @view_module) %>
4
+    <%= render Koype.Web.LayoutView, "_meta.html", Map.put(assigns, "view_module", @view_module) %>
5 5
   </head>
6 6
   <body class="<%= css_class_names(@conn) %>" data-koype-template="<%= get_current_template(assigns) %>">
7 7
     <%= render Koype.Web.LayoutView, "_header.html", Map.put(assigns, :view_module, @view_module) %>

+ 2
- 0
web/views/indie/webmention_view.ex View File

@@ -26,4 +26,6 @@ defmodule Koype.Web.Indie.WebmentionView do
26 26
   def title("send.html", _assigns), do: "Manually Send a Webmention"
27 27
   @doc false
28 28
   def title("index.html", _assigns), do: "All Webmentions"
29
+  @doc false
30
+  def title("ok.html", _assigns), do: "Webmention Queued for Processing"
29 31
 end

+ 26
- 0
web/views/jobs_view.ex View File

@@ -0,0 +1,26 @@
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 Koype.Web.JobView do
20
+  use Koype.Web, :view
21
+
22
+  def title("index.html", _), do: "Jobs"
23
+
24
+  def tags(_, _, _), do: []
25
+  def tag(_, _, _), do: nil
26
+end

Loading…
Cancel
Save