Files
landing-api/app/Console/Commands/SiteCheckCommand.php
Josep Piguillem 3897113313 Initial commit: Pireon Admin — headless admin panel and public API
Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer
collective, backed by the Pireon OAuth identity server. The public site is a
separate, server-rendered application consuming /api/public/v1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 17:55:27 +02:00

102 lines
3.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Console\Commands;
use App\Support\Health\Finding;
use App\Support\Health\HealthReport;
use App\Support\Health\Severity;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
/**
* The same checks as the panel page, for a deploy script.
*
* Exits non-zero when something is wrong, so a release can refuse to finish
* on a site that is already broken — or, with `--fail-on=error`, only on the
* faults that a visitor would actually hit.
*/
class SiteCheckCommand extends Command
{
protected $signature = 'site:check
{--fail-on=warning : Exit non-zero from this severity up (error|warning|never)}
{--json : Machine-readable output}';
protected $description = 'Check the public site for missing translations, broken URLs, broken images and other faults';
public function handle(HealthReport $report): int
{
// Every check: a deploy is not a person, so there is nobody here to
// hide a result from.
$findings = $report->runAll();
if ($this->option('json')) {
$this->line((string) json_encode($findings->map(fn (Finding $f) => [
'key' => $f->key,
'severity' => $f->severity->value,
'count' => $f->count,
'samples' => $f->samples,
])->all(), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
return $this->exitCode($findings);
}
foreach ($findings as $finding) {
$this->components->twoColumnDetail(
$finding->title(),
$this->badge($finding),
);
foreach ($finding->samples as $sample) {
$this->line(" <fg=gray>· {$sample}</>");
}
}
$this->newLine();
$problems = $findings->filter(fn (Finding $f) => $f->severity->isProblem());
if ($problems->isEmpty()) {
$this->components->info(__('health.all_clear'));
} else {
$this->components->warn(
trans_choice('health.problem_summary', $problems->count(), ['count' => $problems->count()])
);
}
return $this->exitCode($findings);
}
/**
* @param Collection<int, Finding> $findings
*/
protected function exitCode($findings): int
{
$threshold = match ((string) $this->option('fail-on')) {
'error' => Severity::Error,
'never' => null,
default => Severity::Warning,
};
if ($threshold === null) {
return self::SUCCESS;
}
$failing = $findings->contains(
fn (Finding $f) => $f->severity->weight() <= $threshold->weight() && $f->severity->isProblem()
);
return $failing ? self::FAILURE : self::SUCCESS;
}
protected function badge(Finding $finding): string
{
return match ($finding->severity) {
Severity::Ok => '<fg=green>ok</>',
Severity::Warning => "<fg=yellow>{$finding->count}</>",
Severity::Error => "<fg=red>{$finding->count}</>",
};
}
}