style updates
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'backup_disk' => env('ENTERPRISE_BACKUP_DISK', 'local'),
|
||||
'backup_path' => env('ENTERPRISE_BACKUP_PATH', 'backups'),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Enterprise\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
@@ -7,6 +8,14 @@ use Modules\Enterprise\Services\BackupService;
|
||||
final class BackupCreateCommand extends Command
|
||||
{
|
||||
protected $signature = 'enterprise:backup';
|
||||
|
||||
protected $description = 'Create a platform backup archive.';
|
||||
public function handle(BackupService $service): int { $backup = $service->create(); $this->info('Backup created: '.$backup->path); return self::SUCCESS; }
|
||||
|
||||
public function handle(BackupService $service): int
|
||||
{
|
||||
$backup = $service->create();
|
||||
$this->info('Backup created: '.$backup->path);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Enterprise\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
@@ -7,6 +8,13 @@ use Modules\Enterprise\Services\BackupService;
|
||||
final class BackupPruneCommand extends Command
|
||||
{
|
||||
protected $signature = 'enterprise:backup-prune';
|
||||
|
||||
protected $description = 'Delete backups older than the configured retention period.';
|
||||
public function handle(BackupService $service): int { $this->info($service->prune().' backup(s) pruned.'); return self::SUCCESS; }
|
||||
|
||||
public function handle(BackupService $service): int
|
||||
{
|
||||
$this->info($service->prune().' backup(s) pruned.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Enterprise\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
@@ -8,17 +9,20 @@ use Modules\Enterprise\Services\SystemHealthService;
|
||||
final class HealthCheckCommand extends Command
|
||||
{
|
||||
protected $signature = 'enterprise:health {--json}';
|
||||
|
||||
protected $description = 'Run enterprise platform health checks.';
|
||||
|
||||
public function handle(SystemHealthService $service): int
|
||||
{
|
||||
$result = $service->run();
|
||||
HealthRun::create(['status' => $result['status'], 'results' => $result, 'ran_at' => now()]);
|
||||
if ($this->option('json')) $this->line(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
else {
|
||||
if ($this->option('json')) {
|
||||
$this->line(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
} else {
|
||||
$this->table(['Check', 'Status', 'Message'], collect($result['checks'])->map(fn (array $check) => [$check['name'], $check['status'], $check['message']])->all());
|
||||
$this->line('Overall: '.$result['status']);
|
||||
}
|
||||
|
||||
return $result['status'] === 'failed' ? self::FAILURE : self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('enterprise_health_runs', function (Blueprint $table): void {
|
||||
|
||||
+3
-1
@@ -1,9 +1,11 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('enterprise_backups', function (Blueprint $table): void {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Enterprise\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
@@ -33,6 +34,7 @@ final class EnterpriseController
|
||||
{
|
||||
$result = $service->run();
|
||||
HealthRun::create(['user_id' => auth()->id(), 'status' => $result['status'], 'results' => $result, 'ran_at' => now()]);
|
||||
|
||||
return back()->with('status', 'Health checks completed.');
|
||||
}
|
||||
|
||||
@@ -40,18 +42,21 @@ final class EnterpriseController
|
||||
{
|
||||
Artisan::call('optimize:clear');
|
||||
Cache::flush();
|
||||
|
||||
return back()->with('status', 'Application and cache stores cleared.');
|
||||
}
|
||||
|
||||
public function runScheduler(): RedirectResponse
|
||||
{
|
||||
Artisan::call('schedule:run');
|
||||
|
||||
return back()->with('status', trim(Artisan::output()) ?: 'Scheduler executed.');
|
||||
}
|
||||
|
||||
public function retryFailedJobs(): RedirectResponse
|
||||
{
|
||||
Artisan::call('queue:retry', ['id' => 'all']);
|
||||
|
||||
return back()->with('status', trim(Artisan::output()) ?: 'Failed jobs queued for retry.');
|
||||
}
|
||||
|
||||
@@ -59,9 +64,11 @@ final class EnterpriseController
|
||||
{
|
||||
try {
|
||||
$service->create(auth()->id());
|
||||
|
||||
return back()->with('status', 'Backup created.');
|
||||
} catch (Throwable $e) {
|
||||
report($e);
|
||||
|
||||
return back()->withErrors(['backup' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
@@ -69,6 +76,7 @@ final class EnterpriseController
|
||||
public function deleteBackup(Backup $backup, BackupService $service): RedirectResponse
|
||||
{
|
||||
$service->delete($backup);
|
||||
|
||||
return back()->with('status', 'Backup deleted.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Enterprise\Models;
|
||||
|
||||
use App\Models\User;
|
||||
@@ -8,7 +9,16 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
final class Backup extends Model
|
||||
{
|
||||
protected $table = 'enterprise_backups';
|
||||
|
||||
protected $fillable = ['user_id', 'disk', 'path', 'size', 'status', 'message', 'completed_at'];
|
||||
protected function casts(): array { return ['completed_at' => 'datetime']; }
|
||||
public function user(): BelongsTo { return $this->belongsTo(User::class); }
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return ['completed_at' => 'datetime'];
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Enterprise\Models;
|
||||
|
||||
use App\Models\User;
|
||||
@@ -8,7 +9,16 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
final class HealthRun extends Model
|
||||
{
|
||||
protected $table = 'enterprise_health_runs';
|
||||
|
||||
protected $fillable = ['user_id', 'status', 'results', 'ran_at'];
|
||||
protected function casts(): array { return ['results' => 'array', 'ran_at' => 'datetime']; }
|
||||
public function user(): BelongsTo { return $this->belongsTo(User::class); }
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return ['results' => 'array', 'ran_at' => 'datetime'];
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Enterprise\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Enterprise\Providers;
|
||||
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Modules\Enterprise\Http\Controllers\EnterpriseController;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Enterprise\Services;
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
@@ -11,15 +12,19 @@ final class BackupService
|
||||
{
|
||||
public function create(?int $userId = null): Backup
|
||||
{
|
||||
if (! class_exists(ZipArchive::class)) throw new RuntimeException('The PHP zip extension is required.');
|
||||
if (! class_exists(ZipArchive::class)) {
|
||||
throw new RuntimeException('The PHP zip extension is required.');
|
||||
}
|
||||
|
||||
$disk = (string) config('enterprise.backup_disk', 'local');
|
||||
$directory = trim((string) config('enterprise.backup_path', 'backups'), '/');
|
||||
$name = 'cms-'.now()->format('Ymd-His').'.zip';
|
||||
$relativePath = $directory.'/'.$name;
|
||||
$temporary = storage_path('app/'.$name);
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($temporary, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) throw new RuntimeException('Unable to create backup archive.');
|
||||
$zip = new ZipArchive;
|
||||
if ($zip->open($temporary, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
|
||||
throw new RuntimeException('Unable to create backup archive.');
|
||||
}
|
||||
|
||||
$manifest = json_encode([
|
||||
'application' => config('app.name'),
|
||||
@@ -30,10 +35,14 @@ final class BackupService
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
$zip->addFromString('manifest.json', $manifest ?: '{}');
|
||||
$zip->addFile(base_path('composer.json'), 'composer.json');
|
||||
if (File::exists(base_path('modules_statuses.json'))) $zip->addFile(base_path('modules_statuses.json'), 'modules_statuses.json');
|
||||
if (File::exists(base_path('modules_statuses.json'))) {
|
||||
$zip->addFile(base_path('modules_statuses.json'), 'modules_statuses.json');
|
||||
}
|
||||
if (config('database.default') === 'sqlite') {
|
||||
$database = database_path('database.sqlite');
|
||||
if (File::exists($database)) $zip->addFile($database, 'database/database.sqlite');
|
||||
if (File::exists($database)) {
|
||||
$zip->addFile($database, 'database/database.sqlite');
|
||||
}
|
||||
}
|
||||
$zip->close();
|
||||
|
||||
@@ -61,6 +70,7 @@ final class BackupService
|
||||
$cutoff = now()->subDays((int) config('enterprise.retention_days', 30));
|
||||
$backups = Backup::query()->where('created_at', '<', $cutoff)->get();
|
||||
$backups->each(fn (Backup $backup) => $this->delete($backup));
|
||||
|
||||
return $backups->count();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Enterprise\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
@@ -30,6 +31,7 @@ final class SystemHealthService
|
||||
{
|
||||
try {
|
||||
DB::connection()->getPdo();
|
||||
|
||||
return $this->result('Database', 'healthy', DB::connection()->getDriverName());
|
||||
} catch (Throwable $e) {
|
||||
return $this->result('Database', 'failed', $e->getMessage());
|
||||
@@ -43,6 +45,7 @@ final class SystemHealthService
|
||||
Cache::put($key, 'ok', 10);
|
||||
$healthy = Cache::get($key) === 'ok';
|
||||
Cache::forget($key);
|
||||
|
||||
return $this->result('Cache', $healthy ? 'healthy' : 'failed', config('cache.default'));
|
||||
} catch (Throwable $e) {
|
||||
return $this->result('Cache', 'failed', $e->getMessage());
|
||||
@@ -56,6 +59,7 @@ final class SystemHealthService
|
||||
Storage::disk('local')->delete('enterprise-health.txt');
|
||||
$freeMb = (int) floor(disk_free_space(storage_path()) / 1024 / 1024);
|
||||
$minimum = (int) config('enterprise.health.minimum_free_disk_mb', 512);
|
||||
|
||||
return $this->result('Storage', $freeMb < $minimum ? 'warning' : 'healthy', $freeMb.' MB free');
|
||||
} catch (Throwable $e) {
|
||||
return $this->result('Storage', 'failed', $e->getMessage());
|
||||
@@ -66,14 +70,20 @@ final class SystemHealthService
|
||||
{
|
||||
$failed = Schema::hasTable('failed_jobs') ? DB::table('failed_jobs')->count() : 0;
|
||||
$maximum = (int) config('enterprise.health.maximum_failed_jobs', 0);
|
||||
|
||||
return $this->result('Queue', $failed > $maximum ? 'warning' : 'healthy', $failed.' failed jobs');
|
||||
}
|
||||
|
||||
private function environment(): array
|
||||
{
|
||||
$warnings = [];
|
||||
if (app()->environment('production') && config('app.debug')) $warnings[] = 'APP_DEBUG is enabled';
|
||||
if (! app()->environment('production')) $warnings[] = 'Not running in production';
|
||||
if (app()->environment('production') && config('app.debug')) {
|
||||
$warnings[] = 'APP_DEBUG is enabled';
|
||||
}
|
||||
if (! app()->environment('production')) {
|
||||
$warnings[] = 'Not running in production';
|
||||
}
|
||||
|
||||
return $this->result('Environment', $warnings ? 'warning' : 'healthy', $warnings ? implode('; ', $warnings) : app()->environment());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
|
||||
use Modules\Enterprise\Providers\EnterpriseServiceProvider;
|
||||
use Modules\Enterprise\Services\SystemHealthService;
|
||||
|
||||
it('boots the enterprise module', fn () => expect(app()->providerIsLoaded(Modules\Enterprise\Providers\EnterpriseServiceProvider::class))->toBeTrue());
|
||||
it('boots the enterprise module', fn () => expect(app()->providerIsLoaded(EnterpriseServiceProvider::class))->toBeTrue());
|
||||
it('runs platform health checks', function (): void {
|
||||
$result = app(SystemHealthService::class)->run();
|
||||
expect($result)->toHaveKeys(['status', 'checks', 'generated_at'])->and($result['checks'])->not->toBeEmpty();
|
||||
|
||||
Reference in New Issue
Block a user