ADD: Plantilla Holos (#1)
This commit is contained in:
parent
97723f5b5c
commit
517628b92d
30
.env.example
30
.env.example
@ -2,16 +2,23 @@ APP_NAME=Laravel
|
|||||||
APP_ENV=local
|
APP_ENV=local
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_TIMEZONE=UTC
|
APP_TIMEZONE=America/Mexico_City
|
||||||
APP_URL=http://localhost
|
APP_URL=http://backend.holos.test
|
||||||
|
APP_FRONTEND_URL=http://frontend.holos.test
|
||||||
|
APP_PAGINATION=25
|
||||||
|
|
||||||
APP_LOCALE=en
|
APP_LOCALE=es
|
||||||
APP_FALLBACK_LOCALE=en
|
APP_FALLBACK_LOCALE=es
|
||||||
APP_FAKER_LOCALE=en_US
|
APP_FAKER_LOCALE=es_MX
|
||||||
|
|
||||||
APP_MAINTENANCE_DRIVER=file
|
APP_MAINTENANCE_DRIVER=file
|
||||||
# APP_MAINTENANCE_STORE=database
|
# APP_MAINTENANCE_STORE=database
|
||||||
|
|
||||||
|
CORS_ALLOWED_ORIGINS=frontend.holos.test
|
||||||
|
|
||||||
|
PULSE_ENABLED=false
|
||||||
|
TELESCOPE_ENABLED=false
|
||||||
|
|
||||||
PHP_CLI_SERVER_WORKERS=4
|
PHP_CLI_SERVER_WORKERS=4
|
||||||
|
|
||||||
BCRYPT_ROUNDS=12
|
BCRYPT_ROUNDS=12
|
||||||
@ -21,12 +28,12 @@ LOG_STACK=single
|
|||||||
LOG_DEPRECATIONS_CHANNEL=null
|
LOG_DEPRECATIONS_CHANNEL=null
|
||||||
LOG_LEVEL=debug
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
DB_CONNECTION=sqlite
|
DB_CONNECTION=mysql
|
||||||
# DB_HOST=127.0.0.1
|
DB_HOST=127.0.0.1
|
||||||
# DB_PORT=3306
|
DB_PORT=3306
|
||||||
# DB_DATABASE=laravel
|
DB_DATABASE=holos-backend
|
||||||
# DB_USERNAME=root
|
DB_USERNAME=notsoweb
|
||||||
# DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
|
|
||||||
SESSION_DRIVER=database
|
SESSION_DRIVER=database
|
||||||
SESSION_LIFETIME=120
|
SESSION_LIFETIME=120
|
||||||
@ -51,6 +58,7 @@ REDIS_PORT=6379
|
|||||||
MAIL_MAILER=log
|
MAIL_MAILER=log
|
||||||
MAIL_HOST=127.0.0.1
|
MAIL_HOST=127.0.0.1
|
||||||
MAIL_PORT=2525
|
MAIL_PORT=2525
|
||||||
|
MAIL_DOMAIN=notsoweb.com
|
||||||
MAIL_USERNAME=null
|
MAIL_USERNAME=null
|
||||||
MAIL_PASSWORD=null
|
MAIL_PASSWORD=null
|
||||||
MAIL_ENCRYPTION=null
|
MAIL_ENCRYPTION=null
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@
|
|||||||
/public/build
|
/public/build
|
||||||
/public/hot
|
/public/hot
|
||||||
/public/storage
|
/public/storage
|
||||||
|
/public/vendor
|
||||||
/storage/*.key
|
/storage/*.key
|
||||||
/storage/pail
|
/storage/pail
|
||||||
/vendor
|
/vendor
|
||||||
|
|||||||
48
app/Console/Commands/Broadcast.php
Normal file
48
app/Console/Commands/Broadcast.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iniciar servicio de broadcast
|
||||||
|
*/
|
||||||
|
class Broadcast extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'app:broadcast
|
||||||
|
{--start : Iniciar servicio de broadcast}
|
||||||
|
{--stop : Detener servicio de broadcast}
|
||||||
|
{--restart : Reiniciar servicio de broadcast}
|
||||||
|
{--status : Mostrar estado del servicio de broadcast}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Command description';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
if ($this->option('stop') || $this->option('restart')) {
|
||||||
|
echo "# Deteniendo servicio de broadcast... \n";
|
||||||
|
echo shell_exec('pm2 delete broadcast');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->option('start') || $this->option('restart')) {
|
||||||
|
echo "# Iniciando servicio de broadcast... \n";
|
||||||
|
echo shell_exec("pm2 start --name broadcast \"php artisan reverb:start --hostname=" . env('APP_URL') . "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->option('status')) {
|
||||||
|
echo "# Estado del servicio de broadcast: \n";
|
||||||
|
echo shell_exec('pm2 status broadcast');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
57
app/Console/Commands/DownSecure.php
Normal file
57
app/Console/Commands/DownSecure.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php namespace App\Console\Commands;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb (https://notsoweb.com) - All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Ramsey\Uuid\Uuid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mantenimiento seguro
|
||||||
|
*
|
||||||
|
* De forma automática pondrá al sistema en modo mantenimiento con una URL secreta para
|
||||||
|
* poder acceder al sitio. El tiempo se puede configurar desde las variables de entorno.
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class DownSecure extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'app:secure';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Modo mantenimiento con un hash seguro';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$secret = Uuid::uuid4();
|
||||||
|
|
||||||
|
Artisan::call('down', [
|
||||||
|
'--secret' => $secret
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo url($secret);
|
||||||
|
echo "\n";
|
||||||
|
|
||||||
|
Log::channel('notsoweb')->info("Maintenance Mode Secure. Key: {$secret}");
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
app/Console/Commands/NotificationGlobal.php
Normal file
59
app/Console/Commands/NotificationGlobal.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php namespace App\Console\Commands;
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2023 Notsoweb (https://notsoweb.com) - All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use App\Events\GlobalNotification;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notificación global
|
||||||
|
*
|
||||||
|
* Notificación global a todos los usuarios conectados.
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class NotificationGlobal extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'notification:global
|
||||||
|
{--message=Notificación de prueba : Mensaje de la notificación}
|
||||||
|
{--title=Notificación Global : Título de la notificación}
|
||||||
|
{--type=info : Tipo de notificación (info, success, warning, error)}
|
||||||
|
{--timeout=15 : Tiempo de duración de la notificación}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Enviar notificación a todos los usuarios conectados';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$message = $this->option('message');
|
||||||
|
$title = $this->option('title');
|
||||||
|
$type = $this->option('type');
|
||||||
|
$timeout = $this->option('timeout');
|
||||||
|
|
||||||
|
broadcast(new GlobalNotification(
|
||||||
|
$title,
|
||||||
|
$message,
|
||||||
|
$type,
|
||||||
|
$timeout
|
||||||
|
));
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
55
app/Events/GlobalNotification.php
Normal file
55
app/Events/GlobalNotification.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php namespace App\Events;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb (https://notsoweb.com) - All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithBroadcasting;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notificación global
|
||||||
|
*
|
||||||
|
* Notificación enviada a todos los usuarios conectados al canal Global.
|
||||||
|
*
|
||||||
|
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class GlobalNotification implements ShouldBroadcastNow
|
||||||
|
{
|
||||||
|
use Dispatchable,
|
||||||
|
InteractsWithBroadcasting,
|
||||||
|
InteractsWithSockets,
|
||||||
|
SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public string $title,
|
||||||
|
public string $message,
|
||||||
|
public string $type = 'info',
|
||||||
|
public int $timeout = 15
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nombre del evento
|
||||||
|
*/
|
||||||
|
public function broadcastAs(): string
|
||||||
|
{
|
||||||
|
return 'App\Events\Notification';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Canal de envío
|
||||||
|
*/
|
||||||
|
public function broadcastOn(): Channel
|
||||||
|
{
|
||||||
|
return new PrivateChannel('Global');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,15 @@
|
|||||||
<?php
|
<?php namespace App\Http\Controllers;
|
||||||
|
/**
|
||||||
namespace App\Http\Controllers;
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controlador base
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
abstract class Controller
|
abstract class Controller
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
|
|||||||
128
app/Http/Controllers/MyUserController.php
Normal file
128
app/Http/Controllers/MyUserController.php
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<?php namespace App\Http\Controllers;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use App\Http\Requests\User\UserUpdateRequest;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
||||||
|
use Notsoweb\LaravelCore\Supports\NotifySupport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Usuario autenticado
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class MyUserController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Obtener usuario autenticado
|
||||||
|
*/
|
||||||
|
public function show()
|
||||||
|
{
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'user' => auth()->user()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualizar
|
||||||
|
*/
|
||||||
|
public function update(UserUpdateRequest $request)
|
||||||
|
{
|
||||||
|
$form = $request->validated();
|
||||||
|
|
||||||
|
if (isset($form['photo'])) {
|
||||||
|
auth()->user()->updateProfilePhoto($form['photo']);
|
||||||
|
}
|
||||||
|
|
||||||
|
auth()->user()->update($form);
|
||||||
|
|
||||||
|
return ApiResponse::OK->response();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eliminar
|
||||||
|
*/
|
||||||
|
public function destroy()
|
||||||
|
{
|
||||||
|
auth()->user()->delete();
|
||||||
|
|
||||||
|
return ApiResponse::OK->response();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirmar contraseña
|
||||||
|
*
|
||||||
|
* Autoriza una acción si la contraseña es correcta.
|
||||||
|
*/
|
||||||
|
public function confirmPassword(Request $request)
|
||||||
|
{
|
||||||
|
$form = $request->validate([
|
||||||
|
'password' => ['required', 'string', 'min:8']
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!Hash::check($form['password'], auth()->user()->password)) {
|
||||||
|
NotifySupport::errorIn('password', __('validation.password'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiResponse::OK->response();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualizar contraseña
|
||||||
|
*/
|
||||||
|
public function updatePassword(Request $request)
|
||||||
|
{
|
||||||
|
$form = $request->validate([
|
||||||
|
'current_password' => ['required', 'string'],
|
||||||
|
'password' => ['required', 'string', 'min:8', 'confirmed']
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!Hash::check($form['current_password'], auth()->user()->password)) {
|
||||||
|
NotifySupport::errorIn('current_password', __('validation.password'));
|
||||||
|
}
|
||||||
|
|
||||||
|
auth()->user()->update([
|
||||||
|
'password' => bcrypt($form['password'])
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ApiResponse::OK->response();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eliminar foto de perfil
|
||||||
|
*/
|
||||||
|
public function destroyPhoto()
|
||||||
|
{
|
||||||
|
auth()->user()->deleteProfilePhoto();
|
||||||
|
|
||||||
|
return ApiResponse::OK->response();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permisos
|
||||||
|
*/
|
||||||
|
public function permissions()
|
||||||
|
{
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'permissions' => auth()->user()->getAllPermissions()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roles
|
||||||
|
*/
|
||||||
|
public function roles()
|
||||||
|
{
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'roles' => auth()->user()
|
||||||
|
->roles()
|
||||||
|
->select('id', 'name', 'description')
|
||||||
|
->get()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
75
app/Http/Controllers/System/LoginController.php
Normal file
75
app/Http/Controllers/System/LoginController.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php namespace App\Http\Controllers\System;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Auth\LoginRequest;
|
||||||
|
use App\Http\Requests\User\ForgotRequest;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Notifications\ForgotPasswordNotification;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controlador de sesiones
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class LoginController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Iniciar sesión
|
||||||
|
*/
|
||||||
|
public function login(LoginRequest $request)
|
||||||
|
{
|
||||||
|
return (Auth::attempt($request->all()))
|
||||||
|
? ApiResponse::OK->onSuccess([
|
||||||
|
'user' => auth()->user(),
|
||||||
|
'token' => auth()->user()
|
||||||
|
->createToken('golscore')
|
||||||
|
->accessToken,
|
||||||
|
])
|
||||||
|
: ApiResponse::UNPROCESSABLE_CONTENT->response([
|
||||||
|
'email' => ['Usuario no valido']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cerrar sesión
|
||||||
|
*/
|
||||||
|
public function logout()
|
||||||
|
{
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'is_revoked' => auth()->user()->token()->revoke()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contraseña olvidada
|
||||||
|
*/
|
||||||
|
public function forgotPassword(ForgotRequest $request)
|
||||||
|
{
|
||||||
|
$data = $request->validated();
|
||||||
|
|
||||||
|
$user = User::where('email', $data['email'])->first();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$user->notify(new ForgotPasswordNotification());
|
||||||
|
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'is_sent' => true
|
||||||
|
]);
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
Log::channel('mail')->info("Email: {$data['email']}");
|
||||||
|
Log::channel('mail')->error($th->getMessage());
|
||||||
|
|
||||||
|
return ApiResponse::INTERNAL_ERROR->response([
|
||||||
|
'is_sent' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
95
app/Http/Controllers/System/NotificationController.php
Executable file
95
app/Http/Controllers/System/NotificationController.php
Executable file
@ -0,0 +1,95 @@
|
|||||||
|
<?php namespace App\Http\Controllers\System;
|
||||||
|
/**
|
||||||
|
* @copyright 2024 Notsoweb (https://notsoweb.com) - All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Notifications\DatabaseNotification;
|
||||||
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
||||||
|
use Notsoweb\LaravelCore\Controllers\VueController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sistema de notificaciones
|
||||||
|
*
|
||||||
|
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class NotificationController extends VueController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->root('notifications');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listar notificaciones del usuario
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$q = request()->get('query');
|
||||||
|
|
||||||
|
$model = auth()->user()
|
||||||
|
->notifications();
|
||||||
|
|
||||||
|
if($q) {
|
||||||
|
$model = $model->where('data->title', 'LIKE', "%{$q}%")
|
||||||
|
->orWhere('data->message', "LIKE", "%{$q}%");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->view('index', [
|
||||||
|
'models' => $model
|
||||||
|
->paginate(config('app.pagination'))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Todas las notificaciones
|
||||||
|
*/
|
||||||
|
public function all(): JsonResponse
|
||||||
|
{
|
||||||
|
$query = request()->get('query');
|
||||||
|
|
||||||
|
$model = auth()->user()
|
||||||
|
->notifications();
|
||||||
|
|
||||||
|
if($query) {
|
||||||
|
$model = $model->where('data->title', 'LIKE', "%{$query}%")
|
||||||
|
->orWhere('data->message', "LIKE", "%{$query}%");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiResponse::OK->axios([
|
||||||
|
'notifications' => $model
|
||||||
|
->paginate(config('app.pagination'))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marcar notificación como leída
|
||||||
|
*/
|
||||||
|
public function read(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$notification = DatabaseNotification::find($request->get('id'));
|
||||||
|
|
||||||
|
if ($notification) {
|
||||||
|
$notification->markAsRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiResponse::OK->axios();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener notificaciones no leídas recientes
|
||||||
|
*/
|
||||||
|
public function allUnread(): JsonResponse
|
||||||
|
{
|
||||||
|
return ApiResponse::OK->axios([
|
||||||
|
'total' => auth()->user()->unreadNotifications()->count(),
|
||||||
|
'notifications' => auth()->user()->unreadNotifications()->limit(10)->get(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
app/Http/Controllers/System/SystemController.php
Normal file
46
app/Http/Controllers/System/SystemController.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php namespace App\Http\Controllers\System;
|
||||||
|
/**
|
||||||
|
* @copyright 2024 Notsoweb (https://notsoweb.com) - All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursos del sistema
|
||||||
|
*
|
||||||
|
* Contiene determinados recursos que el sistema requiere para funcionar por parte del
|
||||||
|
* frontend.
|
||||||
|
*
|
||||||
|
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class SystemController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Listar permisos del sistema
|
||||||
|
*/
|
||||||
|
public function permissions()
|
||||||
|
{
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'permissions' => Permission::orderBy('name')
|
||||||
|
->select('id', 'name', 'description')
|
||||||
|
->get()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listar roles del sistema
|
||||||
|
*/
|
||||||
|
public function roles()
|
||||||
|
{
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'roles' => Role::orderBy('description')
|
||||||
|
->select('id', 'name', 'description')
|
||||||
|
->get()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
131
app/Http/Controllers/UserController.php
Normal file
131
app/Http/Controllers/UserController.php
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?php namespace App\Http\Controllers;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Users\PasswordUpdateRequest;
|
||||||
|
use App\Http\Requests\Users\UserStoreRequest;
|
||||||
|
use App\Http\Requests\Users\UserUpdateRequest;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Supports\QuerySupport;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controlador de usuarios
|
||||||
|
*
|
||||||
|
* Permite la administración de los usuarios en general.
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class UserController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Listar
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$users = User::orderBy('name');
|
||||||
|
|
||||||
|
QuerySupport::queryByKeys($users, ['name', 'email']);
|
||||||
|
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'users' => $users->paginate(config('app.pagination'))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Almacenar
|
||||||
|
*/
|
||||||
|
public function store(UserStoreRequest $request)
|
||||||
|
{
|
||||||
|
$user = User::create($request->all());
|
||||||
|
|
||||||
|
if ($request->has('roles')) {
|
||||||
|
$user->roles()->sync($request->roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiResponse::OK->response();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mostrar
|
||||||
|
*/
|
||||||
|
public function show(User $user)
|
||||||
|
{
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'user' => $user
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualizar
|
||||||
|
*/
|
||||||
|
public function update(UserUpdateRequest $request, User $user)
|
||||||
|
{
|
||||||
|
$user->update($request->all());
|
||||||
|
|
||||||
|
return ApiResponse::OK->response();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eliminar
|
||||||
|
*/
|
||||||
|
public function destroy(User $user)
|
||||||
|
{
|
||||||
|
$user->delete();
|
||||||
|
|
||||||
|
return ApiResponse::OK->response();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permisos del usuario
|
||||||
|
*/
|
||||||
|
public function permissions(User $user)
|
||||||
|
{
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'permissions' => $user->getAllPermissions()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roles del usuario
|
||||||
|
*/
|
||||||
|
public function roles(User $user)
|
||||||
|
{
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'roles' => $user
|
||||||
|
->roles()
|
||||||
|
->select('id', 'name', 'description')
|
||||||
|
->get()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualizar roles
|
||||||
|
*/
|
||||||
|
public function updateRoles(Request $request, User $user)
|
||||||
|
{
|
||||||
|
if ($request->has('roles')) {
|
||||||
|
$user->roles()->sync($request->roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiResponse::OK->response();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualizar contraseña
|
||||||
|
*/
|
||||||
|
public function updatePassword(PasswordUpdateRequest $request, User $user)
|
||||||
|
{
|
||||||
|
$user->update([
|
||||||
|
'password' => bcrypt($request->password)
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ApiResponse::OK->response();
|
||||||
|
}
|
||||||
|
}
|
||||||
37
app/Http/Requests/Auth/LoginRequest.php
Normal file
37
app/Http/Requests/Auth/LoginRequest.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php namespace App\Http\Requests\Auth;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solicitud de login
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class LoginRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'email' => ['required', 'email'],
|
||||||
|
'password' => ['required', 'min:8'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Http/Requests/User/ForgotRequest.php
Normal file
34
app/Http/Requests/User/ForgotRequest.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php namespace App\Http\Requests\User;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solicitud de olvido de contraseña
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class ForgotRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determinar si el usuario está autorizado para realizar esta solicitud
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener las reglas de validación que se aplican a la solicitud
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'email' => ['required', 'email', 'exists:users,email']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
46
app/Http/Requests/User/UserUpdateRequest.php
Normal file
46
app/Http/Requests/User/UserUpdateRequest.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php namespace App\Http\Requests\User;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualizar perfil usuario actual
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class UserUpdateRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determinar si el usuario está autorizado para realizar esta solicitud
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener las reglas de validación que se aplican a la solicitud
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => ['required', 'string', 'max:255'],
|
||||||
|
'paternal' => ['required', 'string', 'max:255'],
|
||||||
|
'maternal' => ['required', 'string', 'max:255'],
|
||||||
|
'email' => [
|
||||||
|
'required',
|
||||||
|
'string',
|
||||||
|
'email',
|
||||||
|
'max:255',
|
||||||
|
Rule::unique('users')->ignore(auth()->user()->id),
|
||||||
|
],
|
||||||
|
'phone' => ['nullable', 'numeric', 'digits:10'],
|
||||||
|
'photo' => ['nullable', 'mimes:jpg,jpeg,png', 'max:2048'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/Http/Requests/Users/PasswordUpdateRequest.php
Normal file
34
app/Http/Requests/Users/PasswordUpdateRequest.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php namespace App\Http\Requests\Users;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualizar contraseña desde gestión de usuarios
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class PasswordUpdateRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determinar si el usuario está autorizado para realizar esta solicitud
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener las reglas de validación que se aplican a la solicitud
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'password' => ['required', 'string', 'min:8', 'confirmed']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
42
app/Http/Requests/Users/UserStoreRequest.php
Normal file
42
app/Http/Requests/Users/UserStoreRequest.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php namespace App\Http\Requests\Users;
|
||||||
|
/**
|
||||||
|
* @copyright (C) 2024 Notsoweb Software (https://notsoweb.com) - All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Almacenar usuario
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class UserStoreRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => ['required', 'string', 'max:255'],
|
||||||
|
'paternal' => ['required', 'string', 'max:255'],
|
||||||
|
'maternal' => ['required', 'string', 'max:255'],
|
||||||
|
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
|
||||||
|
'phone' => ['nullable', 'numeric', 'digits:10'],
|
||||||
|
'password' => ['required', 'string', 'min:8'],
|
||||||
|
'roles' => ['nullable', 'array']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
42
app/Http/Requests/Users/UserUpdateRequest.php
Normal file
42
app/Http/Requests/Users/UserUpdateRequest.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php namespace App\Http\Requests\Users;
|
||||||
|
/**
|
||||||
|
* @copyright (C) 2024 Notsoweb Software (https://notsoweb.com) - All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actualizar usuario
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class UserUpdateRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => ['required', 'string', 'max:255'],
|
||||||
|
'paternal' => ['required', 'string', 'max:255'],
|
||||||
|
'maternal' => ['required', 'string', 'max:255'],
|
||||||
|
'email' => ['required', 'string', 'email', 'max:255', Rule::unique('users')->ignore($this->route('user'))],
|
||||||
|
'phone' => ['nullable', 'numeric', 'digits:10'],
|
||||||
|
'roles' => ['nullable', 'array']
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
86
app/Http/Traits/HasProfilePhoto.php
Normal file
86
app/Http/Traits/HasProfilePhoto.php
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?php namespace App\Http\Traits;
|
||||||
|
/**
|
||||||
|
* @copyright (C) 2024 Notsoweb Software (https://notsoweb.com) - All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait para manejar la foto de perfil
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
trait HasProfilePhoto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Actualizar la foto de perfil del usuario
|
||||||
|
*/
|
||||||
|
public function updateProfilePhoto(UploadedFile $photo, $storagePath = 'photos') : void
|
||||||
|
{
|
||||||
|
tap($this->profile_photo_path, function ($previous) use ($photo, $storagePath) {
|
||||||
|
$this->forceFill([
|
||||||
|
'profile_photo_path' => $photo->storePublicly(
|
||||||
|
$storagePath, ['disk' => $this->profilePhotoDisk()]
|
||||||
|
),
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
if ($previous) {
|
||||||
|
Storage::disk($this->profilePhotoDisk())->delete($previous);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eliminar la foto de perfil del usuario
|
||||||
|
*/
|
||||||
|
public function deleteProfilePhoto() : void
|
||||||
|
{
|
||||||
|
if (is_null($this->profile_photo_path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Storage::disk($this->profilePhotoDisk())->delete($this->profile_photo_path);
|
||||||
|
|
||||||
|
$this->forceFill([
|
||||||
|
'profile_photo_path' => null,
|
||||||
|
])->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener la URL de la foto de perfil del usuario
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Casts\Attribute
|
||||||
|
*/
|
||||||
|
public function profilePhotoUrl(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::get(function (): string {
|
||||||
|
return $this->profile_photo_path
|
||||||
|
? Storage::disk($this->profilePhotoDisk())->url($this->profile_photo_path)
|
||||||
|
: $this->defaultProfilePhotoUrl();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener la URL de la foto de perfil por defecto si no se ha subido ninguna
|
||||||
|
*/
|
||||||
|
protected function defaultProfilePhotoUrl() : string
|
||||||
|
{
|
||||||
|
$name = trim(collect(explode(' ', $this->name))->map(function ($segment) {
|
||||||
|
return mb_substr($segment, 0, 1);
|
||||||
|
})->join(' '));
|
||||||
|
|
||||||
|
return 'https://ui-avatars.com/api/?name='.urlencode($name).'&color=7F9CF5&background=EBF4FF';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener el disco donde se deben almacenar las fotos de perfil
|
||||||
|
*/
|
||||||
|
protected function profilePhotoDisk() : string
|
||||||
|
{
|
||||||
|
return 'profile';
|
||||||
|
}
|
||||||
|
}
|
||||||
24
app/Models/PermissionType.php
Normal file
24
app/Models/PermissionType.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php namespace App\Models;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tipos de permisos
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class PermissionType extends Model
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Atributos permitidos
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
'description'
|
||||||
|
];
|
||||||
|
}
|
||||||
25
app/Models/ResetPassword.php
Normal file
25
app/Models/ResetPassword.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php namespace App\Models;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modelo de contraseñas olvidadas
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class ResetPassword extends Model
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Atributos asignables
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'email',
|
||||||
|
'token',
|
||||||
|
'created_at',
|
||||||
|
];
|
||||||
|
}
|
||||||
@ -1,32 +1,46 @@
|
|||||||
<?php
|
<?php namespace App\Models;
|
||||||
|
/**
|
||||||
namespace App\Models;
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
|
use App\Http\Traits\HasProfilePhoto;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
use Laravel\Passport\HasApiTokens;
|
||||||
class User extends Authenticatable
|
use Spatie\Permission\Traits\HasRoles;
|
||||||
{
|
|
||||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
|
||||||
use HasFactory, Notifiable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that are mass assignable.
|
* Modelo de usuario
|
||||||
*
|
*
|
||||||
* @var array<int, string>
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class User extends Authenticatable
|
||||||
|
{
|
||||||
|
use HasApiTokens,
|
||||||
|
HasFactory,
|
||||||
|
HasRoles,
|
||||||
|
HasProfilePhoto,
|
||||||
|
Notifiable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atributos permitidos
|
||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name',
|
'name',
|
||||||
|
'paternal',
|
||||||
|
'maternal',
|
||||||
'email',
|
'email',
|
||||||
|
'phone',
|
||||||
'password',
|
'password',
|
||||||
|
'profile_photo_path',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that should be hidden for serialization.
|
* Atributos ocultos
|
||||||
*
|
|
||||||
* @var array<int, string>
|
|
||||||
*/
|
*/
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
'password',
|
'password',
|
||||||
@ -34,9 +48,7 @@ class User extends Authenticatable
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the attributes that should be cast.
|
* Atributos que se deben convertir
|
||||||
*
|
|
||||||
* @return array<string, string>
|
|
||||||
*/
|
*/
|
||||||
protected function casts(): array
|
protected function casts(): array
|
||||||
{
|
{
|
||||||
@ -45,4 +57,11 @@ protected function casts(): array
|
|||||||
'password' => 'hashed',
|
'password' => 'hashed',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Los accesores a añadir al modelo en su forma de array
|
||||||
|
*/
|
||||||
|
protected $appends = [
|
||||||
|
'profile_photo_url',
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
57
app/Notifications/ForgotPasswordNotification.php
Normal file
57
app/Notifications/ForgotPasswordNotification.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php namespace App\Notifications;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Descripción
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class ForgotPasswordNotification extends Notification
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new notification instance.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener los canales de entrega de la notificación
|
||||||
|
*/
|
||||||
|
public function via(object $notifiable): array
|
||||||
|
{
|
||||||
|
return ['mail'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener la representación del mensaje de la notificación
|
||||||
|
*/
|
||||||
|
public function toMail(object $notifiable): MailMessage
|
||||||
|
{
|
||||||
|
return (new MailMessage)
|
||||||
|
->subject(__('auth.forgot-password.subject'))
|
||||||
|
->markdown('user.password-forgot');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtener la representación en array de la notificación
|
||||||
|
*/
|
||||||
|
public function toArray(object $notifiable): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
62
app/Notifications/UserNotification.php
Normal file
62
app/Notifications/UserNotification.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php namespace App\Notifications;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Notifications\Messages\BroadcastMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
|
class UserNotification extends Notification
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new notification instance.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public string $title,
|
||||||
|
public string $description,
|
||||||
|
public ?string $message = null,
|
||||||
|
public string $type = 'info',
|
||||||
|
public int $timeout = 20,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the notification's delivery channels.
|
||||||
|
*
|
||||||
|
* @return array<int, string>
|
||||||
|
*/
|
||||||
|
public function via(object $notifiable): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'broadcast',
|
||||||
|
'database'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the array representation of the notification.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(object $notifiable): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'title' => $this->title,
|
||||||
|
'description' => $this->description,
|
||||||
|
'message' => $this->message,
|
||||||
|
'type' => $this->type,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transmitir notificación
|
||||||
|
*/
|
||||||
|
public function toBroadcast($notifiable)
|
||||||
|
{
|
||||||
|
return new BroadcastMessage([
|
||||||
|
'title' => $this->title,
|
||||||
|
'description' => $this->description,
|
||||||
|
'typeNotification' => $this->type,
|
||||||
|
'timeout' => $this->timeout,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,17 +1,31 @@
|
|||||||
<?php
|
<?php namespace App\Providers;
|
||||||
|
/**
|
||||||
namespace App\Providers;
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Laravel\Passport\Passport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proveedor de servicios de la aplicación
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Register any application services.
|
* Registrar cualquier servicio de la aplicación
|
||||||
*/
|
*/
|
||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
//
|
if ($this->app->environment('local')) {
|
||||||
|
$this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class);
|
||||||
|
$this->app->register(TelescopeServiceProvider::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,6 +33,14 @@ public function register(): void
|
|||||||
*/
|
*/
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
//
|
Passport::loadKeysFrom(storage_path('app/keys'));
|
||||||
|
Passport::tokensExpireIn(now()->addDays(30));
|
||||||
|
Passport::refreshTokensExpireIn(now()->addDays(60));
|
||||||
|
Passport::personalAccessTokensExpireIn(now()->addMonths(12));
|
||||||
|
|
||||||
|
// Acceso a Pulse
|
||||||
|
Gate::define('viewPulse', function (User $user) {
|
||||||
|
return $user->hasRole('developer');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
71
app/Providers/TelescopeServiceProvider.php
Normal file
71
app/Providers/TelescopeServiceProvider.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?php namespace App\Providers;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
use Laravel\Telescope\IncomingEntry;
|
||||||
|
use Laravel\Telescope\Telescope;
|
||||||
|
use Laravel\Telescope\TelescopeApplicationServiceProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proveedor de servicios de Telescope
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class TelescopeServiceProvider extends TelescopeApplicationServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Registrar cualquier servicio de la aplicación
|
||||||
|
*/
|
||||||
|
public function register(): void
|
||||||
|
{
|
||||||
|
Telescope::night();
|
||||||
|
|
||||||
|
$this->hideSensitiveRequestDetails();
|
||||||
|
|
||||||
|
$isLocal = $this->app->environment('local');
|
||||||
|
|
||||||
|
Telescope::filter(function (IncomingEntry $entry) use ($isLocal) {
|
||||||
|
return $isLocal ||
|
||||||
|
$entry->isReportableException() ||
|
||||||
|
$entry->isFailedRequest() ||
|
||||||
|
$entry->isFailedJob() ||
|
||||||
|
$entry->isScheduledTask() ||
|
||||||
|
$entry->hasMonitoredTag();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevenir que los detalles de las solicitudes sensibles se registren en Telescope.
|
||||||
|
*/
|
||||||
|
protected function hideSensitiveRequestDetails(): void
|
||||||
|
{
|
||||||
|
if ($this->app->environment('local')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Telescope::hideRequestParameters(['_token']);
|
||||||
|
|
||||||
|
Telescope::hideRequestHeaders([
|
||||||
|
'cookie',
|
||||||
|
'x-csrf-token',
|
||||||
|
'x-xsrf-token',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registrar la puerta de acceso de Telescope.
|
||||||
|
*
|
||||||
|
* Esta puerta determina quién puede acceder a Telescope en entornos no locales.
|
||||||
|
*/
|
||||||
|
protected function gate(): void
|
||||||
|
{
|
||||||
|
Gate::define('viewTelescope', function (User $user) {
|
||||||
|
return $user->hasRole('developer');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
23
app/Schedules/DeleteResetPasswords.php
Normal file
23
app/Schedules/DeleteResetPasswords.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php namespace App\Schedules;
|
||||||
|
/**
|
||||||
|
* @copyright (c) 2024 Notsoweb Software (https://www.notsoweb.com) - All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use App\Models\ResetPassword;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eliminar tokens de reseteos de contraseñas
|
||||||
|
*
|
||||||
|
* @author Moisés Cortés C <moises.cortes@notsoweb.com>
|
||||||
|
*/
|
||||||
|
class DeleteResetPasswords
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Manipulador
|
||||||
|
*/
|
||||||
|
public function __invoke()
|
||||||
|
{
|
||||||
|
ResetPassword::where('created_at', '<', Carbon::now()->subMinutes(10))->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
64
app/Supports/QuerySupport.php
Normal file
64
app/Supports/QuerySupport.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php namespace App\Supports;
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2024 Notsoweb (https://notsoweb.com) - All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Facilita algunas consultas de búsqueda
|
||||||
|
*
|
||||||
|
* @author Moisés de Jesús Cortés Castellanos <ing.moisesdejesuscortesc@notsoweb.com>
|
||||||
|
*
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
class QuerySupport
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Realiza una búsqueda por medio de una clave
|
||||||
|
*/
|
||||||
|
public static function queryByKey(&$model, $key = 'name')
|
||||||
|
{
|
||||||
|
$query = request()->get('q');
|
||||||
|
|
||||||
|
if ($query) {
|
||||||
|
$model = $model->where($key, $query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Realiza una búsqueda por medio de una clave e incluye relaciones anidadas
|
||||||
|
*/
|
||||||
|
public static function queryByKeys(&$model, array $keys = ['name'])
|
||||||
|
{
|
||||||
|
$query = request()->get('q');
|
||||||
|
|
||||||
|
if ($query) {
|
||||||
|
$model = $model->where(function ($x) use ($query, $keys) {
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
if (strpos($key, '.') !== false) {
|
||||||
|
// Si la clave contiene un punto, asumimos que es una relación anidada
|
||||||
|
list($relation, $relationKey) = explode('.', $key);
|
||||||
|
|
||||||
|
$x = ($count == 0)
|
||||||
|
? $x->whereHas($relation, function ($q) use ($query, $relationKey) {
|
||||||
|
$q->where($relationKey, 'LIKE', "%{$query}%");
|
||||||
|
})
|
||||||
|
: $x->orWhereHas($relation, function ($q) use ($query, $relationKey) {
|
||||||
|
$q->where($relationKey, 'LIKE', "%{$query}%");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$x = ($count == 0)
|
||||||
|
? $x->where($key, 'LIKE', "%{$query}%")
|
||||||
|
: $x->orWhere($key, 'LIKE', "%{$query}%");
|
||||||
|
}
|
||||||
|
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $x;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -6,12 +6,25 @@
|
|||||||
|
|
||||||
return Application::configure(basePath: dirname(__DIR__))
|
return Application::configure(basePath: dirname(__DIR__))
|
||||||
->withRouting(
|
->withRouting(
|
||||||
|
api: __DIR__.'/../routes/api.php',
|
||||||
web: __DIR__.'/../routes/web.php',
|
web: __DIR__.'/../routes/web.php',
|
||||||
commands: __DIR__.'/../routes/console.php',
|
commands: __DIR__.'/../routes/console.php',
|
||||||
health: '/up',
|
health: '/up',
|
||||||
)
|
)
|
||||||
|
->withBroadcasting(
|
||||||
|
channels: __DIR__.'/../routes/channels.php',
|
||||||
|
attributes: ['middleware' => ['auth:api']]
|
||||||
|
)
|
||||||
->withMiddleware(function (Middleware $middleware) {
|
->withMiddleware(function (Middleware $middleware) {
|
||||||
//
|
$middleware->validateCsrfTokens(except: [
|
||||||
|
'sanctum/csrf-cookie',
|
||||||
|
'user/*'
|
||||||
|
]);
|
||||||
|
$middleware->alias([
|
||||||
|
'role' => \Spatie\Permission\Middleware\RoleMiddleware::class,
|
||||||
|
'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class,
|
||||||
|
'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class,
|
||||||
|
]);
|
||||||
})
|
})
|
||||||
->withExceptions(function (Exceptions $exceptions) {
|
->withExceptions(function (Exceptions $exceptions) {
|
||||||
//
|
//
|
||||||
|
|||||||
0
bootstrap/cache/.gitignore
vendored
Normal file → Executable file
0
bootstrap/cache/.gitignore
vendored
Normal file → Executable file
@ -2,4 +2,6 @@
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
App\Providers\AppServiceProvider::class,
|
App\Providers\AppServiceProvider::class,
|
||||||
|
Notsoweb\LaravelCore\ServiceProvider::class,
|
||||||
|
Spatie\Permission\PermissionServiceProvider::class,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -8,7 +8,12 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
"laravel/framework": "^11.31",
|
"laravel/framework": "^11.31",
|
||||||
|
"laravel/passport": "^12.0",
|
||||||
|
"laravel/pulse": "^1.2",
|
||||||
|
"laravel/reverb": "^1.0",
|
||||||
"laravel/tinker": "^2.9",
|
"laravel/tinker": "^2.9",
|
||||||
|
"notsoweb/laravel-core": "dev-main",
|
||||||
|
"spatie/laravel-permission": "^6.10",
|
||||||
"tightenco/ziggy": "^2.4"
|
"tightenco/ziggy": "^2.4"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
@ -16,6 +21,7 @@
|
|||||||
"laravel/pail": "^1.1",
|
"laravel/pail": "^1.1",
|
||||||
"laravel/pint": "^1.13",
|
"laravel/pint": "^1.13",
|
||||||
"laravel/sail": "^1.26",
|
"laravel/sail": "^1.26",
|
||||||
|
"laravel/telescope": "^5.2",
|
||||||
"mockery/mockery": "^1.6",
|
"mockery/mockery": "^1.6",
|
||||||
"nunomaduro/collision": "^8.1",
|
"nunomaduro/collision": "^8.1",
|
||||||
"phpunit/phpunit": "^11.0.1"
|
"phpunit/phpunit": "^11.0.1"
|
||||||
@ -51,11 +57,54 @@
|
|||||||
"dev": [
|
"dev": [
|
||||||
"Composer\\Config::disableProcessTimeout",
|
"Composer\\Config::disableProcessTimeout",
|
||||||
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
|
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
|
||||||
|
],
|
||||||
|
"env:dev": [
|
||||||
|
"composer install",
|
||||||
|
"composer run post-root-package-install",
|
||||||
|
"composer run post-create-project-cmd",
|
||||||
|
"@php artisan storage:link"
|
||||||
|
],
|
||||||
|
"env:prod": [
|
||||||
|
"composer install --no-dev",
|
||||||
|
"composer run post-root-package-install",
|
||||||
|
"composer run post-create-project-cmd",
|
||||||
|
"@php artisan storage:link"
|
||||||
|
],
|
||||||
|
"db:dev": [
|
||||||
|
"@php artisan migrate:fresh --seeder=DevSeeder",
|
||||||
|
"@php artisan passport:client --personal --name=Holos"
|
||||||
|
],
|
||||||
|
"db:prod": [
|
||||||
|
"@php artisan migrate:fresh --seed",
|
||||||
|
"@php artisan passport:client --personal --name=Holos"
|
||||||
|
],
|
||||||
|
"jobs:start": [
|
||||||
|
"pm2 start \"php artisan schedule:work\" --name holos-schedules",
|
||||||
|
"pm2 start \"php artisan queue:work\" --name holos-queue"
|
||||||
|
],
|
||||||
|
"jobs:stop": [
|
||||||
|
"pm2 delete holos-schedules",
|
||||||
|
"pm2 delete holos-queue"
|
||||||
|
],
|
||||||
|
"jobs:status": [
|
||||||
|
"pm2 show holos-schedules",
|
||||||
|
"pm2 show holos-queue"
|
||||||
|
],
|
||||||
|
"broadcast:start": [
|
||||||
|
"pm2 start \"php artisan reverb:start\" --name=holos-broadcasts"
|
||||||
|
],
|
||||||
|
"broadcast:stop": [
|
||||||
|
"pm2 delete holos-broadcasts"
|
||||||
|
],
|
||||||
|
"broadcast:status": [
|
||||||
|
"pm2 show holos-broadcasts"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"laravel": {
|
"laravel": {
|
||||||
"dont-discover": []
|
"dont-discover": [
|
||||||
|
"laravel/telescope"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
@ -67,6 +116,6 @@
|
|||||||
"php-http/discovery": true
|
"php-http/discovery": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minimum-stability": "stable",
|
"minimum-stability": "dev",
|
||||||
"prefer-stable": true
|
"prefer-stable": true
|
||||||
}
|
}
|
||||||
|
|||||||
2952
composer.lock
generated
2952
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -12,9 +12,12 @@
|
|||||||
| other UI elements where an application name needs to be displayed.
|
| other UI elements where an application name needs to be displayed.
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
'version' => '0.0.1',
|
||||||
|
|
||||||
'name' => env('APP_NAME', 'Laravel'),
|
'name' => env('APP_NAME', 'Laravel'),
|
||||||
|
|
||||||
|
'pagination' => env('APP_PAGINATION', 25),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Application Environment
|
| Application Environment
|
||||||
@ -122,5 +125,4 @@
|
|||||||
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
|
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
|
||||||
'store' => env('APP_MAINTENANCE_STORE', 'database'),
|
'store' => env('APP_MAINTENANCE_STORE', 'database'),
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@ -40,6 +40,10 @@
|
|||||||
'driver' => 'session',
|
'driver' => 'session',
|
||||||
'provider' => 'users',
|
'provider' => 'users',
|
||||||
],
|
],
|
||||||
|
'api' => [
|
||||||
|
'driver' => 'passport',
|
||||||
|
'provider' => 'users',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -64,11 +68,6 @@
|
|||||||
'driver' => 'eloquent',
|
'driver' => 'eloquent',
|
||||||
'model' => env('AUTH_MODEL', App\Models\User::class),
|
'model' => env('AUTH_MODEL', App\Models\User::class),
|
||||||
],
|
],
|
||||||
|
|
||||||
// 'users' => [
|
|
||||||
// 'driver' => 'database',
|
|
||||||
// 'table' => 'users',
|
|
||||||
// ],
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
82
config/broadcasting.php
Normal file
82
config/broadcasting.php
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Default Broadcaster
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option controls the default broadcaster that will be used by the
|
||||||
|
| framework when an event needs to be broadcast. You may set this to
|
||||||
|
| any of the connections defined in the "connections" array below.
|
||||||
|
|
|
||||||
|
| Supported: "reverb", "pusher", "ably", "redis", "log", "null"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'default' => env('BROADCAST_CONNECTION', 'null'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Broadcast Connections
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may define all of the broadcast connections that will be used
|
||||||
|
| to broadcast events to other systems or over WebSockets. Samples of
|
||||||
|
| each available type of connection are provided inside this array.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'connections' => [
|
||||||
|
|
||||||
|
'reverb' => [
|
||||||
|
'driver' => 'reverb',
|
||||||
|
'key' => env('REVERB_APP_KEY'),
|
||||||
|
'secret' => env('REVERB_APP_SECRET'),
|
||||||
|
'app_id' => env('REVERB_APP_ID'),
|
||||||
|
'options' => [
|
||||||
|
'host' => env('REVERB_HOST'),
|
||||||
|
'port' => env('REVERB_PORT', 443),
|
||||||
|
'scheme' => env('REVERB_SCHEME', 'https'),
|
||||||
|
'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
|
||||||
|
],
|
||||||
|
'client_options' => [
|
||||||
|
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'pusher' => [
|
||||||
|
'driver' => 'pusher',
|
||||||
|
'key' => env('PUSHER_APP_KEY'),
|
||||||
|
'secret' => env('PUSHER_APP_SECRET'),
|
||||||
|
'app_id' => env('PUSHER_APP_ID'),
|
||||||
|
'options' => [
|
||||||
|
'cluster' => env('PUSHER_APP_CLUSTER'),
|
||||||
|
'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com',
|
||||||
|
'port' => env('PUSHER_PORT', 443),
|
||||||
|
'scheme' => env('PUSHER_SCHEME', 'https'),
|
||||||
|
'encrypted' => true,
|
||||||
|
'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
|
||||||
|
],
|
||||||
|
'client_options' => [
|
||||||
|
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'ably' => [
|
||||||
|
'driver' => 'ably',
|
||||||
|
'key' => env('ABLY_KEY'),
|
||||||
|
],
|
||||||
|
|
||||||
|
'log' => [
|
||||||
|
'driver' => 'log',
|
||||||
|
],
|
||||||
|
|
||||||
|
'null' => [
|
||||||
|
'driver' => 'null',
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
33
config/cors.php
Normal file
33
config/cors.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Cross-Origin Resource Sharing (CORS) Configuration
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may configure your settings for cross-origin resource sharing
|
||||||
|
| or "CORS". This determines what cross-origin operations may execute
|
||||||
|
| in web browsers. You are free to adjust these settings as needed.
|
||||||
|
|
|
||||||
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'paths' => ['api/*', 'sanctum/csrf-cookie', 'user/*', 'broadcasting/auth'],
|
||||||
|
|
||||||
|
'allowed_methods' => ['*'],
|
||||||
|
|
||||||
|
'allowed_origins' => explode(',', env('CORS_ALLOWED_ORIGINS')),
|
||||||
|
|
||||||
|
'allowed_origins_patterns' => [],
|
||||||
|
|
||||||
|
'allowed_headers' => ['*'],
|
||||||
|
|
||||||
|
'exposed_headers' => [],
|
||||||
|
|
||||||
|
'max_age' => 0,
|
||||||
|
|
||||||
|
'supports_credentials' => true,
|
||||||
|
];
|
||||||
@ -45,6 +45,14 @@
|
|||||||
'throw' => false,
|
'throw' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'profile' => [
|
||||||
|
'driver' => 'local',
|
||||||
|
'root' => storage_path('app/profile'),
|
||||||
|
'url' => env('APP_URL').'/profile',
|
||||||
|
'visibility' => 'public',
|
||||||
|
'throw' => false,
|
||||||
|
],
|
||||||
|
|
||||||
's3' => [
|
's3' => [
|
||||||
'driver' => 's3',
|
'driver' => 's3',
|
||||||
'key' => env('AWS_ACCESS_KEY_ID'),
|
'key' => env('AWS_ACCESS_KEY_ID'),
|
||||||
@ -56,7 +64,6 @@
|
|||||||
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
|
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
|
||||||
'throw' => false,
|
'throw' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -72,6 +79,6 @@
|
|||||||
|
|
||||||
'links' => [
|
'links' => [
|
||||||
public_path('storage') => storage_path('app/public'),
|
public_path('storage') => storage_path('app/public'),
|
||||||
|
public_path('profile') => storage_path('app/profile'),
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@ -51,7 +51,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
'channels' => [
|
'channels' => [
|
||||||
|
|
||||||
'stack' => [
|
'stack' => [
|
||||||
'driver' => 'stack',
|
'driver' => 'stack',
|
||||||
'channels' => explode(',', env('LOG_STACK', 'single')),
|
'channels' => explode(',', env('LOG_STACK', 'single')),
|
||||||
@ -127,6 +126,9 @@
|
|||||||
'path' => storage_path('logs/laravel.log'),
|
'path' => storage_path('logs/laravel.log'),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'mail' => [
|
||||||
|
'driver' => 'single',
|
||||||
|
'path' => storage_path('logs/mail.log'),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
|
'domain' => env('MAIL_DOMAIN', 'notsoweb.com'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Default Mailer
|
| Default Mailer
|
||||||
|
|||||||
75
config/passport.php
Normal file
75
config/passport.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Passport Guard
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify which authentication guard Passport will use when
|
||||||
|
| authenticating users. This value should correspond with one of your
|
||||||
|
| guards that is already present in your "auth" configuration file.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'guard' => 'web',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Encryption Keys
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Passport uses encryption keys while generating secure access tokens for
|
||||||
|
| your application. By default, the keys are stored as local files but
|
||||||
|
| can be set via environment variables when that is more convenient.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'private_key' => env('PASSPORT_PRIVATE_KEY'),
|
||||||
|
|
||||||
|
'public_key' => env('PASSPORT_PUBLIC_KEY'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Passport Database Connection
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| By default, Passport's models will utilize your application's default
|
||||||
|
| database connection. If you wish to use a different connection you
|
||||||
|
| may specify the configured name of the database connection here.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'connection' => env('PASSPORT_CONNECTION'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Client UUIDs
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| By default, Passport uses auto-incrementing primary keys when assigning
|
||||||
|
| IDs to clients. However, if Passport is installed using the provided
|
||||||
|
| --uuids switch, this will be set to "true" and UUIDs will be used.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'client_uuids' => true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Personal Access Client
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| If you enable client hashing, you should set the personal access client
|
||||||
|
| ID and unhashed secret within your environment file. The values will
|
||||||
|
| get used while issuing fresh personal access tokens to your users.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'personal_access_client' => [
|
||||||
|
'id' => env('PASSPORT_PERSONAL_ACCESS_CLIENT_ID'),
|
||||||
|
'secret' => env('PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET'),
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
186
config/permission.php
Normal file
186
config/permission.php
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
'models' => [
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasPermissions" trait from this package, we need to know which
|
||||||
|
* Eloquent model should be used to retrieve your permissions. Of course, it
|
||||||
|
* is often just the "Permission" model but you may use whatever you like.
|
||||||
|
*
|
||||||
|
* The model you want to use as a Permission model needs to implement the
|
||||||
|
* `Spatie\Permission\Contracts\Permission` contract.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'permission' => Spatie\Permission\Models\Permission::class,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasRoles" trait from this package, we need to know which
|
||||||
|
* Eloquent model should be used to retrieve your roles. Of course, it
|
||||||
|
* is often just the "Role" model but you may use whatever you like.
|
||||||
|
*
|
||||||
|
* The model you want to use as a Role model needs to implement the
|
||||||
|
* `Spatie\Permission\Contracts\Role` contract.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'role' => Spatie\Permission\Models\Role::class,
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
'table_names' => [
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasRoles" trait from this package, we need to know which
|
||||||
|
* table should be used to retrieve your roles. We have chosen a basic
|
||||||
|
* default value but you may easily change it to any table you like.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'roles' => 'roles',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasPermissions" trait from this package, we need to know which
|
||||||
|
* table should be used to retrieve your permissions. We have chosen a basic
|
||||||
|
* default value but you may easily change it to any table you like.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'permissions' => 'permissions',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasPermissions" trait from this package, we need to know which
|
||||||
|
* table should be used to retrieve your models permissions. We have chosen a
|
||||||
|
* basic default value but you may easily change it to any table you like.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'model_has_permissions' => 'model_has_permissions',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasRoles" trait from this package, we need to know which
|
||||||
|
* table should be used to retrieve your models roles. We have chosen a
|
||||||
|
* basic default value but you may easily change it to any table you like.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'model_has_roles' => 'model_has_roles',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasRoles" trait from this package, we need to know which
|
||||||
|
* table should be used to retrieve your roles permissions. We have chosen a
|
||||||
|
* basic default value but you may easily change it to any table you like.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'role_has_permissions' => 'role_has_permissions',
|
||||||
|
],
|
||||||
|
|
||||||
|
'column_names' => [
|
||||||
|
/*
|
||||||
|
* Change this if you want to name the related pivots other than defaults
|
||||||
|
*/
|
||||||
|
'role_pivot_key' => null, //default 'role_id',
|
||||||
|
'permission_pivot_key' => null, //default 'permission_id',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Change this if you want to name the related model primary key other than
|
||||||
|
* `model_id`.
|
||||||
|
*
|
||||||
|
* For example, this would be nice if your primary keys are all UUIDs. In
|
||||||
|
* that case, name this `model_uuid`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'model_morph_key' => 'model_id',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Change this if you want to use the teams feature and your related model's
|
||||||
|
* foreign key is other than `team_id`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'team_foreign_key' => 'team_id',
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When set to true, the method for checking permissions will be registered on the gate.
|
||||||
|
* Set this to false if you want to implement custom logic for checking permissions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'register_permission_check_method' => true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When set to true, Laravel\Octane\Events\OperationTerminated event listener will be registered
|
||||||
|
* this will refresh permissions on every TickTerminated, TaskTerminated and RequestTerminated
|
||||||
|
* NOTE: This should not be needed in most cases, but an Octane/Vapor combination benefited from it.
|
||||||
|
*/
|
||||||
|
'register_octane_reset_listener' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Teams Feature.
|
||||||
|
* When set to true the package implements teams using the 'team_foreign_key'.
|
||||||
|
* If you want the migrations to register the 'team_foreign_key', you must
|
||||||
|
* set this to true before doing the migration.
|
||||||
|
* If you already did the migration then you must make a new migration to also
|
||||||
|
* add 'team_foreign_key' to 'roles', 'model_has_roles', and 'model_has_permissions'
|
||||||
|
* (view the latest version of this package's migration file)
|
||||||
|
*/
|
||||||
|
|
||||||
|
'teams' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Passport Client Credentials Grant
|
||||||
|
* When set to true the package will use Passports Client to check permissions
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use_passport_client_credentials' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When set to true, the required permission names are added to exception messages.
|
||||||
|
* This could be considered an information leak in some contexts, so the default
|
||||||
|
* setting is false here for optimum safety.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'display_permission_in_exception' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When set to true, the required role names are added to exception messages.
|
||||||
|
* This could be considered an information leak in some contexts, so the default
|
||||||
|
* setting is false here for optimum safety.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'display_role_in_exception' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* By default wildcard permission lookups are disabled.
|
||||||
|
* See documentation to understand supported syntax.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'enable_wildcard_permission' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The class to use for interpreting wildcard permissions.
|
||||||
|
* If you need to modify delimiters, override the class and specify its name here.
|
||||||
|
*/
|
||||||
|
// 'permission.wildcard_permission' => Spatie\Permission\WildcardPermission::class,
|
||||||
|
|
||||||
|
/* Cache-specific settings */
|
||||||
|
|
||||||
|
'cache' => [
|
||||||
|
|
||||||
|
/*
|
||||||
|
* By default all permissions are cached for 24 hours to speed up performance.
|
||||||
|
* When permissions or roles are updated the cache is flushed automatically.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'expiration_time' => \DateInterval::createFromDateString('24 hours'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The cache key used to store all permissions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'key' => 'spatie.permission.cache',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* You may optionally indicate a specific cache driver to use for permission and
|
||||||
|
* role caching using any of the `store` drivers listed in the cache.php config
|
||||||
|
* file. Using 'default' here means to use the `default` set in cache.php.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'store' => 'default',
|
||||||
|
],
|
||||||
|
];
|
||||||
232
config/pulse.php
Normal file
232
config/pulse.php
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Laravel\Pulse\Http\Middleware\Authorize;
|
||||||
|
use Laravel\Pulse\Pulse;
|
||||||
|
use Laravel\Pulse\Recorders;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Pulse Domain
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This is the subdomain which the Pulse dashboard will be accessible from.
|
||||||
|
| When set to null, the dashboard will reside under the same domain as
|
||||||
|
| the application. Remember to configure your DNS entries correctly.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'domain' => env('PULSE_DOMAIN'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Pulse Path
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This is the path which the Pulse dashboard will be accessible from. Feel
|
||||||
|
| free to change this path to anything you'd like. Note that this won't
|
||||||
|
| affect the path of the internal API that is never exposed to users.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'path' => env('PULSE_PATH', 'pulse'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Pulse Master Switch
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This configuration option may be used to completely disable all Pulse
|
||||||
|
| data recorders regardless of their individual configurations. This
|
||||||
|
| provides a single option to quickly disable all Pulse recording.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'enabled' => env('PULSE_ENABLED', true),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Pulse Storage Driver
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This configuration option determines which storage driver will be used
|
||||||
|
| while storing entries from Pulse's recorders. In addition, you also
|
||||||
|
| may provide any options to configure the selected storage driver.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'storage' => [
|
||||||
|
'driver' => env('PULSE_STORAGE_DRIVER', 'database'),
|
||||||
|
|
||||||
|
'database' => [
|
||||||
|
'connection' => env('PULSE_DB_CONNECTION'),
|
||||||
|
'chunk' => 1000,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Pulse Ingest Driver
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This configuration options determines the ingest driver that will be used
|
||||||
|
| to capture entries from Pulse's recorders. Ingest drivers are great to
|
||||||
|
| free up your request workers quickly by offloading the data storage.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'ingest' => [
|
||||||
|
'driver' => env('PULSE_INGEST_DRIVER', 'storage'),
|
||||||
|
|
||||||
|
'buffer' => env('PULSE_INGEST_BUFFER', 5_000),
|
||||||
|
|
||||||
|
'trim' => [
|
||||||
|
'lottery' => [1, 1_000],
|
||||||
|
'keep' => '7 days',
|
||||||
|
],
|
||||||
|
|
||||||
|
'redis' => [
|
||||||
|
'connection' => env('PULSE_REDIS_CONNECTION'),
|
||||||
|
'chunk' => 1000,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Pulse Cache Driver
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This configuration option determines the cache driver that will be used
|
||||||
|
| for various tasks, including caching dashboard results, establishing
|
||||||
|
| locks for events that should only occur on one server and signals.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'cache' => env('PULSE_CACHE_DRIVER'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Pulse Route Middleware
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| These middleware will be assigned to every Pulse route, giving you the
|
||||||
|
| chance to add your own middleware to this list or change any of the
|
||||||
|
| existing middleware. Of course, reasonable defaults are provided.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'middleware' => [
|
||||||
|
'web',
|
||||||
|
Authorize::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Pulse Recorders
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following array lists the "recorders" that will be registered with
|
||||||
|
| Pulse, along with their configuration. Recorders gather application
|
||||||
|
| event data from requests and tasks to pass to your ingest driver.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'recorders' => [
|
||||||
|
Recorders\CacheInteractions::class => [
|
||||||
|
'enabled' => env('PULSE_CACHE_INTERACTIONS_ENABLED', true),
|
||||||
|
'sample_rate' => env('PULSE_CACHE_INTERACTIONS_SAMPLE_RATE', 1),
|
||||||
|
'ignore' => [
|
||||||
|
...Pulse::defaultVendorCacheKeys(),
|
||||||
|
],
|
||||||
|
'groups' => [
|
||||||
|
'/^job-exceptions:.*/' => 'job-exceptions:*',
|
||||||
|
// '/:\d+/' => ':*',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
Recorders\Exceptions::class => [
|
||||||
|
'enabled' => env('PULSE_EXCEPTIONS_ENABLED', true),
|
||||||
|
'sample_rate' => env('PULSE_EXCEPTIONS_SAMPLE_RATE', 1),
|
||||||
|
'location' => env('PULSE_EXCEPTIONS_LOCATION', true),
|
||||||
|
'ignore' => [
|
||||||
|
// '/^Package\\\\Exceptions\\\\/',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
Recorders\Queues::class => [
|
||||||
|
'enabled' => env('PULSE_QUEUES_ENABLED', true),
|
||||||
|
'sample_rate' => env('PULSE_QUEUES_SAMPLE_RATE', 1),
|
||||||
|
'ignore' => [
|
||||||
|
// '/^Package\\\\Jobs\\\\/',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
Recorders\Servers::class => [
|
||||||
|
'server_name' => env('PULSE_SERVER_NAME', gethostname()),
|
||||||
|
'directories' => explode(':', env('PULSE_SERVER_DIRECTORIES', '/')),
|
||||||
|
],
|
||||||
|
|
||||||
|
Recorders\SlowJobs::class => [
|
||||||
|
'enabled' => env('PULSE_SLOW_JOBS_ENABLED', true),
|
||||||
|
'sample_rate' => env('PULSE_SLOW_JOBS_SAMPLE_RATE', 1),
|
||||||
|
'threshold' => env('PULSE_SLOW_JOBS_THRESHOLD', 1000),
|
||||||
|
'ignore' => [
|
||||||
|
// '/^Package\\\\Jobs\\\\/',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
Recorders\SlowOutgoingRequests::class => [
|
||||||
|
'enabled' => env('PULSE_SLOW_OUTGOING_REQUESTS_ENABLED', true),
|
||||||
|
'sample_rate' => env('PULSE_SLOW_OUTGOING_REQUESTS_SAMPLE_RATE', 1),
|
||||||
|
'threshold' => env('PULSE_SLOW_OUTGOING_REQUESTS_THRESHOLD', 1000),
|
||||||
|
'ignore' => [
|
||||||
|
// '#^http://127\.0\.0\.1:13714#', // Inertia SSR...
|
||||||
|
],
|
||||||
|
'groups' => [
|
||||||
|
// '#^https://api\.github\.com/repos/.*$#' => 'api.github.com/repos/*',
|
||||||
|
// '#^https?://([^/]*).*$#' => '\1',
|
||||||
|
// '#/\d+#' => '/*',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
Recorders\SlowQueries::class => [
|
||||||
|
'enabled' => env('PULSE_SLOW_QUERIES_ENABLED', true),
|
||||||
|
'sample_rate' => env('PULSE_SLOW_QUERIES_SAMPLE_RATE', 1),
|
||||||
|
'threshold' => env('PULSE_SLOW_QUERIES_THRESHOLD', 1000),
|
||||||
|
'location' => env('PULSE_SLOW_QUERIES_LOCATION', true),
|
||||||
|
'max_query_length' => env('PULSE_SLOW_QUERIES_MAX_QUERY_LENGTH'),
|
||||||
|
'ignore' => [
|
||||||
|
'/(["`])pulse_[\w]+?\1/', // Pulse tables...
|
||||||
|
'/(["`])telescope_[\w]+?\1/', // Telescope tables...
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
Recorders\SlowRequests::class => [
|
||||||
|
'enabled' => env('PULSE_SLOW_REQUESTS_ENABLED', true),
|
||||||
|
'sample_rate' => env('PULSE_SLOW_REQUESTS_SAMPLE_RATE', 1),
|
||||||
|
'threshold' => env('PULSE_SLOW_REQUESTS_THRESHOLD', 1000),
|
||||||
|
'ignore' => [
|
||||||
|
'#^/'.env('PULSE_PATH', 'pulse').'$#', // Pulse dashboard...
|
||||||
|
'#^/telescope#', // Telescope dashboard...
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
Recorders\UserJobs::class => [
|
||||||
|
'enabled' => env('PULSE_USER_JOBS_ENABLED', true),
|
||||||
|
'sample_rate' => env('PULSE_USER_JOBS_SAMPLE_RATE', 1),
|
||||||
|
'ignore' => [
|
||||||
|
// '/^Package\\\\Jobs\\\\/',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
Recorders\UserRequests::class => [
|
||||||
|
'enabled' => env('PULSE_USER_REQUESTS_ENABLED', true),
|
||||||
|
'sample_rate' => env('PULSE_USER_REQUESTS_SAMPLE_RATE', 1),
|
||||||
|
'ignore' => [
|
||||||
|
'#^/'.env('PULSE_PATH', 'pulse').'$#', // Pulse dashboard...
|
||||||
|
'#^/telescope#', // Telescope dashboard...
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
92
config/reverb.php
Normal file
92
config/reverb.php
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Default Reverb Server
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option controls the default server used by Reverb to handle
|
||||||
|
| incoming messages as well as broadcasting message to all your
|
||||||
|
| connected clients. At this time only "reverb" is supported.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'default' => env('REVERB_SERVER', 'reverb'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Reverb Servers
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may define details for each of the supported Reverb servers.
|
||||||
|
| Each server has its own configuration options that are defined in
|
||||||
|
| the array below. You should ensure all the options are present.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'servers' => [
|
||||||
|
|
||||||
|
'reverb' => [
|
||||||
|
'host' => env('REVERB_SERVER_HOST', '0.0.0.0'),
|
||||||
|
'port' => env('REVERB_SERVER_PORT', 8080),
|
||||||
|
'hostname' => env('REVERB_HOST'),
|
||||||
|
'options' => [
|
||||||
|
'tls' => [],
|
||||||
|
],
|
||||||
|
'max_request_size' => env('REVERB_MAX_REQUEST_SIZE', 10_000),
|
||||||
|
'scaling' => [
|
||||||
|
'enabled' => env('REVERB_SCALING_ENABLED', false),
|
||||||
|
'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'),
|
||||||
|
'server' => [
|
||||||
|
'url' => env('REDIS_URL'),
|
||||||
|
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||||
|
'port' => env('REDIS_PORT', '6379'),
|
||||||
|
'username' => env('REDIS_USERNAME'),
|
||||||
|
'password' => env('REDIS_PASSWORD'),
|
||||||
|
'database' => env('REDIS_DB', '0'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'pulse_ingest_interval' => env('REVERB_PULSE_INGEST_INTERVAL', 15),
|
||||||
|
'telescope_ingest_interval' => env('REVERB_TELESCOPE_INGEST_INTERVAL', 15),
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Reverb Applications
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may define how Reverb applications are managed. If you choose
|
||||||
|
| to use the "config" provider, you may define an array of apps which
|
||||||
|
| your server will support, including their connection credentials.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'apps' => [
|
||||||
|
|
||||||
|
'provider' => 'config',
|
||||||
|
|
||||||
|
'apps' => [
|
||||||
|
[
|
||||||
|
'key' => env('REVERB_APP_KEY'),
|
||||||
|
'secret' => env('REVERB_APP_SECRET'),
|
||||||
|
'app_id' => env('REVERB_APP_ID'),
|
||||||
|
'options' => [
|
||||||
|
'host' => env('REVERB_HOST'),
|
||||||
|
'port' => env('REVERB_PORT', 443),
|
||||||
|
'scheme' => env('REVERB_SCHEME', 'https'),
|
||||||
|
'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
|
||||||
|
],
|
||||||
|
'allowed_origins' => ['*'],
|
||||||
|
'ping_interval' => env('REVERB_APP_PING_INTERVAL', 60),
|
||||||
|
'activity_timeout' => env('REVERB_APP_ACTIVITY_TIMEOUT', 30),
|
||||||
|
'max_message_size' => env('REVERB_APP_MAX_MESSAGE_SIZE', 10_000),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
205
config/telescope.php
Normal file
205
config/telescope.php
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Laravel\Telescope\Http\Middleware\Authorize;
|
||||||
|
use Laravel\Telescope\Watchers;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Telescope Master Switch
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option may be used to disable all Telescope watchers regardless
|
||||||
|
| of their individual configuration, which simply provides a single
|
||||||
|
| and convenient way to enable or disable Telescope data storage.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'enabled' => env('TELESCOPE_ENABLED', true),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Telescope Domain
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This is the subdomain where Telescope will be accessible from. If the
|
||||||
|
| setting is null, Telescope will reside under the same domain as the
|
||||||
|
| application. Otherwise, this value will be used as the subdomain.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'domain' => env('TELESCOPE_DOMAIN'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Telescope Path
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This is the URI path where Telescope will be accessible from. Feel free
|
||||||
|
| to change this path to anything you like. Note that the URI will not
|
||||||
|
| affect the paths of its internal API that aren't exposed to users.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'path' => env('TELESCOPE_PATH', 'telescope'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Telescope Storage Driver
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This configuration options determines the storage driver that will
|
||||||
|
| be used to store Telescope's data. In addition, you may set any
|
||||||
|
| custom options as needed by the particular driver you choose.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'driver' => env('TELESCOPE_DRIVER', 'database'),
|
||||||
|
|
||||||
|
'storage' => [
|
||||||
|
'database' => [
|
||||||
|
'connection' => env('DB_CONNECTION', 'mysql'),
|
||||||
|
'chunk' => 1000,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Telescope Queue
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This configuration options determines the queue connection and queue
|
||||||
|
| which will be used to process ProcessPendingUpdate jobs. This can
|
||||||
|
| be changed if you would prefer to use a non-default connection.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'queue' => [
|
||||||
|
'connection' => env('TELESCOPE_QUEUE_CONNECTION', null),
|
||||||
|
'queue' => env('TELESCOPE_QUEUE', null),
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Telescope Route Middleware
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| These middleware will be assigned to every Telescope route, giving you
|
||||||
|
| the chance to add your own middleware to this list or change any of
|
||||||
|
| the existing middleware. Or, you can simply stick with this list.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'middleware' => [
|
||||||
|
'web',
|
||||||
|
Authorize::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Allowed / Ignored Paths & Commands
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following array lists the URI paths and Artisan commands that will
|
||||||
|
| not be watched by Telescope. In addition to this list, some Laravel
|
||||||
|
| commands, like migrations and queue commands, are always ignored.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'only_paths' => [
|
||||||
|
// 'api/*'
|
||||||
|
],
|
||||||
|
|
||||||
|
'ignore_paths' => [
|
||||||
|
'livewire*',
|
||||||
|
'nova-api*',
|
||||||
|
'pulse*',
|
||||||
|
],
|
||||||
|
|
||||||
|
'ignore_commands' => [
|
||||||
|
//
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Telescope Watchers
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following array lists the "watchers" that will be registered with
|
||||||
|
| Telescope. The watchers gather the application's profile data when
|
||||||
|
| a request or task is executed. Feel free to customize this list.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'watchers' => [
|
||||||
|
Watchers\BatchWatcher::class => env('TELESCOPE_BATCH_WATCHER', true),
|
||||||
|
|
||||||
|
Watchers\CacheWatcher::class => [
|
||||||
|
'enabled' => env('TELESCOPE_CACHE_WATCHER', true),
|
||||||
|
'hidden' => [],
|
||||||
|
],
|
||||||
|
|
||||||
|
Watchers\ClientRequestWatcher::class => env('TELESCOPE_CLIENT_REQUEST_WATCHER', true),
|
||||||
|
|
||||||
|
Watchers\CommandWatcher::class => [
|
||||||
|
'enabled' => env('TELESCOPE_COMMAND_WATCHER', true),
|
||||||
|
'ignore' => [],
|
||||||
|
],
|
||||||
|
|
||||||
|
Watchers\DumpWatcher::class => [
|
||||||
|
'enabled' => env('TELESCOPE_DUMP_WATCHER', true),
|
||||||
|
'always' => env('TELESCOPE_DUMP_WATCHER_ALWAYS', false),
|
||||||
|
],
|
||||||
|
|
||||||
|
Watchers\EventWatcher::class => [
|
||||||
|
'enabled' => env('TELESCOPE_EVENT_WATCHER', true),
|
||||||
|
'ignore' => [],
|
||||||
|
],
|
||||||
|
|
||||||
|
Watchers\ExceptionWatcher::class => env('TELESCOPE_EXCEPTION_WATCHER', true),
|
||||||
|
|
||||||
|
Watchers\GateWatcher::class => [
|
||||||
|
'enabled' => env('TELESCOPE_GATE_WATCHER', true),
|
||||||
|
'ignore_abilities' => [],
|
||||||
|
'ignore_packages' => true,
|
||||||
|
'ignore_paths' => [],
|
||||||
|
],
|
||||||
|
|
||||||
|
Watchers\JobWatcher::class => env('TELESCOPE_JOB_WATCHER', true),
|
||||||
|
|
||||||
|
Watchers\LogWatcher::class => [
|
||||||
|
'enabled' => env('TELESCOPE_LOG_WATCHER', true),
|
||||||
|
'level' => 'error',
|
||||||
|
],
|
||||||
|
|
||||||
|
Watchers\MailWatcher::class => env('TELESCOPE_MAIL_WATCHER', true),
|
||||||
|
|
||||||
|
Watchers\ModelWatcher::class => [
|
||||||
|
'enabled' => env('TELESCOPE_MODEL_WATCHER', true),
|
||||||
|
'events' => ['eloquent.*'],
|
||||||
|
'hydrations' => true,
|
||||||
|
],
|
||||||
|
|
||||||
|
Watchers\NotificationWatcher::class => env('TELESCOPE_NOTIFICATION_WATCHER', true),
|
||||||
|
|
||||||
|
Watchers\QueryWatcher::class => [
|
||||||
|
'enabled' => env('TELESCOPE_QUERY_WATCHER', true),
|
||||||
|
'ignore_packages' => true,
|
||||||
|
'ignore_paths' => [],
|
||||||
|
'slow' => 100,
|
||||||
|
],
|
||||||
|
|
||||||
|
Watchers\RedisWatcher::class => env('TELESCOPE_REDIS_WATCHER', true),
|
||||||
|
|
||||||
|
Watchers\RequestWatcher::class => [
|
||||||
|
'enabled' => env('TELESCOPE_REQUEST_WATCHER', true),
|
||||||
|
'size_limit' => env('TELESCOPE_RESPONSE_SIZE_LIMIT', 64),
|
||||||
|
'ignore_http_methods' => [],
|
||||||
|
'ignore_status_codes' => [],
|
||||||
|
],
|
||||||
|
|
||||||
|
Watchers\ScheduleWatcher::class => env('TELESCOPE_SCHEDULE_WATCHER', true),
|
||||||
|
Watchers\ViewWatcher::class => env('TELESCOPE_VIEW_WATCHER', true),
|
||||||
|
],
|
||||||
|
];
|
||||||
@ -14,10 +14,15 @@ public function up(): void
|
|||||||
Schema::create('users', function (Blueprint $table) {
|
Schema::create('users', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
|
$table->string('paternal');
|
||||||
|
$table->string('maternal')->nullable();
|
||||||
|
$table->string('phone')->nullable();
|
||||||
$table->string('email')->unique();
|
$table->string('email')->unique();
|
||||||
$table->timestamp('email_verified_at')->nullable();
|
$table->timestamp('email_verified_at')->nullable();
|
||||||
$table->string('password');
|
$table->string('password');
|
||||||
$table->rememberToken();
|
$table->rememberToken();
|
||||||
|
$table->foreignId('current_team_id')->nullable();
|
||||||
|
$table->string('profile_photo_path', 2048)->nullable();
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('oauth_auth_codes', function (Blueprint $table) {
|
||||||
|
$table->string('id', 100)->primary();
|
||||||
|
$table->unsignedBigInteger('user_id')->index();
|
||||||
|
$table->uuid('client_id');
|
||||||
|
$table->text('scopes')->nullable();
|
||||||
|
$table->boolean('revoked');
|
||||||
|
$table->dateTime('expires_at')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('oauth_auth_codes');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('oauth_access_tokens', function (Blueprint $table) {
|
||||||
|
$table->string('id', 100)->primary();
|
||||||
|
$table->unsignedBigInteger('user_id')->nullable()->index();
|
||||||
|
$table->uuid('client_id');
|
||||||
|
$table->string('name')->nullable();
|
||||||
|
$table->text('scopes')->nullable();
|
||||||
|
$table->boolean('revoked');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->dateTime('expires_at')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('oauth_access_tokens');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('oauth_refresh_tokens', function (Blueprint $table) {
|
||||||
|
$table->string('id', 100)->primary();
|
||||||
|
$table->string('access_token_id', 100)->index();
|
||||||
|
$table->boolean('revoked');
|
||||||
|
$table->dateTime('expires_at')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('oauth_refresh_tokens');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('oauth_clients', function (Blueprint $table) {
|
||||||
|
$table->uuid('id')->primary();
|
||||||
|
$table->unsignedBigInteger('user_id')->nullable()->index();
|
||||||
|
$table->string('name');
|
||||||
|
$table->string('secret', 100)->nullable();
|
||||||
|
$table->string('provider')->nullable();
|
||||||
|
$table->text('redirect');
|
||||||
|
$table->boolean('personal_access_client');
|
||||||
|
$table->boolean('password_client');
|
||||||
|
$table->boolean('revoked');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('oauth_clients');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('oauth_personal_access_clients', function (Blueprint $table) {
|
||||||
|
$table->bigIncrements('id');
|
||||||
|
$table->uuid('client_id');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('oauth_personal_access_clients');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Laravel\Pulse\Support\PulseMigration;
|
||||||
|
|
||||||
|
return new class extends PulseMigration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (! $this->shouldRun()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::create('pulse_values', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->unsignedInteger('timestamp');
|
||||||
|
$table->string('type');
|
||||||
|
$table->mediumText('key');
|
||||||
|
match ($this->driver()) {
|
||||||
|
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
|
||||||
|
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
|
||||||
|
'sqlite' => $table->string('key_hash'),
|
||||||
|
};
|
||||||
|
$table->mediumText('value');
|
||||||
|
|
||||||
|
$table->index('timestamp'); // For trimming...
|
||||||
|
$table->index('type'); // For fast lookups and purging...
|
||||||
|
$table->unique(['type', 'key_hash']); // For data integrity and upserts...
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('pulse_entries', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->unsignedInteger('timestamp');
|
||||||
|
$table->string('type');
|
||||||
|
$table->mediumText('key');
|
||||||
|
match ($this->driver()) {
|
||||||
|
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
|
||||||
|
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
|
||||||
|
'sqlite' => $table->string('key_hash'),
|
||||||
|
};
|
||||||
|
$table->bigInteger('value')->nullable();
|
||||||
|
|
||||||
|
$table->index('timestamp'); // For trimming...
|
||||||
|
$table->index('type'); // For purging...
|
||||||
|
$table->index('key_hash'); // For mapping...
|
||||||
|
$table->index(['timestamp', 'type', 'key_hash', 'value']); // For aggregate queries...
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('pulse_aggregates', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->unsignedInteger('bucket');
|
||||||
|
$table->unsignedMediumInteger('period');
|
||||||
|
$table->string('type');
|
||||||
|
$table->mediumText('key');
|
||||||
|
match ($this->driver()) {
|
||||||
|
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
|
||||||
|
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
|
||||||
|
'sqlite' => $table->string('key_hash'),
|
||||||
|
};
|
||||||
|
$table->string('aggregate');
|
||||||
|
$table->decimal('value', 20, 2);
|
||||||
|
$table->unsignedInteger('count')->nullable();
|
||||||
|
|
||||||
|
$table->unique(['bucket', 'period', 'type', 'aggregate', 'key_hash']); // Force "on duplicate update"...
|
||||||
|
$table->index(['period', 'bucket']); // For trimming...
|
||||||
|
$table->index('type'); // For purging...
|
||||||
|
$table->index(['period', 'type', 'aggregate', 'bucket']); // For aggregate queries...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('pulse_values');
|
||||||
|
Schema::dropIfExists('pulse_entries');
|
||||||
|
Schema::dropIfExists('pulse_aggregates');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the migration connection name.
|
||||||
|
*/
|
||||||
|
public function getConnection(): ?string
|
||||||
|
{
|
||||||
|
return config('telescope.storage.database.connection');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
$schema = Schema::connection($this->getConnection());
|
||||||
|
|
||||||
|
$schema->create('telescope_entries', function (Blueprint $table) {
|
||||||
|
$table->bigIncrements('sequence');
|
||||||
|
$table->uuid('uuid');
|
||||||
|
$table->uuid('batch_id');
|
||||||
|
$table->string('family_hash')->nullable();
|
||||||
|
$table->boolean('should_display_on_index')->default(true);
|
||||||
|
$table->string('type', 20);
|
||||||
|
$table->longText('content');
|
||||||
|
$table->dateTime('created_at')->nullable();
|
||||||
|
|
||||||
|
$table->unique('uuid');
|
||||||
|
$table->index('batch_id');
|
||||||
|
$table->index('family_hash');
|
||||||
|
$table->index('created_at');
|
||||||
|
$table->index(['type', 'should_display_on_index']);
|
||||||
|
});
|
||||||
|
|
||||||
|
$schema->create('telescope_entries_tags', function (Blueprint $table) {
|
||||||
|
$table->uuid('entry_uuid');
|
||||||
|
$table->string('tag');
|
||||||
|
|
||||||
|
$table->primary(['entry_uuid', 'tag']);
|
||||||
|
$table->index('tag');
|
||||||
|
|
||||||
|
$table->foreign('entry_uuid')
|
||||||
|
->references('uuid')
|
||||||
|
->on('telescope_entries')
|
||||||
|
->onDelete('cascade');
|
||||||
|
});
|
||||||
|
|
||||||
|
$schema->create('telescope_monitoring', function (Blueprint $table) {
|
||||||
|
$table->string('tag')->primary();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
$schema = Schema::connection($this->getConnection());
|
||||||
|
|
||||||
|
$schema->dropIfExists('telescope_entries_tags');
|
||||||
|
$schema->dropIfExists('telescope_entries');
|
||||||
|
$schema->dropIfExists('telescope_monitoring');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,166 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('permission_types', function (Blueprint $table) {
|
||||||
|
$table->id('id');
|
||||||
|
$table->string('name');
|
||||||
|
$table->string('description')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tabla de equipos
|
||||||
|
$teams = config('permission.teams');
|
||||||
|
// Nombre de tablas
|
||||||
|
$tableNames = config('permission.table_names');
|
||||||
|
// Nombre columnas
|
||||||
|
$columnNames = config('permission.column_names');
|
||||||
|
// FK de roles
|
||||||
|
$pivotRole = $columnNames['role_pivot_key'] ?? 'role_id';
|
||||||
|
// FK de permisos
|
||||||
|
$pivotPermission = $columnNames['permission_pivot_key'] ?? 'permission_id';
|
||||||
|
|
||||||
|
// No permitir continuar sin tener declarado el nombre de las tablas
|
||||||
|
if (empty($tableNames)) {
|
||||||
|
throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
|
||||||
|
}
|
||||||
|
// No permitir continuar sin tener declarado el nombre de la columna de equipo
|
||||||
|
if ($teams && empty($columnNames['team_foreign_key'] ?? null)) {
|
||||||
|
throw new \Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear tabla de permisos
|
||||||
|
Schema::create($tableNames['permissions'], function (Blueprint $table) {
|
||||||
|
//$table->engine('InnoDB');
|
||||||
|
$table->bigIncrements('id'); // permission id
|
||||||
|
$table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
|
||||||
|
$table->string('guard_name'); // For MyISAM use string('guard_name', 25);
|
||||||
|
$table->string('description')->nullable();
|
||||||
|
$table->foreignId('permission_type_id')
|
||||||
|
->nullable()
|
||||||
|
->constrained()
|
||||||
|
->nullOnDelete();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
$table->unique(['name', 'guard_name']);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Crear tabla de roles
|
||||||
|
Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) {
|
||||||
|
//$table->engine('InnoDB');
|
||||||
|
$table->bigIncrements('id'); // role id
|
||||||
|
if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
|
||||||
|
$table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
|
||||||
|
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
|
||||||
|
}
|
||||||
|
$table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
|
||||||
|
$table->string('guard_name'); // For MyISAM use string('guard_name', 25);
|
||||||
|
$table->string('description')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
if ($teams || config('permission.testing')) {
|
||||||
|
$table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
|
||||||
|
} else {
|
||||||
|
$table->unique(['name', 'guard_name']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Crear tabla de relación de modelos con permisos
|
||||||
|
Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) {
|
||||||
|
$table->unsignedBigInteger($pivotPermission);
|
||||||
|
|
||||||
|
$table->string('model_type');
|
||||||
|
$table->unsignedBigInteger($columnNames['model_morph_key']);
|
||||||
|
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
|
||||||
|
|
||||||
|
$table->foreign($pivotPermission)
|
||||||
|
->references('id') // permission id
|
||||||
|
->on($tableNames['permissions'])
|
||||||
|
->onDelete('cascade');
|
||||||
|
if ($teams) {
|
||||||
|
$table->unsignedBigInteger($columnNames['team_foreign_key']);
|
||||||
|
$table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
|
||||||
|
|
||||||
|
$table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'],
|
||||||
|
'model_has_permissions_permission_model_type_primary');
|
||||||
|
} else {
|
||||||
|
$table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
|
||||||
|
'model_has_permissions_permission_model_type_primary');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Crear tabla de relación de modelos con roles
|
||||||
|
Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) {
|
||||||
|
$table->unsignedBigInteger($pivotRole);
|
||||||
|
|
||||||
|
$table->string('model_type');
|
||||||
|
$table->unsignedBigInteger($columnNames['model_morph_key']);
|
||||||
|
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
|
||||||
|
|
||||||
|
$table->foreign($pivotRole)
|
||||||
|
->references('id') // role id
|
||||||
|
->on($tableNames['roles'])
|
||||||
|
->onDelete('cascade');
|
||||||
|
if ($teams) {
|
||||||
|
$table->unsignedBigInteger($columnNames['team_foreign_key']);
|
||||||
|
$table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
|
||||||
|
|
||||||
|
$table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'],
|
||||||
|
'model_has_roles_role_model_type_primary');
|
||||||
|
} else {
|
||||||
|
$table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'],
|
||||||
|
'model_has_roles_role_model_type_primary');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Crear tabla de relación de roles con permisos
|
||||||
|
Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) {
|
||||||
|
$table->unsignedBigInteger($pivotPermission);
|
||||||
|
$table->unsignedBigInteger($pivotRole);
|
||||||
|
|
||||||
|
$table->foreign($pivotPermission)
|
||||||
|
->references('id') // permission id
|
||||||
|
->on($tableNames['permissions'])
|
||||||
|
->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->foreign($pivotRole)
|
||||||
|
->references('id') // role id
|
||||||
|
->on($tableNames['roles'])
|
||||||
|
->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary');
|
||||||
|
});
|
||||||
|
|
||||||
|
app('cache')
|
||||||
|
->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
|
||||||
|
->forget(config('permission.cache.key'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
$tableNames = config('permission.table_names');
|
||||||
|
|
||||||
|
if (empty($tableNames)) {
|
||||||
|
throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::drop('permission_types');
|
||||||
|
Schema::drop($tableNames['role_has_permissions']);
|
||||||
|
Schema::drop($tableNames['model_has_roles']);
|
||||||
|
Schema::drop($tableNames['model_has_permissions']);
|
||||||
|
Schema::drop($tableNames['roles']);
|
||||||
|
Schema::drop($tableNames['permissions']);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('reset_passwords', function (Blueprint $table) {
|
||||||
|
$table->string('email')->index();
|
||||||
|
$table->string('token');
|
||||||
|
$table->timestamp('created_at')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('reset_passwords');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('notifications', function (Blueprint $table) {
|
||||||
|
$table->uuid('id')->primary();
|
||||||
|
$table->string('type');
|
||||||
|
$table->morphs('notifiable');
|
||||||
|
$table->text('data');
|
||||||
|
$table->timestamp('read_at')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('notifications');
|
||||||
|
}
|
||||||
|
};
|
||||||
15
database/seeders/DevSeeder.php
Normal file
15
database/seeders/DevSeeder.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class DevSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Seed the application's database.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$this->call(RoleSeeder::class);
|
||||||
|
$this->call(UserSeeder::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
database/seeders/RoleSeeder.php
Normal file
49
database/seeders/RoleSeeder.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\PermissionType;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Notsoweb\LaravelCore\Traits\MySql\RolePermission;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
class RoleSeeder extends Seeder
|
||||||
|
{
|
||||||
|
use RolePermission;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$users = PermissionType::create([
|
||||||
|
'name' => 'Usuarios'
|
||||||
|
]);
|
||||||
|
|
||||||
|
[
|
||||||
|
$userIndex,
|
||||||
|
$userCreate,
|
||||||
|
$userEdit,
|
||||||
|
$userDestroy
|
||||||
|
] = $this->onCRUD('users', $users);
|
||||||
|
|
||||||
|
$userSettings = $this->onPermission('users.settings', 'Configuración de usuarios', $users);
|
||||||
|
|
||||||
|
// Desarrollador
|
||||||
|
Role::create([
|
||||||
|
'name' => 'developer',
|
||||||
|
'description' => 'Desarrollador'
|
||||||
|
])->givePermissionTo(Permission::all());
|
||||||
|
|
||||||
|
// Administrador
|
||||||
|
Role::create([
|
||||||
|
'name' => 'admin',
|
||||||
|
'description' => 'Administrador'
|
||||||
|
])->givePermissionTo(
|
||||||
|
$userIndex,
|
||||||
|
$userCreate,
|
||||||
|
$userEdit,
|
||||||
|
$userDestroy,
|
||||||
|
$userSettings
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
32
database/seeders/UserSeeder.php
Normal file
32
database/seeders/UserSeeder.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Notsoweb\LaravelCore\Supports\UserSecureSupport;
|
||||||
|
|
||||||
|
class UserSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$developer = UserSecureSupport::create('developer@notsoweb.com');
|
||||||
|
|
||||||
|
User::create([
|
||||||
|
'name' => 'Developer',
|
||||||
|
'paternal' => 'Notsoweb',
|
||||||
|
'email' => $developer->email,
|
||||||
|
'password' => $developer->hash,
|
||||||
|
])->assignRole(__('developer'));
|
||||||
|
|
||||||
|
$admin = UserSecureSupport::create('admin@notsoweb.com');
|
||||||
|
|
||||||
|
User::create([
|
||||||
|
'name' => 'Developer',
|
||||||
|
'paternal' => 'Notsoweb',
|
||||||
|
'email' => $admin->email,
|
||||||
|
'password' => $admin->hash,
|
||||||
|
])->assignRole(__('admin'));
|
||||||
|
}
|
||||||
|
}
|
||||||
20
lang/en/auth.php
Normal file
20
lang/en/auth.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Authentication Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are used during authentication for various
|
||||||
|
| messages that we need to display to the user. You are free to modify
|
||||||
|
| these language lines according to your application's requirements.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'failed' => 'These credentials do not match our records.',
|
||||||
|
'password' => 'The provided password is incorrect.',
|
||||||
|
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
|
||||||
|
|
||||||
|
];
|
||||||
6
lang/en/cache.php
Normal file
6
lang/en/cache.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'no-cache' => ':label ":var" does not exist in the cache',
|
||||||
|
'save' => ':label ":var" saved in the cache',
|
||||||
|
];
|
||||||
19
lang/en/pagination.php
Normal file
19
lang/en/pagination.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Pagination Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are used by the paginator library to build
|
||||||
|
| the simple pagination links. You are free to change them to anything
|
||||||
|
| you want to customize your views to better match your application.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'previous' => '« Previous',
|
||||||
|
'next' => 'Next »',
|
||||||
|
|
||||||
|
];
|
||||||
23
lang/en/passwords.php
Normal file
23
lang/en/passwords.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Password Reset Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are the default lines which match reasons
|
||||||
|
| that are given by the password broker for a password update attempt
|
||||||
|
| has failed, such as for an invalid token or invalid new password.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'no_match' => 'The provided password does not match your current password.',
|
||||||
|
'reset' => 'Your password has been reset!',
|
||||||
|
'sent' => 'We have emailed your password reset link!',
|
||||||
|
'throttled' => 'Please wait before retrying.',
|
||||||
|
'token' => 'This password reset token is invalid.',
|
||||||
|
'user' => "We can't find a user with that email address.",
|
||||||
|
|
||||||
|
];
|
||||||
174
lang/en/validation.php
Normal file
174
lang/en/validation.php
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Validation Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines contain the default error messages used by
|
||||||
|
| the validator class. Some of these rules have multiple versions such
|
||||||
|
| as the size rules. Feel free to tweak each of these messages here.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'accepted' => 'The :attribute must be accepted.',
|
||||||
|
'accepted_if' => 'The :attribute must be accepted when :other is :value.',
|
||||||
|
'active_url' => 'The :attribute is not a valid URL.',
|
||||||
|
'after' => 'The :attribute must be a date after :date.',
|
||||||
|
'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
|
||||||
|
'alpha' => 'The :attribute must only contain letters.',
|
||||||
|
'alpha_dash' => 'The :attribute must only contain letters, numbers, dashes and underscores.',
|
||||||
|
'alpha_num' => 'The :attribute must only contain letters and numbers.',
|
||||||
|
'array' => 'The :attribute must be an array.',
|
||||||
|
'before' => 'The :attribute must be a date before :date.',
|
||||||
|
'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
|
||||||
|
'between' => [
|
||||||
|
'array' => 'The :attribute must have between :min and :max items.',
|
||||||
|
'file' => 'The :attribute must be between :min and :max kilobytes.',
|
||||||
|
'numeric' => 'The :attribute must be between :min and :max.',
|
||||||
|
'string' => 'The :attribute must be between :min and :max characters.',
|
||||||
|
],
|
||||||
|
'boolean' => 'The :attribute field must be true or false.',
|
||||||
|
'confirmed' => 'The :attribute confirmation does not match.',
|
||||||
|
'current_password' => 'The password is incorrect.',
|
||||||
|
'date' => 'The :attribute is not a valid date.',
|
||||||
|
'date_equals' => 'The :attribute must be a date equal to :date.',
|
||||||
|
'date_format' => 'The :attribute does not match the format :format.',
|
||||||
|
'declined' => 'The :attribute must be declined.',
|
||||||
|
'declined_if' => 'The :attribute must be declined when :other is :value.',
|
||||||
|
'different' => 'The :attribute and :other must be different.',
|
||||||
|
'digits' => 'The :attribute must be :digits digits.',
|
||||||
|
'digits_between' => 'The :attribute must be between :min and :max digits.',
|
||||||
|
'dimensions' => 'The :attribute has invalid image dimensions.',
|
||||||
|
'distinct' => 'The :attribute field has a duplicate value.',
|
||||||
|
'doesnt_end_with' => 'The :attribute may not end with one of the following: :values.',
|
||||||
|
'doesnt_start_with' => 'The :attribute may not start with one of the following: :values.',
|
||||||
|
'email' => 'The :attribute must be a valid email address.',
|
||||||
|
'ends_with' => 'The :attribute must end with one of the following: :values.',
|
||||||
|
'enum' => 'The selected :attribute is invalid.',
|
||||||
|
'exists' => 'The selected :attribute is invalid.',
|
||||||
|
'file' => 'The :attribute must be a file.',
|
||||||
|
'filled' => 'The :attribute field must have a value.',
|
||||||
|
'gt' => [
|
||||||
|
'array' => 'The :attribute must have more than :value items.',
|
||||||
|
'file' => 'The :attribute must be greater than :value kilobytes.',
|
||||||
|
'numeric' => 'The :attribute must be greater than :value.',
|
||||||
|
'string' => 'The :attribute must be greater than :value characters.',
|
||||||
|
],
|
||||||
|
'gte' => [
|
||||||
|
'array' => 'The :attribute must have :value items or more.',
|
||||||
|
'file' => 'The :attribute must be greater than or equal to :value kilobytes.',
|
||||||
|
'numeric' => 'The :attribute must be greater than or equal to :value.',
|
||||||
|
'string' => 'The :attribute must be greater than or equal to :value characters.',
|
||||||
|
],
|
||||||
|
'image' => 'The :attribute must be an image.',
|
||||||
|
'in' => 'The selected :attribute is invalid.',
|
||||||
|
'in_array' => 'The :attribute field does not exist in :other.',
|
||||||
|
'integer' => 'The :attribute must be an integer.',
|
||||||
|
'ip' => 'The :attribute must be a valid IP address.',
|
||||||
|
'ipv4' => 'The :attribute must be a valid IPv4 address.',
|
||||||
|
'ipv6' => 'The :attribute must be a valid IPv6 address.',
|
||||||
|
'json' => 'The :attribute must be a valid JSON string.',
|
||||||
|
'lt' => [
|
||||||
|
'array' => 'The :attribute must have less than :value items.',
|
||||||
|
'file' => 'The :attribute must be less than :value kilobytes.',
|
||||||
|
'numeric' => 'The :attribute must be less than :value.',
|
||||||
|
'string' => 'The :attribute must be less than :value characters.',
|
||||||
|
],
|
||||||
|
'lte' => [
|
||||||
|
'array' => 'The :attribute must not have more than :value items.',
|
||||||
|
'file' => 'The :attribute must be less than or equal to :value kilobytes.',
|
||||||
|
'numeric' => 'The :attribute must be less than or equal to :value.',
|
||||||
|
'string' => 'The :attribute must be less than or equal to :value characters.',
|
||||||
|
],
|
||||||
|
'mac_address' => 'The :attribute must be a valid MAC address.',
|
||||||
|
'max' => [
|
||||||
|
'array' => 'The :attribute must not have more than :max items.',
|
||||||
|
'file' => 'The :attribute must not be greater than :max kilobytes.',
|
||||||
|
'numeric' => 'The :attribute must not be greater than :max.',
|
||||||
|
'string' => 'The :attribute must not be greater than :max characters.',
|
||||||
|
],
|
||||||
|
'max_digits' => 'The :attribute must not have more than :max digits.',
|
||||||
|
'mimes' => 'The :attribute must be a file of type: :values.',
|
||||||
|
'mimetypes' => 'The :attribute must be a file of type: :values.',
|
||||||
|
'min' => [
|
||||||
|
'array' => 'The :attribute must have at least :min items.',
|
||||||
|
'file' => 'The :attribute must be at least :min kilobytes.',
|
||||||
|
'numeric' => 'The :attribute must be at least :min.',
|
||||||
|
'string' => 'The :attribute must be at least :min characters.',
|
||||||
|
],
|
||||||
|
'min_digits' => 'The :attribute must have at least :min digits.',
|
||||||
|
'multiple_of' => 'The :attribute must be a multiple of :value.',
|
||||||
|
'not_in' => 'The selected :attribute is invalid.',
|
||||||
|
'not_regex' => 'The :attribute format is invalid.',
|
||||||
|
'numeric' => 'The :attribute must be a number.',
|
||||||
|
'password' => [
|
||||||
|
'letters' => 'The :attribute must contain at least one letter.',
|
||||||
|
'mixed' => 'The :attribute must contain at least one uppercase and one lowercase letter.',
|
||||||
|
'numbers' => 'The :attribute must contain at least one number.',
|
||||||
|
'symbols' => 'The :attribute must contain at least one symbol.',
|
||||||
|
'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.',
|
||||||
|
],
|
||||||
|
'present' => 'The :attribute field must be present.',
|
||||||
|
'prohibited' => 'The :attribute field is prohibited.',
|
||||||
|
'prohibited_if' => 'The :attribute field is prohibited when :other is :value.',
|
||||||
|
'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.',
|
||||||
|
'prohibits' => 'The :attribute field prohibits :other from being present.',
|
||||||
|
'regex' => 'The :attribute format is invalid.',
|
||||||
|
'required' => 'The :attribute field is required.',
|
||||||
|
'required_array_keys' => 'The :attribute field must contain entries for: :values.',
|
||||||
|
'required_if' => 'The :attribute field is required when :other is :value.',
|
||||||
|
'required_if_accepted' => 'The :attribute field is required when :other is accepted.',
|
||||||
|
'required_unless' => 'The :attribute field is required unless :other is in :values.',
|
||||||
|
'required_with' => 'The :attribute field is required when :values is present.',
|
||||||
|
'required_with_all' => 'The :attribute field is required when :values are present.',
|
||||||
|
'required_without' => 'The :attribute field is required when :values is not present.',
|
||||||
|
'required_without_all' => 'The :attribute field is required when none of :values are present.',
|
||||||
|
'same' => 'The :attribute and :other must match.',
|
||||||
|
'size' => [
|
||||||
|
'array' => 'The :attribute must contain :size items.',
|
||||||
|
'file' => 'The :attribute must be :size kilobytes.',
|
||||||
|
'numeric' => 'The :attribute must be :size.',
|
||||||
|
'string' => 'The :attribute must be :size characters.',
|
||||||
|
],
|
||||||
|
'starts_with' => 'The :attribute must start with one of the following: :values.',
|
||||||
|
'string' => 'The :attribute must be a string.',
|
||||||
|
'timezone' => 'The :attribute must be a valid timezone.',
|
||||||
|
'unique' => 'The :attribute has already been taken.',
|
||||||
|
'uploaded' => 'The :attribute failed to upload.',
|
||||||
|
'url' => 'The :attribute must be a valid URL.',
|
||||||
|
'uuid' => 'The :attribute must be a valid UUID.',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Custom Validation Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify custom validation messages for attributes using the
|
||||||
|
| convention "attribute.rule" to name the lines. This makes it quick to
|
||||||
|
| specify a specific custom language line for a given attribute rule.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'custom' => [
|
||||||
|
'attribute-name' => [
|
||||||
|
'rule-name' => 'custom-message',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Custom Validation Attributes
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are used to swap our attribute placeholder
|
||||||
|
| with something more reader friendly such as "E-Mail Address" instead
|
||||||
|
| of "email". This simply helps us make our message more expressive.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'attributes' => [],
|
||||||
|
|
||||||
|
];
|
||||||
4
lang/es.json
Normal file
4
lang/es.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"Female": "Femenino",
|
||||||
|
"Male": "Masculino"
|
||||||
|
}
|
||||||
24
lang/es/auth.php
Normal file
24
lang/es/auth.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Authentication Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are used during authentication for various
|
||||||
|
| messages that we need to display to the user. You are free to modify
|
||||||
|
| these language lines according to your application's requirements.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'failed' => 'Estas credenciales no coinciden con nuestros registros.',
|
||||||
|
'password' => 'The provided password is incorrect.',
|
||||||
|
'throttle' => 'Demasiados intentos de acceso. Por favor inténtelo de nuevo en :seconds segundos.',
|
||||||
|
'forgot' => [
|
||||||
|
'subject' => 'Recuperación de contraseña',
|
||||||
|
'line' => 'Por favor, haga clic en el siguiente enlace para restaurar su contraseña. El enlace expira en 15 minutos.',
|
||||||
|
'button' => 'Restaurar contraseña',
|
||||||
|
],
|
||||||
|
];
|
||||||
6
lang/es/cache.php
Normal file
6
lang/es/cache.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'no-cache' => ':label ":var" no existe en la cache',
|
||||||
|
'save' => ':label ":var" guardado en la cache',
|
||||||
|
];
|
||||||
5
lang/es/login.php
Normal file
5
lang/es/login.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'required' => 'Es necesario autenticarse para continuar.'
|
||||||
|
];
|
||||||
19
lang/es/pagination.php
Normal file
19
lang/es/pagination.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Pagination Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are used by the paginator library to build
|
||||||
|
| the simple pagination links. You are free to change them to anything
|
||||||
|
| you want to customize your views to better match your application.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'previous' => '« Anterior',
|
||||||
|
'next' => 'Siguiente »',
|
||||||
|
|
||||||
|
];
|
||||||
24
lang/es/passwords.php
Normal file
24
lang/es/passwords.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Password Reset Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are the default lines which match reasons
|
||||||
|
| that are given by the password broker for a password update attempt
|
||||||
|
| has failed, such as for an invalid token or invalid new password.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'no_match' => 'La contraseña proporcionada no coincide con su contraseña actual.',
|
||||||
|
'reset' => '¡Su contraseña ha sido restablecida!',
|
||||||
|
'sent' => '¡Recordatorio de contraseña enviado!',
|
||||||
|
'token' => 'Este token de restablecimiento de contraseña es inválido.',
|
||||||
|
'user' => 'No se ha encontrado un usuario con esa dirección de correo.',
|
||||||
|
'throttled' => 'Por favor espere antes de volver a intentarlo.',
|
||||||
|
'password' => 'Las contraseñas deben tener al menos seis caracteres y coincidir con la confirmación.',
|
||||||
|
'verify' => 'Si el correo :email existe, te enviaremos un correo con el token de recuperación.'
|
||||||
|
];
|
||||||
174
lang/es/validation.php
Normal file
174
lang/es/validation.php
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Validation Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines contain the default error messages used by
|
||||||
|
| the validator class. Some of these rules have multiple versions such
|
||||||
|
| as the size rules. Feel free to tweak each of these messages here.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'accepted' => 'El campo :attribute debe ser aceptado.',
|
||||||
|
'active_url' => 'El campo :attribute no es una URL válida.',
|
||||||
|
'after' => 'El campo :attribute debe ser una fecha posterior a :date.',
|
||||||
|
'after_or_equal' => 'El campo :attribute debe ser una fecha posterior o igual a :date.',
|
||||||
|
'alpha' => 'El campo :attribute solo puede contener letras.',
|
||||||
|
'alpha_dash' => 'El campo :attribute solo puede contener letras, números, guiones y guiones bajos.',
|
||||||
|
'alpha_num' => 'El campo :attribute solo puede contener letras y números.',
|
||||||
|
'array' => 'El campo :attribute debe ser un array.',
|
||||||
|
'before' => 'El campo :attribute debe ser una fecha anterior a :date.',
|
||||||
|
'before_or_equal' => 'El campo :attribute debe ser una fecha anterior o igual a :date.',
|
||||||
|
'between' => [
|
||||||
|
'numeric' => 'El campo :attribute debe ser un valor entre :min y :max.',
|
||||||
|
'file' => 'El archivo :attribute debe pesar entre :min y :max kilobytes.',
|
||||||
|
'string' => 'El campo :attribute debe contener entre :min y :max caracteres.',
|
||||||
|
'array' => 'El campo :attribute debe contener entre :min y :max elementos.',
|
||||||
|
],
|
||||||
|
'boolean' => 'El campo :attribute debe ser verdadero o falso.',
|
||||||
|
'confirmed' => 'El campo confirmación de :attribute no coincide.',
|
||||||
|
'current_password' => 'La contraseñas son diferentes',
|
||||||
|
'date' => 'El campo :attribute no corresponde con una fecha válida.',
|
||||||
|
'date_equals' => 'El campo :attribute debe ser una fecha igual a :date.',
|
||||||
|
'date_format' => 'El campo :attribute no corresponde con el formato de fecha :format.',
|
||||||
|
'different' => 'Los campos :attribute y :other deben ser diferentes.',
|
||||||
|
'digits' => 'El campo :attribute debe ser un número de :digits dígitos.',
|
||||||
|
'digits_between' => 'El campo :attribute debe contener entre :min y :max dígitos.',
|
||||||
|
'dimensions' => 'El campo :attribute tiene dimensiones de imagen inválidas.',
|
||||||
|
'distinct' => 'El campo :attribute tiene un valor duplicado.',
|
||||||
|
'email' => 'El campo :attribute debe ser una dirección de correo válida.',
|
||||||
|
'ends_with' => 'El campo :attribute debe finalizar con alguno de los siguientes valores: :values',
|
||||||
|
'exists' => 'El campo :attribute seleccionado no existe.',
|
||||||
|
'exist_section' => 'Este :attribute no existe.',
|
||||||
|
'file' => 'El campo :attribute debe ser un archivo.',
|
||||||
|
'filled' => 'El campo :attribute debe tener un valor.',
|
||||||
|
'gt' => [
|
||||||
|
'numeric' => 'El campo :attribute debe ser mayor a :value.',
|
||||||
|
'file' => 'El archivo :attribute debe pesar más de :value kilobytes.',
|
||||||
|
'string' => 'El campo :attribute debe contener más de :value caracteres.',
|
||||||
|
'array' => 'El campo :attribute debe contener más de :value elementos.',
|
||||||
|
],
|
||||||
|
'gte' => [
|
||||||
|
'numeric' => 'El campo :attribute debe ser mayor o igual a :value.',
|
||||||
|
'file' => 'El archivo :attribute debe pesar :value o más kilobytes.',
|
||||||
|
'string' => 'El campo :attribute debe contener :value o más caracteres.',
|
||||||
|
'array' => 'El campo :attribute debe contener :value o más elementos.',
|
||||||
|
],
|
||||||
|
'image' => 'El campo :attribute debe ser una imagen.',
|
||||||
|
'in' => 'El campo :attribute es inválido.',
|
||||||
|
'in_array' => 'El campo :attribute no existe en :other.',
|
||||||
|
'integer' => 'El campo :attribute debe ser un número entero.',
|
||||||
|
'ip' => 'El campo :attribute debe ser una dirección IP válida.',
|
||||||
|
'ipv4' => 'El campo :attribute debe ser una dirección IPv4 válida.',
|
||||||
|
'ipv6' => 'El campo :attribute debe ser una dirección IPv6 válida.',
|
||||||
|
'json' => 'El campo :attribute debe ser una cadena de texto JSON válida.',
|
||||||
|
'lt' => [
|
||||||
|
'numeric' => 'El campo :attribute debe ser menor a :value.',
|
||||||
|
'file' => 'El archivo :attribute debe pesar menos de :value kilobytes.',
|
||||||
|
'string' => 'El campo :attribute debe contener menos de :value caracteres.',
|
||||||
|
'array' => 'El campo :attribute debe contener menos de :value elementos.',
|
||||||
|
],
|
||||||
|
'lte' => [
|
||||||
|
'numeric' => 'El campo :attribute debe ser menor o igual a :value.',
|
||||||
|
'file' => 'El archivo :attribute debe pesar :value o menos kilobytes.',
|
||||||
|
'string' => 'El campo :attribute debe contener :value o menos caracteres.',
|
||||||
|
'array' => 'El campo :attribute debe contener :value o menos elementos.',
|
||||||
|
],
|
||||||
|
'max' => [
|
||||||
|
'numeric' => 'El campo :attribute no debe ser mayor a :max.',
|
||||||
|
'file' => 'El archivo :attribute no debe pesar más de :max kilobytes.',
|
||||||
|
'string' => 'El campo :attribute no debe contener más de :max caracteres.',
|
||||||
|
'array' => 'El campo :attribute no debe contener más de :max elementos.',
|
||||||
|
],
|
||||||
|
'mimes' => 'El campo :attribute debe ser un archivo de tipo: :values.',
|
||||||
|
'mimetypes' => 'El campo :attribute debe ser un archivo de tipo: :values.',
|
||||||
|
'min' => [
|
||||||
|
'numeric' => 'El campo :attribute debe ser al menos :min.',
|
||||||
|
'file' => 'El archivo :attribute debe pesar al menos :min kilobytes.',
|
||||||
|
'string' => 'El campo :attribute debe contener al menos :min caracteres.',
|
||||||
|
'array' => 'El campo :attribute debe contener al menos :min elementos.',
|
||||||
|
],
|
||||||
|
'not_in' => 'El campo :attribute seleccionado es inválido.',
|
||||||
|
'not_regex' => 'El formato del campo :attribute es inválido.',
|
||||||
|
'numeric' => 'El campo :attribute debe ser un número.',
|
||||||
|
'password' => 'La contraseña es incorrecta.',
|
||||||
|
'present' => 'El campo :attribute debe estar presente.',
|
||||||
|
'regex' => 'El formato del campo :attribute es inválido.',
|
||||||
|
'required' => 'El campo :attribute es obligatorio.',
|
||||||
|
'required_if' => 'El campo :attribute es obligatorio cuando el campo :other es :value.',
|
||||||
|
'required_unless' => 'El campo :attribute es requerido a menos que :other se encuentre en :values.',
|
||||||
|
'required_with' => 'El campo :attribute es obligatorio cuando :values está presente.',
|
||||||
|
'required_with_all' => 'El campo :attribute es obligatorio cuando :values están presentes.',
|
||||||
|
'required_without' => 'El campo :attribute es obligatorio cuando :values no está presente.',
|
||||||
|
'required_without_all' => 'El campo :attribute es obligatorio cuando ninguno de los campos :values están presentes.',
|
||||||
|
'same' => 'Los campos :attribute y :other deben coincidir.',
|
||||||
|
'size' => [
|
||||||
|
'numeric' => 'El campo :attribute debe ser :size.',
|
||||||
|
'file' => 'El archivo :attribute debe pesar :size kilobytes.',
|
||||||
|
'string' => 'El campo :attribute debe contener :size caracteres.',
|
||||||
|
'array' => 'El campo :attribute debe contener :size elementos.',
|
||||||
|
],
|
||||||
|
'starts_with' => 'El campo :attribute debe comenzar con uno de los siguientes valores: :values',
|
||||||
|
'string' => 'El campo :attribute debe ser una cadena de caracteres.',
|
||||||
|
'timezone' => 'El campo :attribute debe ser una zona horaria válida.',
|
||||||
|
'unique' => 'El valor del campo :attribute ya está en uso.',
|
||||||
|
'uploaded' => 'El campo :attribute no se pudo subir.',
|
||||||
|
'url' => 'El formato del campo :attribute es inválido.',
|
||||||
|
'uuid' => 'El campo :attribute debe ser un UUID válido.',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Custom Validation Language Lines
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may specify custom validation messages for attributes using the
|
||||||
|
| convention "attribute.rule" to name the lines. This makes it quick to
|
||||||
|
| specify a specific custom language line for a given attribute rule.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'custom' => [
|
||||||
|
'attribute-name' => [
|
||||||
|
'rule-name' => 'custom-message',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Custom Validation Attributes
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The following language lines are used to swap attribute place-holders
|
||||||
|
| with something more reader friendly such as E-Mail Address instead
|
||||||
|
| of "email". This simply helps us make messages a little cleaner.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'attributes' => [
|
||||||
|
'barcode' => 'código de barras',
|
||||||
|
'section.number' => 'sección',
|
||||||
|
'name' => 'nombre',
|
||||||
|
'paternal' => 'apellido paterno',
|
||||||
|
'maternal' => 'apellido materno',
|
||||||
|
'email' => 'correo',
|
||||||
|
'phone' => 'teléfono',
|
||||||
|
'type_ck' => 'tipo',
|
||||||
|
'*.name' => 'nombre',
|
||||||
|
'*.paternal' => 'apellido paterno',
|
||||||
|
'*.maternal' => 'apellido materno',
|
||||||
|
'*.email' => 'correo',
|
||||||
|
'*.phone' => 'teléfono',
|
||||||
|
'*.street' => 'calle',
|
||||||
|
'*.inner_number' => 'número interior',
|
||||||
|
'*.outer_number' => 'número exterior',
|
||||||
|
'*.colony' => 'colonia',
|
||||||
|
'*.zipcode' => 'código postal',
|
||||||
|
'*.expiration' => 'vencimiento',
|
||||||
|
'*.section' => 'sección',
|
||||||
|
'*.voter_identifier' => 'clave de elector'
|
||||||
|
],
|
||||||
|
];
|
||||||
2948
package-lock.json
generated
Normal file
2948
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -9,8 +9,10 @@
|
|||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"axios": "^1.7.4",
|
"axios": "^1.7.4",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.0.1",
|
||||||
|
"laravel-echo": "^1.17.1",
|
||||||
"laravel-vite-plugin": "^1.0",
|
"laravel-vite-plugin": "^1.0",
|
||||||
"postcss": "^8.4.47",
|
"postcss": "^8.4.47",
|
||||||
|
"pusher-js": "^8.4.0-rc2",
|
||||||
"tailwindcss": "^3.4.13",
|
"tailwindcss": "^3.4.13",
|
||||||
"vite": "^5.0"
|
"vite": "^5.0"
|
||||||
}
|
}
|
||||||
|
|||||||
7
permission.sh
Executable file
7
permission.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
read -p "Usuario del sistema: " myuser
|
||||||
|
|
||||||
|
chown -R $myuser:www-data bootstrap/cache/ storage/
|
||||||
|
chmod -R 775 bootstrap/cache/ storage/
|
||||||
|
|
||||||
|
echo "Done!"
|
||||||
1
public/profile
Symbolic link
1
public/profile
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
/var/www/notsoweb/holos.backend/storage/app/profile
|
||||||
8
resources/js/bootstrap.js
vendored
8
resources/js/bootstrap.js
vendored
@ -2,3 +2,11 @@ import axios from 'axios';
|
|||||||
window.axios = axios;
|
window.axios = axios;
|
||||||
|
|
||||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Echo exposes an expressive API for subscribing to channels and listening
|
||||||
|
* for events that are broadcast by Laravel. Echo and event broadcasting
|
||||||
|
* allow your team to quickly build robust real-time web applications.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import './echo';
|
||||||
|
|||||||
14
resources/js/echo.js
Normal file
14
resources/js/echo.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import Echo from 'laravel-echo';
|
||||||
|
|
||||||
|
import Pusher from 'pusher-js';
|
||||||
|
window.Pusher = Pusher;
|
||||||
|
|
||||||
|
window.Echo = new Echo({
|
||||||
|
broadcaster: 'reverb',
|
||||||
|
key: import.meta.env.VITE_REVERB_APP_KEY,
|
||||||
|
wsHost: import.meta.env.VITE_REVERB_HOST,
|
||||||
|
wsPort: import.meta.env.VITE_REVERB_PORT ?? 80,
|
||||||
|
wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
|
||||||
|
forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
|
||||||
|
enabledTransports: ['ws', 'wss'],
|
||||||
|
});
|
||||||
12
resources/views/mail/forgot-password-mail.blade.php
Normal file
12
resources/views/mail/forgot-password-mail.blade.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<x-mail::message>
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
The body of your message.
|
||||||
|
|
||||||
|
<x-mail::button :url="''">
|
||||||
|
Button Text
|
||||||
|
</x-mail::button>
|
||||||
|
|
||||||
|
Thanks,<br>
|
||||||
|
{{ config('app.name') }}
|
||||||
|
</x-mail::message>
|
||||||
10
resources/views/user/password-forgot.blade.php
Normal file
10
resources/views/user/password-forgot.blade.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<x-mail::message>
|
||||||
|
{{ __('auth.forgot.line') }}
|
||||||
|
|
||||||
|
<x-mail::button :url="env('APP_FRONTEND_URL') . '/auth.html#/reset-password?code=12345234234'">
|
||||||
|
{{ __('auth.forgot.button') }}
|
||||||
|
</x-mail::button>
|
||||||
|
|
||||||
|
{{ __('thanks')}},<br>
|
||||||
|
{{ config('app.name') }}
|
||||||
|
</x-mail::message>
|
||||||
24
resources/views/vendor/mail/html/button.blade.php
vendored
Normal file
24
resources/views/vendor/mail/html/button.blade.php
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
@props([
|
||||||
|
'url',
|
||||||
|
'color' => 'primary',
|
||||||
|
'align' => 'center',
|
||||||
|
])
|
||||||
|
<table class="action" align="{{ $align }}" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td align="{{ $align }}">
|
||||||
|
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td align="{{ $align }}">
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="{{ $url }}" class="button button-{{ $color }}" target="_blank" rel="noopener">{{ $slot }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
11
resources/views/vendor/mail/html/footer.blade.php
vendored
Normal file
11
resources/views/vendor/mail/html/footer.blade.php
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<table class="footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td class="content-cell" align="center">
|
||||||
|
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
12
resources/views/vendor/mail/html/header.blade.php
vendored
Normal file
12
resources/views/vendor/mail/html/header.blade.php
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
@props(['url'])
|
||||||
|
<tr>
|
||||||
|
<td class="header">
|
||||||
|
<a href="{{ $url }}" style="display: inline-block;">
|
||||||
|
@if (trim($slot) === 'Laravel')
|
||||||
|
<img src="https://laravel.com/img/notification-logo.png" class="logo" alt="Laravel Logo">
|
||||||
|
@else
|
||||||
|
{{ $slot }}
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
58
resources/views/vendor/mail/html/layout.blade.php
vendored
Normal file
58
resources/views/vendor/mail/html/layout.blade.php
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<title>{{ config('app.name') }}</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta name="color-scheme" content="light">
|
||||||
|
<meta name="supported-color-schemes" content="light">
|
||||||
|
<style>
|
||||||
|
@media only screen and (max-width: 600px) {
|
||||||
|
.inner-body {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 500px) {
|
||||||
|
.button {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{{ $head ?? '' }}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<table class="wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<table class="content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
{{ $header ?? '' }}
|
||||||
|
|
||||||
|
<!-- Email Body -->
|
||||||
|
<tr>
|
||||||
|
<td class="body" width="100%" cellpadding="0" cellspacing="0" style="border: hidden !important;">
|
||||||
|
<table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<!-- Body content -->
|
||||||
|
<tr>
|
||||||
|
<td class="content-cell">
|
||||||
|
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||||
|
|
||||||
|
{{ $subcopy ?? '' }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{{ $footer ?? '' }}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
27
resources/views/vendor/mail/html/message.blade.php
vendored
Normal file
27
resources/views/vendor/mail/html/message.blade.php
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<x-mail::layout>
|
||||||
|
{{-- Header --}}
|
||||||
|
<x-slot:header>
|
||||||
|
<x-mail::header :url="config('app.url')">
|
||||||
|
{{ config('app.name') }}
|
||||||
|
</x-mail::header>
|
||||||
|
</x-slot:header>
|
||||||
|
|
||||||
|
{{-- Body --}}
|
||||||
|
{{ $slot }}
|
||||||
|
|
||||||
|
{{-- Subcopy --}}
|
||||||
|
@isset($subcopy)
|
||||||
|
<x-slot:subcopy>
|
||||||
|
<x-mail::subcopy>
|
||||||
|
{{ $subcopy }}
|
||||||
|
</x-mail::subcopy>
|
||||||
|
</x-slot:subcopy>
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
{{-- Footer --}}
|
||||||
|
<x-slot:footer>
|
||||||
|
<x-mail::footer>
|
||||||
|
© {{ date('Y') }} {{ config('app.name') }}. {{ __('All rights reserved.') }}
|
||||||
|
</x-mail::footer>
|
||||||
|
</x-slot:footer>
|
||||||
|
</x-mail::layout>
|
||||||
14
resources/views/vendor/mail/html/panel.blade.php
vendored
Normal file
14
resources/views/vendor/mail/html/panel.blade.php
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<table class="panel" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td class="panel-content">
|
||||||
|
<table width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td class="panel-item">
|
||||||
|
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
7
resources/views/vendor/mail/html/subcopy.blade.php
vendored
Normal file
7
resources/views/vendor/mail/html/subcopy.blade.php
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<table class="subcopy" width="100%" cellpadding="0" cellspacing="0" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
3
resources/views/vendor/mail/html/table.blade.php
vendored
Normal file
3
resources/views/vendor/mail/html/table.blade.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div class="table">
|
||||||
|
{{ Illuminate\Mail\Markdown::parse($slot) }}
|
||||||
|
</div>
|
||||||
291
resources/views/vendor/mail/html/themes/default.css
vendored
Normal file
291
resources/views/vendor/mail/html/themes/default.css
vendored
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
/* Base */
|
||||||
|
|
||||||
|
body,
|
||||||
|
body *:not(html):not(style):not(br):not(tr):not(code) {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
|
||||||
|
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
-webkit-text-size-adjust: none;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #718096;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
ul,
|
||||||
|
ol,
|
||||||
|
blockquote {
|
||||||
|
line-height: 1.4;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #3869d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
a img {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Typography */
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #3d4852;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5em;
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.sub {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Layout */
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 100%;
|
||||||
|
background-color: #edf2f7;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding: 25px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header a {
|
||||||
|
color: #3d4852;
|
||||||
|
font-size: 19px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Logo */
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
height: 75px;
|
||||||
|
max-height: 75px;
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Body */
|
||||||
|
|
||||||
|
.body {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 100%;
|
||||||
|
background-color: #edf2f7;
|
||||||
|
border-bottom: 1px solid #edf2f7;
|
||||||
|
border-top: 1px solid #edf2f7;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-body {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 570px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-color: #e8e5ef;
|
||||||
|
border-radius: 2px;
|
||||||
|
border-width: 1px;
|
||||||
|
box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0;
|
||||||
|
width: 570px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subcopy */
|
||||||
|
|
||||||
|
.subcopy {
|
||||||
|
border-top: 1px solid #e8e5ef;
|
||||||
|
margin-top: 25px;
|
||||||
|
padding-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subcopy p {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 570px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
width: 570px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer p {
|
||||||
|
color: #b0adc5;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a {
|
||||||
|
color: #b0adc5;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tables */
|
||||||
|
|
||||||
|
.table table {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 100%;
|
||||||
|
margin: 30px auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th {
|
||||||
|
border-bottom: 1px solid #edeff2;
|
||||||
|
margin: 0;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table td {
|
||||||
|
color: #74787e;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 18px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-cell {
|
||||||
|
max-width: 100vw;
|
||||||
|
padding: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
|
||||||
|
.action {
|
||||||
|
-premailer-cellpadding: 0;
|
||||||
|
-premailer-cellspacing: 0;
|
||||||
|
-premailer-width: 100%;
|
||||||
|
margin: 30px auto;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
float: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
-webkit-text-size-adjust: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-blue,
|
||||||
|
.button-primary {
|
||||||
|
background-color: #2d3748;
|
||||||
|
border-bottom: 8px solid #2d3748;
|
||||||
|
border-left: 18px solid #2d3748;
|
||||||
|
border-right: 18px solid #2d3748;
|
||||||
|
border-top: 8px solid #2d3748;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-green,
|
||||||
|
.button-success {
|
||||||
|
background-color: #48bb78;
|
||||||
|
border-bottom: 8px solid #48bb78;
|
||||||
|
border-left: 18px solid #48bb78;
|
||||||
|
border-right: 18px solid #48bb78;
|
||||||
|
border-top: 8px solid #48bb78;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-red,
|
||||||
|
.button-error {
|
||||||
|
background-color: #e53e3e;
|
||||||
|
border-bottom: 8px solid #e53e3e;
|
||||||
|
border-left: 18px solid #e53e3e;
|
||||||
|
border-right: 18px solid #e53e3e;
|
||||||
|
border-top: 8px solid #e53e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Panels */
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
border-left: #2d3748 solid 4px;
|
||||||
|
margin: 21px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-content {
|
||||||
|
background-color: #edf2f7;
|
||||||
|
color: #718096;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-content p {
|
||||||
|
color: #718096;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-item {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-item p:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Utilities */
|
||||||
|
|
||||||
|
.break-all {
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
1
resources/views/vendor/mail/text/button.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/button.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}: {{ $url }}
|
||||||
1
resources/views/vendor/mail/text/footer.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/footer.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}
|
||||||
1
resources/views/vendor/mail/text/header.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/header.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}: {{ $url }}
|
||||||
9
resources/views/vendor/mail/text/layout.blade.php
vendored
Normal file
9
resources/views/vendor/mail/text/layout.blade.php
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{!! strip_tags($header ?? '') !!}
|
||||||
|
|
||||||
|
{!! strip_tags($slot) !!}
|
||||||
|
@isset($subcopy)
|
||||||
|
|
||||||
|
{!! strip_tags($subcopy) !!}
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
{!! strip_tags($footer ?? '') !!}
|
||||||
27
resources/views/vendor/mail/text/message.blade.php
vendored
Normal file
27
resources/views/vendor/mail/text/message.blade.php
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<x-mail::layout>
|
||||||
|
{{-- Header --}}
|
||||||
|
<x-slot:header>
|
||||||
|
<x-mail::header :url="config('app.url')">
|
||||||
|
{{ config('app.name') }}
|
||||||
|
</x-mail::header>
|
||||||
|
</x-slot:header>
|
||||||
|
|
||||||
|
{{-- Body --}}
|
||||||
|
{{ $slot }}
|
||||||
|
|
||||||
|
{{-- Subcopy --}}
|
||||||
|
@isset($subcopy)
|
||||||
|
<x-slot:subcopy>
|
||||||
|
<x-mail::subcopy>
|
||||||
|
{{ $subcopy }}
|
||||||
|
</x-mail::subcopy>
|
||||||
|
</x-slot:subcopy>
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
{{-- Footer --}}
|
||||||
|
<x-slot:footer>
|
||||||
|
<x-mail::footer>
|
||||||
|
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
|
||||||
|
</x-mail::footer>
|
||||||
|
</x-slot:footer>
|
||||||
|
</x-mail::layout>
|
||||||
1
resources/views/vendor/mail/text/panel.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/panel.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}
|
||||||
1
resources/views/vendor/mail/text/subcopy.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/subcopy.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}
|
||||||
1
resources/views/vendor/mail/text/table.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/table.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ $slot }}
|
||||||
58
resources/views/vendor/notifications/email.blade.php
vendored
Normal file
58
resources/views/vendor/notifications/email.blade.php
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<x-mail::message>
|
||||||
|
{{-- Greeting --}}
|
||||||
|
@if (! empty($greeting))
|
||||||
|
# {{ $greeting }}
|
||||||
|
@else
|
||||||
|
@if ($level === 'error')
|
||||||
|
# @lang('Whoops!')
|
||||||
|
@else
|
||||||
|
# @lang('Hello!')
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Intro Lines --}}
|
||||||
|
@foreach ($introLines as $line)
|
||||||
|
{{ $line }}
|
||||||
|
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Action Button --}}
|
||||||
|
@isset($actionText)
|
||||||
|
<?php
|
||||||
|
$color = match ($level) {
|
||||||
|
'success', 'error' => $level,
|
||||||
|
default => 'primary',
|
||||||
|
};
|
||||||
|
?>
|
||||||
|
<x-mail::button :url="$actionUrl" :color="$color">
|
||||||
|
{{ $actionText }}
|
||||||
|
</x-mail::button>
|
||||||
|
@endisset
|
||||||
|
|
||||||
|
{{-- Outro Lines --}}
|
||||||
|
@foreach ($outroLines as $line)
|
||||||
|
{{ $line }}
|
||||||
|
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
{{-- Salutation --}}
|
||||||
|
@if (! empty($salutation))
|
||||||
|
{{ $salutation }}
|
||||||
|
@else
|
||||||
|
@lang('Regards,')<br>
|
||||||
|
{{ config('app.name') }}
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Subcopy --}}
|
||||||
|
@isset($actionText)
|
||||||
|
<x-slot:subcopy>
|
||||||
|
@lang(
|
||||||
|
"If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\n".
|
||||||
|
'into your web browser:',
|
||||||
|
[
|
||||||
|
'actionText' => $actionText,
|
||||||
|
]
|
||||||
|
) <span class="break-all">[{{ $displayableActionUrl }}]({{ $actionUrl }})</span>
|
||||||
|
</x-slot:subcopy>
|
||||||
|
@endisset
|
||||||
|
</x-mail::message>
|
||||||
File diff suppressed because one or more lines are too long
64
routes/api.php
Normal file
64
routes/api.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Http\Controllers\MyUserController;
|
||||||
|
use App\Http\Controllers\System\LoginController;
|
||||||
|
use App\Http\Controllers\System\NotificationController;
|
||||||
|
use App\Http\Controllers\System\SystemController;
|
||||||
|
use App\Http\Controllers\UserController;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
||||||
|
use Tighten\Ziggy\Ziggy;
|
||||||
|
|
||||||
|
Route::get('/', function () {
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
"message" => "It's fine :D"
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::name('api.')->group(function () {
|
||||||
|
Route::get('/routes', function () {
|
||||||
|
return (new Ziggy('api'))->toArray();
|
||||||
|
})->name('routes');
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::middleware('auth:api')->group(function () {
|
||||||
|
// Aplicación
|
||||||
|
Route::prefix('user')->name('user.')->group(function() {
|
||||||
|
Route::get('/', [MyUserController::class, 'show'])->name('show');
|
||||||
|
Route::put('/', [MyUserController::class, 'update'])->name('update');
|
||||||
|
Route::delete('/', [MyUserController::class, 'destroy'])->name('destroy');
|
||||||
|
Route::delete('photo', [MyUserController::class, 'destroyPhoto'])->name('photo');
|
||||||
|
Route::put('password', [MyUserController::class, 'updatePassword'])->name('password');
|
||||||
|
Route::post('password-confirm', [MyUserController::class, 'confirmPassword'])->name('password-confirm');
|
||||||
|
Route::get('permissions', [MyUserController::class, 'permissions'])->name('permissions');
|
||||||
|
Route::get('roles', [MyUserController::class, 'roles'])->name('roles');
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::prefix('users')->name('users.')->group(function() {
|
||||||
|
Route::prefix('{user}')->group(function() {
|
||||||
|
Route::get('roles', [UserController::class, 'roles'])->name('roles');
|
||||||
|
Route::put('roles', [UserController::class, 'updateRoles']);
|
||||||
|
Route::put('password', [UserController::class, 'updatePassword'])->name('password');
|
||||||
|
Route::get('permissions', [UserController::class, 'permissions'])->name('permissions');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Route::apiResource('users', UserController::class);
|
||||||
|
|
||||||
|
// Sistema
|
||||||
|
Route::prefix('system')->name('system.')->group(function() {
|
||||||
|
Route::get('permissions', [SystemController::class, 'permissions'])->name('permissions');
|
||||||
|
Route::get('roles', [SystemController::class, 'roles'])->name('roles');
|
||||||
|
Route::prefix('notifications')->name('notifications.')->group(function() {
|
||||||
|
Route::get('all-unread', [NotificationController::class, 'allUnread'])->name('all-unread');
|
||||||
|
Route::post('read', [NotificationController::class, 'read'])->name('read');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::post('auth/logout', [LoginController::class, 'logout'])->name('auth.logout');
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::prefix('auth')->name('auth.')->group(function () {
|
||||||
|
Route::post('login', [LoginController::class, 'login'])->name('login');
|
||||||
|
Route::post('forgot-password', [LoginController::class, 'forgotPassword'])->name('forgot-password');
|
||||||
|
Route::post('reset-password', [LoginController::class, 'resetPassword'])->name('reset-password');
|
||||||
|
});
|
||||||
9
routes/channels.php
Normal file
9
routes/channels.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php use Illuminate\Support\Facades\Broadcast;
|
||||||
|
|
||||||
|
Broadcast::channel('App.Models.User.{id}', function ($user, $id) {
|
||||||
|
return (int) $user->id === (int) $id;
|
||||||
|
});
|
||||||
|
|
||||||
|
Broadcast::channel('Global', function ($user) {
|
||||||
|
return $user->id !== null;
|
||||||
|
});
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user