1
0

Store checked links in bundle metadata for the long term and cache for the middle term

This commit is contained in:
Richard Dern 2024-04-24 11:39:23 +02:00
parent 7e53a39638
commit 11fc9262bf
5 changed files with 92 additions and 38 deletions

View File

@ -19,7 +19,7 @@ class Link
private bool $isDead; private bool $isDead;
private string $reason; private ?string $reason;
private Carbon $checked; private Carbon $checked;
@ -29,7 +29,7 @@ class Link
private string $title; private string $title;
public function __construct(protected string $url, protected ?string $innerHtml = null) public function __construct(protected string $url, protected ?string $innerHtml, protected ?Bundle $bundle = null)
{ {
} }
@ -138,19 +138,57 @@ public function title(): string
return $this->title; return $this->title;
} }
public function toArray(): array
{
if ($this->bundle !== null) {
$existingData = $this->bundle->metadata('links')->get($this->url, []);
if (!empty($existingData['checked']) && Carbon::parse($existingData['checked'])->gt(now()->subMonth())) {
return $existingData;
}
}
$cacheKey = sprintf('link_%s', Str::slug($this->url));
if (Cache::has($cacheKey)) {
$data = Cache::get($cacheKey);
} else {
$data = [
'isAnchor' => $this->isAnchor(),
'isExternal' => $this->isExternal(),
'isDead' => $this->isDead(),
'reason' => $this->reason(),
'checked' => $this->checked(),
'partner' => $this->partner() ? get_class($this->partner()) : null,
'finalUrl' => $this->finalUrl(),
'title' => $this->title(),
'rel' => implode(' ', $this->fetchRel()),
'classes' => implode(' ', $this->fetchCssClasses()),
];
}
if ($this->bundle !== null) {
$this->bundle->metadata('links')->set($this->url, $data, false);
$this->bundle->metadata('links')->save();
}
Cache::put($cacheKey, $data, now()->addWeek());
return $data;
}
/** /**
* Return the link as a HtmlElement * Return the link as a HtmlElement
*/ */
public function toHtmlElement(): HtmlElement public function toHtmlElement(): HtmlElement
{ {
$rel = $this->fetchRel(); $data = $this->toArray();
$classes = $this->fetchCssClasses();
return new HtmlElement('a', [ return new HtmlElement('a', [
'href' => $this->finalUrl(), 'href' => $data['finalUrl'],
'rel' => implode(' ', $rel), 'rel' => $data['rel'],
'title' => $this->title(), 'title' => $data['title'],
'class' => implode(' ', $classes), 'class' => $data['classes'],
], $this->innerHtml); ], $this->innerHtml);
} }
@ -159,19 +197,26 @@ public function toHtmlElement(): HtmlElement
*/ */
private function fetchIsDead() private function fetchIsDead()
{ {
$cacheKey = sprintf('link_status_%s', md5($this->url)); if ($this->isAnchor()) {
$isDead = false;
$reason = null;
$checked = now();
} elseif (!$this->isExternal()) {
$isDead = false;
$reason = null;
$checked = now();
if (Cache::has($cacheKey)) { $bundle = new Bundle($this->url, Storage::disk(env('CONTENT_DISK')));
return Cache::get($cacheKey);
}
$isDead = !$bundle->exists();
} else {
$isDead = true; $isDead = true;
$reason = null; $reason = null;
$checked = now(); $checked = now();
foreach (['head', 'get'] as $method) { foreach (['head', 'get'] as $method) {
try { try {
$result = Http::{$method}($this->url); $result = Http::timeout(10)->{$method}($this->url);
if ($result->ok()) { if ($result->ok()) {
$isDead = false; $isDead = false;
@ -188,11 +233,10 @@ private function fetchIsDead()
$reason = $ex->getMessage(); $reason = $ex->getMessage();
} }
} }
}
$result = compact('isDead', 'reason', 'checked'); $result = compact('isDead', 'reason', 'checked');
Cache::put($cacheKey, $result, now()->addMonth());
return $result; return $result;
} }

View File

@ -65,7 +65,7 @@ public function get(): ?string
public function render() public function render()
{ {
$content = $this->bundle->replaceAttachmentsInMarkdown($this->content ?? ''); $content = $this->bundle->replaceAttachmentsInMarkdown($this->content ?? '');
$formatter = new Formatter($content); $formatter = new Formatter($content, $this->bundle);
$result = $formatter->render(); $result = $formatter->render();
return $result; return $result;

View File

@ -82,9 +82,13 @@ public function save(): bool
* @param mixed $key The key under which to store the value. * @param mixed $key The key under which to store the value.
* @param mixed $value The value to store. * @param mixed $value The value to store.
*/ */
public function set($key, $value) public function set($key, $value, bool $respectDots = true)
{ {
if ($respectDots) {
data_set($this->content, $key, $value, true); data_set($this->content, $key, $value, true);
} else {
$this->content[$key] = $value;
}
} }
/** /**

View File

@ -2,6 +2,7 @@
namespace App\Services\Markdown; namespace App\Services\Markdown;
use App\Classes\Bundle;
use App\Services\Markdown\Renderers\LinkRenderer; use App\Services\Markdown\Renderers\LinkRenderer;
use DOMDocument; use DOMDocument;
use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\Blade;
@ -32,7 +33,7 @@ class Formatter
* *
* @param string $source The markdown source content. * @param string $source The markdown source content.
*/ */
public function __construct(protected string $source) public function __construct(protected string $source, protected ?Bundle $bundle = null)
{ {
} }
@ -101,7 +102,7 @@ protected function prepareEnvironment(): Environment
} }
// Use a custom renderer for links // Use a custom renderer for links
$environment->addRenderer(Link::class, new LinkRenderer()); $environment->addRenderer(Link::class, new LinkRenderer($this->bundle));
return $environment; return $environment;
} }

View File

@ -2,6 +2,7 @@
namespace App\Services\Markdown\Renderers; namespace App\Services\Markdown\Renderers;
use App\Classes\Bundle;
use App\Classes\Link; use App\Classes\Link;
use League\CommonMark\Extension\CommonMark\Node\Inline\Link as CommonMarkLink; use League\CommonMark\Extension\CommonMark\Node\Inline\Link as CommonMarkLink;
use League\CommonMark\Node\Node; use League\CommonMark\Node\Node;
@ -11,6 +12,10 @@
class LinkRenderer implements NodeRendererInterface class LinkRenderer implements NodeRendererInterface
{ {
public function __construct(protected ?Bundle $bundle = null)
{
}
/** /**
* Renders a link node into an HTML element. * Renders a link node into an HTML element.
*/ */
@ -21,6 +26,6 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): H
$innerHtml = $childRenderer->renderNodes($node->children()); $innerHtml = $childRenderer->renderNodes($node->children());
$url = $node->getUrl(); $url = $node->getUrl();
return (new Link($url, $innerHtml))->toHtmlElement(); return (new Link($url, $innerHtml, $this->bundle))->toHtmlElement();
} }
} }