feat(blog): add admin endpoint for blog details and cache invalidation

- Implemented `adminDetail()` method in `BlogController` to retrieve blog posts of any status for admin editing.
- Introduced `BlogCacheInvalidator` service to handle cache invalidation after blog create/update/delete actions.
- Updated existing methods in `BlogController` and `RepresentationBlogController` to call cache invalidation on blog modifications.
- Enhanced `BlogFormPage` and `RepresentationBlogFormPage` to utilize the new admin endpoint for fetching blog data.
- Added tests for `BlogCacheInvalidator` to ensure proper functionality and error handling.
- Updated documentation to reflect new API endpoint and cache invalidation behavior.
This commit is contained in:
hamed
2026-07-27 18:54:35 +03:30
parent e4edaea9b8
commit 15abcb5c8a
12 changed files with 972 additions and 47 deletions
+55
View File
@@ -0,0 +1,55 @@
<?php
namespace App\Blog\Service;
use App\Blog\Entity\Blog;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
/**
* پس از هر نوشتن روی بلاگ، کش ISR سایت عمومی (nobat724_front) را باطل می‌کند.
*
* سایت پاسخ `GET /api/v1/blog/{slug}` را با `revalidate: 3600` و tag ذخیره
* می‌کند؛ بدون این فراخوانی، تغییر پنل ادمین تا یک ساعت روی سایت دیده نمی‌شود.
*
* قرارداد: `POST {webhookUrl}` با هدر `X-Revalidate-Secret` و بدنهٔ
* `{"tags": ["blog-<slug>", "blog-<uuid>", "blog-list"]}`.
*
* fail-open: شکست شبکه فقط لاگ می‌شود و هرگز ذخیرهٔ مقاله را نمی‌شکند.
*/
class BlogCacheInvalidator
{
public function __construct(
private readonly HttpClientInterface $httpClient,
private readonly LoggerInterface $logger,
private readonly string $webhookUrl, // خالی = سایت عمومی پیکربندی نشده
private readonly string $webhookSecret,
) {}
public function invalidate(Blog $blog): void
{
if ($this->webhookUrl === '' || $this->webhookSecret === '') {
return;
}
// مقادیر قبل از هر تغییر بعدی برداشته می‌شوند تا حذف مقاله هم قابل باطل‌سازی باشد.
$tags = array_values(array_filter([
$blog->getSlug() !== '' ? 'blog-' . $blog->getSlug() : null,
'blog-' . $blog->getUuid(),
'blog-list',
]));
try {
$this->httpClient->request('POST', $this->webhookUrl, [
'json' => ['tags' => $tags],
'headers' => ['X-Revalidate-Secret' => $this->webhookSecret],
'timeout' => 3,
])->getStatusCode();
} catch (\Throwable $e) {
$this->logger->warning('blog cache invalidation failed', [
'uuid' => $blog->getUuid(),
'error' => $e->getMessage(),
]);
}
}
}