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>
This commit is contained in:
101
app/Console/Commands/SiteCheckCommand.php
Normal file
101
app/Console/Commands/SiteCheckCommand.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?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}</>",
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user