Skip to content

Commit 0e5c460

Browse files
committed
feat: process queue from panel
1 parent 01b8db0 commit 0e5c460

File tree

13 files changed

+477
-53
lines changed

13 files changed

+477
-53
lines changed

composer.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

index.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

index.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
@include_once __DIR__ . '/vendor/autoload.php';
99

1010
Kirby::plugin('mauricerenck/indieConnector', [
11+
'api' => require_once __DIR__ . '/plugin/api.php',
1112
'hooks' => require_once __DIR__ . '/plugin/hooks.php',
1213
'areas' => require_once __DIR__ . '/plugin/areas.php',
1314
'snippets' => [

lib/QueueHandler.php

Lines changed: 81 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
namespace mauricerenck\IndieConnector;
34

45
use Exception;
@@ -45,52 +46,101 @@ public function queueWebmention(string $sourceUrl, string $targetUrl)
4546

4647
public function processQueue(int $limit = 0)
4748
{
48-
$limitQuery = $limit > 0 ? ' LIMIT ' . $limit : '';
49-
$queue = $this->indieDb->select(
50-
'queue',
51-
['id', 'sourceUrl', 'targetUrl', 'retries'],
52-
'WHERE queueStatus = "queued" OR queueStatus = "error"' . $limitQuery
53-
);
49+
$queue = $this->getQueuedItems($limit);
5450

5551
if (empty($queue)) {
5652
return;
5753
}
5854

55+
$processedItems = [];
5956
foreach ($queue as $mention) {
60-
$sourceUrl = $mention->sourceUrl();
61-
$targetUrl = $mention->targetUrl();
62-
$mentionId = $mention->id();
63-
$retries = $mention->retries()->toInt();
57+
$processedItems[] = $this->processQueueItem($mention);
58+
}
59+
60+
return $processedItems;
61+
}
62+
63+
public function getQueuedItems(int $limit = 0, bool $includeFailed = false)
64+
{
65+
$limitQuery = $limit > 0 ? ' LIMIT ' . $limit : '';
66+
$failedQuery = $includeFailed ? ' OR queueStatus = "failed"' : '';
67+
return $this->indieDb->select(
68+
'queue',
69+
['id', 'sourceUrl', 'targetUrl', 'retries', 'queueStatus', 'processLog'],
70+
'WHERE queueStatus = "queued" OR queueStatus = "error"' . $failedQuery . $limitQuery
71+
);
72+
}
73+
74+
public function getAndProcessQueuedItem(string $id)
75+
{
76+
$mention = $this->indieDb->select(
77+
'queue',
78+
['id', 'sourceUrl', 'targetUrl', 'retries', 'queueStatus'],
79+
'WHERE id = "' . $id . '"'
80+
)->first();
81+
82+
if (empty($mention)) {
83+
return ['id' => $id, 'queueStatus' => 'confusion', 'processLog' => 'Entry not found', 'retries' => 0];
84+
}
85+
86+
return $this->processQueueItem($mention);
87+
}
88+
89+
public function processQueueItem($mention)
90+
{
91+
$sourceUrl = $mention->sourceUrl();
92+
$targetUrl = $mention->targetUrl();
93+
$mentionId = $mention->id();
94+
$retries = 0;
95+
96+
if (!is_null($mention->retries())) {
97+
$retries = is_string($mention->retries()) ? (int)$mention->retries() : $mention->retries()->toInt();
98+
}
99+
100+
if ($retries >= $this->retries) {
101+
$this->indieDb->update(
102+
'queue',
103+
['queueStatus', 'processLog'],
104+
['failed', 'max retries reached'],
105+
'WHERE id = "' . $mentionId . '"'
106+
);
107+
108+
return ['id' => $mentionId, 'queueStatus' => 'failed', 'processLog' => 'max retries reached', 'retries' => $retries];
109+
}
64110

65-
if ($retries >= $this->retries) {
111+
$result = $this->receiver->processWebmention($sourceUrl, $targetUrl);
112+
113+
switch ($result['status']) {
114+
case 'success':
115+
$this->indieDb->delete('queue', 'WHERE id = "' . $mentionId . '"');
116+
return ['id' => $mentionId, 'queueStatus' => 'success', 'processLog' => 'done', 'retries' => $retries];
117+
118+
case 'error':
66119
$this->indieDb->update(
67120
'queue',
68-
['queueStatus', 'processLog'],
69-
['failed', 'max retries reached'],
121+
['queueStatus', 'processLog', 'retries'],
122+
['error', $result['message'], $retries + 1],
70123
'WHERE id = "' . $mentionId . '"'
71124
);
72125

73-
continue;
74-
}
75-
76-
$result = $this->receiver->processWebmention($sourceUrl, $targetUrl);
126+
return ['id' => $mentionId, 'queueStatus' => 'error', 'processLog' => $result['message'], 'retries' => $retries + 1];
127+
}
77128

78-
switch ($result['status']) {
79-
case 'success':
80-
$this->indieDb->delete('queue', 'WHERE id = "' . $mentionId . '"');
81-
break;
129+
return ['id' => $mentionId, 'queueStatus' => $result['status'], 'processLog' => $result['message'], 'retries' => $retries + 1];
130+
}
82131

83-
case 'error':
84-
$this->indieDb->update(
85-
'queue',
86-
['queueStatus', 'processLog', 'retries'],
87-
['error', $result['message'], $retries + 1],
88-
'WHERE id = "' . $mentionId . '"'
89-
);
132+
public function deleteQueueItem(string $id)
133+
{
134+
return $this->indieDb->delete('queue', 'WHERE id = "' . $id . '"');
135+
}
90136

91-
break;
92-
}
93-
return $result;
137+
public function cleanQueue(string $status)
138+
{
139+
$acceptableStatus = ['queued', 'error', 'failed'];
140+
if (!in_array($status, $acceptableStatus) || empty($status)) {
141+
return [];
94142
}
143+
144+
return $this->indieDb->delete('queue', 'WHERE queueStatus = "' . $status . '"');
95145
}
96146
}

plugin/api.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace mauricerenck\IndieConnector;
4+
5+
use Kirby\Http\Response;
6+
7+
return [
8+
'routes' => [
9+
[
10+
'pattern' => 'indieconnector/queue/processItem/(:any)',
11+
'method' => 'POST',
12+
'action' => function (string $id) {
13+
$queueHandler = new QueueHandler();
14+
$response = $queueHandler->getAndProcessQueuedItem($id);
15+
16+
return new Response(json_encode($response), 'application/json');
17+
},
18+
],
19+
20+
[
21+
'pattern' => 'indieconnector/queue/process',
22+
'method' => 'POST',
23+
'action' => function () {
24+
$postBody = kirby()->request()->data();
25+
$queueHandler = new QueueHandler();
26+
27+
$results = [];
28+
foreach ($postBody as $item) {
29+
$response = $queueHandler->getAndProcessQueuedItem($item);
30+
$results[] = $response;
31+
}
32+
33+
return new Response(json_encode($results), 'application/json');
34+
},
35+
],
36+
],
37+
];

plugin/areas.php

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
}
88

99
return [
10-
'webmentions' => function ($kirby) {
10+
'webmentions' => function () {
1111
return [
1212
'label' => 'Webmentions',
1313
'icon' => 'live',
@@ -17,6 +17,11 @@
1717
[
1818
'pattern' => ['webmentions', 'webmentions/(:any)/(:any)'],
1919
'action' => function ($year = null, $month = null) {
20+
21+
$queueHandler = new QueueHandler();
22+
$queuedItems = $queueHandler->getQueuedItems(limit: 0, includeFailed: true);
23+
$itemsInQueue = $queuedItems->count();
24+
2025
if (is_null($year) || is_null($month)) {
2126
$timestamp = time();
2227
$year = date('Y', $timestamp);
@@ -56,6 +61,7 @@
5661
'targets' => [],
5762
'sources' => [],
5863
'sent' => [],
64+
'itemsInQueue' => $itemsInQueue ?? 0
5965
],
6066
];
6167
}
@@ -79,11 +85,65 @@
7985
'targets' => $targets,
8086
'sources' => $sources,
8187
'sent' => $sent,
88+
'itemsInQueue' => $itemsInQueue ?? 0
8289
],
8390
];
8491
},
8592
],
93+
[
94+
'pattern' => ['webmentions/queue'],
95+
'action' => function () {
96+
97+
$queueHandler = new QueueHandler();
98+
$queuedItems = $queueHandler->getQueuedItems(limit: 0, includeFailed: true);
99+
$itemsInQueue = $queuedItems->count();
100+
101+
return [
102+
'component' => 'k-webmentions-queue-view',
103+
'title' => 'Test',
104+
'props' => [
105+
'disabled' => !$queueHandler->queueEnabled(),
106+
'itemsInQueue' => $itemsInQueue ?? 0,
107+
'queuedItems' => $queuedItems->toArray(),
108+
],
109+
];
110+
}
111+
],
86112
],
113+
'dialogs' => [
114+
'queue/delete/(:any)' => [
115+
'load' => function () {
116+
return [
117+
'component' => 'k-remove-dialog',
118+
'props' => [
119+
'text' => 'Do you really want to delete this item?'
120+
]
121+
];
122+
},
123+
'submit' => function (string $id) {
124+
$queueHandler = new QueueHandler();
125+
$result = $queueHandler->deleteQueueItem($id);
126+
127+
return $result;
128+
}
129+
],
130+
'queue/clean/(:any)' => [
131+
'load' => function (string $status) {
132+
return [
133+
'component' => 'k-remove-dialog',
134+
'props' => [
135+
'text' => 'This will delete all entries with the status <strong>' . $status . '</strong>. Do you really want to proceed?'
136+
]
137+
];
138+
},
139+
'submit' => function (string $status) {
140+
$queueHandler = new QueueHandler();
141+
$result = $queueHandler->cleanQueue($status);
142+
143+
return $result;
144+
}
145+
],
146+
]
87147
];
88148
},
89149
];

