Skip to content

Commit

Permalink
feat: process queue from panel
Browse files Browse the repository at this point in the history
  • Loading branch information
mauricerenck committed Nov 23, 2024
1 parent 01b8db0 commit 0e5c460
Show file tree
Hide file tree
Showing 13 changed files with 477 additions and 53 deletions.
2 changes: 1 addition & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion index.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion index.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
@include_once __DIR__ . '/vendor/autoload.php';

Kirby::plugin('mauricerenck/indieConnector', [
'api' => require_once __DIR__ . '/plugin/api.php',
'hooks' => require_once __DIR__ . '/plugin/hooks.php',
'areas' => require_once __DIR__ . '/plugin/areas.php',
'snippets' => [
Expand Down
112 changes: 81 additions & 31 deletions lib/QueueHandler.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

namespace mauricerenck\IndieConnector;

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

public function processQueue(int $limit = 0)
{
$limitQuery = $limit > 0 ? ' LIMIT ' . $limit : '';
$queue = $this->indieDb->select(
'queue',
['id', 'sourceUrl', 'targetUrl', 'retries'],
'WHERE queueStatus = "queued" OR queueStatus = "error"' . $limitQuery
);
$queue = $this->getQueuedItems($limit);

if (empty($queue)) {
return;
}

$processedItems = [];
foreach ($queue as $mention) {
$sourceUrl = $mention->sourceUrl();
$targetUrl = $mention->targetUrl();
$mentionId = $mention->id();
$retries = $mention->retries()->toInt();
$processedItems[] = $this->processQueueItem($mention);
}

return $processedItems;
}

public function getQueuedItems(int $limit = 0, bool $includeFailed = false)
{
$limitQuery = $limit > 0 ? ' LIMIT ' . $limit : '';
$failedQuery = $includeFailed ? ' OR queueStatus = "failed"' : '';
return $this->indieDb->select(
'queue',
['id', 'sourceUrl', 'targetUrl', 'retries', 'queueStatus', 'processLog'],
'WHERE queueStatus = "queued" OR queueStatus = "error"' . $failedQuery . $limitQuery
);
}

public function getAndProcessQueuedItem(string $id)
{
$mention = $this->indieDb->select(
'queue',
['id', 'sourceUrl', 'targetUrl', 'retries', 'queueStatus'],
'WHERE id = "' . $id . '"'
)->first();

if (empty($mention)) {
return ['id' => $id, 'queueStatus' => 'confusion', 'processLog' => 'Entry not found', 'retries' => 0];
}

return $this->processQueueItem($mention);
}

public function processQueueItem($mention)
{
$sourceUrl = $mention->sourceUrl();
$targetUrl = $mention->targetUrl();
$mentionId = $mention->id();
$retries = 0;

if (!is_null($mention->retries())) {
$retries = is_string($mention->retries()) ? (int)$mention->retries() : $mention->retries()->toInt();
}

if ($retries >= $this->retries) {
$this->indieDb->update(
'queue',
['queueStatus', 'processLog'],
['failed', 'max retries reached'],
'WHERE id = "' . $mentionId . '"'
);

return ['id' => $mentionId, 'queueStatus' => 'failed', 'processLog' => 'max retries reached', 'retries' => $retries];
}

if ($retries >= $this->retries) {
$result = $this->receiver->processWebmention($sourceUrl, $targetUrl);

switch ($result['status']) {
case 'success':
$this->indieDb->delete('queue', 'WHERE id = "' . $mentionId . '"');
return ['id' => $mentionId, 'queueStatus' => 'success', 'processLog' => 'done', 'retries' => $retries];

case 'error':
$this->indieDb->update(
'queue',
['queueStatus', 'processLog'],
['failed', 'max retries reached'],
['queueStatus', 'processLog', 'retries'],
['error', $result['message'], $retries + 1],
'WHERE id = "' . $mentionId . '"'
);

continue;
}

$result = $this->receiver->processWebmention($sourceUrl, $targetUrl);
return ['id' => $mentionId, 'queueStatus' => 'error', 'processLog' => $result['message'], 'retries' => $retries + 1];
}

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

case 'error':
$this->indieDb->update(
'queue',
['queueStatus', 'processLog', 'retries'],
['error', $result['message'], $retries + 1],
'WHERE id = "' . $mentionId . '"'
);
public function deleteQueueItem(string $id)
{
return $this->indieDb->delete('queue', 'WHERE id = "' . $id . '"');
}

break;
}
return $result;
public function cleanQueue(string $status)
{
$acceptableStatus = ['queued', 'error', 'failed'];
if (!in_array($status, $acceptableStatus) || empty($status)) {
return [];
}

return $this->indieDb->delete('queue', 'WHERE queueStatus = "' . $status . '"');
}
}
37 changes: 37 additions & 0 deletions plugin/api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace mauricerenck\IndieConnector;

use Kirby\Http\Response;

return [
'routes' => [
[
'pattern' => 'indieconnector/queue/processItem/(:any)',
'method' => 'POST',
'action' => function (string $id) {
$queueHandler = new QueueHandler();
$response = $queueHandler->getAndProcessQueuedItem($id);

return new Response(json_encode($response), 'application/json');
},
],

[
'pattern' => 'indieconnector/queue/process',
'method' => 'POST',
'action' => function () {
$postBody = kirby()->request()->data();
$queueHandler = new QueueHandler();

$results = [];
foreach ($postBody as $item) {
$response = $queueHandler->getAndProcessQueuedItem($item);
$results[] = $response;
}

return new Response(json_encode($results), 'application/json');
},
],
],
];
62 changes: 61 additions & 1 deletion plugin/areas.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
}

return [
'webmentions' => function ($kirby) {
'webmentions' => function () {
return [
'label' => 'Webmentions',
'icon' => 'live',
Expand All @@ -17,6 +17,11 @@
[
'pattern' => ['webmentions', 'webmentions/(:any)/(:any)'],
'action' => function ($year = null, $month = null) {

$queueHandler = new QueueHandler();
$queuedItems = $queueHandler->getQueuedItems(limit: 0, includeFailed: true);
$itemsInQueue = $queuedItems->count();

if (is_null($year) || is_null($month)) {
$timestamp = time();
$year = date('Y', $timestamp);
Expand Down Expand Up @@ -56,6 +61,7 @@
'targets' => [],
'sources' => [],
'sent' => [],
'itemsInQueue' => $itemsInQueue ?? 0
],
];
}
Expand All @@ -79,11 +85,65 @@
'targets' => $targets,
'sources' => $sources,
'sent' => $sent,
'itemsInQueue' => $itemsInQueue ?? 0
],
];
},
],
[
'pattern' => ['webmentions/queue'],
'action' => function () {

$queueHandler = new QueueHandler();
$queuedItems = $queueHandler->getQueuedItems(limit: 0, includeFailed: true);
$itemsInQueue = $queuedItems->count();

return [
'component' => 'k-webmentions-queue-view',
'title' => 'Test',
'props' => [
'disabled' => !$queueHandler->queueEnabled(),
'itemsInQueue' => $itemsInQueue ?? 0,
'queuedItems' => $queuedItems->toArray(),
],
];
}
],
],
'dialogs' => [
'queue/delete/(:any)' => [
'load' => function () {
return [
'component' => 'k-remove-dialog',
'props' => [
'text' => 'Do you really want to delete this item?'
]
];
},
'submit' => function (string $id) {
$queueHandler = new QueueHandler();
$result = $queueHandler->deleteQueueItem($id);

return $result;
}
],
'queue/clean/(:any)' => [
'load' => function (string $status) {
return [
'component' => 'k-remove-dialog',
'props' => [
'text' => 'This will delete all entries with the status <strong>' . $status . '</strong>. Do you really want to proceed?'
]
];
},
'submit' => function (string $status) {
$queueHandler = new QueueHandler();
$result = $queueHandler->cleanQueue($status);

return $result;
}
],
]
];
},
];
86 changes: 86 additions & 0 deletions src/components/Queue.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<template>
<k-inside>
<div class="k-webmentions-queue-view">
<k-header>IndieConnector</k-header>
<k-tabs
tab="queue"
:tabs="[
{ name: 'webmentions', label: 'Webmentions', link: '/webmentions' },
{ name: 'queue', label: 'Queue', link: '/webmentions/queue', badge: itemsInQueue }
]"
theme='warning'

