234 lines
8.9 KiB
PHP
234 lines
8.9 KiB
PHP
<?php
|
|
|
|
use App\Models\Administrador;
|
|
use App\Models\Formulario;
|
|
use App\Models\LogSeguridad;
|
|
use Database\Seeders\BulkDataSeeder;
|
|
use Illuminate\Foundation\Inspiring;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use Illuminate\Support\Facades\Schedule;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
Artisan::command('inspire', function () {
|
|
$this->comment(Inspiring::quote());
|
|
})->purpose('Display an inspiring quote');
|
|
|
|
Artisan::command('logs:purge-old-security', function () {
|
|
$fechaLimite = now()->subMonths(2);
|
|
|
|
$eliminados = LogSeguridad::query()
|
|
->where('fechahora', '<', $fechaLimite)
|
|
->delete();
|
|
|
|
$this->info('Logs eliminados: ' . $eliminados);
|
|
})->purpose('Elimina logs de seguridad con más de 2 meses de antigüedad');
|
|
|
|
Artisan::command('seed:bulk {--modo=masivo} {--clientes=} {--profesionales=} {--servicios=} {--profesiones=} {--fresh}', function () {
|
|
$presets = [
|
|
'rapido' => ['clientes' => 100, 'profesionales' => 20, 'servicios' => 10, 'profesiones' => 2],
|
|
'medio' => ['clientes' => 500, 'profesionales' => 50, 'servicios' => 20, 'profesiones' => 3],
|
|
'masivo' => ['clientes' => 1000, 'profesionales' => 100, 'servicios' => 30, 'profesiones' => 4],
|
|
];
|
|
|
|
$modo = strtolower((string) $this->option('modo'));
|
|
|
|
if (!array_key_exists($modo, $presets)) {
|
|
$this->error('Modo invalido. Usa: rapido, medio o masivo.');
|
|
|
|
return 1;
|
|
}
|
|
|
|
$clientes = max(0, (int) ($this->option('clientes') ?? $presets[$modo]['clientes']));
|
|
$profesionales = max(0, (int) ($this->option('profesionales') ?? $presets[$modo]['profesionales']));
|
|
$servicios = max(0, (int) ($this->option('servicios') ?? $presets[$modo]['servicios']));
|
|
$profesiones = max(0, (int) ($this->option('profesiones') ?? $presets[$modo]['profesiones']));
|
|
|
|
config([
|
|
'bulk_seed.enabled' => false,
|
|
'bulk_seed.clientes' => $clientes,
|
|
'bulk_seed.profesionales' => $profesionales,
|
|
'bulk_seed.servicios' => $servicios,
|
|
'bulk_seed.profesiones' => $profesiones,
|
|
]);
|
|
|
|
if ((bool) $this->option('fresh')) {
|
|
$this->info('Ejecutando migrate:fresh --seed...');
|
|
$exitMigrate = Artisan::call('migrate:fresh', ['--seed' => true]);
|
|
$this->line(Artisan::output());
|
|
|
|
if ($exitMigrate !== 0) {
|
|
$this->error('Fallo migrate:fresh --seed');
|
|
|
|
return $exitMigrate;
|
|
}
|
|
}
|
|
|
|
$this->info("Cargando datos masivos (modo={$modo})...");
|
|
$exitBulk = Artisan::call('db:seed', ['--class' => BulkDataSeeder::class]);
|
|
$this->line(Artisan::output());
|
|
|
|
if ($exitBulk !== 0) {
|
|
$this->error('Fallo la ejecucion de BulkDataSeeder.');
|
|
|
|
return $exitBulk;
|
|
}
|
|
|
|
$this->info("Datos creados: {$profesiones} profesiones, {$servicios} servicios, {$clientes} clientes y {$profesionales} profesionales.");
|
|
|
|
return 0;
|
|
})->purpose('Carga datos de prueba masivos por modo o por cantidades personalizadas');
|
|
|
|
Artisan::command('formularios:sync-rejected-status', function () {
|
|
$formulariosPendientes = Formulario::query()
|
|
->where('estado', 'Pendiente')
|
|
->get(['id', 'profesion_id']);
|
|
|
|
$actualizados = 0;
|
|
|
|
foreach ($formulariosPendientes as $formulario) {
|
|
$formularioId = (int) $formulario->id;
|
|
$profesionFormularioId = (int) $formulario->profesion_id;
|
|
|
|
$asignadosActivos = DB::table('profesionales_formularios as pf')
|
|
->join('profesionales as p', 'p.id', '=', 'pf.profesional_id')
|
|
->where('pf.formulario_id', $formularioId)
|
|
->where('p.baja_id', 1)
|
|
->where(function ($query) use ($profesionFormularioId): void {
|
|
$query->where('p.profesion_id', $profesionFormularioId)
|
|
->orWhereExists(function ($subquery) use ($profesionFormularioId): void {
|
|
$subquery->select(DB::raw(1))
|
|
->from('profesionales_profesiones as pp')
|
|
->whereColumn('pp.profesional_id', 'p.id')
|
|
->where('pp.profesion_id', $profesionFormularioId);
|
|
});
|
|
})
|
|
->count();
|
|
|
|
if ($asignadosActivos === 0) {
|
|
continue;
|
|
}
|
|
|
|
$hayActivosSinRechazar = DB::table('profesionales_formularios as pf')
|
|
->join('profesionales as p', 'p.id', '=', 'pf.profesional_id')
|
|
->where('pf.formulario_id', $formularioId)
|
|
->where('p.baja_id', 1)
|
|
->where(function ($query) use ($profesionFormularioId): void {
|
|
$query->where('p.profesion_id', $profesionFormularioId)
|
|
->orWhereExists(function ($subquery) use ($profesionFormularioId): void {
|
|
$subquery->select(DB::raw(1))
|
|
->from('profesionales_profesiones as pp')
|
|
->whereColumn('pp.profesional_id', 'p.id')
|
|
->where('pp.profesion_id', $profesionFormularioId);
|
|
});
|
|
})
|
|
->whereRaw("LOWER(TRIM(pf.estado)) <> 'rechazado'")
|
|
->exists();
|
|
|
|
if (!$hayActivosSinRechazar) {
|
|
Formulario::query()
|
|
->where('id', $formularioId)
|
|
->update(['estado' => 'Rechazado por todos']);
|
|
|
|
$actualizados++;
|
|
}
|
|
}
|
|
|
|
$this->info('Formularios actualizados: ' . $actualizados);
|
|
})->purpose('Sincroniza formularios pendientes a Rechazado por todos cuando todos los profesionales activos rechazaron');
|
|
|
|
Artisan::command('backup:daily-email {--keep=4}', function () {
|
|
$includeEnv = (bool) filter_var((string) env('BACKUP_INCLUDE_ENV', false), FILTER_VALIDATE_BOOLEAN);
|
|
$archivePassword = trim((string) env('BACKUP_ARCHIVE_PASSWORD', ''));
|
|
|
|
if ($includeEnv && $archivePassword === '') {
|
|
$this->error('BACKUP_INCLUDE_ENV=true requiere BACKUP_ARCHIVE_PASSWORD configurado para cifrar el ZIP.');
|
|
|
|
return 1;
|
|
}
|
|
|
|
$this->info('Iniciando backup semanal de base de datos y archivos...');
|
|
|
|
$backupExitCode = Artisan::call('backup:run');
|
|
$this->line(Artisan::output());
|
|
|
|
if ($backupExitCode !== 0) {
|
|
$this->error('No se pudo generar el backup.');
|
|
|
|
return 1;
|
|
}
|
|
|
|
$disk = Storage::disk('local');
|
|
$backupFolder = trim((string) config('backup.backup.name', 'Laravel'), '/');
|
|
|
|
$files = collect($disk->files($backupFolder))
|
|
->filter(fn (string $file): bool => str_ends_with(strtolower($file), '.zip'))
|
|
->sortByDesc(fn (string $file): int => $disk->lastModified($file))
|
|
->values();
|
|
|
|
if ($files->isEmpty()) {
|
|
$this->error('No se encontró el archivo ZIP del backup para enviar por email.');
|
|
|
|
return 1;
|
|
}
|
|
|
|
$latestBackup = (string) $files->first();
|
|
|
|
$adminRecipients = Administrador::query()
|
|
->select('correo')
|
|
->whereNotNull('correo')
|
|
->pluck('correo')
|
|
->map(fn ($correo): string => trim((string) $correo))
|
|
->filter(fn (string $correo): bool => $correo !== '' && filter_var($correo, FILTER_VALIDATE_EMAIL) !== false)
|
|
->unique()
|
|
->values()
|
|
->all();
|
|
|
|
$fallbackRecipient = (string) env('BACKUP_MAIL_TO', (string) config('backup.notifications.mail.to'));
|
|
$recipients = !empty($adminRecipients)
|
|
? $adminRecipients
|
|
: array_values(array_filter([(string) $fallbackRecipient]));
|
|
|
|
if (empty($recipients)) {
|
|
$this->error('No hay correos de administradores ni BACKUP_MAIL_TO configurado para enviar el backup.');
|
|
|
|
return 1;
|
|
}
|
|
|
|
try {
|
|
Mail::raw('Se adjunta el backup semanal de base de datos y archivos.', function ($message) use ($disk, $latestBackup, $recipients): void {
|
|
$message->to($recipients)
|
|
->subject('Backup semanal - ' . now()->format('d/m/Y'))
|
|
->attachData(
|
|
$disk->get($latestBackup),
|
|
basename($latestBackup),
|
|
['mime' => 'application/zip']
|
|
);
|
|
});
|
|
} catch (\Throwable $e) {
|
|
$this->error('El backup se generó, pero falló el envío de email: ' . $e->getMessage());
|
|
|
|
return 1;
|
|
}
|
|
|
|
$keep = max(1, (int) $this->option('keep'));
|
|
|
|
$files->slice($keep)->each(function (string $file) use ($disk): void {
|
|
$disk->delete($file);
|
|
});
|
|
|
|
$deletedCount = max(0, $files->count() - $keep);
|
|
|
|
$this->info('Backup enviado a: ' . implode(', ', $recipients));
|
|
$this->info('Backups eliminados por retención: ' . $deletedCount);
|
|
$this->info('Backups conservados: ' . $keep);
|
|
|
|
return 0;
|
|
})->purpose('Genera backup semanal de BD y archivos, lo envía por mail y conserva solo los últimos N backups');
|
|
|
|
Schedule::command('logs:purge-old-security')->daily();
|
|
Schedule::command('backup:daily-email --keep=4')->dailyAt('16:00'); //ejecuta backup:run
|
|
Schedule::command('backup:clean')->dailyAt('16:20');
|
|
Schedule::command('backup:monitor')->dailyAt('16:30');
|