src/components/Queue.vue

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<template>
2+
<k-inside>
3+
<div class="k-webmentions-queue-view">
4+
<k-header>IndieConnector</k-header>
5+
<k-tabs
6+
tab="queue"
7+
:tabs="[
8+
{ name: 'webmentions', label: 'Webmentions', link: '/webmentions' },
9+
{ name: 'queue', label: 'Queue', link: '/webmentions/queue', badge: itemsInQueue }
10+
]"
11+
theme='warning'
12+
13+
/>
14+
15+
<k-info-field v-if="disabled" label="Queue disabled" text="The queue feature is disabled. Configure it in your config.php" />
16+
17+
<QueueList :queuedItems="queuedItems" />
18+
</div>
19+
</k-inside>
20+
</template>
21+
22+
<script>
23+
export default {
24+
props: {
25+
disabled: Boolean,
26+
queuedItems: Object,
27+
itemsInQueue: Number
28+
},
29+
30+
methods: {
31+
32+
},
33+
}
34+
</script>
35+
36+
<style lang="scss">
37+
.k-webmentions-queue-view {
38+
.wrapper {
39+
background: #fff;
40+
box-shadow: var(--box-shadow-item);
41+
padding: 10px 20px;
42+
margin-top: var(--spacing-6);
43+
}
44+
45+
.muted {
46+
color: var(--color-gray-600);
47+
}
48+
49+
h2 {
50+
font-size: var(--text-3xl);
51+
margin: 2em 0 1em 0;
52+
}
53+
54+
.bottom-margin {
55+
margin-bottom: var(--spacing-6);
56+
}
57+
58+
.status {
59+
border: 1px solid var(--color-gray-400);
60+
background-color: var(--color-gray-200);
61+
62+
border-radius: var(--rounded-md);
63+
padding: var(--spacing-1) var(--spacing-2);
64+
65+
&.error {
66+
border: 1px solid var(--color-red-400);
67+
background-color: var(--color-red-200);
68+
}
69+
70+
&.running {
71+
border: 1px solid var(--color-blue-400);
72+
background-color: var(--color-blue-200);
73+
}
74+
75+
&.failed {
76+
border: 1px solid var(--color-red-600);
77+
background-color: var(--color-red-400);
78+
}
79+
80+
&.success {
81+
border: 1px solid var(--color-green-400);
82+
background-color: var(--color-green-200);
83+
}
84+
}
85+
}
86+
</style>

0 commit comments

Comments
 (0)