/>

<k-info-field v-if="disabled" label="Queue disabled" text="The queue feature is disabled. Configure it in your config.php" />

<QueueList :queuedItems="queuedItems" />
</div>
</k-inside>
</template>

<script>
export default {
props: {
disabled: Boolean,
queuedItems: Object,
itemsInQueue: Number
},
methods: {
},
}
</script>

<style lang="scss">
.k-webmentions-queue-view {
.wrapper {
background: #fff;
box-shadow: var(--box-shadow-item);
padding: 10px 20px;
margin-top: var(--spacing-6);
}
.muted {
color: var(--color-gray-600);
}
h2 {
font-size: var(--text-3xl);
margin: 2em 0 1em 0;
}
.bottom-margin {
margin-bottom: var(--spacing-6);
}
.status {
border: 1px solid var(--color-gray-400);
background-color: var(--color-gray-200);
border-radius: var(--rounded-md);
padding: var(--spacing-1) var(--spacing-2);
&.error {
border: 1px solid var(--color-red-400);
background-color: var(--color-red-200);
}
&.running {
border: 1px solid var(--color-blue-400);
background-color: var(--color-blue-200);
}
&.failed {
border: 1px solid var(--color-red-600);
background-color: var(--color-red-400);
}
&.success {
border: 1px solid var(--color-green-400);
background-color: var(--color-green-200);
}
}
}
</style>
Loading

0 comments on commit 0e5c460

Please sign in to comment.