feat(blog): allow ROLE_IMPORTER to create drafts and access review queue

This commit is contained in:
hamed
2026-07-24 20:19:25 +03:30
parent c729bb13e0
commit 1a9eda3576
3 changed files with 72 additions and 8 deletions
+25 -6
View File
@@ -205,7 +205,7 @@ class BlogController extends BaseController
#[OA\Post(
path: '/api/v1/blog',
summary: 'Create a new blog post (admin only)',
summary: 'Create a new blog post (ROLE_ADMIN, or ROLE_IMPORTER as a pending-review draft)',
security: [['bearerAuth' => []]],
requestBody: new OA\RequestBody(
required: true,
@@ -239,7 +239,7 @@ class BlogController extends BaseController
)
),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden — admin role required'),
new OA\Response(response: 403, description: 'Forbidden — ROLE_ADMIN or ROLE_IMPORTER required'),
new OA\Response(
response: 422,
description: 'Validation error',
@@ -261,10 +261,17 @@ class BlogController extends BaseController
),
]
)]
#[IsGranted('ROLE_ADMIN')]
#[Route('/api/v1/blog', methods: ['POST'])]
public function create(Request $request, #[CurrentUser] User $user): JsonResponse
{
// ROLE_IMPORTER هم مجاز است: خط تولید محتوا (content/ در کرالر) با همان کاربر
// سیستمیِ ایمپورت لاگین می‌کند و پیش‌نویس می‌سازد. پیش‌نویس هیچ‌وقت منتشر
// نمی‌شود — تأیید (review) همچنان فقط ROLE_ADMIN است.
$isAdmin = $this->isGranted('ROLE_ADMIN');
if (!$isAdmin && !$this->isGranted('ROLE_IMPORTER')) {
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'دسترسی به این منبع مجاز نیست', 403);
}
$data = json_decode($request->getContent(), true) ?? [];
if ((isset($data['title']) && !is_string($data['title'])) || (isset($data['body']) && !is_string($data['body']))) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'title و body باید رشته باشند', 422);
@@ -296,6 +303,13 @@ class BlogController extends BaseController
// review_status: null means "manual admin post". The pipeline sends
// "pending_review" so the post enters the doctor review queue.
if (!empty($data['review_status'])) $blog->setReviewStatus($data['review_status']);
// ROLE_IMPORTER هرگز نباید بتواند مستقیم منتشر کند: هرچه در بدنه بفرستد،
// خروجی‌اش پیش‌نویسِ در انتظار بررسی است. بدون این، مجازکردن کرالر روی این
// اندپوینت یعنی دور زدن کاملِ گیتِ تأیید پزشک.
if (!$isAdmin) {
$blog->setStatus(Blog::STATUS_DRAFT);
$blog->setReviewStatus(Blog::REVIEW_PENDING);
}
// نبودِ city_id یعنی سراسری — پس همیشه اعمال می‌شود، نه فقط وقتی مقدار دارد.
$blog->setCity($this->resolveCity($data['city_id'] ?? null));
$this->blogWriter->applySeoFields($blog, $data);
@@ -386,7 +400,7 @@ class BlogController extends BaseController
#[OA\Get(
path: '/api/v1/admin/blog/review-queue',
summary: 'List blog drafts awaiting doctor review (admin only)',
summary: 'List blog drafts awaiting doctor review (ROLE_ADMIN or ROLE_IMPORTER)',
security: [['bearerAuth' => []]],
parameters: [
new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
@@ -395,13 +409,18 @@ class BlogController extends BaseController
responses: [
new OA\Response(response: 200, description: 'Paginated review queue'),
new OA\Response(response: 401, description: 'Unauthorized'),
new OA\Response(response: 403, description: 'Forbidden — admin role required'),
new OA\Response(response: 403, description: 'Forbidden — ROLE_ADMIN or ROLE_IMPORTER required'),
]
)]
#[IsGranted('ROLE_ADMIN')]
#[Route('/api/v1/admin/blog/review-queue', methods: ['GET'])]
public function reviewQueue(Request $request): JsonResponse
{
// خواندنی است، پس ROLE_IMPORTER هم مجاز است تا پنل کرالر صف را نشان دهد.
// تصمیم تأیید/رد (review) همچنان فقط ROLE_ADMIN.
if (!$this->isGranted('ROLE_ADMIN') && !$this->isGranted('ROLE_IMPORTER')) {
return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'دسترسی به این منبع مجاز نیست', 403);
}
$page = max(1, (int) $request->query->get('page', 1));
$limit = min(50, max(1, (int) $request->query->get('limit', 20)));