├── .dockerignore ├── .editorconfig ├── .env.example ├── .gitattributes ├── .gitignore ├── README.md ├── app ├── Http │ ├── Controllers │ │ ├── ActionsTableController.php │ │ ├── Controller.php │ │ ├── FormsTableController.php │ │ ├── HeadersTableController.php │ │ ├── ModuleController.php │ │ ├── ParametersController.php │ │ ├── PermissionController.php │ │ ├── RoleController.php │ │ ├── TableController.php │ │ ├── TypeFieldController.php │ │ └── UserController.php │ └── Middleware │ │ ├── ForceJsonResponse.php │ │ └── JwtMiddleware.php ├── Models │ ├── ActionsTable.php │ ├── FormsTable.php │ ├── HeadersTable.php │ ├── Module.php │ ├── Parameters.php │ ├── Table.php │ ├── TypeField.php │ └── User.php └── Providers │ └── AppServiceProvider.php ├── artisan ├── bootstrap ├── app.php ├── cache │ └── .gitignore └── providers.php ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── cache.php ├── database.php ├── filesystems.php ├── jwt.php ├── logging.php ├── mail.php ├── permission.php ├── queue.php ├── sanctum.php ├── services.php └── session.php ├── database ├── .gitignore ├── factories │ └── UserFactory.php ├── migrations │ ├── 0001_01_01_000000_create_users_table.php │ ├── 0001_01_01_000001_create_cache_table.php │ ├── 0001_01_01_000002_create_jobs_table.php │ ├── 2024_10_12_023908_create_personal_access_tokens_table.php │ ├── 2024_10_12_024544_create_permission_tables.php │ ├── 2024_10_12_024624_create_modules_table.php │ ├── 2024_10_12_024636_create_tables_table.php │ ├── 2024_10_12_024650_create_headers_tables_table.php │ ├── 2024_10_12_024703_action_button.php │ ├── 2024_10_12_024704_create_actions_tables_table.php │ ├── 2024_10_12_024712_create_parameters_table.php │ ├── 2024_10_12_024728_create_type_fields_table.php │ ├── 2024_10_12_024738_create_forms_tables_table.php │ └── 2024_10_12_035747_add_alias_to_permissions_table.php └── seeders │ ├── DatabaseSeeder.php │ ├── HeadersTableSeeder.php │ ├── ModuleSeeder.php │ ├── PermissionSeeder.php │ ├── RoleSeeder.php │ ├── TableSeeder.php │ └── UserSeeder.php ├── docker-compose.yml ├── docker ├── apache │ └── 000-default.conf ├── start.sh └── supervisor │ └── laravel-worker.conf ├── dockerfile ├── documentation └── postman │ ├── api.json │ └── environment.json ├── package.json ├── phpunit.xml ├── public ├── .htaccess ├── favicon.ico ├── index.php └── robots.txt ├── resources ├── css │ └── app.css ├── js │ ├── app.js │ └── bootstrap.js └── views │ └── welcome.blade.php ├── routes ├── api.php ├── console.php └── web.php ├── storage ├── app │ ├── .gitignore │ ├── private │ │ └── .gitignore │ └── public │ │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ ├── .gitignore │ │ └── data │ │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ ├── testing │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore ├── tests ├── Feature │ └── ExampleTest.php ├── TestCase.php └── Unit │ └── ExampleTest.php └── vite.config.js /.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /vendor 3 | .git 4 | .idea 5 | .env 6 | .env.backup 7 | .phpunit.result.cache 8 | docker-compose.override.yml 9 | Homestead.json 10 | Homestead.yaml 11 | npm-debug.log 12 | yarn-error.log -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Laravel 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_TIMEZONE=UTC 6 | APP_URL=http://localhost 7 | 8 | APP_LOCALE=en 9 | APP_FALLBACK_LOCALE=en 10 | APP_FAKER_LOCALE=en_US 11 | 12 | APP_MAINTENANCE_DRIVER=file 13 | # APP_MAINTENANCE_STORE=database 14 | 15 | BCRYPT_ROUNDS=12 16 | 17 | LOG_CHANNEL=stack 18 | LOG_STACK=single 19 | LOG_DEPRECATIONS_CHANNEL=null 20 | LOG_LEVEL=debug 21 | 22 | DB_CONNECTION=sqlite 23 | DB_HOST=127.0.0.1 24 | DB_PORT=3306 25 | DB_DATABASE=laravel 26 | DB_USERNAME=laravel_user 27 | DB_PASSWORD=laravel_password 28 | 29 | SESSION_DRIVER=database 30 | SESSION_LIFETIME=120 31 | SESSION_ENCRYPT=false 32 | SESSION_PATH=/ 33 | SESSION_DOMAIN=null 34 | 35 | BROADCAST_CONNECTION=log 36 | FILESYSTEM_DISK=local 37 | QUEUE_CONNECTION=database 38 | 39 | CACHE_STORE=database 40 | CACHE_PREFIX= 41 | 42 | MEMCACHED_HOST=127.0.0.1 43 | 44 | REDIS_CLIENT=phpredis 45 | REDIS_HOST=127.0.0.1 46 | REDIS_PASSWORD=null 47 | REDIS_PORT=6379 48 | 49 | MAIL_MAILER=log 50 | MAIL_HOST=127.0.0.1 51 | MAIL_PORT=2525 52 | MAIL_USERNAME=null 53 | MAIL_PASSWORD=null 54 | MAIL_ENCRYPTION=null 55 | MAIL_FROM_ADDRESS="hello@example.com" 56 | MAIL_FROM_NAME="${APP_NAME}" 57 | 58 | AWS_ACCESS_KEY_ID= 59 | AWS_SECRET_ACCESS_KEY= 60 | AWS_DEFAULT_REGION=us-east-1 61 | AWS_BUCKET= 62 | AWS_USE_PATH_STYLE_ENDPOINT=false 63 | 64 | VITE_APP_NAME="${APP_NAME}" 65 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.blade.php diff=html 4 | *.css diff=css 5 | *.html diff=html 6 | *.md diff=markdown 7 | *.php diff=php 8 | 9 | /.github export-ignore 10 | CHANGELOG.md export-ignore 11 | .styleci.yml export-ignore 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.phpunit.cache 2 | /node_modules 3 | /public/build 4 | /public/hot 5 | /public/storage 6 | /storage/*.key 7 | /vendor 8 | .env 9 | .env.backup 10 | .env.production 11 | .phpactor.json 12 | .phpunit.result.cache 13 | Homestead.json 14 | Homestead.yaml 15 | auth.json 16 | npm-debug.log 17 | yarn-error.log 18 | /.fleet 19 | /.idea 20 | /.vscode 21 | /.zed 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Template API usign Laravel V. 11 2 | 3 | ## Integrated technologies 4 | 5 | This api has the following integrated technologies 6 | 7 | - [Laravel 11](https://laravel.com/docs/11.x/releases) 8 | - [Laravel-permission](https://spatie.be/docs/laravel-permission/v6/introduction) 9 | - [JWT-Authentication](https://github.com/tymondesigns/jwt-auth) 10 | 11 | ## Setting Up the Project 12 | 13 | 1. Run the following command to install the project dependencies using Composer: 14 | 15 | ``` 16 | composer install 17 | ``` 18 | This command will download and install all the required dependencies for the project. 19 | 20 | 2. Create a Copy of the .env.example File 21 | 22 | Create a copy of the .env.example file and rename it to .env. This file will contain environment-specific settings for the project. It is important to pay close attention to the following fields for the configuration of the database 23 | 24 | ``` 25 | DB_CONNECTION=mysql 26 | DB_HOST=127.0.0.1 27 | DB_PORT=3306 28 | DB_DATABASE=database_name 29 | DB_USERNAME=database_user 30 | DB_PASSWORD=database_password 31 | ``` 32 | 33 | _In linux you can use the following command to create a copy of the file_ 34 | 35 | ``` 36 | cp .env.example .env 37 | ``` 38 | 39 | 3. Generate the Application Key 40 | 41 | Run the following command to generate a new application key: 42 | 43 | ``` 44 | php artisan key:generate 45 | ``` 46 | 4. Run Database Migrations with Default Data 47 | 48 | Run the following command to execute the database migrations and seed the database with default data: 49 | 50 | ``` 51 | php artisan migrate:fresh --seed 52 | ``` 53 | 54 | This command will create the database tables and populate them with default data. 55 | 56 | 5. Generate the JWT Secret 57 | 58 | Finally, run the following command to generate a secret key for JSON Web Tokens (JWT): 59 | 60 | ``` 61 | php artisan jwt:secret 62 | ``` 63 | 64 | This command will generate a new secret key and store it in the .env file. 65 | 66 | 67 | That's it! Your project should now be set up and ready to use. 68 | 69 | 70 | Now all we have to do is execute the following command to run it locally 71 | 72 | ``` 73 | php artisan serve 74 | ``` 75 | 76 | ## API Documentation 77 | 78 | ### Postman 79 | To perform various queries to the API endpoints, you can use the following .json files: 80 | 81 | - [Collections](./documentation/postman/api.json) 82 | - [Environment](./documentation/postman/environment.json) 83 | 84 | If you're unsure how to use these JSON files, please refer to this guide: [Importing and exporting Overview](https://learning.postman.com/docs/getting-started/importing-and-exporting/importing-and-exporting-overview/) 85 | 86 | The goal is to import both the collection and the environment to enable you to conduct different tests more efficiently. 87 | 88 | For the login endpoint, the following script is added: 89 | 90 | ```js 91 | // post-response 92 | var data = pm.response.json(); 93 | pm.environment.set ("token", data['access_token']); 94 | ``` 95 | 96 | This script allows you to save the session token, enabling you to access all endpoints that require authentication. -------------------------------------------------------------------------------- /app/Http/Controllers/ActionsTableController.php: -------------------------------------------------------------------------------- 1 | json($modules); 18 | } catch (\Throwable $th) { 19 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 20 | } 21 | } 22 | 23 | /** 24 | * Store a newly created resource in storage. 25 | */ 26 | public function store(Request $request) 27 | { 28 | try { 29 | $module = Module::create($request->all()); 30 | return response()->json([ 31 | 'message' => 'The record has been successfully created', 32 | 'data' => $module 33 | ]); 34 | } catch (\Throwable $th) { 35 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 36 | } 37 | } 38 | 39 | /** 40 | * Display the specified resource. 41 | */ 42 | public function show(string $id) 43 | { 44 | try { 45 | $modules = Module::whereId($id)->get(); 46 | return response()->json($modules); 47 | } catch (\Throwable $th) { 48 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 49 | } 50 | } 51 | 52 | /** 53 | * Update the specified resource in storage. 54 | */ 55 | public function update(Request $request, string $id) 56 | { 57 | try { 58 | $module = Module::whereId($id)->update($request->all()); 59 | return response()->json([ 60 | 'message' => 'The record has been successfully updated', 61 | 'data' => $module 62 | ]); 63 | } catch (\Throwable $th) { 64 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 65 | } 66 | } 67 | 68 | /** 69 | * Remove the specified resource from storage. 70 | */ 71 | public function destroy(Module $module) 72 | { 73 | try { 74 | $module->delete(); 75 | return response()->json([ 'message' => 'The record has been successfully deleted' ]); 76 | } catch (\Throwable $th) { 77 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/Http/Controllers/ParametersController.php: -------------------------------------------------------------------------------- 1 | json($permissions); 20 | } catch (\Throwable $th) { 21 | return response()->json([ 'message' => 'An error has occurred', 'error' => $th->getMessage() ], 500); 22 | } 23 | } 24 | 25 | public function gridIndex(Request $request) 26 | { 27 | try { 28 | $params = $request->query(); 29 | $table = Table::whereId($params['nIdTable'])->first() ?? (Object) []; 30 | $headers = HeadersTable::whereTableId($params['nIdTable'])->orderBy('order')->get(); 31 | $data = Permission::get(); 32 | 33 | return response()->json([ 34 | 'data' => $data, 35 | 'tabla' => $table, 36 | 'headers' => $headers 37 | ]); 38 | } catch (\Throwable $th) { 39 | return response()->json([]); 40 | } 41 | } 42 | 43 | /** 44 | * Store a newly created resource in storage. 45 | */ 46 | public function store(Request $request) 47 | { 48 | try { 49 | $permissions = Permission::create($request->all()); 50 | return response()->json([ 51 | 'message' => 'The record has been successfully created', 52 | 'data' => $permissions 53 | ]); 54 | } catch (\Throwable $th) { 55 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 56 | } 57 | } 58 | 59 | /** 60 | * Display the specified resource. 61 | */ 62 | public function show(string $id) 63 | { 64 | try { 65 | $permissions = Permission::whereId($id)->get(); 66 | return response()->json($permissions); 67 | } catch (\Throwable $th) { 68 | return response()->json([ 'message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 69 | } 70 | } 71 | 72 | /** 73 | * Update the specified resource in storage. 74 | */ 75 | public function update(Request $request, string $id) 76 | { 77 | try { 78 | $permission = Permission::whereId($id)->update($request->all()); 79 | return response()->json([ 80 | 'message' => 'The record has been successfully updated', 81 | 'data' => $permission 82 | ]); 83 | } catch (\Throwable $th) { 84 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 85 | } 86 | } 87 | 88 | /** 89 | * Remove the specified resource from storage. 90 | */ 91 | public function destroy(string $id) 92 | { 93 | try { 94 | $permission = Permission::find($id); 95 | $permission->delete(); 96 | return response()->json([ 'message' => 'The record has been successfully deleted' ]); 97 | } catch (\Throwable $th) { 98 | return response()->json([ 'message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/Http/Controllers/RoleController.php: -------------------------------------------------------------------------------- 1 | json($roles); 20 | } catch (\Throwable $th) { 21 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 22 | } 23 | } 24 | 25 | public function gridIndex(Request $request) 26 | { 27 | try { 28 | $params = $request->query(); 29 | $table = Table::whereId($params['nIdTable'])->first() ?? (Object) []; 30 | $headers = HeadersTable::whereTableId($params['nIdTable'])->orderBy('order')->get(); 31 | $data = Role::get(); 32 | 33 | return response()->json([ 34 | 'data' => $data, 35 | 'tabla' => $table, 36 | 'headers' => $headers 37 | ]); 38 | } catch (\Throwable $th) { 39 | return response()->json([]); 40 | } 41 | } 42 | 43 | /** 44 | * Store a newly created resource in storage. 45 | */ 46 | public function store(Request $request) 47 | { 48 | try { 49 | $role = Role::create($request->all()); 50 | return response()->json([ 51 | 'message' => 'The record has been successfully created', 52 | 'data' => $role 53 | ]); 54 | } catch (\Throwable $th) { 55 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 56 | } 57 | } 58 | 59 | /** 60 | * Display the specified resource. 61 | */ 62 | public function show(string $id) 63 | { 64 | try { 65 | $roles = Role::whereId($id)->get(); 66 | return response()->json($roles); 67 | } catch (\Throwable $th) { 68 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 69 | } 70 | } 71 | 72 | /** 73 | * Update the specified resource in storage. 74 | */ 75 | public function update(Request $request, string $id) 76 | { 77 | try { 78 | $role = Role::whereId($id)->update($request->all()); 79 | return response()->json([ 80 | 'message' => 'The record has been successfully updated', 81 | 'data' => $role 82 | ]); 83 | } catch (\Throwable $th) { 84 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 85 | } 86 | } 87 | 88 | /** 89 | * Remove the specified resource from storage. 90 | */ 91 | public function destroy(string $id) 92 | { 93 | try { 94 | $role = Role::find($id); 95 | 96 | $role->delete(); 97 | return response()->json([ 'message' => 'The record has been successfully deleted' ]); 98 | } catch (\Throwable $th) { 99 | return response()->json([ 'message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/Http/Controllers/TableController.php: -------------------------------------------------------------------------------- 1 | only('email', 'password'); 22 | try { 23 | if (! $token = JWTAuth::attempt($credentials)) return response()->json(['error' => 'Incorrect credentials', 'status' => 'error'], 400); // Credenciales incorrectas 24 | } catch (JWTException $e) { 25 | return response()->json(['error' => 'Could not create token.', 'status' => 'error'], 500); 26 | } 27 | 28 | $oUser = Auth::user(); 29 | 30 | try { 31 | if($oUser->status !== 1) return response()->json(['error' => 'Inactive user', 'status' => 'error'], 400); 32 | if(!is_null($oUser->deleted_at)) return response()->json(['error' => 'Unauthorized user', 'status' => 'error'], 400); 33 | // Obtener roles del usuario 34 | $roles = $oUser->getRoleNames(); // Esto devolverá una colección de nombres de roles 35 | 36 | // Obtener permisos del usuario 37 | $permissions = $oUser->getAllPermissions(); // Esto devolverá una colección de permisos 38 | // $activity = [ 39 | // 'method' => 'POST', 40 | // 'action' => 'Login', 41 | // 'description' => "El usuario inicia sesión: {$oUser->name}" 42 | // ]; 43 | } catch (JWTException $th) { 44 | return response()->json(['error' => 'Could not create token'], 500); 45 | } 46 | $oUser->access_token = $token; 47 | // $oUser->aParametros = ParametroController::getAll(); 48 | 49 | return response()->json($oUser); 50 | } 51 | 52 | public function getAuthenticatedUser() 53 | { 54 | try { 55 | if (!$user = JWTAuth::parseToken()->authenticate()) return response()->json(['User not found'], 404); 56 | } catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) { 57 | return response()->json(['token_expired'], $e->getStatusCode()); 58 | } catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) { 59 | return response()->json(['token_invalid'], $e->getStatusCode()); 60 | } catch (Tymon\JWTAuth\Exceptions\JWTException $e) { 61 | return response()->json(['token_absent'], $e->getStatusCode()); 62 | } 63 | return response()->json(compact('user')); 64 | } 65 | 66 | 67 | /** 68 | * Display a listing of the resource. 69 | * 70 | * @return \Illuminate\Http\Response 71 | */ 72 | 73 | public function index() 74 | { 75 | try { 76 | $users = User::withRole()->get(); 77 | return response()->json($users); 78 | } catch (\Throwable $th) { 79 | return response()->json(['message' => 'An error has occurred'], 500); 80 | } 81 | } 82 | 83 | public function gridIndex(Request $request) 84 | { 85 | try { 86 | $params = $request->query(); 87 | $table = Table::whereId($params['nIdTable'])->first() ?? (Object) []; 88 | $headers = HeadersTable::whereTableId($params['nIdTable'])->orderBy('order')->get(); 89 | $users = User::withRole()->withTrashed()->get(); 90 | 91 | return response()->json([ 92 | 'data' => $users, 93 | 'tabla' => $table, 94 | 'headers' => $headers 95 | ]); 96 | } catch (\Throwable $th) { 97 | return response()->json([]); 98 | } 99 | } 100 | 101 | /** 102 | * Display the specified resource. 103 | * 104 | * @param \App\Models\Usuario $usuario 105 | * @return \Illuminate\Http\Response 106 | */ 107 | public function show($id) 108 | { 109 | try { 110 | $user = User::withRole()->find($id); 111 | return response()->json([ 112 | 'message' => 'User found', 113 | 'data' => $user ?? [] 114 | ]); 115 | } catch (\Throwable $th) { 116 | return response()->json([ 117 | 'message' => 'An error has occurred', 118 | 'error' => $th->getMessage() 119 | ], 500); 120 | } 121 | } 122 | 123 | /** 124 | * Request 125 | * name 126 | * fullname 127 | * email 128 | * password (in MD5) 129 | * role_id 130 | */ 131 | public function store(Request $request) 132 | { 133 | try { 134 | $user = [ 135 | 'name' => $request->name, 136 | 'fullname' => $request->fullname, 137 | 'email' => $request->email, 138 | 'password' => Hash::make($request->password) 139 | ]; 140 | 141 | $user = User::create($user); 142 | 143 | $role = Role::find($request->role_id); 144 | $user->assignRole($role); 145 | return response()->json([ 146 | 'message' => 'User has been successfully created', 147 | 'data' => $user 148 | ]); 149 | 150 | } catch (\Throwable $th) { 151 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage().$th->getMessage()], 500); 152 | } 153 | } 154 | 155 | /** 156 | * Update the specified resource in storage. 157 | * 158 | * @param \Illuminate\Http\Request $request 159 | * @param \App\Models\Usuario $usuario 160 | * @return \Illuminate\Http\Response 161 | */ 162 | public function update(Request $request, $id) 163 | { 164 | try { 165 | $user = User::find($id); 166 | 167 | if($user) { 168 | $password = isset($request->password) ? Hash::make($request->password) : null; 169 | $aUpdate = [ // array for update 170 | 'name' => $request->name, 171 | 'fullname' => $request->fullname, 172 | 'email' => $request->email 173 | ]; 174 | if(!is_null($password)) { 175 | $aUpdate['password'] = $password; 176 | } 177 | 178 | $user->update($aUpdate); 179 | 180 | 181 | // Elimina todos los roles actuales del usuario 182 | DB::table('model_has_roles') 183 | ->where('model_id', $user->id) 184 | ->where('model_type', 'App\Models\User') // Ajusta esto según el namespace de tu modelo de usuario si es diferente 185 | ->delete(); 186 | 187 | $role = Role::find($request->role_id); 188 | $user->assignRole($role); 189 | } 190 | return response()->json(['message' => 'The registry has been successfully updated', 'user' => $user]); 191 | } catch (\Throwable $th) { 192 | return response()->json([ 193 | 'message' => 'An error has occurred', 194 | 'error' => $th->getMessage() 195 | ], 500); 196 | } 197 | } 198 | 199 | 200 | // metodo para restaurar un usuario eliminado 201 | public function restoreUser($id) { 202 | try { 203 | $usuario = User::withTrashed()->find($id); 204 | if($usuario) { 205 | $usuario->restore(); 206 | return response()->json(['message' => 'The user has been successfully activated']); 207 | } else { 208 | return response()->json(['message' => 'User not found']); 209 | } 210 | } catch (\Throwable $th) { 211 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 212 | } 213 | } 214 | 215 | /** 216 | * Remove the specified resource from storage. 217 | * 218 | * @param \App\Models\Usuario $usuario 219 | * @return \Illuminate\Http\Response 220 | */ 221 | public function destroy($id) 222 | { 223 | try { 224 | $usuario = User::find($id); 225 | 226 | if($usuario) $usuario->delete(); 227 | 228 | return response()->json(['message' => 'deleted user successfully']); 229 | } catch (\Throwable $th) { 230 | return response()->json(['message' => 'An error has occurred', 'error' => $th->getMessage()], 500); 231 | } 232 | } 233 | 234 | public function logout(Request $request) { 235 | try { 236 | $user = Auth::user(); 237 | JWTAuth::parseToken()->invalidate(); // Invalida el token actualmente en uso 238 | return response()->json(['message' => 'Successfully logged out'], Response::HTTP_OK); 239 | } catch (\Exception $e) { 240 | return response()->json(['message' => 'Failed to logout'], Response::HTTP_INTERNAL_SERVER_ERROR); 241 | } 242 | } 243 | 244 | 245 | 246 | } 247 | -------------------------------------------------------------------------------- /app/Http/Middleware/ForceJsonResponse.php: -------------------------------------------------------------------------------- 1 | headers->set('Accept', 'application/json'); 20 | 21 | // Continuar con la solicitud 22 | return $next($request); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Http/Middleware/JwtMiddleware.php: -------------------------------------------------------------------------------- 1 | authenticate(); 20 | } catch (JWTException $e) { 21 | return response()->json(['error' => 'Token not valid'], 401); 22 | } 23 | 24 | return $next($request); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Models/ActionsTable.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | protected $fillable = [ 18 | 'module', 19 | 'description', 20 | 'icon', 21 | 'name', 22 | 'order', 23 | 'status', 24 | 'permission_id' 25 | ]; 26 | 27 | protected $hidden = []; 28 | 29 | protected function casts(): array 30 | { 31 | return [ 32 | // example 33 | // 'email_verified_at' => 'datetime', 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/Models/Parameters.php: -------------------------------------------------------------------------------- 1 | */ 19 | use HasFactory, Notifiable, SoftDeletes, HasRoles; 20 | 21 | /** 22 | * The attributes that are mass assignable. 23 | * 24 | * @var array 25 | */ 26 | protected $fillable = [ 27 | 'name', 28 | 'email', 29 | 'password', 30 | 'fullname', 31 | 'status' 32 | ]; 33 | 34 | /** 35 | * The attributes that should be hidden for serialization. 36 | * 37 | * @var array 38 | */ 39 | protected $hidden = [ 40 | 'password', 41 | 'remember_token', 42 | ]; 43 | 44 | /** 45 | * Get the attributes that should be cast. 46 | * 47 | * @return array 48 | */ 49 | protected function casts(): array 50 | { 51 | return [ 52 | 'email_verified_at' => 'datetime', 53 | 'password' => 'hashed', 54 | ]; 55 | } 56 | 57 | /* Añadiremos estos dos métodos */ 58 | public function getJWTIdentifier() { 59 | return $this->getKey(); 60 | } 61 | public function getJWTCustomClaims() { 62 | return []; 63 | } 64 | 65 | public function scopeWithRole($query) 66 | { 67 | return $query->with('roles:id,name'); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | handleCommand(new ArgvInput); 14 | 15 | exit($status); 16 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | withRouting( 11 | web: __DIR__.'/../routes/web.php', 12 | api: __DIR__.'/../routes/api.php', 13 | commands: __DIR__.'/../routes/console.php', 14 | health: '/up', 15 | ) 16 | ->withMiddleware(function (Middleware $middleware) { 17 | // $middleware->append(JwtMiddleware::class); 18 | $middleware->append(ForceJsonResponse::class); 19 | }) 20 | ->withExceptions(function (Exceptions $exceptions) { 21 | // 22 | })->create(); 23 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /bootstrap/providers.php: -------------------------------------------------------------------------------- 1 | env('APP_NAME', 'Laravel'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Application Environment 21 | |-------------------------------------------------------------------------- 22 | | 23 | | This value determines the "environment" your application is currently 24 | | running in. This may determine how you prefer to configure various 25 | | services the application utilizes. Set this in your ".env" file. 26 | | 27 | */ 28 | 29 | 'env' => env('APP_ENV', 'production'), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Application Debug Mode 34 | |-------------------------------------------------------------------------- 35 | | 36 | | When your application is in debug mode, detailed error messages with 37 | | stack traces will be shown on every error that occurs within your 38 | | application. If disabled, a simple generic error page is shown. 39 | | 40 | */ 41 | 42 | 'debug' => (bool) env('APP_DEBUG', false), 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Application URL 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This URL is used by the console to properly generate URLs when using 50 | | the Artisan command line tool. You should set this to the root of 51 | | the application so that it's available within Artisan commands. 52 | | 53 | */ 54 | 55 | 'url' => env('APP_URL', 'http://localhost'), 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | Application Timezone 60 | |-------------------------------------------------------------------------- 61 | | 62 | | Here you may specify the default timezone for your application, which 63 | | will be used by the PHP date and date-time functions. The timezone 64 | | is set to "UTC" by default as it is suitable for most use cases. 65 | | 66 | */ 67 | 68 | 'timezone' => env('APP_TIMEZONE', 'UTC'), 69 | 70 | /* 71 | |-------------------------------------------------------------------------- 72 | | Application Locale Configuration 73 | |-------------------------------------------------------------------------- 74 | | 75 | | The application locale determines the default locale that will be used 76 | | by Laravel's translation / localization methods. This option can be 77 | | set to any locale for which you plan to have translation strings. 78 | | 79 | */ 80 | 81 | 'locale' => env('APP_LOCALE', 'en'), 82 | 83 | 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), 84 | 85 | 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), 86 | 87 | /* 88 | |-------------------------------------------------------------------------- 89 | | Encryption Key 90 | |-------------------------------------------------------------------------- 91 | | 92 | | This key is utilized by Laravel's encryption services and should be set 93 | | to a random, 32 character string to ensure that all encrypted values 94 | | are secure. You should do this prior to deploying the application. 95 | | 96 | */ 97 | 98 | 'cipher' => 'AES-256-CBC', 99 | 100 | 'key' => env('APP_KEY'), 101 | 102 | 'previous_keys' => [ 103 | ...array_filter( 104 | explode(',', env('APP_PREVIOUS_KEYS', '')) 105 | ), 106 | ], 107 | 108 | /* 109 | |-------------------------------------------------------------------------- 110 | | Maintenance Mode Driver 111 | |-------------------------------------------------------------------------- 112 | | 113 | | These configuration options determine the driver used to determine and 114 | | manage Laravel's "maintenance mode" status. The "cache" driver will 115 | | allow maintenance mode to be controlled across multiple machines. 116 | | 117 | | Supported drivers: "file", "cache" 118 | | 119 | */ 120 | 121 | 'maintenance' => [ 122 | 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), 123 | 'store' => env('APP_MAINTENANCE_STORE', 'database'), 124 | ] 125 | 126 | ]; 127 | -------------------------------------------------------------------------------- /config/auth.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'guard' => env('AUTH_GUARD', 'web'), 18 | 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), 19 | ], 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Authentication Guards 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Next, you may define every authentication guard for your application. 27 | | Of course, a great default configuration has been defined for you 28 | | which utilizes session storage plus the Eloquent user provider. 29 | | 30 | | All authentication guards have a user provider, which defines how the 31 | | users are actually retrieved out of your database or other storage 32 | | system used by the application. Typically, Eloquent is utilized. 33 | | 34 | | Supported: "session" 35 | | 36 | */ 37 | 38 | 'guards' => [ 39 | 'web' => [ 40 | 'driver' => 'session', 41 | 'provider' => 'users', 42 | ], 43 | ], 44 | 45 | /* 46 | |-------------------------------------------------------------------------- 47 | | User Providers 48 | |-------------------------------------------------------------------------- 49 | | 50 | | All authentication guards have a user provider, which defines how the 51 | | users are actually retrieved out of your database or other storage 52 | | system used by the application. Typically, Eloquent is utilized. 53 | | 54 | | If you have multiple user tables or models you may configure multiple 55 | | providers to represent the model / table. These providers may then 56 | | be assigned to any extra authentication guards you have defined. 57 | | 58 | | Supported: "database", "eloquent" 59 | | 60 | */ 61 | 62 | 'providers' => [ 63 | 'users' => [ 64 | 'driver' => 'eloquent', 65 | 'model' => env('AUTH_MODEL', App\Models\User::class), 66 | ], 67 | 68 | // 'users' => [ 69 | // 'driver' => 'database', 70 | // 'table' => 'users', 71 | // ], 72 | ], 73 | 74 | /* 75 | |-------------------------------------------------------------------------- 76 | | Resetting Passwords 77 | |-------------------------------------------------------------------------- 78 | | 79 | | These configuration options specify the behavior of Laravel's password 80 | | reset functionality, including the table utilized for token storage 81 | | and the user provider that is invoked to actually retrieve users. 82 | | 83 | | The expiry time is the number of minutes that each reset token will be 84 | | considered valid. This security feature keeps tokens short-lived so 85 | | they have less time to be guessed. You may change this as needed. 86 | | 87 | | The throttle setting is the number of seconds a user must wait before 88 | | generating more password reset tokens. This prevents the user from 89 | | quickly generating a very large amount of password reset tokens. 90 | | 91 | */ 92 | 93 | 'passwords' => [ 94 | 'users' => [ 95 | 'provider' => 'users', 96 | 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), 97 | 'expire' => 60, 98 | 'throttle' => 60, 99 | ], 100 | ], 101 | 102 | /* 103 | |-------------------------------------------------------------------------- 104 | | Password Confirmation Timeout 105 | |-------------------------------------------------------------------------- 106 | | 107 | | Here you may define the amount of seconds before a password confirmation 108 | | window expires and users are asked to re-enter their password via the 109 | | confirmation screen. By default, the timeout lasts for three hours. 110 | | 111 | */ 112 | 113 | 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), 114 | 115 | ]; 116 | -------------------------------------------------------------------------------- /config/cache.php: -------------------------------------------------------------------------------- 1 | env('CACHE_STORE', 'database'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Cache Stores 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the cache "stores" for your application as 26 | | well as their drivers. You may even define multiple stores for the 27 | | same cache driver to group types of items stored in your caches. 28 | | 29 | | Supported drivers: "array", "database", "file", "memcached", 30 | | "redis", "dynamodb", "octane", "null" 31 | | 32 | */ 33 | 34 | 'stores' => [ 35 | 36 | 'array' => [ 37 | 'driver' => 'array', 38 | 'serialize' => false, 39 | ], 40 | 41 | 'database' => [ 42 | 'driver' => 'database', 43 | 'connection' => env('DB_CACHE_CONNECTION'), 44 | 'table' => env('DB_CACHE_TABLE', 'cache'), 45 | 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), 46 | 'lock_table' => env('DB_CACHE_LOCK_TABLE'), 47 | ], 48 | 49 | 'file' => [ 50 | 'driver' => 'file', 51 | 'path' => storage_path('framework/cache/data'), 52 | 'lock_path' => storage_path('framework/cache/data'), 53 | ], 54 | 55 | 'memcached' => [ 56 | 'driver' => 'memcached', 57 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), 58 | 'sasl' => [ 59 | env('MEMCACHED_USERNAME'), 60 | env('MEMCACHED_PASSWORD'), 61 | ], 62 | 'options' => [ 63 | // Memcached::OPT_CONNECT_TIMEOUT => 2000, 64 | ], 65 | 'servers' => [ 66 | [ 67 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 68 | 'port' => env('MEMCACHED_PORT', 11211), 69 | 'weight' => 100, 70 | ], 71 | ], 72 | ], 73 | 74 | 'redis' => [ 75 | 'driver' => 'redis', 76 | 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), 77 | 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), 78 | ], 79 | 80 | 'dynamodb' => [ 81 | 'driver' => 'dynamodb', 82 | 'key' => env('AWS_ACCESS_KEY_ID'), 83 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 84 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 85 | 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), 86 | 'endpoint' => env('DYNAMODB_ENDPOINT'), 87 | ], 88 | 89 | 'octane' => [ 90 | 'driver' => 'octane', 91 | ], 92 | 93 | ], 94 | 95 | /* 96 | |-------------------------------------------------------------------------- 97 | | Cache Key Prefix 98 | |-------------------------------------------------------------------------- 99 | | 100 | | When utilizing the APC, database, memcached, Redis, and DynamoDB cache 101 | | stores, there might be other applications using the same cache. For 102 | | that reason, you may prefix every cache key to avoid collisions. 103 | | 104 | */ 105 | 106 | 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), 107 | 108 | ]; 109 | -------------------------------------------------------------------------------- /config/database.php: -------------------------------------------------------------------------------- 1 | env('DB_CONNECTION', 'sqlite'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Database Connections 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Below are all of the database connections defined for your application. 27 | | An example configuration is provided for each database system which 28 | | is supported by Laravel. You're free to add / remove connections. 29 | | 30 | */ 31 | 32 | 'connections' => [ 33 | 34 | 'sqlite' => [ 35 | 'driver' => 'sqlite', 36 | 'url' => env('DB_URL'), 37 | 'database' => env('DB_DATABASE', database_path('database.sqlite')), 38 | 'prefix' => '', 39 | 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), 40 | 'busy_timeout' => null, 41 | 'journal_mode' => null, 42 | 'synchronous' => null, 43 | ], 44 | 45 | 'mysql' => [ 46 | 'driver' => 'mysql', 47 | 'url' => env('DB_URL'), 48 | 'host' => env('DB_HOST', '127.0.0.1'), 49 | 'port' => env('DB_PORT', '3306'), 50 | 'database' => env('DB_DATABASE', 'laravel'), 51 | 'username' => env('DB_USERNAME', 'root'), 52 | 'password' => env('DB_PASSWORD', ''), 53 | 'unix_socket' => env('DB_SOCKET', ''), 54 | 'charset' => env('DB_CHARSET', 'utf8mb4'), 55 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 56 | 'prefix' => '', 57 | 'prefix_indexes' => true, 58 | 'strict' => true, 59 | 'engine' => null, 60 | 'options' => extension_loaded('pdo_mysql') ? array_filter([ 61 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), 62 | ]) : [], 63 | ], 64 | 65 | 'mariadb' => [ 66 | 'driver' => 'mariadb', 67 | 'url' => env('DB_URL'), 68 | 'host' => env('DB_HOST', '127.0.0.1'), 69 | 'port' => env('DB_PORT', '3306'), 70 | 'database' => env('DB_DATABASE', 'laravel'), 71 | 'username' => env('DB_USERNAME', 'root'), 72 | 'password' => env('DB_PASSWORD', ''), 73 | 'unix_socket' => env('DB_SOCKET', ''), 74 | 'charset' => env('DB_CHARSET', 'utf8mb4'), 75 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 76 | 'prefix' => '', 77 | 'prefix_indexes' => true, 78 | 'strict' => true, 79 | 'engine' => null, 80 | 'options' => extension_loaded('pdo_mysql') ? array_filter([ 81 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), 82 | ]) : [], 83 | ], 84 | 85 | 'pgsql' => [ 86 | 'driver' => 'pgsql', 87 | 'url' => env('DB_URL'), 88 | 'host' => env('DB_HOST', '127.0.0.1'), 89 | 'port' => env('DB_PORT', '5432'), 90 | 'database' => env('DB_DATABASE', 'laravel'), 91 | 'username' => env('DB_USERNAME', 'root'), 92 | 'password' => env('DB_PASSWORD', ''), 93 | 'charset' => env('DB_CHARSET', 'utf8'), 94 | 'prefix' => '', 95 | 'prefix_indexes' => true, 96 | 'search_path' => 'public', 97 | 'sslmode' => 'prefer', 98 | ], 99 | 100 | 'sqlsrv' => [ 101 | 'driver' => 'sqlsrv', 102 | 'url' => env('DB_URL'), 103 | 'host' => env('DB_HOST', 'localhost'), 104 | 'port' => env('DB_PORT', '1433'), 105 | 'database' => env('DB_DATABASE', 'laravel'), 106 | 'username' => env('DB_USERNAME', 'root'), 107 | 'password' => env('DB_PASSWORD', ''), 108 | 'charset' => env('DB_CHARSET', 'utf8'), 109 | 'prefix' => '', 110 | 'prefix_indexes' => true, 111 | // 'encrypt' => env('DB_ENCRYPT', 'yes'), 112 | // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), 113 | ], 114 | 115 | ], 116 | 117 | /* 118 | |-------------------------------------------------------------------------- 119 | | Migration Repository Table 120 | |-------------------------------------------------------------------------- 121 | | 122 | | This table keeps track of all the migrations that have already run for 123 | | your application. Using this information, we can determine which of 124 | | the migrations on disk haven't actually been run on the database. 125 | | 126 | */ 127 | 128 | 'migrations' => [ 129 | 'table' => 'migrations', 130 | 'update_date_on_publish' => true, 131 | ], 132 | 133 | /* 134 | |-------------------------------------------------------------------------- 135 | | Redis Databases 136 | |-------------------------------------------------------------------------- 137 | | 138 | | Redis is an open source, fast, and advanced key-value store that also 139 | | provides a richer body of commands than a typical key-value system 140 | | such as Memcached. You may define your connection settings here. 141 | | 142 | */ 143 | 144 | 'redis' => [ 145 | 146 | 'client' => env('REDIS_CLIENT', 'phpredis'), 147 | 148 | 'options' => [ 149 | 'cluster' => env('REDIS_CLUSTER', 'redis'), 150 | 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), 151 | ], 152 | 153 | 'default' => [ 154 | 'url' => env('REDIS_URL'), 155 | 'host' => env('REDIS_HOST', '127.0.0.1'), 156 | 'username' => env('REDIS_USERNAME'), 157 | 'password' => env('REDIS_PASSWORD'), 158 | 'port' => env('REDIS_PORT', '6379'), 159 | 'database' => env('REDIS_DB', '0'), 160 | ], 161 | 162 | 'cache' => [ 163 | 'url' => env('REDIS_URL'), 164 | 'host' => env('REDIS_HOST', '127.0.0.1'), 165 | 'username' => env('REDIS_USERNAME'), 166 | 'password' => env('REDIS_PASSWORD'), 167 | 'port' => env('REDIS_PORT', '6379'), 168 | 'database' => env('REDIS_CACHE_DB', '1'), 169 | ], 170 | 171 | ], 172 | 173 | ]; 174 | -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DISK', 'local'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Filesystem Disks 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Below you may configure as many filesystem disks as necessary, and you 24 | | may even configure multiple disks for the same driver. Examples for 25 | | most supported storage drivers are configured here for reference. 26 | | 27 | | Supported drivers: "local", "ftp", "sftp", "s3" 28 | | 29 | */ 30 | 31 | 'disks' => [ 32 | 33 | 'local' => [ 34 | 'driver' => 'local', 35 | 'root' => storage_path('app/private'), 36 | 'serve' => true, 37 | 'throw' => false, 38 | ], 39 | 40 | 'public' => [ 41 | 'driver' => 'local', 42 | 'root' => storage_path('app/public'), 43 | 'url' => env('APP_URL').'/storage', 44 | 'visibility' => 'public', 45 | 'throw' => false, 46 | ], 47 | 48 | 's3' => [ 49 | 'driver' => 's3', 50 | 'key' => env('AWS_ACCESS_KEY_ID'), 51 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 52 | 'region' => env('AWS_DEFAULT_REGION'), 53 | 'bucket' => env('AWS_BUCKET'), 54 | 'url' => env('AWS_URL'), 55 | 'endpoint' => env('AWS_ENDPOINT'), 56 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), 57 | 'throw' => false, 58 | ], 59 | 60 | ], 61 | 62 | /* 63 | |-------------------------------------------------------------------------- 64 | | Symbolic Links 65 | |-------------------------------------------------------------------------- 66 | | 67 | | Here you may configure the symbolic links that will be created when the 68 | | `storage:link` Artisan command is executed. The array keys should be 69 | | the locations of the links and the values should be their targets. 70 | | 71 | */ 72 | 73 | 'links' => [ 74 | public_path('storage') => storage_path('app/public'), 75 | ], 76 | 77 | ]; 78 | -------------------------------------------------------------------------------- /config/jwt.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | return [ 13 | 14 | /* 15 | |-------------------------------------------------------------------------- 16 | | JWT Authentication Secret 17 | |-------------------------------------------------------------------------- 18 | | 19 | | Don't forget to set this in your .env file, as it will be used to sign 20 | | your tokens. A helper command is provided for this: 21 | | `php artisan jwt:secret` 22 | | 23 | | Note: This will be used for Symmetric algorithms only (HMAC), 24 | | since RSA and ECDSA use a private/public key combo (See below). 25 | | 26 | */ 27 | 28 | 'secret' => env('JWT_SECRET'), 29 | 30 | /* 31 | |-------------------------------------------------------------------------- 32 | | JWT Authentication Keys 33 | |-------------------------------------------------------------------------- 34 | | 35 | | The algorithm you are using, will determine whether your tokens are 36 | | signed with a random string (defined in `JWT_SECRET`) or using the 37 | | following public & private keys. 38 | | 39 | | Symmetric Algorithms: 40 | | HS256, HS384 & HS512 will use `JWT_SECRET`. 41 | | 42 | | Asymmetric Algorithms: 43 | | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below. 44 | | 45 | */ 46 | 47 | 'keys' => [ 48 | 49 | /* 50 | |-------------------------------------------------------------------------- 51 | | Public Key 52 | |-------------------------------------------------------------------------- 53 | | 54 | | A path or resource to your public key. 55 | | 56 | | E.g. 'file://path/to/public/key' 57 | | 58 | */ 59 | 60 | 'public' => env('JWT_PUBLIC_KEY'), 61 | 62 | /* 63 | |-------------------------------------------------------------------------- 64 | | Private Key 65 | |-------------------------------------------------------------------------- 66 | | 67 | | A path or resource to your private key. 68 | | 69 | | E.g. 'file://path/to/private/key' 70 | | 71 | */ 72 | 73 | 'private' => env('JWT_PRIVATE_KEY'), 74 | 75 | /* 76 | |-------------------------------------------------------------------------- 77 | | Passphrase 78 | |-------------------------------------------------------------------------- 79 | | 80 | | The passphrase for your private key. Can be null if none set. 81 | | 82 | */ 83 | 84 | 'passphrase' => env('JWT_PASSPHRASE'), 85 | 86 | ], 87 | 88 | /* 89 | |-------------------------------------------------------------------------- 90 | | JWT time to live 91 | |-------------------------------------------------------------------------- 92 | | 93 | | Specify the length of time (in minutes) that the token will be valid for. 94 | | Defaults to 1 hour. 95 | | 96 | | You can also set this to null, to yield a never expiring token. 97 | | Some people may want this behaviour for e.g. a mobile app. 98 | | This is not particularly recommended, so make sure you have appropriate 99 | | systems in place to revoke the token if necessary. 100 | | Notice: If you set this to null you should remove 'exp' element from 'required_claims' list. 101 | | 102 | */ 103 | 104 | 'ttl' => (int) env('JWT_TTL', 60), 105 | 106 | /* 107 | |-------------------------------------------------------------------------- 108 | | Refresh time to live 109 | |-------------------------------------------------------------------------- 110 | | 111 | | Specify the length of time (in minutes) that the token can be refreshed 112 | | within. I.E. The user can refresh their token within a 2 week window of 113 | | the original token being created until they must re-authenticate. 114 | | Defaults to 2 weeks. 115 | | 116 | | You can also set this to null, to yield an infinite refresh time. 117 | | Some may want this instead of never expiring tokens for e.g. a mobile app. 118 | | This is not particularly recommended, so make sure you have appropriate 119 | | systems in place to revoke the token if necessary. 120 | | 121 | */ 122 | 123 | 'refresh_ttl' => (int) env('JWT_REFRESH_TTL', 20160), 124 | 125 | /* 126 | |-------------------------------------------------------------------------- 127 | | JWT hashing algorithm 128 | |-------------------------------------------------------------------------- 129 | | 130 | | Specify the hashing algorithm that will be used to sign the token. 131 | | 132 | */ 133 | 134 | 'algo' => env('JWT_ALGO', Tymon\JWTAuth\Providers\JWT\Provider::ALGO_HS256), 135 | 136 | /* 137 | |-------------------------------------------------------------------------- 138 | | Required Claims 139 | |-------------------------------------------------------------------------- 140 | | 141 | | Specify the required claims that must exist in any token. 142 | | A TokenInvalidException will be thrown if any of these claims are not 143 | | present in the payload. 144 | | 145 | */ 146 | 147 | 'required_claims' => [ 148 | 'iss', 149 | 'iat', 150 | 'exp', 151 | 'nbf', 152 | 'sub', 153 | 'jti', 154 | ], 155 | 156 | /* 157 | |-------------------------------------------------------------------------- 158 | | Persistent Claims 159 | |-------------------------------------------------------------------------- 160 | | 161 | | Specify the claim keys to be persisted when refreshing a token. 162 | | `sub` and `iat` will automatically be persisted, in 163 | | addition to the these claims. 164 | | 165 | | Note: If a claim does not exist then it will be ignored. 166 | | 167 | */ 168 | 169 | 'persistent_claims' => [ 170 | // 'foo', 171 | // 'bar', 172 | ], 173 | 174 | /* 175 | |-------------------------------------------------------------------------- 176 | | Lock Subject 177 | |-------------------------------------------------------------------------- 178 | | 179 | | This will determine whether a `prv` claim is automatically added to 180 | | the token. The purpose of this is to ensure that if you have multiple 181 | | authentication models e.g. `App\User` & `App\OtherPerson`, then we 182 | | should prevent one authentication request from impersonating another, 183 | | if 2 tokens happen to have the same id across the 2 different models. 184 | | 185 | | Under specific circumstances, you may want to disable this behaviour 186 | | e.g. if you only have one authentication model, then you would save 187 | | a little on token size. 188 | | 189 | */ 190 | 191 | 'lock_subject' => true, 192 | 193 | /* 194 | |-------------------------------------------------------------------------- 195 | | Leeway 196 | |-------------------------------------------------------------------------- 197 | | 198 | | This property gives the jwt timestamp claims some "leeway". 199 | | Meaning that if you have any unavoidable slight clock skew on 200 | | any of your servers then this will afford you some level of cushioning. 201 | | 202 | | This applies to the claims `iat`, `nbf` and `exp`. 203 | | 204 | | Specify in seconds - only if you know you need it. 205 | | 206 | */ 207 | 208 | 'leeway' => env('JWT_LEEWAY', 0), 209 | 210 | /* 211 | |-------------------------------------------------------------------------- 212 | | Blacklist Enabled 213 | |-------------------------------------------------------------------------- 214 | | 215 | | In order to invalidate tokens, you must have the blacklist enabled. 216 | | If you do not want or need this functionality, then set this to false. 217 | | 218 | */ 219 | 220 | 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true), 221 | 222 | /* 223 | | ------------------------------------------------------------------------- 224 | | Blacklist Grace Period 225 | | ------------------------------------------------------------------------- 226 | | 227 | | When multiple concurrent requests are made with the same JWT, 228 | | it is possible that some of them fail, due to token regeneration 229 | | on every request. 230 | | 231 | | Set grace period in seconds to prevent parallel request failure. 232 | | 233 | */ 234 | 235 | 'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0), 236 | 237 | /* 238 | |-------------------------------------------------------------------------- 239 | | Cookies encryption 240 | |-------------------------------------------------------------------------- 241 | | 242 | | By default Laravel encrypt cookies for security reason. 243 | | If you decide to not decrypt cookies, you will have to configure Laravel 244 | | to not encrypt your cookie token by adding its name into the $except 245 | | array available in the middleware "EncryptCookies" provided by Laravel. 246 | | see https://laravel.com/docs/master/responses#cookies-and-encryption 247 | | for details. 248 | | 249 | | Set it to true if you want to decrypt cookies. 250 | | 251 | */ 252 | 253 | 'decrypt_cookies' => false, 254 | 255 | /* 256 | |-------------------------------------------------------------------------- 257 | | Providers 258 | |-------------------------------------------------------------------------- 259 | | 260 | | Specify the various providers used throughout the package. 261 | | 262 | */ 263 | 264 | 'providers' => [ 265 | 266 | /* 267 | |-------------------------------------------------------------------------- 268 | | JWT Provider 269 | |-------------------------------------------------------------------------- 270 | | 271 | | Specify the provider that is used to create and decode the tokens. 272 | | 273 | */ 274 | 275 | 'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class, 276 | 277 | /* 278 | |-------------------------------------------------------------------------- 279 | | Authentication Provider 280 | |-------------------------------------------------------------------------- 281 | | 282 | | Specify the provider that is used to authenticate users. 283 | | 284 | */ 285 | 286 | 'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class, 287 | 288 | /* 289 | |-------------------------------------------------------------------------- 290 | | Storage Provider 291 | |-------------------------------------------------------------------------- 292 | | 293 | | Specify the provider that is used to store tokens in the blacklist. 294 | | 295 | */ 296 | 297 | 'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class, 298 | 299 | ], 300 | 301 | ]; 302 | -------------------------------------------------------------------------------- /config/logging.php: -------------------------------------------------------------------------------- 1 | env('LOG_CHANNEL', 'stack'), 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Deprecations Log Channel 26 | |-------------------------------------------------------------------------- 27 | | 28 | | This option controls the log channel that should be used to log warnings 29 | | regarding deprecated PHP and library features. This allows you to get 30 | | your application ready for upcoming major versions of dependencies. 31 | | 32 | */ 33 | 34 | 'deprecations' => [ 35 | 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), 36 | 'trace' => env('LOG_DEPRECATIONS_TRACE', false), 37 | ], 38 | 39 | /* 40 | |-------------------------------------------------------------------------- 41 | | Log Channels 42 | |-------------------------------------------------------------------------- 43 | | 44 | | Here you may configure the log channels for your application. Laravel 45 | | utilizes the Monolog PHP logging library, which includes a variety 46 | | of powerful log handlers and formatters that you're free to use. 47 | | 48 | | Available drivers: "single", "daily", "slack", "syslog", 49 | | "errorlog", "monolog", "custom", "stack" 50 | | 51 | */ 52 | 53 | 'channels' => [ 54 | 55 | 'stack' => [ 56 | 'driver' => 'stack', 57 | 'channels' => explode(',', env('LOG_STACK', 'single')), 58 | 'ignore_exceptions' => false, 59 | ], 60 | 61 | 'single' => [ 62 | 'driver' => 'single', 63 | 'path' => storage_path('logs/laravel.log'), 64 | 'level' => env('LOG_LEVEL', 'debug'), 65 | 'replace_placeholders' => true, 66 | ], 67 | 68 | 'daily' => [ 69 | 'driver' => 'daily', 70 | 'path' => storage_path('logs/laravel.log'), 71 | 'level' => env('LOG_LEVEL', 'debug'), 72 | 'days' => env('LOG_DAILY_DAYS', 14), 73 | 'replace_placeholders' => true, 74 | ], 75 | 76 | 'slack' => [ 77 | 'driver' => 'slack', 78 | 'url' => env('LOG_SLACK_WEBHOOK_URL'), 79 | 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), 80 | 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), 81 | 'level' => env('LOG_LEVEL', 'critical'), 82 | 'replace_placeholders' => true, 83 | ], 84 | 85 | 'papertrail' => [ 86 | 'driver' => 'monolog', 87 | 'level' => env('LOG_LEVEL', 'debug'), 88 | 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), 89 | 'handler_with' => [ 90 | 'host' => env('PAPERTRAIL_URL'), 91 | 'port' => env('PAPERTRAIL_PORT'), 92 | 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), 93 | ], 94 | 'processors' => [PsrLogMessageProcessor::class], 95 | ], 96 | 97 | 'stderr' => [ 98 | 'driver' => 'monolog', 99 | 'level' => env('LOG_LEVEL', 'debug'), 100 | 'handler' => StreamHandler::class, 101 | 'formatter' => env('LOG_STDERR_FORMATTER'), 102 | 'with' => [ 103 | 'stream' => 'php://stderr', 104 | ], 105 | 'processors' => [PsrLogMessageProcessor::class], 106 | ], 107 | 108 | 'syslog' => [ 109 | 'driver' => 'syslog', 110 | 'level' => env('LOG_LEVEL', 'debug'), 111 | 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), 112 | 'replace_placeholders' => true, 113 | ], 114 | 115 | 'errorlog' => [ 116 | 'driver' => 'errorlog', 117 | 'level' => env('LOG_LEVEL', 'debug'), 118 | 'replace_placeholders' => true, 119 | ], 120 | 121 | 'null' => [ 122 | 'driver' => 'monolog', 123 | 'handler' => NullHandler::class, 124 | ], 125 | 126 | 'emergency' => [ 127 | 'path' => storage_path('logs/laravel.log'), 128 | ], 129 | 130 | ], 131 | 132 | ]; 133 | -------------------------------------------------------------------------------- /config/mail.php: -------------------------------------------------------------------------------- 1 | env('MAIL_MAILER', 'log'), 18 | 19 | /* 20 | |-------------------------------------------------------------------------- 21 | | Mailer Configurations 22 | |-------------------------------------------------------------------------- 23 | | 24 | | Here you may configure all of the mailers used by your application plus 25 | | their respective settings. Several examples have been configured for 26 | | you and you are free to add your own as your application requires. 27 | | 28 | | Laravel supports a variety of mail "transport" drivers that can be used 29 | | when delivering an email. You may specify which one you're using for 30 | | your mailers below. You may also add additional mailers if needed. 31 | | 32 | | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", 33 | | "postmark", "resend", "log", "array", 34 | | "failover", "roundrobin" 35 | | 36 | */ 37 | 38 | 'mailers' => [ 39 | 40 | 'smtp' => [ 41 | 'transport' => 'smtp', 42 | 'url' => env('MAIL_URL'), 43 | 'host' => env('MAIL_HOST', '127.0.0.1'), 44 | 'port' => env('MAIL_PORT', 2525), 45 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 46 | 'username' => env('MAIL_USERNAME'), 47 | 'password' => env('MAIL_PASSWORD'), 48 | 'timeout' => null, 49 | 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url(env('APP_URL', 'http://localhost'), PHP_URL_HOST)), 50 | ], 51 | 52 | 'ses' => [ 53 | 'transport' => 'ses', 54 | ], 55 | 56 | 'postmark' => [ 57 | 'transport' => 'postmark', 58 | // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), 59 | // 'client' => [ 60 | // 'timeout' => 5, 61 | // ], 62 | ], 63 | 64 | 'resend' => [ 65 | 'transport' => 'resend', 66 | ], 67 | 68 | 'sendmail' => [ 69 | 'transport' => 'sendmail', 70 | 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), 71 | ], 72 | 73 | 'log' => [ 74 | 'transport' => 'log', 75 | 'channel' => env('MAIL_LOG_CHANNEL'), 76 | ], 77 | 78 | 'array' => [ 79 | 'transport' => 'array', 80 | ], 81 | 82 | 'failover' => [ 83 | 'transport' => 'failover', 84 | 'mailers' => [ 85 | 'smtp', 86 | 'log', 87 | ], 88 | ], 89 | 90 | 'roundrobin' => [ 91 | 'transport' => 'roundrobin', 92 | 'mailers' => [ 93 | 'ses', 94 | 'postmark', 95 | ], 96 | ], 97 | 98 | ], 99 | 100 | /* 101 | |-------------------------------------------------------------------------- 102 | | Global "From" Address 103 | |-------------------------------------------------------------------------- 104 | | 105 | | You may wish for all emails sent by your application to be sent from 106 | | the same address. Here you may specify a name and address that is 107 | | used globally for all emails that are sent by your application. 108 | | 109 | */ 110 | 111 | 'from' => [ 112 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), 113 | 'name' => env('MAIL_FROM_NAME', 'Example'), 114 | ], 115 | 116 | ]; 117 | -------------------------------------------------------------------------------- /config/permission.php: -------------------------------------------------------------------------------- 1 | [ 6 | 7 | /* 8 | * When using the "HasPermissions" trait from this package, we need to know which 9 | * Eloquent model should be used to retrieve your permissions. Of course, it 10 | * is often just the "Permission" model but you may use whatever you like. 11 | * 12 | * The model you want to use as a Permission model needs to implement the 13 | * `Spatie\Permission\Contracts\Permission` contract. 14 | */ 15 | 16 | 'permission' => Spatie\Permission\Models\Permission::class, 17 | 18 | /* 19 | * When using the "HasRoles" trait from this package, we need to know which 20 | * Eloquent model should be used to retrieve your roles. Of course, it 21 | * is often just the "Role" model but you may use whatever you like. 22 | * 23 | * The model you want to use as a Role model needs to implement the 24 | * `Spatie\Permission\Contracts\Role` contract. 25 | */ 26 | 27 | 'role' => Spatie\Permission\Models\Role::class, 28 | 29 | ], 30 | 31 | 'table_names' => [ 32 | 33 | /* 34 | * When using the "HasRoles" trait from this package, we need to know which 35 | * table should be used to retrieve your roles. We have chosen a basic 36 | * default value but you may easily change it to any table you like. 37 | */ 38 | 39 | 'roles' => 'roles', 40 | 41 | /* 42 | * When using the "HasPermissions" trait from this package, we need to know which 43 | * table should be used to retrieve your permissions. We have chosen a basic 44 | * default value but you may easily change it to any table you like. 45 | */ 46 | 47 | 'permissions' => 'permissions', 48 | 49 | /* 50 | * When using the "HasPermissions" trait from this package, we need to know which 51 | * table should be used to retrieve your models permissions. We have chosen a 52 | * basic default value but you may easily change it to any table you like. 53 | */ 54 | 55 | 'model_has_permissions' => 'model_has_permissions', 56 | 57 | /* 58 | * When using the "HasRoles" trait from this package, we need to know which 59 | * table should be used to retrieve your models roles. We have chosen a 60 | * basic default value but you may easily change it to any table you like. 61 | */ 62 | 63 | 'model_has_roles' => 'model_has_roles', 64 | 65 | /* 66 | * When using the "HasRoles" trait from this package, we need to know which 67 | * table should be used to retrieve your roles permissions. We have chosen a 68 | * basic default value but you may easily change it to any table you like. 69 | */ 70 | 71 | 'role_has_permissions' => 'role_has_permissions', 72 | ], 73 | 74 | 'column_names' => [ 75 | /* 76 | * Change this if you want to name the related pivots other than defaults 77 | */ 78 | 'role_pivot_key' => null, //default 'role_id', 79 | 'permission_pivot_key' => null, //default 'permission_id', 80 | 81 | /* 82 | * Change this if you want to name the related model primary key other than 83 | * `model_id`. 84 | * 85 | * For example, this would be nice if your primary keys are all UUIDs. In 86 | * that case, name this `model_uuid`. 87 | */ 88 | 89 | 'model_morph_key' => 'model_id', 90 | 91 | /* 92 | * Change this if you want to use the teams feature and your related model's 93 | * foreign key is other than `team_id`. 94 | */ 95 | 96 | 'team_foreign_key' => 'team_id', 97 | ], 98 | 99 | /* 100 | * When set to true, the method for checking permissions will be registered on the gate. 101 | * Set this to false if you want to implement custom logic for checking permissions. 102 | */ 103 | 104 | 'register_permission_check_method' => true, 105 | 106 | /* 107 | * When set to true, Laravel\Octane\Events\OperationTerminated event listener will be registered 108 | * this will refresh permissions on every TickTerminated, TaskTerminated and RequestTerminated 109 | * NOTE: This should not be needed in most cases, but an Octane/Vapor combination benefited from it. 110 | */ 111 | 'register_octane_reset_listener' => false, 112 | 113 | /* 114 | * Teams Feature. 115 | * When set to true the package implements teams using the 'team_foreign_key'. 116 | * If you want the migrations to register the 'team_foreign_key', you must 117 | * set this to true before doing the migration. 118 | * If you already did the migration then you must make a new migration to also 119 | * add 'team_foreign_key' to 'roles', 'model_has_roles', and 'model_has_permissions' 120 | * (view the latest version of this package's migration file) 121 | */ 122 | 123 | 'teams' => false, 124 | 125 | /* 126 | * Passport Client Credentials Grant 127 | * When set to true the package will use Passports Client to check permissions 128 | */ 129 | 130 | 'use_passport_client_credentials' => false, 131 | 132 | /* 133 | * When set to true, the required permission names are added to exception messages. 134 | * This could be considered an information leak in some contexts, so the default 135 | * setting is false here for optimum safety. 136 | */ 137 | 138 | 'display_permission_in_exception' => false, 139 | 140 | /* 141 | * When set to true, the required role names are added to exception messages. 142 | * This could be considered an information leak in some contexts, so the default 143 | * setting is false here for optimum safety. 144 | */ 145 | 146 | 'display_role_in_exception' => false, 147 | 148 | /* 149 | * By default wildcard permission lookups are disabled. 150 | * See documentation to understand supported syntax. 151 | */ 152 | 153 | 'enable_wildcard_permission' => false, 154 | 155 | /* 156 | * The class to use for interpreting wildcard permissions. 157 | * If you need to modify delimiters, override the class and specify its name here. 158 | */ 159 | // 'permission.wildcard_permission' => Spatie\Permission\WildcardPermission::class, 160 | 161 | /* Cache-specific settings */ 162 | 163 | 'cache' => [ 164 | 165 | /* 166 | * By default all permissions are cached for 24 hours to speed up performance. 167 | * When permissions or roles are updated the cache is flushed automatically. 168 | */ 169 | 170 | 'expiration_time' => \DateInterval::createFromDateString('24 hours'), 171 | 172 | /* 173 | * The cache key used to store all permissions. 174 | */ 175 | 176 | 'key' => 'spatie.permission.cache', 177 | 178 | /* 179 | * You may optionally indicate a specific cache driver to use for permission and 180 | * role caching using any of the `store` drivers listed in the cache.php config 181 | * file. Using 'default' here means to use the `default` set in cache.php. 182 | */ 183 | 184 | 'store' => 'default', 185 | ], 186 | ]; 187 | -------------------------------------------------------------------------------- /config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_CONNECTION', 'database'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Queue Connections 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may configure the connection options for every queue backend 24 | | used by your application. An example configuration is provided for 25 | | each backend supported by Laravel. You're also free to add more. 26 | | 27 | | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'sync' => [ 34 | 'driver' => 'sync', 35 | ], 36 | 37 | 'database' => [ 38 | 'driver' => 'database', 39 | 'connection' => env('DB_QUEUE_CONNECTION'), 40 | 'table' => env('DB_QUEUE_TABLE', 'jobs'), 41 | 'queue' => env('DB_QUEUE', 'default'), 42 | 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), 43 | 'after_commit' => false, 44 | ], 45 | 46 | 'beanstalkd' => [ 47 | 'driver' => 'beanstalkd', 48 | 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), 49 | 'queue' => env('BEANSTALKD_QUEUE', 'default'), 50 | 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), 51 | 'block_for' => 0, 52 | 'after_commit' => false, 53 | ], 54 | 55 | 'sqs' => [ 56 | 'driver' => 'sqs', 57 | 'key' => env('AWS_ACCESS_KEY_ID'), 58 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 59 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 60 | 'queue' => env('SQS_QUEUE', 'default'), 61 | 'suffix' => env('SQS_SUFFIX'), 62 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 63 | 'after_commit' => false, 64 | ], 65 | 66 | 'redis' => [ 67 | 'driver' => 'redis', 68 | 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), 69 | 'queue' => env('REDIS_QUEUE', 'default'), 70 | 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), 71 | 'block_for' => null, 72 | 'after_commit' => false, 73 | ], 74 | 75 | ], 76 | 77 | /* 78 | |-------------------------------------------------------------------------- 79 | | Job Batching 80 | |-------------------------------------------------------------------------- 81 | | 82 | | The following options configure the database and table that store job 83 | | batching information. These options can be updated to any database 84 | | connection and table which has been defined by your application. 85 | | 86 | */ 87 | 88 | 'batching' => [ 89 | 'database' => env('DB_CONNECTION', 'sqlite'), 90 | 'table' => 'job_batches', 91 | ], 92 | 93 | /* 94 | |-------------------------------------------------------------------------- 95 | | Failed Queue Jobs 96 | |-------------------------------------------------------------------------- 97 | | 98 | | These options configure the behavior of failed queue job logging so you 99 | | can control how and where failed jobs are stored. Laravel ships with 100 | | support for storing failed jobs in a simple file or in a database. 101 | | 102 | | Supported drivers: "database-uuids", "dynamodb", "file", "null" 103 | | 104 | */ 105 | 106 | 'failed' => [ 107 | 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), 108 | 'database' => env('DB_CONNECTION', 'sqlite'), 109 | 'table' => 'failed_jobs', 110 | ], 111 | 112 | ]; 113 | -------------------------------------------------------------------------------- /config/sanctum.php: -------------------------------------------------------------------------------- 1 | explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( 19 | '%s%s', 20 | 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', 21 | Sanctum::currentApplicationUrlWithPort() 22 | ))), 23 | 24 | /* 25 | |-------------------------------------------------------------------------- 26 | | Sanctum Guards 27 | |-------------------------------------------------------------------------- 28 | | 29 | | This array contains the authentication guards that will be checked when 30 | | Sanctum is trying to authenticate a request. If none of these guards 31 | | are able to authenticate the request, Sanctum will use the bearer 32 | | token that's present on an incoming request for authentication. 33 | | 34 | */ 35 | 36 | 'guard' => ['web'], 37 | 38 | /* 39 | |-------------------------------------------------------------------------- 40 | | Expiration Minutes 41 | |-------------------------------------------------------------------------- 42 | | 43 | | This value controls the number of minutes until an issued token will be 44 | | considered expired. This will override any values set in the token's 45 | | "expires_at" attribute, but first-party sessions are not affected. 46 | | 47 | */ 48 | 49 | 'expiration' => null, 50 | 51 | /* 52 | |-------------------------------------------------------------------------- 53 | | Token Prefix 54 | |-------------------------------------------------------------------------- 55 | | 56 | | Sanctum can prefix new tokens in order to take advantage of numerous 57 | | security scanning initiatives maintained by open source platforms 58 | | that notify developers if they commit tokens into repositories. 59 | | 60 | | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning 61 | | 62 | */ 63 | 64 | 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), 65 | 66 | /* 67 | |-------------------------------------------------------------------------- 68 | | Sanctum Middleware 69 | |-------------------------------------------------------------------------- 70 | | 71 | | When authenticating your first-party SPA with Sanctum you may need to 72 | | customize some of the middleware Sanctum uses while processing the 73 | | request. You may change the middleware listed below as required. 74 | | 75 | */ 76 | 77 | 'middleware' => [ 78 | 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, 79 | 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, 80 | 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, 81 | ], 82 | 83 | ]; 84 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'token' => env('POSTMARK_TOKEN'), 19 | ], 20 | 21 | 'ses' => [ 22 | 'key' => env('AWS_ACCESS_KEY_ID'), 23 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 24 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 25 | ], 26 | 27 | 'resend' => [ 28 | 'key' => env('RESEND_KEY'), 29 | ], 30 | 31 | 'slack' => [ 32 | 'notifications' => [ 33 | 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), 34 | 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), 35 | ], 36 | ], 37 | 38 | ]; 39 | -------------------------------------------------------------------------------- /config/session.php: -------------------------------------------------------------------------------- 1 | env('SESSION_DRIVER', 'database'), 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Session Lifetime 26 | |-------------------------------------------------------------------------- 27 | | 28 | | Here you may specify the number of minutes that you wish the session 29 | | to be allowed to remain idle before it expires. If you want them 30 | | to expire immediately when the browser is closed then you may 31 | | indicate that via the expire_on_close configuration option. 32 | | 33 | */ 34 | 35 | 'lifetime' => env('SESSION_LIFETIME', 120), 36 | 37 | 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), 38 | 39 | /* 40 | |-------------------------------------------------------------------------- 41 | | Session Encryption 42 | |-------------------------------------------------------------------------- 43 | | 44 | | This option allows you to easily specify that all of your session data 45 | | should be encrypted before it's stored. All encryption is performed 46 | | automatically by Laravel and you may use the session like normal. 47 | | 48 | */ 49 | 50 | 'encrypt' => env('SESSION_ENCRYPT', false), 51 | 52 | /* 53 | |-------------------------------------------------------------------------- 54 | | Session File Location 55 | |-------------------------------------------------------------------------- 56 | | 57 | | When utilizing the "file" session driver, the session files are placed 58 | | on disk. The default storage location is defined here; however, you 59 | | are free to provide another location where they should be stored. 60 | | 61 | */ 62 | 63 | 'files' => storage_path('framework/sessions'), 64 | 65 | /* 66 | |-------------------------------------------------------------------------- 67 | | Session Database Connection 68 | |-------------------------------------------------------------------------- 69 | | 70 | | When using the "database" or "redis" session drivers, you may specify a 71 | | connection that should be used to manage these sessions. This should 72 | | correspond to a connection in your database configuration options. 73 | | 74 | */ 75 | 76 | 'connection' => env('SESSION_CONNECTION'), 77 | 78 | /* 79 | |-------------------------------------------------------------------------- 80 | | Session Database Table 81 | |-------------------------------------------------------------------------- 82 | | 83 | | When using the "database" session driver, you may specify the table to 84 | | be used to store sessions. Of course, a sensible default is defined 85 | | for you; however, you're welcome to change this to another table. 86 | | 87 | */ 88 | 89 | 'table' => env('SESSION_TABLE', 'sessions'), 90 | 91 | /* 92 | |-------------------------------------------------------------------------- 93 | | Session Cache Store 94 | |-------------------------------------------------------------------------- 95 | | 96 | | When using one of the framework's cache driven session backends, you may 97 | | define the cache store which should be used to store the session data 98 | | between requests. This must match one of your defined cache stores. 99 | | 100 | | Affects: "apc", "dynamodb", "memcached", "redis" 101 | | 102 | */ 103 | 104 | 'store' => env('SESSION_STORE'), 105 | 106 | /* 107 | |-------------------------------------------------------------------------- 108 | | Session Sweeping Lottery 109 | |-------------------------------------------------------------------------- 110 | | 111 | | Some session drivers must manually sweep their storage location to get 112 | | rid of old sessions from storage. Here are the chances that it will 113 | | happen on a given request. By default, the odds are 2 out of 100. 114 | | 115 | */ 116 | 117 | 'lottery' => [2, 100], 118 | 119 | /* 120 | |-------------------------------------------------------------------------- 121 | | Session Cookie Name 122 | |-------------------------------------------------------------------------- 123 | | 124 | | Here you may change the name of the session cookie that is created by 125 | | the framework. Typically, you should not need to change this value 126 | | since doing so does not grant a meaningful security improvement. 127 | | 128 | */ 129 | 130 | 'cookie' => env( 131 | 'SESSION_COOKIE', 132 | Str::slug(env('APP_NAME', 'laravel'), '_').'_session' 133 | ), 134 | 135 | /* 136 | |-------------------------------------------------------------------------- 137 | | Session Cookie Path 138 | |-------------------------------------------------------------------------- 139 | | 140 | | The session cookie path determines the path for which the cookie will 141 | | be regarded as available. Typically, this will be the root path of 142 | | your application, but you're free to change this when necessary. 143 | | 144 | */ 145 | 146 | 'path' => env('SESSION_PATH', '/'), 147 | 148 | /* 149 | |-------------------------------------------------------------------------- 150 | | Session Cookie Domain 151 | |-------------------------------------------------------------------------- 152 | | 153 | | This value determines the domain and subdomains the session cookie is 154 | | available to. By default, the cookie will be available to the root 155 | | domain and all subdomains. Typically, this shouldn't be changed. 156 | | 157 | */ 158 | 159 | 'domain' => env('SESSION_DOMAIN'), 160 | 161 | /* 162 | |-------------------------------------------------------------------------- 163 | | HTTPS Only Cookies 164 | |-------------------------------------------------------------------------- 165 | | 166 | | By setting this option to true, session cookies will only be sent back 167 | | to the server if the browser has a HTTPS connection. This will keep 168 | | the cookie from being sent to you when it can't be done securely. 169 | | 170 | */ 171 | 172 | 'secure' => env('SESSION_SECURE_COOKIE'), 173 | 174 | /* 175 | |-------------------------------------------------------------------------- 176 | | HTTP Access Only 177 | |-------------------------------------------------------------------------- 178 | | 179 | | Setting this value to true will prevent JavaScript from accessing the 180 | | value of the cookie and the cookie will only be accessible through 181 | | the HTTP protocol. It's unlikely you should disable this option. 182 | | 183 | */ 184 | 185 | 'http_only' => env('SESSION_HTTP_ONLY', true), 186 | 187 | /* 188 | |-------------------------------------------------------------------------- 189 | | Same-Site Cookies 190 | |-------------------------------------------------------------------------- 191 | | 192 | | This option determines how your cookies behave when cross-site requests 193 | | take place, and can be used to mitigate CSRF attacks. By default, we 194 | | will set this value to "lax" to permit secure cross-site requests. 195 | | 196 | | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value 197 | | 198 | | Supported: "lax", "strict", "none", null 199 | | 200 | */ 201 | 202 | 'same_site' => env('SESSION_SAME_SITE', 'lax'), 203 | 204 | /* 205 | |-------------------------------------------------------------------------- 206 | | Partitioned Cookies 207 | |-------------------------------------------------------------------------- 208 | | 209 | | Setting this value to true will tie the cookie to the top-level site for 210 | | a cross-site context. Partitioned cookies are accepted by the browser 211 | | when flagged "secure" and the Same-Site attribute is set to "none". 212 | | 213 | */ 214 | 215 | 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), 216 | 217 | ]; 218 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class UserFactory extends Factory 13 | { 14 | /** 15 | * The current password being used by the factory. 16 | */ 17 | protected static ?string $password; 18 | 19 | /** 20 | * Define the model's default state. 21 | * 22 | * @return array 23 | */ 24 | public function definition(): array 25 | { 26 | return [ 27 | 'name' => fake()->name(), 28 | 'email' => fake()->unique()->safeEmail(), 29 | 'email_verified_at' => now(), 30 | 'password' => static::$password ??= Hash::make('password'), 31 | 'remember_token' => Str::random(10), 32 | ]; 33 | } 34 | 35 | /** 36 | * Indicate that the model's email address should be unverified. 37 | */ 38 | public function unverified(): static 39 | { 40 | return $this->state(fn (array $attributes) => [ 41 | 'email_verified_at' => null, 42 | ]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('name'); 17 | $table->string('email')->unique(); 18 | $table->timestamp('email_verified_at')->nullable(); 19 | $table->string('password'); 20 | $table->string('fullname'); 21 | $table->unsignedTinyInteger("status")->default(1); 22 | $table->rememberToken(); 23 | $table->timestamps(); 24 | $table->softDeletes(); 25 | }); 26 | 27 | Schema::create('password_reset_tokens', function (Blueprint $table) { 28 | $table->string('email')->primary(); 29 | $table->string('token'); 30 | $table->timestamp('created_at')->nullable(); 31 | }); 32 | 33 | Schema::create('sessions', function (Blueprint $table) { 34 | $table->string('id')->primary(); 35 | $table->foreignId('user_id')->nullable()->index(); 36 | $table->string('ip_address', 45)->nullable(); 37 | $table->text('user_agent')->nullable(); 38 | $table->longText('payload'); 39 | $table->integer('last_activity')->index(); 40 | }); 41 | } 42 | 43 | /** 44 | * Reverse the migrations. 45 | */ 46 | public function down(): void 47 | { 48 | Schema::dropIfExists('users'); 49 | Schema::dropIfExists('password_reset_tokens'); 50 | Schema::dropIfExists('sessions'); 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000001_create_cache_table.php: -------------------------------------------------------------------------------- 1 | string('key')->primary(); 16 | $table->mediumText('value'); 17 | $table->integer('expiration'); 18 | }); 19 | 20 | Schema::create('cache_locks', function (Blueprint $table) { 21 | $table->string('key')->primary(); 22 | $table->string('owner'); 23 | $table->integer('expiration'); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | */ 30 | public function down(): void 31 | { 32 | Schema::dropIfExists('cache'); 33 | Schema::dropIfExists('cache_locks'); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000002_create_jobs_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('queue')->index(); 17 | $table->longText('payload'); 18 | $table->unsignedTinyInteger('attempts'); 19 | $table->unsignedInteger('reserved_at')->nullable(); 20 | $table->unsignedInteger('available_at'); 21 | $table->unsignedInteger('created_at'); 22 | }); 23 | 24 | Schema::create('job_batches', function (Blueprint $table) { 25 | $table->string('id')->primary(); 26 | $table->string('name'); 27 | $table->integer('total_jobs'); 28 | $table->integer('pending_jobs'); 29 | $table->integer('failed_jobs'); 30 | $table->longText('failed_job_ids'); 31 | $table->mediumText('options')->nullable(); 32 | $table->integer('cancelled_at')->nullable(); 33 | $table->integer('created_at'); 34 | $table->integer('finished_at')->nullable(); 35 | }); 36 | 37 | Schema::create('failed_jobs', function (Blueprint $table) { 38 | $table->id(); 39 | $table->string('uuid')->unique(); 40 | $table->text('connection'); 41 | $table->text('queue'); 42 | $table->longText('payload'); 43 | $table->longText('exception'); 44 | $table->timestamp('failed_at')->useCurrent(); 45 | }); 46 | } 47 | 48 | /** 49 | * Reverse the migrations. 50 | */ 51 | public function down(): void 52 | { 53 | Schema::dropIfExists('jobs'); 54 | Schema::dropIfExists('job_batches'); 55 | Schema::dropIfExists('failed_jobs'); 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /database/migrations/2024_10_12_023908_create_personal_access_tokens_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->morphs('tokenable'); 17 | $table->string('name'); 18 | $table->string('token', 64)->unique(); 19 | $table->text('abilities')->nullable(); 20 | $table->timestamp('last_used_at')->nullable(); 21 | $table->timestamp('expires_at')->nullable(); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | */ 29 | public function down(): void 30 | { 31 | Schema::dropIfExists('personal_access_tokens'); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /database/migrations/2024_10_12_024544_create_permission_tables.php: -------------------------------------------------------------------------------- 1 | engine('InnoDB'); 29 | $table->bigIncrements('id'); // permission id 30 | $table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format) 31 | $table->string('guard_name'); // For MyISAM use string('guard_name', 25); 32 | $table->timestamps(); 33 | 34 | $table->unique(['name', 'guard_name']); 35 | }); 36 | 37 | Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) { 38 | //$table->engine('InnoDB'); 39 | $table->bigIncrements('id'); // role id 40 | if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing 41 | $table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable(); 42 | $table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index'); 43 | } 44 | $table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format) 45 | $table->string('guard_name'); // For MyISAM use string('guard_name', 25); 46 | $table->timestamps(); 47 | if ($teams || config('permission.testing')) { 48 | $table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']); 49 | } else { 50 | $table->unique(['name', 'guard_name']); 51 | } 52 | }); 53 | 54 | Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) { 55 | $table->unsignedBigInteger($pivotPermission); 56 | 57 | $table->string('model_type'); 58 | $table->unsignedBigInteger($columnNames['model_morph_key']); 59 | $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index'); 60 | 61 | $table->foreign($pivotPermission) 62 | ->references('id') // permission id 63 | ->on($tableNames['permissions']) 64 | ->onDelete('cascade'); 65 | if ($teams) { 66 | $table->unsignedBigInteger($columnNames['team_foreign_key']); 67 | $table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index'); 68 | 69 | $table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'], 70 | 'model_has_permissions_permission_model_type_primary'); 71 | } else { 72 | $table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'], 73 | 'model_has_permissions_permission_model_type_primary'); 74 | } 75 | 76 | }); 77 | 78 | Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) { 79 | $table->unsignedBigInteger($pivotRole); 80 | 81 | $table->string('model_type'); 82 | $table->unsignedBigInteger($columnNames['model_morph_key']); 83 | $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index'); 84 | 85 | $table->foreign($pivotRole) 86 | ->references('id') // role id 87 | ->on($tableNames['roles']) 88 | ->onDelete('cascade'); 89 | if ($teams) { 90 | $table->unsignedBigInteger($columnNames['team_foreign_key']); 91 | $table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index'); 92 | 93 | $table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'], 94 | 'model_has_roles_role_model_type_primary'); 95 | } else { 96 | $table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'], 97 | 'model_has_roles_role_model_type_primary'); 98 | } 99 | }); 100 | 101 | Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) { 102 | $table->unsignedBigInteger($pivotPermission); 103 | $table->unsignedBigInteger($pivotRole); 104 | 105 | $table->foreign($pivotPermission) 106 | ->references('id') // permission id 107 | ->on($tableNames['permissions']) 108 | ->onDelete('cascade'); 109 | 110 | $table->foreign($pivotRole) 111 | ->references('id') // role id 112 | ->on($tableNames['roles']) 113 | ->onDelete('cascade'); 114 | 115 | $table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary'); 116 | }); 117 | 118 | app('cache') 119 | ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null) 120 | ->forget(config('permission.cache.key')); 121 | } 122 | 123 | /** 124 | * Reverse the migrations. 125 | */ 126 | public function down(): void 127 | { 128 | $tableNames = config('permission.table_names'); 129 | 130 | if (empty($tableNames)) { 131 | 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.'); 132 | } 133 | 134 | Schema::drop($tableNames['role_has_permissions']); 135 | Schema::drop($tableNames['model_has_roles']); 136 | Schema::drop($tableNames['model_has_permissions']); 137 | Schema::drop($tableNames['roles']); 138 | Schema::drop($tableNames['permissions']); 139 | } 140 | }; 141 | -------------------------------------------------------------------------------- /database/migrations/2024_10_12_024624_create_modules_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('module'); 17 | $table->string('description'); 18 | $table->string('icon'); 19 | $table->string('name'); 20 | $table->unsignedInteger('order'); 21 | $table->unsignedTinyInteger('status')->default(1); 22 | $table->unsignedBigInteger('permission_id'); 23 | $table->timestamps(); 24 | 25 | $table->softDeletes(); 26 | $table->foreign('permission_id')->references('id')->on('permissions'); 27 | 28 | }); 29 | } 30 | 31 | /** 32 | * Reverse the migrations. 33 | */ 34 | public function down(): void 35 | { 36 | Schema::dropIfExists('modules'); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /database/migrations/2024_10_12_024636_create_tables_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('table'); 17 | $table->string('descripcion'); 18 | $table->string('endpoint'); 19 | $table->string('icon'); 20 | 21 | $table->timestamps(); 22 | $table->softDeletes(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | */ 29 | public function down(): void 30 | { 31 | Schema::dropIfExists('tables'); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /database/migrations/2024_10_12_024650_create_headers_tables_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | 17 | $table->unsignedBigInteger('table_id'); 18 | $table->string('text'); 19 | $table->string('value'); 20 | $table->unsignedTinyInteger('sortable')->default(0); 21 | $table->string('width')->nullable(); 22 | $table->unsignedTinyInteger('fixed')->default(0); 23 | $table->unsignedTinyInteger('alignment')->nullable(); 24 | $table->unsignedInteger('order'); 25 | 26 | $table->timestamps(); 27 | $table->softDeletes(); 28 | 29 | $table->foreign('table_id')->references('id')->on('tables'); 30 | }); 31 | } 32 | 33 | /** 34 | * Reverse the migrations. 35 | */ 36 | public function down(): void 37 | { 38 | Schema::dropIfExists('headers_tables'); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /database/migrations/2024_10_12_024703_action_button.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string("action_name"); 17 | $table->string("component"); 18 | $table->string("description")->nullable(); 19 | $table->timestamps(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | */ 26 | public function down(): void 27 | { 28 | Schema::dropIfExists('actions_buttons'); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /database/migrations/2024_10_12_024704_create_actions_tables_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->unsignedBigInteger('table_id'); 17 | $table->unsignedBigInteger('action_id'); 18 | $table->unsignedInteger("order"); 19 | $table->string("message")->nullable(); 20 | $table->timestamps(); 21 | 22 | $table->foreign('table_id')->references('id')->on('tables'); 23 | $table->foreign('action_id')->references('id')->on('actions_buttons'); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | */ 30 | public function down(): void 31 | { 32 | Schema::dropIfExists('actions_tables'); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /database/migrations/2024_10_12_024712_create_parameters_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string("parameter"); 17 | $table->string("description"); 18 | $table->double("valor"); 19 | $table->string("data"); 20 | $table->unsignedTinyInteger("status")->default(1); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::dropIfExists('parameters'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/migrations/2024_10_12_024728_create_type_fields_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string("name"); 17 | $table->string("description")->nullable(); 18 | $table->timestamps(); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | */ 25 | public function down(): void 26 | { 27 | Schema::dropIfExists('type_fields'); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /database/migrations/2024_10_12_024738_create_forms_tables_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->unsignedBigInteger('table_id'); 17 | $table->string("field_name"); 18 | $table->string("label"); 19 | $table->unsignedInteger("size")->comment("Indicamos el tamaño del campo en el formulario teniendo en cuenta 1-12 según la grid"); 20 | $table->unsignedTinyInteger("required")->default(0)->comment("Campo obligatorio"); 21 | $table->unsignedInteger("order")->comment("Orden de los campos"); 22 | $table->unsignedTinyInteger("visible")->default(1); 23 | $table->unsignedTinyInteger("editable")->default(1); 24 | $table->unsignedTinyInteger("filter_field")->nullable()->comment("Indica el valor con el que va a filtrar otro componente"); 25 | $table->unsignedTinyInteger("modify_to")->nullable()->comment("Indica el field_name que va a modificar"); 26 | $table->string("info")->nullable()->comment("Corresponde a la información que se le quiera añadir al campo, con un tooltip"); 27 | 28 | $table->timestamps(); 29 | $table->foreign('table_id')->references('id')->on('tables'); 30 | }); 31 | } 32 | 33 | /** 34 | * Reverse the migrations. 35 | */ 36 | public function down(): void 37 | { 38 | Schema::dropIfExists('forms_tables'); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /database/migrations/2024_10_12_035747_add_alias_to_permissions_table.php: -------------------------------------------------------------------------------- 1 | string('alias')->after('guard_name'); 16 | }); 17 | } 18 | 19 | /** 20 | * Reverse the migrations. 21 | */ 22 | public function down(): void 23 | { 24 | Schema::table('permissions', function (Blueprint $table) { 25 | $table->dropColumn('alias'); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | call([ 17 | // Confiuraciones iniciales de tablas, permisos y perfiles 18 | TableSeeder::class, 19 | HeadersTableSeeder::class, 20 | PermissionSeeder::class, 21 | RoleSeeder::class, 22 | 23 | UserSeeder::class, 24 | ModuleSeeder::class, 25 | 26 | ]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /database/seeders/HeadersTableSeeder.php: -------------------------------------------------------------------------------- 1 | 'Home', 19 | // 'description' => 'Modulo principal', 20 | // 'icon' => 'mdi-home', 21 | // 'name' => 'home', 22 | // 'order' => 1, 23 | // 'status' => 1, 24 | // 'permission_id' => 1 25 | // ], 26 | ]; 27 | 28 | // Utiliza un bucle foreach para insertar cada conjunto de datos 29 | foreach ($modules as $data) { 30 | Module::create($data); 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/seeders/PermissionSeeder.php: -------------------------------------------------------------------------------- 1 | 'Home', 19 | 'alias' => 'Vista principal' 20 | ], 21 | ]; 22 | 23 | // Utiliza un bucle foreach para insertar cada conjunto de datos 24 | foreach ($permissions as $data) { 25 | Permission::create($data); 26 | } 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /database/seeders/RoleSeeder.php: -------------------------------------------------------------------------------- 1 | 'SuperAdmin']); 18 | 19 | // consultamos todos los permisos 20 | $permissions = Permission::all(); 21 | 22 | // Asigna todos los permisos al rol 23 | $admin->syncPermissions($permissions); 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /database/seeders/TableSeeder.php: -------------------------------------------------------------------------------- 1 | 'Admin', 19 | 'fullname' => "Test user", 20 | 'email' => 'email@test.com', 21 | 'password' => Hash::make( md5('test123')) 22 | ]); 23 | 24 | 25 | $user->assignRole('SuperAdmin'); // Reemplaza 'admin' con el nombre del rol 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | app: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile 8 | container_name: laravel_app 9 | restart: unless-stopped 10 | ports: 11 | - "9090:80" 12 | volumes: 13 | - .:/var/www/html 14 | depends_on: 15 | - laravel_db 16 | networks: 17 | - laravel-network 18 | environment: 19 | DB_CONNECTION: mysql 20 | DB_HOST: laravel_db 21 | DB_PORT: 3306 22 | DB_DATABASE: naafar 23 | DB_USERNAME: laravel_user 24 | DB_PASSWORD: laravel_password 25 | 26 | laravel_db: 27 | image: mysql:8.0 28 | container_name: laravel_db 29 | restart: unless-stopped 30 | ports: 31 | - "3309:3306" 32 | environment: 33 | MYSQL_DATABASE: naafar 34 | MYSQL_USER: laravel_user 35 | MYSQL_PASSWORD: laravel_password 36 | MYSQL_ROOT_PASSWORD: root_password 37 | MYSQL_ROOT_HOST: '%' 38 | volumes: 39 | - dbdata:/var/lib/mysql 40 | networks: 41 | - laravel-network 42 | command: --default-authentication-plugin=mysql_native_password --bind-address=0.0.0.0 43 | 44 | networks: 45 | laravel-network: 46 | driver: bridge 47 | 48 | volumes: 49 | dbdata: 50 | driver: local 51 | -------------------------------------------------------------------------------- /docker/apache/000-default.conf: -------------------------------------------------------------------------------- 1 | 2 | ServerAdmin webmaster@localhost 3 | DocumentRoot /var/www/html/public 4 | 5 | 6 | Options Indexes FollowSymLinks 7 | AllowOverride All 8 | Require all granted 9 | 10 | 11 | ErrorLog ${APACHE_LOG_DIR}/error.log 12 | CustomLog ${APACHE_LOG_DIR}/access.log combined 13 | -------------------------------------------------------------------------------- /docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Obtener la IP del contenedor de MySQL 4 | MYSQL_IP=$(getent hosts laravel_db | awk '{ print $1 }') 5 | echo "IP de MySQL: $MYSQL_IP" 6 | 7 | # Crear archivo .env si no existe 8 | if [ ! -f .env ]; then 9 | cp .env.example .env 10 | fi 11 | 12 | # Actualizar información de conexión a la base de datos en .env 13 | sed -i "s/DB_HOST=.*/DB_HOST=$MYSQL_IP/" .env 14 | sed -i 's/DB_PORT=.*/DB_PORT=3306/' .env 15 | sed -i 's/DB_DATABASE=.*/DB_DATABASE=laravel/' .env 16 | sed -i 's/DB_USERNAME=.*/DB_USERNAME=laravel_user/' .env 17 | sed -i 's/DB_PASSWORD=.*/DB_PASSWORD=laravel_password/' .env 18 | 19 | # Esperar a que MySQL esté disponible 20 | echo "Esperando a que MySQL esté disponible..." 21 | until nc -z -v -w30 $MYSQL_IP 3306 22 | do 23 | echo "Esperando a que MySQL se inicie..." 24 | sleep 5 25 | done 26 | echo "¡MySQL está disponible! Continuando..." 27 | 28 | # Instalar dependencias 29 | composer install --no-interaction --optimize-autoloader --no-dev 30 | 31 | # Generar clave de aplicación si no está configurada 32 | php artisan key:generate --no-interaction --force 33 | php artisan jwt:secret --no-interaction --force 34 | 35 | # Ejecutar migraciones y seeders 36 | php artisan migrate:fresh --seed --no-interaction --force 37 | 38 | # Create storage link 39 | php artisan storage:link 40 | 41 | # Corregir permisos 42 | chown -R www-data:www-data /var/www/html 43 | chmod -R 755 /var/www/html/storage 44 | 45 | # Iniciar Supervisor para gestionar los workers de cola 46 | /usr/bin/supervisord -c /etc/supervisor/supervisord.conf 47 | 48 | # Iniciar Apache 49 | apache2-foreground 50 | -------------------------------------------------------------------------------- /docker/supervisor/laravel-worker.conf: -------------------------------------------------------------------------------- 1 | [program:laravel-worker] 2 | process_name=%(program_name)s_%(process_num)02d 3 | command=php /var/www/html/artisan queue:work --sleep=3 --tries=3 4 | autostart=true 5 | autorestart=true 6 | stopasgroup=true 7 | killasgroup=true 8 | user=www-data 9 | numprocs=1 10 | redirect_stderr=true 11 | stdout_logfile=/var/www/html/storage/logs/worker.log 12 | stopwaitsecs=3600 -------------------------------------------------------------------------------- /dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-apache 2 | 3 | # Install system dependencies 4 | RUN apt-get update && apt-get install -y \ 5 | git \ 6 | curl \ 7 | libpng-dev \ 8 | libonig-dev \ 9 | libxml2-dev \ 10 | zip \ 11 | unzip \ 12 | libzip-dev \ 13 | default-mysql-client \ 14 | netcat-openbsd 15 | 16 | # Clear cache 17 | RUN apt-get clean && rm -rf /var/lib/apt/lists/* 18 | 19 | # Install PHP extensions 20 | RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip 21 | 22 | # Get latest Composer 23 | COPY --from=composer:latest /usr/bin/composer /usr/bin/composer 24 | 25 | # Set working directory 26 | WORKDIR /var/www/html 27 | 28 | # Copy existing application directory contents 29 | COPY . /var/www/html 30 | 31 | # Copy Apache configuration 32 | COPY docker/apache/000-default.conf /etc/apache2/sites-available/000-default.conf 33 | 34 | # Configure Supervisor 35 | RUN mkdir -p /var/log/supervisor 36 | COPY docker/supervisor/laravel-worker.conf /etc/supervisor/conf.d/laravel-worker.conf 37 | 38 | # Enable Apache modules 39 | RUN a2enmod rewrite 40 | 41 | # Set permissions 42 | RUN chown -R www-data:www-data /var/www/html \ 43 | && chmod -R 755 /var/www/html/storage 44 | 45 | # Expose port 80 46 | EXPOSE 80 47 | 48 | # Start script 49 | COPY docker/start.sh /usr/local/bin/start.sh 50 | RUN chmod +x /usr/local/bin/start.sh 51 | 52 | # Start Apache and PHP 53 | CMD ["/usr/local/bin/start.sh"] 54 | -------------------------------------------------------------------------------- /documentation/postman/api.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "6b541e9c-6d83-45a6-877a-4efef828bdd0", 4 | "name": "laravel-template-api", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 6 | "_exporter_id": "28220348" 7 | }, 8 | "item": [ 9 | { 10 | "name": "modules", 11 | "item": [ 12 | { 13 | "name": "modules", 14 | "request": { 15 | "auth": { 16 | "type": "bearer", 17 | "bearer": [ 18 | { 19 | "key": "token", 20 | "value": "{{token}}", 21 | "type": "string" 22 | } 23 | ] 24 | }, 25 | "method": "GET", 26 | "header": [], 27 | "url": { 28 | "raw": "{{url}}/v1/modules", 29 | "host": [ 30 | "{{url}}" 31 | ], 32 | "path": [ 33 | "v1", 34 | "modules" 35 | ] 36 | } 37 | }, 38 | "response": [] 39 | }, 40 | { 41 | "name": "modules", 42 | "request": { 43 | "auth": { 44 | "type": "bearer", 45 | "bearer": [ 46 | { 47 | "key": "token", 48 | "value": "{{token}}", 49 | "type": "string" 50 | } 51 | ] 52 | }, 53 | "method": "POST", 54 | "header": [], 55 | "body": { 56 | "mode": "raw", 57 | "raw": "{\n \"module\": \"test\",\n \"description\": \"test description\",\n \"icon\": \"test icon\",\n \"name\": \"test name\",\n \"order\": 1,\n \"permission_id\": 1\n}", 58 | "options": { 59 | "raw": { 60 | "language": "json" 61 | } 62 | } 63 | }, 64 | "url": { 65 | "raw": "{{url}}/v1/modules", 66 | "host": [ 67 | "{{url}}" 68 | ], 69 | "path": [ 70 | "v1", 71 | "modules" 72 | ] 73 | } 74 | }, 75 | "response": [] 76 | }, 77 | { 78 | "name": "module", 79 | "request": { 80 | "auth": { 81 | "type": "bearer", 82 | "bearer": [ 83 | { 84 | "key": "token", 85 | "value": "{{token}}", 86 | "type": "string" 87 | } 88 | ] 89 | }, 90 | "method": "PUT", 91 | "header": [], 92 | "body": { 93 | "mode": "raw", 94 | "raw": "{\n \"module\": \"test updated\",\n \"description\": \"test description updated\",\n \"icon\": \"test icon updated\",\n \"name\": \"test name updated\",\n \"order\": 1,\n \"permission_id\": 1\n}", 95 | "options": { 96 | "raw": { 97 | "language": "json" 98 | } 99 | } 100 | }, 101 | "url": { 102 | "raw": "{{url}}/v1/modules/1", 103 | "host": [ 104 | "{{url}}" 105 | ], 106 | "path": [ 107 | "v1", 108 | "modules", 109 | "1" 110 | ] 111 | } 112 | }, 113 | "response": [] 114 | }, 115 | { 116 | "name": "module", 117 | "request": { 118 | "auth": { 119 | "type": "bearer", 120 | "bearer": [ 121 | { 122 | "key": "token", 123 | "value": "{{token}}", 124 | "type": "string" 125 | } 126 | ] 127 | }, 128 | "method": "GET", 129 | "header": [], 130 | "url": { 131 | "raw": "{{url}}/v1/modules/1", 132 | "host": [ 133 | "{{url}}" 134 | ], 135 | "path": [ 136 | "v1", 137 | "modules", 138 | "1" 139 | ] 140 | } 141 | }, 142 | "response": [] 143 | }, 144 | { 145 | "name": "modules", 146 | "request": { 147 | "auth": { 148 | "type": "bearer", 149 | "bearer": [ 150 | { 151 | "key": "token", 152 | "value": "{{token}}", 153 | "type": "string" 154 | } 155 | ] 156 | }, 157 | "method": "DELETE", 158 | "header": [], 159 | "url": { 160 | "raw": "{{url}}/v1/modules/1", 161 | "host": [ 162 | "{{url}}" 163 | ], 164 | "path": [ 165 | "v1", 166 | "modules", 167 | "1" 168 | ] 169 | } 170 | }, 171 | "response": [] 172 | } 173 | ] 174 | }, 175 | { 176 | "name": "permissions", 177 | "item": [ 178 | { 179 | "name": "v1", 180 | "item": [ 181 | { 182 | "name": "permissions - grid", 183 | "request": { 184 | "auth": { 185 | "type": "bearer", 186 | "bearer": [ 187 | { 188 | "key": "token", 189 | "value": "{{token}}", 190 | "type": "string" 191 | } 192 | ] 193 | }, 194 | "method": "GET", 195 | "header": [], 196 | "url": { 197 | "raw": "{{url}}/grid/permissions?nIdTable=3", 198 | "host": [ 199 | "{{url}}" 200 | ], 201 | "path": [ 202 | "grid", 203 | "permissions" 204 | ], 205 | "query": [ 206 | { 207 | "key": "nIdTable", 208 | "value": "3" 209 | } 210 | ] 211 | } 212 | }, 213 | "response": [] 214 | }, 215 | { 216 | "name": "permissions", 217 | "request": { 218 | "auth": { 219 | "type": "bearer", 220 | "bearer": [ 221 | { 222 | "key": "token", 223 | "value": "{{token}}", 224 | "type": "string" 225 | } 226 | ] 227 | }, 228 | "method": "GET", 229 | "header": [], 230 | "url": { 231 | "raw": "{{url}}/v1/permissions", 232 | "host": [ 233 | "{{url}}" 234 | ], 235 | "path": [ 236 | "v1", 237 | "permissions" 238 | ] 239 | } 240 | }, 241 | "response": [] 242 | }, 243 | { 244 | "name": "permission", 245 | "request": { 246 | "auth": { 247 | "type": "bearer", 248 | "bearer": [ 249 | { 250 | "key": "token", 251 | "value": "{{token}}", 252 | "type": "string" 253 | } 254 | ] 255 | }, 256 | "method": "GET", 257 | "header": [], 258 | "url": { 259 | "raw": "{{url}}/v1/permissions/1", 260 | "host": [ 261 | "{{url}}" 262 | ], 263 | "path": [ 264 | "v1", 265 | "permissions", 266 | "1" 267 | ] 268 | } 269 | }, 270 | "response": [] 271 | }, 272 | { 273 | "name": "permissions", 274 | "request": { 275 | "auth": { 276 | "type": "bearer", 277 | "bearer": [ 278 | { 279 | "key": "token", 280 | "value": "{{token}}", 281 | "type": "string" 282 | } 283 | ] 284 | }, 285 | "method": "POST", 286 | "header": [], 287 | "body": { 288 | "mode": "raw", 289 | "raw": "{\n \"name\": \"Test\",\n \"alias\": \"Friendly name or label\"\n}", 290 | "options": { 291 | "raw": { 292 | "language": "json" 293 | } 294 | } 295 | }, 296 | "url": { 297 | "raw": "{{url}}/v1/permissions", 298 | "host": [ 299 | "{{url}}" 300 | ], 301 | "path": [ 302 | "v1", 303 | "permissions" 304 | ] 305 | } 306 | }, 307 | "response": [] 308 | }, 309 | { 310 | "name": "permissions", 311 | "request": { 312 | "auth": { 313 | "type": "bearer", 314 | "bearer": [ 315 | { 316 | "key": "token", 317 | "value": "{{token}}", 318 | "type": "string" 319 | } 320 | ] 321 | }, 322 | "method": "PUT", 323 | "header": [], 324 | "body": { 325 | "mode": "raw", 326 | "raw": "{\n \"name\": \"Test updated\",\n \"alias\": \"Friendly name or label\"\n}", 327 | "options": { 328 | "raw": { 329 | "language": "json" 330 | } 331 | } 332 | }, 333 | "url": { 334 | "raw": "{{url}}/v1/permissions/2", 335 | "host": [ 336 | "{{url}}" 337 | ], 338 | "path": [ 339 | "v1", 340 | "permissions", 341 | "2" 342 | ] 343 | } 344 | }, 345 | "response": [] 346 | }, 347 | { 348 | "name": "permissions", 349 | "request": { 350 | "auth": { 351 | "type": "bearer", 352 | "bearer": [ 353 | { 354 | "key": "token", 355 | "value": "{{token}}", 356 | "type": "string" 357 | } 358 | ] 359 | }, 360 | "method": "DELETE", 361 | "header": [], 362 | "url": { 363 | "raw": "{{url}}/v1/permissions/2", 364 | "host": [ 365 | "{{url}}" 366 | ], 367 | "path": [ 368 | "v1", 369 | "permissions", 370 | "2" 371 | ] 372 | } 373 | }, 374 | "response": [] 375 | } 376 | ] 377 | } 378 | ] 379 | }, 380 | { 381 | "name": "roles", 382 | "item": [ 383 | { 384 | "name": "v1", 385 | "item": [ 386 | { 387 | "name": "roles - grid", 388 | "request": { 389 | "auth": { 390 | "type": "bearer", 391 | "bearer": [ 392 | { 393 | "key": "token", 394 | "value": "{{token}}", 395 | "type": "string" 396 | } 397 | ] 398 | }, 399 | "method": "GET", 400 | "header": [], 401 | "url": { 402 | "raw": "{{url}}/grid/roles?nIdTable=2", 403 | "host": [ 404 | "{{url}}" 405 | ], 406 | "path": [ 407 | "grid", 408 | "roles" 409 | ], 410 | "query": [ 411 | { 412 | "key": "nIdTable", 413 | "value": "2" 414 | } 415 | ] 416 | } 417 | }, 418 | "response": [] 419 | }, 420 | { 421 | "name": "roles", 422 | "request": { 423 | "auth": { 424 | "type": "bearer", 425 | "bearer": [ 426 | { 427 | "key": "token", 428 | "value": "{{token}}", 429 | "type": "string" 430 | } 431 | ] 432 | }, 433 | "method": "GET", 434 | "header": [], 435 | "url": { 436 | "raw": "{{url}}/v1/roles", 437 | "host": [ 438 | "{{url}}" 439 | ], 440 | "path": [ 441 | "v1", 442 | "roles" 443 | ] 444 | } 445 | }, 446 | "response": [] 447 | }, 448 | { 449 | "name": "role", 450 | "request": { 451 | "auth": { 452 | "type": "bearer", 453 | "bearer": [ 454 | { 455 | "key": "token", 456 | "value": "{{token}}", 457 | "type": "string" 458 | } 459 | ] 460 | }, 461 | "method": "GET", 462 | "header": [], 463 | "url": { 464 | "raw": "{{url}}/v1/roles/1", 465 | "host": [ 466 | "{{url}}" 467 | ], 468 | "path": [ 469 | "v1", 470 | "roles", 471 | "1" 472 | ] 473 | } 474 | }, 475 | "response": [] 476 | }, 477 | { 478 | "name": "role", 479 | "request": { 480 | "auth": { 481 | "type": "bearer", 482 | "bearer": [ 483 | { 484 | "key": "token", 485 | "value": "{{token}}", 486 | "type": "string" 487 | } 488 | ] 489 | }, 490 | "method": "POST", 491 | "header": [], 492 | "body": { 493 | "mode": "raw", 494 | "raw": "{\n \"name\": \"Test\"\n}", 495 | "options": { 496 | "raw": { 497 | "language": "json" 498 | } 499 | } 500 | }, 501 | "url": { 502 | "raw": "{{url}}/v1/roles", 503 | "host": [ 504 | "{{url}}" 505 | ], 506 | "path": [ 507 | "v1", 508 | "roles" 509 | ] 510 | } 511 | }, 512 | "response": [] 513 | }, 514 | { 515 | "name": "role", 516 | "request": { 517 | "auth": { 518 | "type": "bearer", 519 | "bearer": [ 520 | { 521 | "key": "token", 522 | "value": "{{token}}", 523 | "type": "string" 524 | } 525 | ] 526 | }, 527 | "method": "PUT", 528 | "header": [], 529 | "body": { 530 | "mode": "raw", 531 | "raw": "{\n \"name\": \"Test updated\"\n}", 532 | "options": { 533 | "raw": { 534 | "language": "json" 535 | } 536 | } 537 | }, 538 | "url": { 539 | "raw": "{{url}}/v1/roles/2", 540 | "host": [ 541 | "{{url}}" 542 | ], 543 | "path": [ 544 | "v1", 545 | "roles", 546 | "2" 547 | ] 548 | } 549 | }, 550 | "response": [] 551 | }, 552 | { 553 | "name": "role", 554 | "request": { 555 | "auth": { 556 | "type": "bearer", 557 | "bearer": [ 558 | { 559 | "key": "token", 560 | "value": "{{token}}", 561 | "type": "string" 562 | } 563 | ] 564 | }, 565 | "method": "DELETE", 566 | "header": [], 567 | "body": { 568 | "mode": "raw", 569 | "raw": "{\n \"name\": \"Test updated\"\n}", 570 | "options": { 571 | "raw": { 572 | "language": "json" 573 | } 574 | } 575 | }, 576 | "url": { 577 | "raw": "{{url}}/v1/roles/2", 578 | "host": [ 579 | "{{url}}" 580 | ], 581 | "path": [ 582 | "v1", 583 | "roles", 584 | "2" 585 | ] 586 | } 587 | }, 588 | "response": [] 589 | } 590 | ] 591 | } 592 | ] 593 | }, 594 | { 595 | "name": "users", 596 | "item": [ 597 | { 598 | "name": "v1", 599 | "item": [ 600 | { 601 | "name": "grid users", 602 | "request": { 603 | "auth": { 604 | "type": "bearer", 605 | "bearer": [ 606 | { 607 | "key": "token", 608 | "value": "{{token}}", 609 | "type": "string" 610 | } 611 | ] 612 | }, 613 | "method": "GET", 614 | "header": [], 615 | "url": { 616 | "raw": "{{url}}/grid/users?nIdTable=1", 617 | "host": [ 618 | "{{url}}" 619 | ], 620 | "path": [ 621 | "grid", 622 | "users" 623 | ], 624 | "query": [ 625 | { 626 | "key": "nIdTable", 627 | "value": "1" 628 | } 629 | ] 630 | } 631 | }, 632 | "response": [] 633 | }, 634 | { 635 | "name": "users", 636 | "request": { 637 | "auth": { 638 | "type": "bearer", 639 | "bearer": [ 640 | { 641 | "key": "token", 642 | "value": "{{token}}", 643 | "type": "string" 644 | } 645 | ] 646 | }, 647 | "method": "GET", 648 | "header": [], 649 | "url": { 650 | "raw": "{{url}}/v1/users", 651 | "host": [ 652 | "{{url}}" 653 | ], 654 | "path": [ 655 | "v1", 656 | "users" 657 | ] 658 | } 659 | }, 660 | "response": [] 661 | }, 662 | { 663 | "name": "user", 664 | "request": { 665 | "auth": { 666 | "type": "bearer", 667 | "bearer": [ 668 | { 669 | "key": "token", 670 | "value": "{{token}}", 671 | "type": "string" 672 | } 673 | ] 674 | }, 675 | "method": "GET", 676 | "header": [], 677 | "url": { 678 | "raw": "{{url}}/v1/users/2", 679 | "host": [ 680 | "{{url}}" 681 | ], 682 | "path": [ 683 | "v1", 684 | "users", 685 | "2" 686 | ] 687 | } 688 | }, 689 | "response": [] 690 | }, 691 | { 692 | "name": "user", 693 | "request": { 694 | "auth": { 695 | "type": "bearer", 696 | "bearer": [ 697 | { 698 | "key": "token", 699 | "value": "{{token}}", 700 | "type": "string" 701 | } 702 | ] 703 | }, 704 | "method": "DELETE", 705 | "header": [], 706 | "url": { 707 | "raw": "{{url}}/v1/users/2", 708 | "host": [ 709 | "{{url}}" 710 | ], 711 | "path": [ 712 | "v1", 713 | "users", 714 | "2" 715 | ] 716 | } 717 | }, 718 | "response": [] 719 | }, 720 | { 721 | "name": "user restore", 722 | "request": { 723 | "auth": { 724 | "type": "bearer", 725 | "bearer": [ 726 | { 727 | "key": "token", 728 | "value": "{{token}}", 729 | "type": "string" 730 | } 731 | ] 732 | }, 733 | "method": "PUT", 734 | "header": [], 735 | "url": { 736 | "raw": "{{url}}/v1/users/restore/2", 737 | "host": [ 738 | "{{url}}" 739 | ], 740 | "path": [ 741 | "v1", 742 | "users", 743 | "restore", 744 | "2" 745 | ] 746 | } 747 | }, 748 | "response": [] 749 | }, 750 | { 751 | "name": "user", 752 | "request": { 753 | "auth": { 754 | "type": "bearer", 755 | "bearer": [ 756 | { 757 | "key": "token", 758 | "value": "{{token}}", 759 | "type": "string" 760 | } 761 | ] 762 | }, 763 | "method": "PUT", 764 | "header": [], 765 | "body": { 766 | "mode": "raw", 767 | "raw": "{\n \"name\": \"Example\",\n \"fullname\": \"Example name\",\n \"email\": \"example@example.test\",\n \"password\": \"cc03e747a6afbbcbf8be7668acfebee5\",\n \"role_id\": 1\n}", 768 | "options": { 769 | "raw": { 770 | "language": "json" 771 | } 772 | } 773 | }, 774 | "url": { 775 | "raw": "{{url}}/v1/users/2", 776 | "host": [ 777 | "{{url}}" 778 | ], 779 | "path": [ 780 | "v1", 781 | "users", 782 | "2" 783 | ] 784 | } 785 | }, 786 | "response": [] 787 | }, 788 | { 789 | "name": "users", 790 | "request": { 791 | "auth": { 792 | "type": "bearer", 793 | "bearer": [ 794 | { 795 | "key": "token", 796 | "value": "{{token}}", 797 | "type": "string" 798 | } 799 | ] 800 | }, 801 | "method": "POST", 802 | "header": [], 803 | "body": { 804 | "mode": "raw", 805 | "raw": "{\n \"name\": \"Example\",\n \"fullname\": \"Example name\",\n \"email\": \"example@example.test\",\n \"password\": \"cc03e747a6afbbcbf8be7668acfebee5\",\n \"role_id\": 1\n}", 806 | "options": { 807 | "raw": { 808 | "language": "json" 809 | } 810 | } 811 | }, 812 | "url": { 813 | "raw": "{{url}}/v1/users", 814 | "host": [ 815 | "{{url}}" 816 | ], 817 | "path": [ 818 | "v1", 819 | "users" 820 | ] 821 | } 822 | }, 823 | "response": [] 824 | } 825 | ] 826 | } 827 | ] 828 | }, 829 | { 830 | "name": "test", 831 | "request": { 832 | "method": "GET", 833 | "header": [], 834 | "url": { 835 | "raw": "{{url}}/test", 836 | "host": [ 837 | "{{url}}" 838 | ], 839 | "path": [ 840 | "test" 841 | ] 842 | } 843 | }, 844 | "response": [] 845 | }, 846 | { 847 | "name": "login", 848 | "event": [ 849 | { 850 | "listen": "test", 851 | "script": { 852 | "exec": [ 853 | "var data = pm.response.json();", 854 | "pm.environment.set (\"token\", data['access_token']);" 855 | ], 856 | "type": "text/javascript", 857 | "packages": {} 858 | } 859 | } 860 | ], 861 | "request": { 862 | "method": "POST", 863 | "header": [], 864 | "body": { 865 | "mode": "raw", 866 | "raw": "{\n \"email\": \"email@test.com\",\n \"password\": \"cc03e747a6afbbcbf8be7668acfebee5\"\n}", 867 | "options": { 868 | "raw": { 869 | "language": "json" 870 | } 871 | } 872 | }, 873 | "url": { 874 | "raw": "{{url}}/login", 875 | "host": [ 876 | "{{url}}" 877 | ], 878 | "path": [ 879 | "login" 880 | ] 881 | } 882 | }, 883 | "response": [] 884 | }, 885 | { 886 | "name": "logout", 887 | "request": { 888 | "auth": { 889 | "type": "bearer", 890 | "bearer": [ 891 | { 892 | "key": "token", 893 | "value": "{{token}}", 894 | "type": "string" 895 | } 896 | ] 897 | }, 898 | "method": "POST", 899 | "header": [], 900 | "body": { 901 | "mode": "raw", 902 | "raw": "{\n \n}", 903 | "options": { 904 | "raw": { 905 | "language": "json" 906 | } 907 | } 908 | }, 909 | "url": { 910 | "raw": "{{url}}/logout", 911 | "host": [ 912 | "{{url}}" 913 | ], 914 | "path": [ 915 | "logout" 916 | ] 917 | } 918 | }, 919 | "response": [] 920 | } 921 | ] 922 | } -------------------------------------------------------------------------------- /documentation/postman/environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "77a2a4b5-77a5-4888-ba8a-4d2d72358c02", 3 | "name": "laravel-template-api", 4 | "values": [ 5 | { 6 | "key": "url", 7 | "value": "http://127.0.0.1:9090/api", 8 | "type": "default", 9 | "enabled": true 10 | }, 11 | { 12 | "key": "token", 13 | "value": "", 14 | "type": "default", 15 | "enabled": true 16 | } 17 | ], 18 | "_postman_variable_scope": "environment", 19 | "_postman_exported_at": "2024-10-13T04:50:04.141Z", 20 | "_postman_exported_using": "Postman/11.16.1" 21 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build" 7 | }, 8 | "devDependencies": { 9 | "axios": "^1.7.4", 10 | "laravel-vite-plugin": "^1.0", 11 | "vite": "^5.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | tests/Unit 10 | 11 | 12 | tests/Feature 13 | 14 | 15 | 16 | 17 | app 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10075016-22/template-laravel-api/17948053019b13c3b03fd43ab155ed92a0cd0002/public/favicon.ico -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | handleRequest(Request::capture()); 18 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /resources/css/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10075016-22/template-laravel-api/17948053019b13c3b03fd43ab155ed92a0cd0002/resources/css/app.css -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- 1 | import './bootstrap'; 2 | -------------------------------------------------------------------------------- /resources/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | window.axios = axios; 3 | 4 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 5 | -------------------------------------------------------------------------------- /resources/views/welcome.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Laravel 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 |
20 | Laravel background 21 |
22 |
23 |
24 |
25 | 26 |
27 | @if (Route::has('login')) 28 | 54 | @endif 55 |
56 | 57 |
58 | 163 |
164 | 165 |
166 | Laravel v{{ Illuminate\Foundation\Application::VERSION }} (PHP v{{ PHP_VERSION }}) 167 |
168 |
169 |
170 |
171 | 172 | 173 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | group(function () { 16 | Route::post('/logout', [UserController::class, 'logout']); 17 | // services 18 | Route::group(['prefix' => 'v1'], function() { 19 | 20 | // modules 21 | Route::resource("/modules", ModuleController::class); 22 | 23 | // users 24 | Route::put("/users/restore/{id}", [UserController::class, "restoreUser"]); 25 | Route::resource("/users", UserController::class); 26 | 27 | // permissions 28 | Route::resource("/permissions", PermissionController::class); 29 | 30 | // roles 31 | Route::resource("/roles", RoleController::class); 32 | }); 33 | 34 | // associated to tables 35 | Route::group(['prefix' => 'grid'], function() { 36 | Route::get("/users", [UserController::class, "gridIndex"]); 37 | Route::get("/roles", [RoleController::class, "gridIndex"]); 38 | Route::get("/permissions", [PermissionController::class, "gridIndex"]); 39 | }); 40 | 41 | }); 42 | 43 | Route::get('/test', function (Request $request) { 44 | return response()->json(['message' => 'Why so seriuos?']); 45 | }); -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 8 | })->purpose('Display an inspiring quote')->hourly(); 9 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | get('/'); 16 | 17 | $response->assertStatus(200); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import laravel from 'laravel-vite-plugin'; 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | laravel({ 7 | input: ['resources/css/app.css', 'resources/js/app.js'], 8 | refresh: true, 9 | }), 10 | ], 11 | }); 12 | --------------------------------------------------------------------------------