Rutas del sistema y archivos de configuración
This commit is contained in:
+4
-4
@@ -65,7 +65,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'UTC',
|
||||
'timezone' => 'America/Argentina/Buenos_Aires',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -78,11 +78,11 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'locale' => env('APP_LOCALE', 'en'),
|
||||
'locale' => env('APP_LOCALE', 'es'),
|
||||
|
||||
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
|
||||
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'es'),
|
||||
|
||||
'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
|
||||
'faker_locale' => env('APP_FAKER_LOCALE', 'es_AR'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'backup' => [
|
||||
/*
|
||||
* The name of this application. You can use this name to monitor
|
||||
* the backups.
|
||||
*/
|
||||
'name' => env('APP_NAME', 'AbogadasDelLitoral'),
|
||||
|
||||
'source' => [
|
||||
'files' => [
|
||||
/*
|
||||
* The list of directories and files that will be included in the backup.
|
||||
*/
|
||||
'include' => array_values(array_filter([
|
||||
storage_path('app/public/fotos'),
|
||||
storage_path('app/private/documentacion-clientes'),
|
||||
public_path('images/profesionales'),
|
||||
public_path('images/servicios'),
|
||||
public_path('images/bugs'),
|
||||
public_path('uploads/documentacion-clientes'),
|
||||
filter_var(env('BACKUP_INCLUDE_ENV', false), FILTER_VALIDATE_BOOLEAN) ? base_path('.env') : null,
|
||||
], static fn (mixed $path): bool => is_string($path) && (is_dir($path) || is_file($path)))),
|
||||
|
||||
/*
|
||||
* These directories and files will be excluded from the backup.
|
||||
*
|
||||
* Directories used by the backup process will automatically be excluded.
|
||||
*/
|
||||
'exclude' => [],
|
||||
|
||||
/*
|
||||
* Determines if symlinks should be followed.
|
||||
*/
|
||||
'follow_links' => false,
|
||||
|
||||
/*
|
||||
* Determines if it should avoid unreadable folders.
|
||||
*/
|
||||
'ignore_unreadable_directories' => false,
|
||||
|
||||
/*
|
||||
* This path is used to make directories in resulting zip-file relative
|
||||
* Set to `null` to include complete absolute path
|
||||
* Example: base_path()
|
||||
*/
|
||||
'relative_path' => base_path(),
|
||||
],
|
||||
|
||||
/*
|
||||
* The names of the connections to the databases that should be backed up
|
||||
* MySQL, PostgreSQL, SQLite and Mongo databases are supported.
|
||||
*
|
||||
* The content of the database dump may be customized for each connection
|
||||
* by adding a 'dump' key to the connection settings in config/database.php.
|
||||
* E.g.
|
||||
* 'mysql' => [
|
||||
* ...
|
||||
* 'dump' => [
|
||||
* 'exclude_tables' => [
|
||||
* 'table_to_exclude_from_backup',
|
||||
* 'another_table_to_exclude'
|
||||
* ]
|
||||
* ],
|
||||
* ],
|
||||
*
|
||||
* If you are using only InnoDB tables on a MySQL server, you can
|
||||
* also supply the useSingleTransaction option to avoid table locking.
|
||||
*
|
||||
* E.g.
|
||||
* 'mysql' => [
|
||||
* ...
|
||||
* 'dump' => [
|
||||
* 'useSingleTransaction' => true,
|
||||
* ],
|
||||
* ],
|
||||
*
|
||||
* For a complete list of available customization options, see https://github.com/spatie/db-dumper
|
||||
*/
|
||||
'databases' => [
|
||||
env('DB_CONNECTION', 'mysql'),
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
* The database dump can be compressed to decrease disk space usage.
|
||||
*
|
||||
* Out of the box Laravel-backup supplies
|
||||
* Spatie\DbDumper\Compressors\GzipCompressor::class.
|
||||
*
|
||||
* You can also create custom compressor. More info on that here:
|
||||
* https://github.com/spatie/db-dumper#using-compression
|
||||
*
|
||||
* If you do not want any compressor at all, set it to null.
|
||||
*/
|
||||
'database_dump_compressor' => null,
|
||||
|
||||
/*
|
||||
* If specified, the database dumped file name will contain a timestamp (e.g.: 'Y-m-d-H-i-s').
|
||||
*/
|
||||
'database_dump_file_timestamp_format' => null,
|
||||
|
||||
/*
|
||||
* The base of the dump filename, either 'database' or 'connection'
|
||||
*
|
||||
* If 'database' (default), the dumped filename will contain the database name.
|
||||
* If 'connection', the dumped filename will contain the connection name.
|
||||
*/
|
||||
'database_dump_filename_base' => 'database',
|
||||
|
||||
/*
|
||||
* The file extension used for the database dump files.
|
||||
*
|
||||
* If not specified, the file extension will be .archive for MongoDB and .sql for all other databases
|
||||
* The file extension should be specified without a leading .
|
||||
*/
|
||||
'database_dump_file_extension' => '',
|
||||
|
||||
'destination' => [
|
||||
/*
|
||||
* The compression algorithm to be used for creating the zip archive.
|
||||
*
|
||||
* If backing up only database, you may choose gzip compression for db dump and no compression at zip.
|
||||
*
|
||||
* Some common algorithms are listed below:
|
||||
* ZipArchive::CM_STORE (no compression at all; set 0 as compression level)
|
||||
* ZipArchive::CM_DEFAULT
|
||||
* ZipArchive::CM_DEFLATE
|
||||
* ZipArchive::CM_BZIP2
|
||||
* ZipArchive::CM_XZ
|
||||
*
|
||||
* For more check https://www.php.net/manual/zip.constants.php and confirm it's supported by your system.
|
||||
*/
|
||||
'compression_method' => ZipArchive::CM_DEFAULT,
|
||||
|
||||
/*
|
||||
* The compression level corresponding to the used algorithm; an integer between 0 and 9.
|
||||
*
|
||||
* Check supported levels for the chosen algorithm, usually 1 means the fastest and weakest compression,
|
||||
* while 9 the slowest and strongest one.
|
||||
*
|
||||
* Setting of 0 for some algorithms may switch to the strongest compression.
|
||||
*/
|
||||
'compression_level' => 9,
|
||||
|
||||
/*
|
||||
* The filename prefix used for the backup zip file.
|
||||
*/
|
||||
'filename_prefix' => '',
|
||||
|
||||
/*
|
||||
* The disk names on which the backups will be stored.
|
||||
*/
|
||||
'disks' => [
|
||||
...array_values(array_filter(array_map('trim', explode(',', env('BACKUP_DESTINATION_DISK', 'local'))))),
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
* The directory where the temporary files will be stored.
|
||||
*/
|
||||
'temporary_directory' => storage_path('app/backup-temp'),
|
||||
|
||||
/*
|
||||
* The password to be used for archive encryption.
|
||||
* Set to `null` to disable encryption.
|
||||
*/
|
||||
'password' => env('BACKUP_ARCHIVE_PASSWORD') ?: null,
|
||||
|
||||
/*
|
||||
* The encryption algorithm to be used for archive encryption.
|
||||
* You can set it to `null` or `false` to disable encryption.
|
||||
*
|
||||
* When set to 'default', we'll use ZipArchive::EM_AES_256 if it is
|
||||
* available on your system.
|
||||
*/
|
||||
'encryption' => env('BACKUP_ARCHIVE_PASSWORD') ? 'default' : false,
|
||||
|
||||
/*
|
||||
* The number of attempts, in case the backup command encounters an exception
|
||||
*/
|
||||
'tries' => 1,
|
||||
|
||||
/*
|
||||
* The number of seconds to wait before attempting a new backup if the previous try failed
|
||||
* Set to `0` for none
|
||||
*/
|
||||
'retry_delay' => 0,
|
||||
],
|
||||
|
||||
/*
|
||||
* You can get notified when specific events occur. Out of the box you can use 'mail' and 'slack'.
|
||||
* For Slack you need to install laravel/slack-notification-channel.
|
||||
*
|
||||
* You can also use your own notification classes, just make sure the class is named after one of
|
||||
* the `Spatie\Backup\Notifications\Notifications` classes.
|
||||
*/
|
||||
'notifications' => [
|
||||
'notifications' => [
|
||||
\Spatie\Backup\Notifications\Notifications\BackupHasFailedNotification::class => ['mail'],
|
||||
\Spatie\Backup\Notifications\Notifications\UnhealthyBackupWasFoundNotification::class => ['mail'],
|
||||
\Spatie\Backup\Notifications\Notifications\CleanupHasFailedNotification::class => ['mail'],
|
||||
\Spatie\Backup\Notifications\Notifications\BackupWasSuccessfulNotification::class => [],
|
||||
\Spatie\Backup\Notifications\Notifications\HealthyBackupWasFoundNotification::class => [],
|
||||
\Spatie\Backup\Notifications\Notifications\CleanupWasSuccessfulNotification::class => [],
|
||||
],
|
||||
|
||||
/*
|
||||
* Here you can specify the notifiable to which the notifications should be sent. The default
|
||||
* notifiable will use the variables specified in this config file.
|
||||
*/
|
||||
'notifiable' => \Spatie\Backup\Notifications\Notifiable::class,
|
||||
|
||||
'mail' => [
|
||||
'to' => env('BACKUP_MAIL_TO', env('MAIL_FROM_ADDRESS', 'admin@example.com')),
|
||||
|
||||
'from' => [
|
||||
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
|
||||
'name' => env('MAIL_FROM_NAME', 'Example'),
|
||||
],
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'webhook_url' => '',
|
||||
|
||||
/*
|
||||
* If this is set to null the default channel of the webhook will be used.
|
||||
*/
|
||||
'channel' => null,
|
||||
|
||||
'username' => null,
|
||||
|
||||
'icon' => null,
|
||||
],
|
||||
|
||||
'discord' => [
|
||||
'webhook_url' => '',
|
||||
|
||||
/*
|
||||
* If this is an empty string, the name field on the webhook will be used.
|
||||
*/
|
||||
'username' => '',
|
||||
|
||||
/*
|
||||
* If this is an empty string, the avatar on the webhook will be used.
|
||||
*/
|
||||
'avatar_url' => '',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
* Here you can specify which backups should be monitored.
|
||||
* If a backup does not meet the specified requirements the
|
||||
* UnHealthyBackupWasFound event will be fired.
|
||||
*/
|
||||
'monitor_backups' => [
|
||||
[
|
||||
'name' => env('APP_NAME', 'laravel-backup'),
|
||||
'disks' => array_values(array_filter(array_map('trim', explode(',', env('BACKUP_DESTINATION_DISK', 'local'))))),
|
||||
'health_checks' => [
|
||||
\Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumAgeInDays::class => (int) env('BACKUP_MAX_AGE_DAYS', 7),
|
||||
\Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumStorageInMegabytes::class => (int) env('BACKUP_MAX_STORAGE_MB', 5000),
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
[
|
||||
'name' => 'name of the second app',
|
||||
'disks' => ['local', 's3'],
|
||||
'health_checks' => [
|
||||
\Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumAgeInDays::class => 1,
|
||||
\Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumStorageInMegabytes::class => 5000,
|
||||
],
|
||||
],
|
||||
*/
|
||||
],
|
||||
|
||||
'cleanup' => [
|
||||
/*
|
||||
* The strategy that will be used to cleanup old backups. The default strategy
|
||||
* will keep all backups for a certain amount of days. After that period only
|
||||
* a daily backup will be kept. After that period only weekly backups will
|
||||
* be kept and so on.
|
||||
*
|
||||
* No matter how you configure it the default strategy will never
|
||||
* delete the newest backup.
|
||||
*/
|
||||
'strategy' => \Spatie\Backup\Tasks\Cleanup\Strategies\DefaultStrategy::class,
|
||||
|
||||
'default_strategy' => [
|
||||
/*
|
||||
* The number of days for which backups must be kept.
|
||||
*/
|
||||
'keep_all_backups_for_days' => (int) env('BACKUP_KEEP_ALL_DAYS', 7),
|
||||
|
||||
/*
|
||||
* After the "keep_all_backups_for_days" period is over, the most recent backup
|
||||
* of that day will be kept. Older backups within the same day will be removed.
|
||||
* If you create backups only once a day, no backups will be removed yet.
|
||||
*/
|
||||
'keep_daily_backups_for_days' => (int) env('BACKUP_KEEP_DAILY_DAYS', 16),
|
||||
|
||||
/*
|
||||
* After the "keep_daily_backups_for_days" period is over, the most recent backup
|
||||
* of that week will be kept. Older backups within the same week will be removed.
|
||||
* If you create backups only once a week, no backups will be removed yet.
|
||||
*/
|
||||
'keep_weekly_backups_for_weeks' => (int) env('BACKUP_KEEP_WEEKLY_WEEKS', 8),
|
||||
|
||||
/*
|
||||
* After the "keep_weekly_backups_for_weeks" period is over, the most recent backup
|
||||
* of that month will be kept. Older backups within the same month will be removed.
|
||||
*/
|
||||
'keep_monthly_backups_for_months' => (int) env('BACKUP_KEEP_MONTHLY_MONTHS', 4),
|
||||
|
||||
/*
|
||||
* After the "keep_monthly_backups_for_months" period is over, the most recent backup
|
||||
* of that year will be kept. Older backups within the same year will be removed.
|
||||
*/
|
||||
'keep_yearly_backups_for_years' => (int) env('BACKUP_KEEP_YEARLY_YEARS', 2),
|
||||
|
||||
/*
|
||||
* After cleaning up the backups remove the oldest backup until
|
||||
* this amount of megabytes has been reached.
|
||||
* Set null for unlimited size.
|
||||
*/
|
||||
'delete_oldest_backups_when_using_more_megabytes_than' => (int) env('BACKUP_MAX_STORAGE_LIMIT_MB', 5000),
|
||||
],
|
||||
|
||||
/*
|
||||
* The number of attempts, in case the cleanup command encounters an exception
|
||||
*/
|
||||
'tries' => 1,
|
||||
|
||||
/*
|
||||
* The number of seconds to wait before attempting a new cleanup if the previous try failed
|
||||
* Set to `0` for none
|
||||
*/
|
||||
'retry_delay' => 0,
|
||||
],
|
||||
|
||||
];
|
||||
+8
-1
@@ -16,7 +16,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('DB_CONNECTION', 'sqlite'),
|
||||
'default' => env('DB_CONNECTION', 'mysql'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@@ -60,7 +60,13 @@ return [
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
||||
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci',
|
||||
]) : [],
|
||||
'dump' => [
|
||||
'dump_binary_path' => env('DB_DUMP_BINARY_PATH', PHP_OS_FAMILY === 'Windows' ? 'C:/xampp/mysql/bin/' : '/usr/bin/'),
|
||||
'use_single_transaction' => true,
|
||||
'timeout' => 60 * 5, // 5 minutos de tiempo límite
|
||||
],
|
||||
],
|
||||
|
||||
'mariadb' => [
|
||||
@@ -80,6 +86,7 @@ return [
|
||||
'engine' => null,
|
||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
||||
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci',
|
||||
]) : [],
|
||||
],
|
||||
|
||||
|
||||
+3
-3
@@ -39,9 +39,9 @@ use App\Http\Controllers\UbicacionController;
|
||||
use App\Http\Controllers\UserController;
|
||||
|
||||
Route::middleware('api')->group(function () {
|
||||
Route::post('auth/login/cliente', [AuthController::class, 'loginCliente']);
|
||||
Route::post('auth/login/personal', [AuthController::class, 'loginPersonal']);
|
||||
Route::post('auth/login', [AuthController::class, 'login']);
|
||||
Route::post('auth/login/cliente', [AuthController::class, 'loginCliente'])->middleware('throttle:login-cliente-api');
|
||||
Route::post('auth/login/personal', [AuthController::class, 'loginPersonal'])->middleware('throttle:login-personal-api');
|
||||
Route::post('auth/login', [AuthController::class, 'login'])->middleware('throttle:login-api-general');
|
||||
Route::post('auth/logout', [AuthController::class, 'logout']);
|
||||
|
||||
// Rutas API Resource estándar
|
||||
|
||||
@@ -1,8 +1,233 @@
|
||||
<?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');
|
||||
|
||||
+6273
-6
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user