Store checked links in bundle metadata for the long term and cache for the middle term
This commit is contained in:
parent
7e53a39638
commit
11fc9262bf
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user