optimizations

This commit is contained in:
2026-03-28 19:15:39 +01:00
parent 0b25d9570a
commit cab4fbd83e
509 changed files with 1016804 additions and 1605 deletions

View File

@@ -0,0 +1,261 @@
<?php
declare(strict_types=1);
namespace App\Support\Moderation;
use App\Models\Collection;
use App\Models\CollectionComment;
use App\Models\CollectionSubmission;
use App\Models\ConversationParticipant;
use App\Models\Message;
use App\Models\NovaCard;
use App\Models\NovaCardChallenge;
use App\Models\NovaCardChallengeEntry;
use App\Models\NovaCardComment;
use App\Models\Report;
use App\Models\Story;
use App\Models\User;
use App\Services\NovaCards\NovaCardPublishModerationService;
use Illuminate\Database\Eloquent\ModelNotFoundException;
class ReportTargetResolver
{
public function __construct(
private readonly NovaCardPublishModerationService $moderation,
) {
}
/**
* @return array<int, string>
*/
public function supportedTargetTypes(): array
{
return [
'message',
'conversation',
'user',
'story',
'collection',
'collection_comment',
'collection_submission',
...$this->novaCardTargetTypes(),
];
}
/**
* @return array<int, string>
*/
public function novaCardTargetTypes(): array
{
return [
'nova_card',
'nova_card_comment',
'nova_card_challenge',
'nova_card_challenge_entry',
];
}
public function validateForReporter(User $user, string $targetType, int $targetId): void
{
switch ($targetType) {
case 'message':
$message = Message::query()->findOrFail($targetId);
$allowed = ConversationParticipant::query()
->where('conversation_id', $message->conversation_id)
->where('user_id', $user->id)
->whereNull('left_at')
->exists();
abort_unless($allowed, 403, 'You are not allowed to report this message.');
return;
case 'conversation':
$allowed = ConversationParticipant::query()
->where('conversation_id', $targetId)
->where('user_id', $user->id)
->whereNull('left_at')
->exists();
abort_unless($allowed, 403, 'You are not allowed to report this conversation.');
return;
case 'user':
User::query()->findOrFail($targetId);
return;
case 'story':
Story::query()->findOrFail($targetId);
return;
case 'collection':
$collection = Collection::query()->findOrFail($targetId);
abort_unless($collection->canBeViewedBy($user), 403, 'You are not allowed to report this collection.');
return;
case 'collection_comment':
$comment = CollectionComment::query()->with('collection')->findOrFail($targetId);
abort_unless($comment->collection && $comment->collection->canBeViewedBy($user), 403, 'You are not allowed to report this comment.');
return;
case 'collection_submission':
$submission = CollectionSubmission::query()->with('collection')->findOrFail($targetId);
abort_unless($submission->collection && $submission->collection->canBeViewedBy($user), 403, 'You are not allowed to report this submission.');
return;
case 'nova_card':
$card = NovaCard::query()->with('user')->findOrFail($targetId);
abort_unless($card->canBeViewedBy($user), 403, 'You are not allowed to report this card.');
return;
case 'nova_card_comment':
$comment = NovaCardComment::query()->with('card.user')->findOrFail($targetId);
abort_unless($comment->card !== null && $comment->card->canBeViewedBy($user), 403, 'You are not allowed to report this card comment.');
return;
case 'nova_card_challenge':
$challenge = NovaCardChallenge::query()->findOrFail($targetId);
abort_unless(in_array($challenge->status, [NovaCardChallenge::STATUS_ACTIVE, NovaCardChallenge::STATUS_COMPLETED, NovaCardChallenge::STATUS_ARCHIVED], true), 404);
return;
case 'nova_card_challenge_entry':
$entry = NovaCardChallengeEntry::query()->with(['challenge', 'card.user'])->findOrFail($targetId);
abort_unless($entry->challenge !== null && $entry->card !== null, 404);
abort_unless(in_array($entry->challenge->status, [NovaCardChallenge::STATUS_ACTIVE, NovaCardChallenge::STATUS_COMPLETED, NovaCardChallenge::STATUS_ARCHIVED], true), 404);
abort_unless($entry->card->canBeViewedBy($user), 403, 'You are not allowed to report this challenge entry.');
return;
default:
throw (new ModelNotFoundException())->setModel(Report::class, [$targetId]);
}
}
public function summarize(Report $report): array
{
$summary = [
'exists' => false,
'type' => (string) $report->target_type,
'id' => (int) $report->target_id,
'label' => 'Unavailable target',
'subtitle' => 'The original target could not be loaded.',
'public_url' => null,
'moderation_url' => null,
'moderation_target' => null,
];
try {
return match ($report->target_type) {
'nova_card' => $this->summarizeNovaCard($report->target_id),
'nova_card_comment' => $this->summarizeNovaCardComment($report->target_id),
'nova_card_challenge' => $this->summarizeNovaCardChallenge($report->target_id),
'nova_card_challenge_entry' => $this->summarizeNovaCardChallengeEntry($report->target_id),
default => $summary,
};
} catch (ModelNotFoundException) {
return $summary;
}
}
public function resolveModerationCard(Report $report): ?NovaCard
{
return match ($report->target_type) {
'nova_card' => NovaCard::query()->find($report->target_id),
'nova_card_comment' => NovaCardComment::query()->with('card')->find($report->target_id)?->card,
'nova_card_challenge_entry' => NovaCardChallengeEntry::query()->with('card')->find($report->target_id)?->card,
default => null,
};
}
private function summarizeNovaCard(int $targetId): array
{
$card = NovaCard::query()
->with(['user', 'category'])
->findOrFail($targetId);
return [
'exists' => true,
'type' => 'nova_card',
'id' => (int) $card->id,
'label' => (string) $card->title,
'subtitle' => trim(sprintf('@%s%s', (string) $card->user?->username, $card->category ? ' • '.$card->category->name : '')),
'public_url' => $card->publicUrl(),
'moderation_url' => route('cp.cards.index'),
'moderation_target' => $this->summarizeModerationCard($card),
];
}
private function summarizeNovaCardChallenge(int $targetId): array
{
$challenge = NovaCardChallenge::query()->findOrFail($targetId);
return [
'exists' => true,
'type' => 'nova_card_challenge',
'id' => (int) $challenge->id,
'label' => (string) $challenge->title,
'subtitle' => sprintf('%s challenge • %d entries', ucfirst((string) $challenge->status), (int) $challenge->entries_count),
'public_url' => route('cards.challenges.show', ['slug' => $challenge->slug]),
'moderation_url' => route('cp.cards.challenges.index'),
'moderation_target' => null,
];
}
private function summarizeNovaCardComment(int $targetId): array
{
$comment = NovaCardComment::query()
->with(['card.user'])
->findOrFail($targetId);
return [
'exists' => $comment->card !== null,
'type' => 'nova_card_comment',
'id' => (int) $comment->id,
'label' => 'Comment on ' . ($comment->card?->title ?? 'Nova Card'),
'subtitle' => trim(sprintf('@%s • %s', (string) $comment->user?->username, str($comment->body)->limit(80))),
'public_url' => $comment->card ? $comment->card->publicUrl() . '#comment-' . $comment->id : null,
'moderation_url' => route('cp.cards.index'),
'moderation_target' => $comment->card ? $this->summarizeModerationCard($comment->card, 'Card attached to this comment') : null,
];
}
private function summarizeNovaCardChallengeEntry(int $targetId): array
{
$entry = NovaCardChallengeEntry::query()
->with(['challenge', 'card.user'])
->findOrFail($targetId);
return [
'exists' => $entry->challenge !== null && $entry->card !== null,
'type' => 'nova_card_challenge_entry',
'id' => (int) $entry->id,
'label' => (string) ($entry->card?->title ?? 'Challenge entry'),
'subtitle' => trim(sprintf('Entry for %s%s', $entry->challenge?->title ? '"'.$entry->challenge->title.'"' : 'challenge', $entry->card?->user?->username ? ' • @'.$entry->card->user->username : '')),
'public_url' => $entry->card?->publicUrl(),
'moderation_url' => $entry->challenge ? route('cp.cards.challenges.index') : route('cp.cards.index'),
'moderation_target' => $entry->card ? $this->summarizeModerationCard($entry->card, 'Card attached to this challenge entry') : null,
];
}
private function summarizeModerationCard(NovaCard $card, ?string $context = null): array
{
$moderationReasons = $this->moderation->storedReasons($card);
$moderationOverride = $this->moderation->latestOverride($card);
return [
'kind' => 'nova_card',
'card_id' => (int) $card->id,
'title' => (string) $card->title,
'context' => $context,
'status' => (string) $card->status,
'moderation_status' => (string) $card->moderation_status,
'moderation_reasons' => $moderationReasons,
'moderation_reason_labels' => $this->moderation->labelsFor($moderationReasons),
'moderation_source' => $this->moderation->storedSource($card),
'moderation_override' => $moderationOverride,
'moderation_override_history' => $this->moderation->overrideHistory($card),
'available_actions' => [
['action' => 'approve_card', 'label' => 'Approve card'],
['action' => 'flag_card', 'label' => 'Flag card'],
['action' => 'reject_card', 'label' => 'Reject card'],
],
];
}
}