├── public ├── favicon.ico ├── robots.txt ├── .DS_Store ├── images │ ├── .DS_Store │ ├── faker │ │ ├── 1.jpg │ │ ├── 2.jpg │ │ ├── 3.jpg │ │ ├── 4.jpg │ │ ├── 5.jpg │ │ └── 6.jpg │ └── default │ │ ├── avatar.png │ │ ├── no_image.png │ │ ├── avatar_1024.png │ │ ├── avatar_512.png │ │ ├── no_image_512.png │ │ ├── no_image_1024.png │ │ ├── avatar_original.png │ │ └── no_image_original.png ├── vendor │ └── telescope │ │ ├── favicon.ico │ │ └── mix-manifest.json ├── .htaccess ├── web.config └── index.php ├── database ├── .gitignore ├── migrations │ ├── 2022_07_24_134939_add_title_column_to_roles_table.php │ ├── 2022_04_02_045642_create_refresh_tokens_table.php │ ├── 2019_08_19_000000_create_failed_jobs_table.php │ ├── 2019_12_14_000001_create_personal_access_tokens_table.php │ ├── 2021_10_31_110750_create_categories_table.php │ ├── 2021_10_31_111105_create_resources_table.php │ ├── 2021_10_31_182630_create_products_table.php │ ├── 2021_10_12_000000_create_users_table.php │ └── 2022_04_28_145148_create_logger_table.php ├── factories │ └── RoleFactory.php └── seeders │ ├── AdminSeeder.php │ ├── PermissionsSeeder.php │ ├── TestSeeder.php │ └── UserSeeder.php ├── app ├── Core │ ├── Http │ │ ├── Controllers │ │ │ ├── .gitkeep │ │ │ └── CoreController.php │ │ └── Requests │ │ │ ├── ChangeStatusRequest.php │ │ │ └── GetAllFilteredRecordsRequest.php │ ├── Helpers │ │ └── Generators │ │ │ ├── Stubs │ │ │ ├── route.stub │ │ │ ├── policy.stub │ │ │ ├── repository.stub │ │ │ ├── service.stub │ │ │ └── featureResourceTest.stub │ │ │ └── EditRoute.php │ ├── Macros │ │ ├── WhereInRelation.php │ │ ├── Closure.php │ │ ├── IsSearchable.php │ │ ├── Relations.php │ │ ├── IsTranslatable.php │ │ ├── Pagination.php │ │ ├── Appends.php │ │ ├── IsDates.php │ │ ├── DbQuery.php │ │ ├── PaginationOrCollection.php │ │ ├── IsActive.php │ │ ├── Deleted.php │ │ ├── WhereLike.php │ │ ├── Namespaces │ │ │ └── QueryBuilder.php │ │ ├── GetBy.php │ │ └── EloquentQuery.php │ ├── Contracts │ │ ├── EnumTranslateContract.php │ │ ├── ResourceTestContract.php │ │ ├── CoreServiceContract.php │ │ └── CoreRepositoryContract.php │ ├── Traits │ │ ├── EnumCasts.php │ │ └── Responsable.php │ ├── Providers │ │ └── CoreServiceProvider.php │ ├── Enums │ │ └── CoreEnum.php │ ├── Test │ │ └── Feature │ │ │ └── CoreTest.php │ ├── Policies │ │ └── CorePolicy.php │ └── Services │ │ └── CoreService.php ├── Policies │ ├── ProductPolicy.php │ ├── CategoryPolicy.php │ ├── UserPolicy.php │ ├── RolePolicy.php │ └── PermissionPolicy.php ├── Helpers │ ├── SubText.php │ ├── RefreshTokenGenerator.php │ ├── ExistsCheck.php │ ├── DateCasts.php │ └── TranslatableJson.php ├── Events │ ├── DestroyFiles.php │ ├── LoggerEvent.php │ ├── AttachImages.php │ ├── UpdateFile.php │ └── UpdateImage.php ├── Repositories │ ├── ProductRepository.php │ ├── CategoryRepository.php │ ├── LoggerRepository.php │ ├── RoleRepository.php │ ├── PermissionRepository.php │ ├── UserRepository.php │ └── ResourceRepository.php ├── Services │ ├── UserService.php │ ├── ProductService.php │ ├── CategoryService.php │ ├── LoggerService.php │ ├── BarcodeService.php │ ├── PermissionService.php │ └── RoleService.php ├── Http │ ├── Middleware │ │ ├── EncryptCookies.php │ │ ├── VerifyCsrfToken.php │ │ ├── TrustHosts.php │ │ ├── PreventRequestsDuringMaintenance.php │ │ ├── TrimStrings.php │ │ ├── Authenticate.php │ │ ├── IsActive.php │ │ ├── SetAppLocale.php │ │ ├── TrustProxies.php │ │ └── RedirectIfAuthenticated.php │ ├── Requests │ │ ├── Role │ │ │ ├── PermissionRequest.php │ │ │ ├── CheckPermissionsRequest.php │ │ │ ├── RolesPermissionsRequest.php │ │ │ ├── SyncPermissionsRequest.php │ │ │ ├── PermissionCreateRequest.php │ │ │ ├── PermissionUpdateRequest.php │ │ │ ├── RoleCreateRequest.php │ │ │ └── RoleUpdateRequest.php │ │ ├── Auth │ │ │ ├── LoginRequest.php │ │ │ └── RegistrationRequest.php │ │ ├── ProfileUpdateRequest.php │ │ ├── UserUpdateRequest.php │ │ ├── UserCreateRequest.php │ │ ├── ProductUpdateRequest.php │ │ └── ProductCreateRequest.php │ └── Controllers │ │ ├── ProductController.php │ │ ├── CategoryController.php │ │ ├── LoggerController.php │ │ └── AuthController.php ├── Listeners │ ├── LoggerListener.php │ ├── DestroyFilesListener.php │ ├── AttachImagesListener.php │ ├── UpdateImageListener.php │ └── UpdateFileListener.php ├── Traits │ ├── IsActive.php │ └── Author.php ├── Providers │ ├── BroadcastServiceProvider.php │ ├── AppServiceProvider.php │ ├── AuthServiceProvider.php │ ├── EventServiceProvider.php │ └── RouteServiceProvider.php ├── Models │ ├── Role.php │ ├── Resource.php │ ├── RefreshToken.php │ └── Logger.php ├── Rules │ ├── PhoneRule.php │ ├── CheckJsonExistsLocale.php │ ├── PermissionRule.php │ ├── UniqueRule.php │ ├── UniqueJsonRule.php │ ├── CheckJsonExistsNullableLocale.php │ ├── checkActiveRule.php │ └── ExistsRule.php ├── Observers │ ├── CategoryObserver.php │ ├── UserObserver.php │ └── ProductObserver.php ├── Jobs │ └── LoggerJob.php └── Console │ └── Kernel.php ├── bootstrap ├── cache │ └── .gitignore └── app.php ├── storage ├── logs │ └── .gitignore ├── app │ ├── public │ │ └── .gitignore │ └── .gitignore └── framework │ ├── testing │ └── .gitignore │ ├── views │ └── .gitignore │ ├── cache │ ├── data │ │ └── .gitignore │ └── .gitignore │ ├── sessions │ └── .gitignore │ └── .gitignore ├── .DS_Store ├── .docker ├── web │ ├── container-start │ ├── crontab.txt │ ├── php-custom.ini │ ├── php-fpm.conf │ ├── opcache.ini │ ├── nginx.conf │ ├── app.conf │ └── supervisord.conf ├── web.debug │ ├── container-start │ ├── crontab.txt │ ├── php-custom.ini │ ├── xdebug.ini │ ├── opcache.ini │ ├── nginx.conf │ ├── app.conf │ └── supervisord.conf ├── web.dev │ ├── container-start │ ├── crontab.txt │ ├── php-custom.ini │ ├── xdebug.ini │ ├── opcache.ini │ ├── nginx.conf │ ├── app.conf │ └── supervisord.conf └── web.loc │ ├── container-start │ ├── crontab.txt │ ├── php-custom.ini │ ├── opcache.ini │ ├── nginx.conf │ ├── app.conf │ └── supervisord.conf ├── phpunit.phar ├── lang ├── en │ ├── errors.php │ ├── attributes.php │ ├── pagination.php │ ├── passwords.php │ ├── auth.php │ └── messages.php ├── ru │ ├── errors.php │ ├── attributes.php │ ├── pagination.php │ ├── auth.php │ ├── passwords.php │ └── messages.php ├── uz │ ├── errors.php │ ├── attributes.php │ ├── pagination.php │ ├── auth.php │ ├── passwords.php │ └── messages.php └── en.json ├── routes ├── api │ ├── products.php │ ├── categories.php │ ├── logger.php │ ├── auth.php │ ├── users.php │ └── roles.php ├── web.php ├── channels.php └── console.php ├── .gitattributes ├── tests ├── TestCase.php ├── CreatesApplication.php └── Unit │ └── BarcodeGenerateTest.php ├── .styleci.yml ├── stubs ├── model.stub ├── migration.stub ├── request.stub ├── migration.create.stub ├── migration.update.stub ├── policy.stub └── observer.stub ├── .gitignore ├── .editorconfig ├── package.json ├── webpack.mix.js ├── server.php ├── .github └── workflows │ ├── conteyner-info.txt │ └── tests.yml ├── config ├── cors.php ├── modulegenerator.php ├── filesystems.php ├── services.php ├── view.php └── hashing.php ├── phpunit.xml ├── .env.example ├── .env.testing.fail ├── .env.example.docker ├── docker-compose-mysql.yaml ├── docker-compose.yml ├── artisan └── docker-compose-prod.yaml /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /app/Core/Http/Controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/.DS_Store -------------------------------------------------------------------------------- /.docker/web/container-start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | /usr/bin/supervisord -c /etc/supervisord.conf 3 | -------------------------------------------------------------------------------- /phpunit.phar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/phpunit.phar -------------------------------------------------------------------------------- /.docker/web.debug/container-start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | /usr/bin/supervisord -c /etc/supervisord.conf 3 | -------------------------------------------------------------------------------- /.docker/web.dev/container-start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | /usr/bin/supervisord -c /etc/supervisord.conf 3 | -------------------------------------------------------------------------------- /.docker/web.loc/container-start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | /usr/bin/supervisord -c /etc/supervisord.conf 3 | -------------------------------------------------------------------------------- /.docker/web/crontab.txt: -------------------------------------------------------------------------------- 1 | * * * * * php /var/www/artisan schedule:run >> /var/log/cron.log 2>&1 2 | -------------------------------------------------------------------------------- /.docker/web.debug/crontab.txt: -------------------------------------------------------------------------------- 1 | * * * * * php /var/www/artisan schedule:run >> /var/log/cron.log 2>&1 2 | -------------------------------------------------------------------------------- /.docker/web.dev/crontab.txt: -------------------------------------------------------------------------------- 1 | * * * * * php /var/www/artisan schedule:run >> /var/log/cron.log 2>&1 2 | -------------------------------------------------------------------------------- /.docker/web.loc/crontab.txt: -------------------------------------------------------------------------------- 1 | * * * * * php /var/www/artisan schedule:run >> /var/log/cron.log 2>&1 2 | -------------------------------------------------------------------------------- /public/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/.DS_Store -------------------------------------------------------------------------------- /public/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/images/.DS_Store -------------------------------------------------------------------------------- /lang/en/errors.php: -------------------------------------------------------------------------------- 1 | 'No records found, try another request', 5 | ]; 6 | -------------------------------------------------------------------------------- /lang/ru/errors.php: -------------------------------------------------------------------------------- 1 | 'No records found, try another request', 5 | ]; 6 | -------------------------------------------------------------------------------- /lang/uz/errors.php: -------------------------------------------------------------------------------- 1 | 'No records found, try another request', 5 | ]; 6 | -------------------------------------------------------------------------------- /public/images/faker/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/images/faker/1.jpg -------------------------------------------------------------------------------- /public/images/faker/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/images/faker/2.jpg -------------------------------------------------------------------------------- /public/images/faker/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/images/faker/3.jpg -------------------------------------------------------------------------------- /public/images/faker/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/images/faker/4.jpg -------------------------------------------------------------------------------- /public/images/faker/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/images/faker/5.jpg -------------------------------------------------------------------------------- /public/images/faker/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/images/faker/6.jpg -------------------------------------------------------------------------------- /public/images/default/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/images/default/avatar.png -------------------------------------------------------------------------------- /public/images/default/no_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/images/default/no_image.png -------------------------------------------------------------------------------- /public/vendor/telescope/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/vendor/telescope/favicon.ico -------------------------------------------------------------------------------- /public/images/default/avatar_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/images/default/avatar_1024.png -------------------------------------------------------------------------------- /public/images/default/avatar_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/images/default/avatar_512.png -------------------------------------------------------------------------------- /public/images/default/no_image_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TursunboyevJahongir/laravel_api/HEAD/public/images/default/no_image_512.png -------------------------------------------------------------------------------- /routes/api/products.php: -------------------------------------------------------------------------------- 1 | 'category', 5 | 'product' => 'product', 6 | 'user' => 'user', 7 | 'role' => 'role', 8 | ]; 9 | -------------------------------------------------------------------------------- /lang/ru/attributes.php: -------------------------------------------------------------------------------- 1 | 'category', 5 | 'product' => 'product', 6 | 'user' => 'user', 7 | 'role' => 'role', 8 | ]; 9 | -------------------------------------------------------------------------------- /lang/uz/attributes.php: -------------------------------------------------------------------------------- 1 | 'category', 5 | 'product' => 'product', 6 | 'user' => 'user', 7 | 'role' => 'role', 8 | ]; 9 | -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /app/Core/Helpers/Generators/Stubs/route.stub: -------------------------------------------------------------------------------- 1 | middleware(['permission:read-logger']) 7 | ->controller('LoggerController') 8 | ->group(function () { 9 | Route::get('/', 'index'); 10 | Route::get('/{logger}', 'show'); 11 | }); 12 | -------------------------------------------------------------------------------- /app/Events/DestroyFiles.php: -------------------------------------------------------------------------------- 1 | whereHas($relation, function (Builder $query) 7 | use ($column, $value, $boolean) { 8 | $query->whereIn($column, $value, boolean: $boolean); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | where(\Closure::fromCallable([$class, $status])); 9 | 10 | return $this; 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /app/Http/Requests/Role/PermissionRequest.php: -------------------------------------------------------------------------------- 1 | rules(); 12 | } 13 | 14 | public function authorize(): bool 15 | { 16 | return true; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /routes/api/auth.php: -------------------------------------------------------------------------------- 1 | controller('AuthController') 7 | ->withoutMiddleware(['auth:api', 'api', 'isActive']) 8 | ->group(function () { 9 | Route::post('register', 'register'); 10 | Route::post('login', 'login'); 11 | Route::post('refresh', 'refresh'); 12 | Route::post('logout', 'logout'); 13 | }); 14 | -------------------------------------------------------------------------------- /routes/api/users.php: -------------------------------------------------------------------------------- 1 | withoutMiddleware('isActive') 7 | ->controller(UserController::class) 8 | ->group(function () { 9 | Route::get('/', 'profile'); 10 | Route::patch('/', 'updateProfile'); 11 | }); 12 | 13 | Route::apiResource('users', UserController::class); 14 | -------------------------------------------------------------------------------- /app/Events/LoggerEvent.php: -------------------------------------------------------------------------------- 1 | allSubdomainsOfApplicationUrl(), 16 | ]; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Listeners/LoggerListener.php: -------------------------------------------------------------------------------- 1 | service->log($event->response, $event->response_status, $event->response_message); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/Core/Macros/IsSearchable.php: -------------------------------------------------------------------------------- 1 | getSearchable(), true); 7 | }); 8 | 9 | EloquentBuilder::macro('getSearchable', function (): array { 10 | return method_exists($this->getModel(), 'getSearchable') ? 11 | $this->getModel()->getSearchable() : []; 12 | }); 13 | -------------------------------------------------------------------------------- /app/Listeners/DestroyFilesListener.php: -------------------------------------------------------------------------------- 1 | resource->destroyImages($event->imageIds); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Traits/IsActive.php: -------------------------------------------------------------------------------- 1 | fillable[] = 'is_active'; 12 | $this->casts['is_active'] = 'bool'; 13 | } 14 | 15 | 16 | public function scopeActive(Builder $query) 17 | { 18 | return $query->whereIsActive(true); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/Listeners/AttachImagesListener.php: -------------------------------------------------------------------------------- 1 | resource->attachImages($event->images, $event->relation, $event->path, $event->identifier); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/Listeners/UpdateImageListener.php: -------------------------------------------------------------------------------- 1 | resource->updateImage($event->file, $event->relation, $event->path, $event->identifier); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/Events/AttachImages.php: -------------------------------------------------------------------------------- 1 | resource->updateFile($event->file, $event->relation, path: $event->path, identifier: $event->identifier); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | 'required|regex:/^998[0-9]{9}/', 16 | 'password' => 'required' 17 | ]; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/Http/Requests/Role/CheckPermissionsRequest.php: -------------------------------------------------------------------------------- 1 | ['required', new PermissionRule()]]; 13 | } 14 | 15 | public function authorize(): bool 16 | { 17 | return true; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 18 | 19 | return $app; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Core/Macros/Relations.php: -------------------------------------------------------------------------------- 1 | with($relations); 13 | }); 14 | -------------------------------------------------------------------------------- /app/Events/UpdateFile.php: -------------------------------------------------------------------------------- 1 | "required|array", 12 | "roles.*" => "required|distinct|exists:roles,id", 13 | ]; 14 | } 15 | 16 | public function authorize(): bool 17 | { 18 | return true; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.docker/web.debug/opcache.ini: -------------------------------------------------------------------------------- 1 | [opcache] 2 | opcache.max_accelerated_files=10000 3 | opcache.memory_consumption=192 4 | opcache.max_wasted_percentage=10 5 | opcache.interned_strings_buffer=16 6 | opcache.fast_shutdown=1 7 | ; 0 means it will check on every request 8 | ; 0 is irrelevant if opcache.validate_timestamps=0 which is desirable in production 9 | # https://www.php.net/manual/en/opcache.configuration.php 10 | opcache.enable=1 11 | opcache.enable_cli=0 12 | opcache.validate_timestamps=1 13 | opcache.revalidate_freq=0 14 | -------------------------------------------------------------------------------- /.docker/web.dev/opcache.ini: -------------------------------------------------------------------------------- 1 | [opcache] 2 | opcache.max_accelerated_files=10000 3 | opcache.memory_consumption=192 4 | opcache.max_wasted_percentage=10 5 | opcache.interned_strings_buffer=16 6 | opcache.fast_shutdown=1 7 | ; 0 means it will check on every request 8 | ; 0 is irrelevant if opcache.validate_timestamps=0 which is desirable in production 9 | # https://www.php.net/manual/en/opcache.configuration.php 10 | opcache.enable=1 11 | opcache.enable_cli=0 12 | opcache.validate_timestamps=1 13 | opcache.revalidate_freq=0 14 | -------------------------------------------------------------------------------- /.docker/web.loc/opcache.ini: -------------------------------------------------------------------------------- 1 | [opcache] 2 | opcache.max_accelerated_files=10000 3 | opcache.memory_consumption=192 4 | opcache.max_wasted_percentage=10 5 | opcache.interned_strings_buffer=16 6 | opcache.fast_shutdown=1 7 | ; 0 means it will check on every request 8 | ; 0 is irrelevant if opcache.validate_timestamps=0 which is desirable in production 9 | # https://www.php.net/manual/en/opcache.configuration.php 10 | opcache.enable=1 11 | opcache.enable_cli=0 12 | opcache.validate_timestamps=1 13 | opcache.revalidate_freq=0 14 | -------------------------------------------------------------------------------- /app/Helpers/ExistsCheck.php: -------------------------------------------------------------------------------- 1 | when($softDeletes, function ($query) { 13 | $query->whereNull('deleted_at'); 14 | }) 15 | ->where($column, $value) 16 | ->exists(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Http/Requests/Role/SyncPermissionsRequest.php: -------------------------------------------------------------------------------- 1 | 'required|array', 13 | 'permissions.*' => ['required', new PermissionRule()]]; 14 | } 15 | 16 | public function authorize(): bool 17 | { 18 | return true; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Controllers/ProductController.php: -------------------------------------------------------------------------------- 1 | id(); 13 | $table->timestamps(); 14 | }); 15 | } 16 | 17 | public function down() 18 | { 19 | Schema::dropIfExists('{{ table }}'); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /app/Models/Role.php: -------------------------------------------------------------------------------- 1 | TranslatableJson::class, 19 | ]; 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "mix", 6 | "watch": "mix watch", 7 | "watch-poll": "mix watch -- --watch-options-poll=1000", 8 | "hot": "mix watch --hot", 9 | "prod": "npm run production", 10 | "production": "mix --production" 11 | }, 12 | "devDependencies": { 13 | "axios": "^0.21", 14 | "laravel-mix": "^6.0.6", 15 | "lodash": "^4.17.19", 16 | "postcss": "^8.1.14" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /stubs/migration.update.stub: -------------------------------------------------------------------------------- 1 | 'Вперёд »', 16 | 'previous' => '« Назад', 17 | ]; 18 | -------------------------------------------------------------------------------- /app/Http/Controllers/CategoryController.php: -------------------------------------------------------------------------------- 1 | 'Keyingi »', 16 | 'previous' => '« Oldingi', 17 | ]; 18 | -------------------------------------------------------------------------------- /app/Core/Macros/IsTranslatable.php: -------------------------------------------------------------------------------- 1 | getLocale(); 7 | 8 | return $this->isTranslatable($field) ? "$field->$lang" : $field; 9 | }); 10 | 11 | EloquentBuilder::macro('isTranslatable', function (string $field): bool { 12 | return method_exists($this->getModel(), 'getTranslatableColumns') && 13 | in_array($field, $this->getModel()->getTranslatableColumns(), true); 14 | }); 15 | -------------------------------------------------------------------------------- /database/migrations/2022_07_24_134939_add_title_column_to_roles_table.php: -------------------------------------------------------------------------------- 1 | jsonb('title'); 12 | }); 13 | } 14 | 15 | public function down(): void 16 | { 17 | Schema::table('roles', function (Blueprint $table) { 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /app/Core/Http/Requests/ChangeStatusRequest.php: -------------------------------------------------------------------------------- 1 | 'required|boolean']; 15 | } 16 | 17 | /** 18 | * Determine if the user is authorized to make this request. 19 | */ 20 | public function authorize(): bool 21 | { 22 | return true; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | expectsJson()) { 16 | return route('login'); 17 | } 18 | 19 | return $request->expectsJson() ? null : route('login'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Http/Middleware/IsActive.php: -------------------------------------------------------------------------------- 1 | check() && !auth()->user()->is_active) { 19 | return $this->responseWith(code: 403, message: __("messages.account_not_active")); 20 | } 21 | 22 | return $next($request); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Core/Macros/Pagination.php: -------------------------------------------------------------------------------- 1 | paginate($per_page); 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /routes/api/roles.php: -------------------------------------------------------------------------------- 1 | controller('RoleController') 8 | ->group(function () { 9 | Route::post('/{role}/permissions/add', 'assignPermission'); 10 | Route::post('/{role}/permissions/sub', 'revokePermission'); 11 | Route::post('/{role}/permissions/sync', 'syncPermissions'); 12 | }); 13 | 14 | Route::apiResource('permissions', 'PermissionController'); 15 | Route::post('permissions/check-permission/{user?}', 'PermissionController@hasAllPermissions'); 16 | -------------------------------------------------------------------------------- /lang/en/pagination.php: -------------------------------------------------------------------------------- 1 | '« Previous', 17 | 'next' => 'Next »', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | const mix = require('laravel-mix'); 2 | 3 | /* 4 | |-------------------------------------------------------------------------- 5 | | Mix Asset Management 6 | |-------------------------------------------------------------------------- 7 | | 8 | | Mix provides a clean, fluent API for defining some Webpack build steps 9 | | for your Laravel applications. By default, we are compiling the CSS 10 | | file for the application as well as bundling up all the JS files. 11 | | 12 | */ 13 | 14 | mix.js('resources/js/app.js', 'public/js') 15 | .postCss('resources/css/app.css', 'public/css', [ 16 | // 17 | ]); 18 | -------------------------------------------------------------------------------- /app/Core/Http/Controllers/CoreController.php: -------------------------------------------------------------------------------- 1 | service = $service; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 18 | }); 19 | -------------------------------------------------------------------------------- /app/Policies/UserPolicy.php: -------------------------------------------------------------------------------- 1 | $this->faker->slug, 16 | 'title' => [ 17 | 'uz' => $this->faker->title, 18 | 'ru' => $this->faker->title, 19 | 'en' => $this->faker->title, 20 | ], 21 | 'guard_name' => 'api', 22 | ]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Policies/RolePolicy.php: -------------------------------------------------------------------------------- 1 | name === "superadmin") { 20 | return Response::deny(__('messages.not_access')); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | $uri = urldecode( 11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) 12 | ); 13 | 14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the 15 | // built-in PHP web server. This provides a convenient way to test a Laravel 16 | // application without having installed a "real" web server software here. 17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { 18 | return false; 19 | } 20 | 21 | require_once __DIR__.'/public/index.php'; 22 | -------------------------------------------------------------------------------- /.github/workflows/conteyner-info.txt: -------------------------------------------------------------------------------- 1 | The ${{matrix.php}} is a reference to a value in the matrix strategy, which specifies a list of values for the php key. 2 | 3 | This strategy creates multiple jobs based on the php values, allowing the same test suite to be run against different versions of PHP in parallel. 4 | 5 | For example, if the matrix was defined as: 6 | 7 | python 8 | Copy code 9 | matrix: 10 | php: ['7.0', '7.1', '7.2'] 11 | Then three jobs would be created, one for each PHP version: 12 | 13 | Copy code 14 | PHP 7.0 15 | PHP 7.1 16 | PHP 7.2 17 | In the job, the value of ${{matrix.php}} is used to set up the correct version of PHP and run the tests. 18 | -------------------------------------------------------------------------------- /app/Policies/PermissionPolicy.php: -------------------------------------------------------------------------------- 1 | append($appends); 18 | } 19 | 20 | return $this; 21 | }); 22 | -------------------------------------------------------------------------------- /app/Http/Requests/Role/PermissionCreateRequest.php: -------------------------------------------------------------------------------- 1 | merge(['name' => Str::kebab(trim($this->name))]); 13 | } 14 | 15 | public function rules(): array 16 | { 17 | return ['name' => 'required|string|unique:permissions,name', 18 | 'guard_name' => 'string']; 19 | } 20 | 21 | public function authorize(): bool 22 | { 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 19 | })->purpose('Display an inspiring quote'); 20 | -------------------------------------------------------------------------------- /tests/Unit/BarcodeGenerateTest.php: -------------------------------------------------------------------------------- 1 | assertIsArray($barcode); 14 | $this->assertArrayHasKey('barcode', $barcode); 15 | $this->assertArrayHasKey('barcode_path', $barcode); 16 | \Storage::disk('public')->assertExists(data_get($barcode, 'barcode_path')); 17 | \Storage::disk('public')->delete(data_get($barcode, 'barcode_path')); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lang/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "The :attribute must contain at least one letter.": "The :attribute must contain at least one letter.", 3 | "The :attribute must contain at least one number.": "The :attribute must contain at least one number.", 4 | "The :attribute must contain at least one symbol.": "The :attribute must contain at least one symbol.", 5 | "The :attribute must contain at least one uppercase and one lowercase letter.": "The :attribute must contain at least one uppercase and one lowercase letter.", 6 | "The given :attribute has appeared in a data leak. Please choose a different :attribute.": "The given :attribute has appeared in a data leak. Please choose a different :attribute." 7 | } 8 | -------------------------------------------------------------------------------- /database/seeders/AdminSeeder.php: -------------------------------------------------------------------------------- 1 | activated() 21 | ->create(['phone' => '998999999999', 22 | 'password' => '111111', 23 | ])->assignRole('superadmin'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Helpers/DateCasts.php: -------------------------------------------------------------------------------- 1 | format($this->format) : null; 19 | } 20 | 21 | public function set($model, string $key, $value, array $attributes) 22 | { 23 | return $value ? Carbon::parse($value) : null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Rules/CheckJsonExistsLocale.php: -------------------------------------------------------------------------------- 1 | config('laravel_api.main_locale')]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/Http/Middleware/SetAppLocale.php: -------------------------------------------------------------------------------- 1 | hasHeader('accept-language') && 17 | in_array($request->header('accept-language'), config('laravel_api.available_locales', []), true)) { 18 | $locale = $request->header('accept-language'); 19 | } 20 | app()->setLocale($locale); 21 | 22 | return $next($request); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Core/Helpers/Generators/Stubs/featureResourceTest.stub: -------------------------------------------------------------------------------- 1 | merge(['name' => Str::kebab($this->name)]); 13 | } 14 | 15 | public function rules(): array 16 | { 17 | return ['name' => 'required|string|unique:permissions,name,' . $this->route('permission')->id, 18 | 'guard_name' => 'string', 19 | ]; 20 | } 21 | 22 | public function authorize(): bool 23 | { 24 | return true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lang/ru/auth.php: -------------------------------------------------------------------------------- 1 | 'Эти учетные данные не соответствуют нашим записям.', 16 | 'password' => 'Неверный номер телефона или пароль.', 17 | 'throttle' => 'Слишком много попыток входа. Пожалуйста, попробуйте еще раз через :seconds секунд.', 18 | ]; 19 | -------------------------------------------------------------------------------- /lang/uz/auth.php: -------------------------------------------------------------------------------- 1 | 'Bunday maʼlumotlarga ega foydalanuvchi mavjud emas.', 16 | 'password' => 'Telefon raqami yoki parol noto‘g‘ri.', 17 | 'throttle' => 'Kirish uchun juda ko‘p urinishlar aniqlandi. Iltimos :seconds soniyadan so‘ng yana urinib ko‘ring.', 18 | ]; 19 | -------------------------------------------------------------------------------- /database/seeders/PermissionsSeeder.php: -------------------------------------------------------------------------------- 1 | 'system']); 14 | Role::findByName('superadmin')->syncPermissions(Permission::all()); 15 | $manager = Role::findByName('moderator'); 16 | $manager->syncPermissions( 17 | ['read-user', 18 | 'create-user', 19 | 'update-user', 20 | 'read-category', 21 | 'create-category', 22 | 'update-category',] 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/Observers/CategoryObserver.php: -------------------------------------------------------------------------------- 1 | ico?->id); 14 | } 15 | 16 | public function created(Category $category) 17 | { 18 | if (request()->hasFile('ico')) { 19 | UpdateImage::dispatch(request('ico'), $category->ico()); 20 | } 21 | } 22 | 23 | public function updated(Category $category) 24 | { 25 | if (request()->hasFile('ico')) { 26 | UpdateImage::dispatch(request('ico'), $category->ico()); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrustProxies.php: -------------------------------------------------------------------------------- 1 | app->environment('local')) { 16 | $this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class); 17 | $this->app->register(TelescopeServiceProvider::class); 18 | } 19 | } 20 | 21 | /** 22 | * Bootstrap any application services. 23 | */ 24 | public function boot() 25 | { 26 | Model::preventLazyLoading(!app()->isProduction()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Http/Requests/Auth/RegistrationRequest.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'required', 19 | new PhoneRule(), 20 | new UniqueRule('users', 'phone'), 21 | ], 22 | 'first_name' => 'required|string', 23 | 'last_name' => 'nullable|string', 24 | 'password' => 'required|confirmed|min:6', 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Traits/Author.php: -------------------------------------------------------------------------------- 1 | author_id = $query->author_id ?? auth()->id(); 14 | }); 15 | } 16 | 17 | public function initializeAuthor(): void 18 | { 19 | $this->fillable[] = 'author_id'; 20 | $this->casts['author_id'] = 'int'; 21 | } 22 | 23 | public function isMine($id): bool 24 | { 25 | return $id === auth()->id(); 26 | } 27 | 28 | public function author(): BelongsTo 29 | { 30 | return $this->belongsTo(User::class, 'author_id'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Services/BarcodeService.php: -------------------------------------------------------------------------------- 1 | where($column, $random)->doesntExist() ? : self::generate($table, $column, $path); 14 | $generator = new BarcodeGeneratorSVG(); 15 | Storage::disk('public') 16 | ->put("uploads/$path/barcodes/$random.svg", $generator 17 | ->getBarcode($random, $generator::TYPE_CODE_128)); 18 | 19 | return [$column => $random, $column . "_path" => "/uploads/$path/barcodes/$random.svg"]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Helpers/TranslatableJson.php: -------------------------------------------------------------------------------- 1 | getLocale()) : null; 12 | } 13 | 14 | public function set($model, string $key, $value, array $attributes) 15 | { 16 | return json_encode($value); 17 | } 18 | 19 | public static function translatable($attribute, $key = null) 20 | { 21 | $arr = json_decode($attribute, true); 22 | if (request()->has('edit_json')) { 23 | return $arr; 24 | } 25 | 26 | return $arr[$key] ?? $arr[config('laravel_api.main_locale')]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lang/ru/passwords.php: -------------------------------------------------------------------------------- 1 | 'Ваш пароль был сброшен!', 16 | 'sent' => 'Ссылка на сброс пароля была отправлена!', 17 | 'throttled' => 'Пожалуйста, подождите перед повторной попыткой.', 18 | 'token' => 'Ошибочный код сброса пароля.', 19 | 'user' => 'Не удалось найти пользователя с указанным электронным адресом.', 20 | ]; 21 | -------------------------------------------------------------------------------- /database/migrations/2022_04_02_045642_create_refresh_tokens_table.php: -------------------------------------------------------------------------------- 1 | id(); 13 | $table->text('token'); 14 | $table->text('refresh_token'); 15 | $table->dateTime('expired_at')->nullable(); 16 | $table->dateTime('refresh_expired_at'); 17 | $table->morphs('user'); 18 | $table->timestamps(); 19 | }); 20 | } 21 | 22 | public function down(): void 23 | { 24 | Schema::dropIfExists('refresh_tokens'); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /lang/en/passwords.php: -------------------------------------------------------------------------------- 1 | 'Your password has been reset!', 17 | 'sent' => 'We have emailed your password reset link!', 18 | 'throttled' => 'Please wait before retrying.', 19 | 'token' => 'This password reset token is invalid.', 20 | 'user' => "We can't find a user with that email address.", 21 | 22 | ]; 23 | -------------------------------------------------------------------------------- /app/Core/Macros/IsDates.php: -------------------------------------------------------------------------------- 1 | getModel()->getTable() . 'inDates', 86400, function () {//60 * 60 * 24=day 8 | $keys = collect($this->getModel()->getCasts()) 9 | ->filter(function ($value, $key) { 10 | if (str_contains($value, 'DateCasts') 11 | || str_contains($value, 'datetime') 12 | || str_contains($value, 'date')) { 13 | return $key; 14 | } 15 | }); 16 | 17 | return array_unique($keys->keys()->toArray() + $this->getModel()->getDates()); 18 | }); 19 | 20 | 21 | return in_array($field, $dates); 22 | }); 23 | -------------------------------------------------------------------------------- /lang/uz/passwords.php: -------------------------------------------------------------------------------- 1 | 'Sizning parolingiz tiklandi!', 16 | 'sent' => 'Parolni tiklash havolasini elektron pochta orqali yubordik!', 17 | 'throttled' => 'Iltimos birozdan so‘ng qayta urinib ko‘ring.', 18 | 'token' => 'Ushbu parolni qayta tiklash kodi noto‘g‘ri.', 19 | 'user' => 'Ushbu elektron pochta manziliga ega foydalanuvchi topilmadi.', 20 | ]; 21 | -------------------------------------------------------------------------------- /app/Http/Requests/ProfileUpdateRequest.php: -------------------------------------------------------------------------------- 1 | 'filled|string', 16 | 'last_name' => 'nullable|string', 17 | 'phone' => [ 18 | 'filled', 19 | new PhoneRule(), 20 | new UniqueRule('users', 'phone', $this->route('user', auth()->user())->id), 21 | ], 22 | 'password' => ['filled', 'string', 'confirmed', Password::min(8)->letters()->numbers()], 23 | 'avatar' => 'image', 24 | ]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /database/seeders/TestSeeder.php: -------------------------------------------------------------------------------- 1 | call(RolesTableSeeder::class); 17 | $this->call(AdminSeeder::class); 18 | 19 | dump('create active user'); 20 | User::factory(2)->activated()->create(); 21 | User::factory(5)->createQuietly(); 22 | 23 | dump('create Category with products'); 24 | Category::factory(2)->hasProducts(random_int(5, 10))->create(); 25 | Category::factory(5)->hasProducts(random_int(5, 10))->create(); 26 | Category::factory(5)->hasProducts(random_int(5, 10))->create(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Core/Macros/DbQuery.php: -------------------------------------------------------------------------------- 1 | make(request()->all(), [ 11 | config('laravel_api.request.columns', 'columns') => 'string', 12 | ]); 13 | 14 | if ($validator->fails()) { 15 | throw ValidationException::withMessages($validator->messages()->toArray()); 16 | } 17 | $requestColumns = request(config('laravel_api.request.columns', 'columns'), ['*']); 18 | 19 | if ($requestColumns !== ['*']) { 20 | $requestColumns = explode(',', $requestColumns); 21 | } 22 | $columns = $columns ?? $requestColumns; 23 | 24 | return $query->select($columns); 25 | }); 26 | -------------------------------------------------------------------------------- /app/Core/Traits/EnumCasts.php: -------------------------------------------------------------------------------- 1 | $this->getTranslate($value)] : null; 10 | if (request()->exists('enums')) { 11 | return $data ? $data + $this->getTranslateValuesToArray() : $this->getTranslateValuesToArray(); 12 | } 13 | 14 | return $data; 15 | } 16 | 17 | //before set check if value is valid else return exception and rollback 18 | public function set($model, string $key, $value, array $attributes) 19 | { 20 | if (!$this->isValid($value)) { 21 | throw new \Exception(__('validation.in_array', 22 | ['attribute' => $value, 'other' => "$key:" . $this]), 422); 23 | } 24 | 25 | return $value; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /database/migrations/2019_08_19_000000_create_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | tableName, function (Blueprint $table) { 13 | $table->id(); 14 | $table->string('uuid')->unique(); 15 | $table->text('connection'); 16 | $table->text('queue'); 17 | $table->longText('payload'); 18 | $table->longText('exception'); 19 | $table->timestamp('failed_at')->useCurrent(); 20 | }); 21 | } 22 | 23 | public function down(): void 24 | { 25 | Schema::dropIfExists($this->tableName); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /.docker/web.dev/nginx.conf: -------------------------------------------------------------------------------- 1 | user www; 2 | worker_processes 4; 3 | error_log /var/log/nginx/error.log notice; 4 | pid /var/run/nginx.pid; 5 | 6 | events { 7 | worker_connections 4096; 8 | } 9 | 10 | http { 11 | include /etc/nginx/mime.types; 12 | include /etc/nginx/fastcgi.conf; 13 | index index.html index.php; 14 | ssl_session_cache shared:SSL:10m; 15 | ssl_session_timeout 10m; 16 | default_type application/octet-stream; 17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 18 | '$status $body_bytes_sent "$http_referer" ' 19 | '"$http_user_agent" "$http_x_forwarded_for"'; 20 | access_log /var/log/nginx/access.log main; 21 | sendfile on; 22 | tcp_nopush on; 23 | server_names_hash_bucket_size 128; 24 | keepalive_timeout 65; 25 | gzip on; 26 | 27 | include /etc/nginx/conf.d/*.conf; 28 | } 29 | -------------------------------------------------------------------------------- /.docker/web.loc/nginx.conf: -------------------------------------------------------------------------------- 1 | user www; 2 | worker_processes 4; 3 | error_log /var/log/nginx/error.log notice; 4 | pid /var/run/nginx.pid; 5 | 6 | events { 7 | worker_connections 4096; 8 | } 9 | 10 | http { 11 | include /etc/nginx/mime.types; 12 | include /etc/nginx/fastcgi.conf; 13 | index index.html index.php; 14 | ssl_session_cache shared:SSL:10m; 15 | ssl_session_timeout 10m; 16 | default_type application/octet-stream; 17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 18 | '$status $body_bytes_sent "$http_referer" ' 19 | '"$http_user_agent" "$http_x_forwarded_for"'; 20 | access_log /var/log/nginx/access.log main; 21 | sendfile on; 22 | tcp_nopush on; 23 | server_names_hash_bucket_size 128; 24 | keepalive_timeout 65; 25 | gzip on; 26 | 27 | include /etc/nginx/conf.d/*.conf; 28 | } 29 | -------------------------------------------------------------------------------- /.docker/web/nginx.conf: -------------------------------------------------------------------------------- 1 | user www; 2 | worker_processes 4; 3 | error_log /var/log/nginx/error.log notice; 4 | pid /var/run/nginx.pid; 5 | 6 | events { 7 | worker_connections 4096; 8 | } 9 | 10 | http { 11 | include /etc/nginx/mime.types; 12 | include /etc/nginx/fastcgi.conf; 13 | index index.html index.php; 14 | ssl_session_cache shared:SSL:10m; 15 | ssl_session_timeout 10m; 16 | default_type application/octet-stream; 17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 18 | '$status $body_bytes_sent "$http_referer" ' 19 | '"$http_user_agent" "$http_x_forwarded_for"'; 20 | access_log /var/log/nginx/access.log main; 21 | sendfile on; 22 | tcp_nopush on; 23 | server_names_hash_bucket_size 128; 24 | keepalive_timeout 65; 25 | gzip on; 26 | 27 | include /etc/nginx/conf.d/*.conf; 28 | } 29 | -------------------------------------------------------------------------------- /app/Http/Middleware/RedirectIfAuthenticated.php: -------------------------------------------------------------------------------- 1 | check()) { 24 | return redirect(RouteServiceProvider::HOME); 25 | } 26 | } 27 | 28 | return $next($request); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.docker/web.debug/nginx.conf: -------------------------------------------------------------------------------- 1 | user www; 2 | worker_processes 4; 3 | error_log /var/log/nginx/error.log notice; 4 | pid /var/run/nginx.pid; 5 | 6 | events { 7 | worker_connections 4096; 8 | } 9 | 10 | http { 11 | include /etc/nginx/mime.types; 12 | include /etc/nginx/fastcgi.conf; 13 | index index.html index.php; 14 | ssl_session_cache shared:SSL:10m; 15 | ssl_session_timeout 10m; 16 | default_type application/octet-stream; 17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 18 | '$status $body_bytes_sent "$http_referer" ' 19 | '"$http_user_agent" "$http_x_forwarded_for"'; 20 | access_log /var/log/nginx/access.log main; 21 | sendfile on; 22 | tcp_nopush on; 23 | server_names_hash_bucket_size 128; 24 | keepalive_timeout 65; 25 | gzip on; 26 | 27 | include /etc/nginx/conf.d/*.conf; 28 | } 29 | -------------------------------------------------------------------------------- /lang/en/auth.php: -------------------------------------------------------------------------------- 1 | 'These credentials do not match our records.', 17 | 'password' => 'Wrong phone number or password.', 18 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 19 | 'profile_requires_activation' => 'You are not activated in the system - contact administrator', 20 | 21 | ]; 22 | -------------------------------------------------------------------------------- /app/Jobs/LoggerJob.php: -------------------------------------------------------------------------------- 1 | log($this->response, $this->status, $this->message); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Http/Requests/Role/RoleCreateRequest.php: -------------------------------------------------------------------------------- 1 | merge(['name' => Str::snake(trim($this->name))]); 13 | } 14 | 15 | public function rules(): array 16 | { 17 | return ['title' => 'required|array', 18 | 'title.' . config('laravel_api.main_locale') => 'required|string', 19 | 'title.*' => 'nullable|string', 20 | 'name' => 'required|string|unique:roles,name', 21 | 'guard_name' => 'string']; 22 | } 23 | 24 | public function authorize(): bool 25 | { 26 | return true; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Core/Contracts/CoreServiceContract.php: -------------------------------------------------------------------------------- 1 | tableName, function (Blueprint $table) { 13 | $table->id(); 14 | $table->morphs('tokenable'); 15 | $table->string('name'); 16 | $table->string('token', 64)->unique(); 17 | $table->text('abilities')->nullable(); 18 | $table->timestamp('last_used_at')->nullable(); 19 | $table->timestamp('expires_at')->nullable(); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | public function down(): void 25 | { 26 | Schema::dropIfExists($this->tableName); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /config/cors.php: -------------------------------------------------------------------------------- 1 | ['api/*', 'sanctum/csrf-cookie'], 19 | 20 | 'allowed_methods' => ['*'], 21 | 22 | 'allowed_origins' => ['*'], 23 | 24 | 'allowed_origins_patterns' => [], 25 | 26 | 'allowed_headers' => ['*'], 27 | 28 | 'exposed_headers' => [], 29 | 30 | 'max_age' => 0, 31 | 32 | 'supports_credentials' => false, 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /app/Http/Requests/Role/RoleUpdateRequest.php: -------------------------------------------------------------------------------- 1 | merge(['name' => Str::snake(trim($this->name))]); 13 | } 14 | 15 | public function rules(): array 16 | { 17 | return ['title' => 'filled|array', 18 | 'title.' . config('laravel_api.main_locale') => 'required_with:title|string', 19 | 'title.*' => 'nullable|string', 20 | 'name' => 'required|string|unique:roles,name,' . $this->route('role')->id, 21 | 'guard_name' => 'string', 22 | ]; 23 | } 24 | 25 | public function authorize(): bool 26 | { 27 | return true; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Core/Macros/PaginationOrCollection.php: -------------------------------------------------------------------------------- 1 | config('laravel_api.request.list_type', 'list_type')])); 13 | } 14 | 15 | return $this->when($listType == 'collection', 16 | fn($q): Collection => $q->collection(), 17 | fn($q): LengthAwarePaginator => $q->pagination() 18 | ); 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /app/Services/PermissionService.php: -------------------------------------------------------------------------------- 1 | user(); 22 | 23 | return $user->hasAllPermissions($request->permission); 24 | } 25 | 26 | public function create(Validator|FormRequest $request): mixed 27 | { 28 | $permission = parent::create($request); 29 | $this->repository->attachToAdmin($permission); 30 | 31 | return $permission; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('inspire')->hourly(); 27 | $schedule->command('cache:prune-stale-tags')->daily(); 28 | } 29 | 30 | /** 31 | * Register the commands for the application. 32 | */ 33 | protected function commands(): void 34 | { 35 | $this->load(__DIR__ . '/Commands'); 36 | 37 | require base_path('routes/console.php'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/Rules/PermissionRule.php: -------------------------------------------------------------------------------- 1 | message = __('messages.error_type'); 21 | } 22 | if (!$permission) { 23 | $this->message = __('messages.not_found'); 24 | } 25 | if (!hasRole() && !hasPermission($permission)) { 26 | $this->message = __('messages.not_access'); 27 | } 28 | 29 | return true; 30 | } 31 | 32 | public function message(): string 33 | { 34 | return $this->message; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/seeders/UserSeeder.php: -------------------------------------------------------------------------------- 1 | fake()->firstName(), 18 | 'last_name' => fake()->lastName, 19 | 'phone' => fake()->phoneNumber, 20 | 'password' => fake()->password, 21 | 'phone_confirmed' => fake()->boolean, 22 | 'is_active' => fake()->boolean, 23 | 'created_at' => fake()->dateTime, 24 | 'updated_at' => fake()->dateTime, 25 | ]); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /config/modulegenerator.php: -------------------------------------------------------------------------------- 1 | 'App\Core\Http\Controllers\CoreController', 5 | 'core_service' => 'App\Core\Services\CoreService', 6 | 'core_repository' => 'App\Core\Repositories\CoreRepository', 7 | 'core_policy' => 'App\Core\Repositories\CoreRepository', 8 | 9 | 'route_path' => 'Routes\api', 10 | 'repository_path' => 'App\Repositories', 11 | 'service_path' => 'App\Services', 12 | 'model_path' => 'App\Models', 13 | 'request_path' => 'App\Http\Requests', 14 | 'policy_path' => 'App\Policies', 15 | 'controller_path' => 'App\Http\Controllers', 16 | 'api' => [ 17 | 'controller_path' => 'App\Http\Controllers\Api', 18 | 'request_path' => 'App\Http\Requests', 19 | 'route' => 'Routes\api.php', 20 | ], 21 | 22 | 'web' => [ 23 | 'controller_path' => 'App\Http\Controllers', 24 | 'request_path' => 'App\Http\Requests', 25 | 'route' => 'Routes\web.php', 26 | ], 27 | ]; 28 | -------------------------------------------------------------------------------- /database/migrations/2021_10_31_110750_create_categories_table.php: -------------------------------------------------------------------------------- 1 | tableName, function (Blueprint $table) { 13 | $table->id(); 14 | $table->jsonb('name'); 15 | $table->jsonb('description')->nullable(); 16 | $table->unsignedBigInteger('position')->default(0); 17 | $table->boolean('is_active')->default(true); 18 | $table->foreignId('author_id')->constrained('users'); 19 | $table->foreignId('parent_id')->nullable()->constrained('categories'); 20 | $table->softDeletes(); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | public function down(): void 26 | { 27 | Schema::dropIfExists($this->tableName); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /database/migrations/2021_10_31_111105_create_resources_table.php: -------------------------------------------------------------------------------- 1 | tableName, function (Blueprint $table) { 13 | $table->id(); 14 | $table->string('display_name')->nullable(); 15 | $table->string('additional_identifier')->nullable(); 16 | $table->string('type')->nullable(); 17 | $table->string('path_original'); 18 | $table->string('path_1024')->nullable();//if file not image it will be null 19 | $table->string('path_512')->nullable(); 20 | $table->morphs('resource'); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | public function down(): void 26 | { 27 | Schema::dropIfExists($this->tableName); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /stubs/policy.stub: -------------------------------------------------------------------------------- 1 | 'filled|string', 16 | 'last_name' => 'nullable|string', 17 | 'phone' => [ 18 | 'filled', 19 | new PhoneRule(), 20 | new UniqueRule('users', 'phone', auth()->user()->id), 21 | ], 22 | 'password' => ['filled', 'string', 'confirmed', Password::min(8)->letters()->numbers()], 23 | 'birthday' => 'nullable|date|before_or_equal:' . now(), 24 | 'is_active' => 'bool', 25 | 'avatar' => 'image', 26 | 'roles' => 'array', 27 | 'roles.*' => 'nullable|exists:roles,name|not_in:superadmin', 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Observers/UserObserver.php: -------------------------------------------------------------------------------- 1 | avatar->id); 14 | } 15 | 16 | public function created(User $user) 17 | { 18 | (request('roles') && !in_array('superadmin', request('roles'))) ? 19 | $user->syncRoles(request('roles')) : $user->syncRoles(['customer']); 20 | 21 | if (request()->hasFile('avatar')) { 22 | UpdateImage::dispatch(request('avatar'), $user->avatar()); 23 | } 24 | } 25 | 26 | public function updating(User $user) 27 | { 28 | if (request('roles')) { 29 | $user->syncRoles(request('roles')); 30 | } 31 | } 32 | public function updated(User $user) 33 | { 34 | if (request()->hasFile('avatar')) { 35 | UpdateImage::dispatch(request('avatar'), $user->avatar()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /stubs/observer.stub: -------------------------------------------------------------------------------- 1 | table) 27 | ->when($this->softDeletes, function ($query) { 28 | $query->whereNull('deleted_at'); 29 | }) 30 | ->where($this->column, $value) 31 | ->when($this->id, fn($query) => $query->where('id', '!=', $this->id)) 32 | ->doesntExist(); 33 | } 34 | 35 | public function message() 36 | { 37 | return __('validation.unique', ['attribute' => $this->column]); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DRIVER', 'local'), 5 | 6 | 'disks' => [ 7 | 8 | 'local' => [ 9 | 'driver' => 'local', 10 | 'root' => storage_path('/app/public/uploads'), 11 | ], 12 | 13 | 'public' => [ 14 | 'driver' => 'local', 15 | 'root' => storage_path('/app/public'), 16 | 'url' => env('APP_URL') . '/storage', 17 | 'visibility' => 'public', 18 | ], 19 | 20 | 's3' => [ 21 | 'driver' => 's3', 22 | 'key' => env('AWS_ACCESS_KEY_ID'), 23 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 24 | 'region' => env('AWS_DEFAULT_REGION'), 25 | 'bucket' => env('AWS_BUCKET'), 26 | 'url' => env('AWS_URL'), 27 | 'endpoint' => env('AWS_ENDPOINT'), 28 | ], 29 | 30 | ], 31 | 32 | //php artisan storage:link 33 | 'links' => [ 34 | public_path('uploads') => storage_path('app/public/uploads'), 35 | ], 36 | ]; 37 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), 21 | ], 22 | 23 | 'postmark' => [ 24 | 'token' => env('POSTMARK_TOKEN'), 25 | ], 26 | 27 | 'ses' => [ 28 | 'key' => env('AWS_ACCESS_KEY_ID'), 29 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 30 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 31 | ], 32 | 33 | ]; 34 | -------------------------------------------------------------------------------- /app/Providers/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | UserPolicy::class, 20 | Category::class => CategoryPolicy::class, 21 | Product::class => ProductPolicy::class, 22 | Role::class => RolePolicy::class, 23 | Permission::class => PermissionPolicy::class, 24 | ]; 25 | 26 | /** 27 | * Register any authentication / authorization services. 28 | */ 29 | public function boot(): void 30 | { 31 | $this->registerPolicies(); 32 | // 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/Rules/UniqueJsonRule.php: -------------------------------------------------------------------------------- 1 | table)->where($this->column . '->' . $lang, '=', $value) 20 | ->when($this->id, function ($query) { 21 | $query->where('id', '!=', $this->id); 22 | }) 23 | ->doesntExist(); 24 | } 25 | 26 | /** 27 | * Create a new rule instance. 28 | */ 29 | public function __construct(protected string $table, protected string $column, protected int|null $id = null) 30 | { 31 | } 32 | 33 | /** 34 | * Get the validation error message. 35 | */ 36 | public function message(): string 37 | { 38 | return __('validation.unique', ['attribute' => $this->column]); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Rules/CheckJsonExistsNullableLocale.php: -------------------------------------------------------------------------------- 1 | config('laravel_api.main_locale')]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/Core/Macros/IsActive.php: -------------------------------------------------------------------------------- 1 | when(!is_null($status), function (EloquentBuilder|QueryBuilder $query) use ($status) { 11 | $acceptable = [true, false, 0, 1, '0', '1']; 12 | 13 | if (!in_array($status, $acceptable, true)) { 14 | throw new \Exception(__('validation.array', ['attribute' => config('laravel_api.request.is_active', 'is_active')])); 15 | } 16 | $query->where(config('laravel_api.check.is_active', 'is_active'), $status); 17 | }); 18 | 19 | return $this; 20 | }); 21 | 22 | $builder::macro('active', function () { 23 | $this->where(config('laravel_api.check.is_active', 'is_active'), '=', true); 24 | 25 | return $this; 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /.docker/web.loc/app.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | root /var/www/public; 4 | server_name laravel.api; 5 | keepalive_timeout 70; 6 | 7 | add_header X-Frame-Options "SAMEORIGIN"; 8 | add_header X-Content-Type-Options "nosniff"; 9 | 10 | index index.php index.html; 11 | charset utf-8; 12 | 13 | location = /favicon.ico { access_log off; log_not_found off; } 14 | location = /robots.txt { access_log off; log_not_found off; } 15 | 16 | error_page 404 /index.php; 17 | 18 | location ~ \.php$ { 19 | try_files $uri =404; 20 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 21 | fastcgi_pass localhost:9000; 22 | fastcgi_index index.php; 23 | include fastcgi_params; 24 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 25 | fastcgi_param PATH_INFO $fastcgi_path_info; 26 | fastcgi_buffering off; 27 | } 28 | 29 | location / { 30 | try_files $uri $uri/ /index.php?$query_string; 31 | gzip_static on; 32 | } 33 | 34 | location ~ /\.(?!well-known).* { 35 | deny all; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | ./tests/Unit 10 | 11 | 12 | ./tests/Feature 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/Core/Providers/CoreServiceProvider.php: -------------------------------------------------------------------------------- 1 | mapWithKeys(function ($path) { 23 | return [$path => pathinfo($path, PATHINFO_FILENAME)]; 24 | }) 25 | ->each(function ($macro, $path) { 26 | require_once $path; 27 | }); 28 | 29 | $this->app->bind(CoreServiceContract::class, CoreService::class); 30 | $this->app->bind(CoreRepositoryContract::class, CoreRepository::class); 31 | } 32 | 33 | /** 34 | * Bootstrap any application services. 35 | */ 36 | public function boot() 37 | { 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /database/migrations/2021_10_31_182630_create_products_table.php: -------------------------------------------------------------------------------- 1 | tableName, function (Blueprint $table) { 13 | $table->id(); 14 | $table->foreignId('author_id')->constrained('users'); 15 | $table->foreignId('category_id')->constrained()->cascadeOnDelete(); 16 | $table->jsonb('name'); 17 | $table->jsonb('description')->nullable(); 18 | $table->unsignedBigInteger('position')->default(0); 19 | $table->boolean('is_active')->default(true); 20 | $table->string('barcode_path'); 21 | $table->string('barcode')->unique(); 22 | $table->softDeletes(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | public function down(): void 28 | { 29 | Schema::dropIfExists($this->tableName); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=LaravelApi 2 | APP_ENV=local 3 | APP_KEY=base64:8KfiBETfRmptGovlHVXHJ/Xzq3t0ABWjeoyNGEI6VC4= 4 | APP_DEBUG=true 5 | APP_URL=http://localhost 6 | 7 | LOG_CHANNEL=stack 8 | LOG_DEPRECATIONS_CHANNEL=null 9 | LOG_LEVEL=debug 10 | 11 | DB_CONNECTION=mysql 12 | DB_HOST=mysql 13 | DB_PORT=3306 14 | DB_DATABASE=test 15 | DB_USERNAME=root 16 | DB_PASSWORD=password 17 | 18 | BROADCAST_DRIVER=log 19 | CACHE_DRIVER=file 20 | FILESYSTEM_DRIVER=local 21 | QUEUE_CONNECTION=sync 22 | SESSION_DRIVER=file 23 | SESSION_LIFETIME=120 24 | 25 | MEMCACHED_HOST=127.0.0.1 26 | 27 | REDIS_HOST=127.0.0.1 28 | REDIS_PASSWORD=null 29 | REDIS_PORT=6379 30 | 31 | MAIL_MAILER=smtp 32 | MAIL_HOST=mailhog 33 | MAIL_PORT=1025 34 | MAIL_USERNAME=null 35 | MAIL_PASSWORD=null 36 | MAIL_ENCRYPTION=null 37 | MAIL_FROM_ADDRESS=null 38 | MAIL_FROM_NAME="${APP_NAME}" 39 | 40 | AWS_ACCESS_KEY_ID= 41 | AWS_SECRET_ACCESS_KEY= 42 | AWS_DEFAULT_REGION=us-east-1 43 | AWS_BUCKET= 44 | AWS_USE_PATH_STYLE_ENDPOINT=false 45 | 46 | PUSHER_APP_ID= 47 | PUSHER_APP_KEY= 48 | PUSHER_APP_SECRET= 49 | PUSHER_APP_CLUSTER=mt1 50 | 51 | MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" 52 | MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" 53 | -------------------------------------------------------------------------------- /app/Core/Helpers/Generators/EditRoute.php: -------------------------------------------------------------------------------- 1 | config('laravel_api.request.onlyDeleted', 'onlyDeleted'), 12 | ])); 13 | } 14 | 15 | return $this->when($trashed, fn($query) => $query->onlyTrashed()); 16 | }); 17 | 18 | Builder::macro('withDeleted', function (bool $trashed = null): Builder { 19 | $trashed = $trashed ?? (bool)request(config('laravel_api.request.withDeleted', 'withDeleted'), false); 20 | 21 | $acceptable = [true, false, 0, 1, '0', '1']; 22 | 23 | if (!in_array($trashed, $acceptable, true)) { 24 | throw new \Exception(__('validation.boolean', ['attribute' => config('laravel_api.request.withDeleted', 'withDeleted'), 25 | ])); 26 | } 27 | 28 | return $this->when($trashed, fn($query) => $query->withTrashed()); 29 | }); 30 | -------------------------------------------------------------------------------- /app/Rules/checkActiveRule.php: -------------------------------------------------------------------------------- 1 | table)->where($this->column, $value)->first(); 23 | if (!$user) { 24 | $this->message = __('messages.attribute_not_found', ['attribute' => __('attributes.' . $this->attribute)]); 25 | 26 | return false; 27 | } 28 | if ($user->is_active === false || $user->deleted_at) { 29 | $this->message = __('messages.inactive', ['attribute' => __('attributes.' . $this->attribute)]); 30 | 31 | return false; 32 | } 33 | 34 | return true; 35 | } 36 | 37 | public function message(): string 38 | { 39 | return $this->message; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /database/migrations/2021_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | tableName, function (Blueprint $table) { 13 | $table->id(); 14 | $table->string('first_name'); 15 | $table->string('last_name')->nullable(); 16 | $table->string('phone'); 17 | $table->string('password'); 18 | $table->boolean('is_active')->default(false); 19 | $table->boolean('phone_confirmed')->default(false); 20 | $table->dateTime('phone_confirmed_at')->nullable(); 21 | $table->date('birthday')->nullable(); 22 | $table->foreignId('author_id')->nullable()->constrained('users'); 23 | $table->softDeletes(); 24 | $table->timestamps(); 25 | }); 26 | } 27 | 28 | public function down(): void 29 | { 30 | Schema::dropIfExists($this->tableName); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /app/Http/Controllers/LoggerController.php: -------------------------------------------------------------------------------- 1 | service->index(); 20 | 21 | return $this->responseWith(['loggers' => $loggers]); 22 | } 23 | 24 | public function show(Logger $logger): JsonResponse 25 | { 26 | $logger = $this->service->show($logger); 27 | 28 | return $this->responseWith(compact('logger')); 29 | } 30 | 31 | public function destroy(Logger $logger): JsonResponse 32 | { 33 | try { 34 | $this->service->delete($logger); 35 | 36 | return $this->responseWith(code: 204); 37 | } catch (\Exception $e) { 38 | return $this->responseWith(code: $e->getCode(), message: $e->getMessage(), logging: true); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | resource_path('views'), 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled View Path 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This option determines where all the compiled Blade templates will be 26 | | stored for your application. Typically, this is within the storage 27 | | directory. However, as usual, you are free to change this value. 28 | | 29 | */ 30 | 31 | 'compiled' => env( 32 | 'VIEW_COMPILED_PATH', 33 | realpath(storage_path('framework/views')) 34 | ), 35 | 36 | ]; 37 | -------------------------------------------------------------------------------- /database/migrations/2022_04_28_145148_create_logger_table.php: -------------------------------------------------------------------------------- 1 | id(); 12 | $table->string('ip'); 13 | $table->text('user_agent'); 14 | $table->foreignId('user_id') 15 | ->nullable()->constrained() 16 | ->cascadeOnDelete()->cascadeOnUpdate(); 17 | $table->json('action'); 18 | $table->string('uri'); 19 | $table->string('method'); 20 | $table->json('headers'); 21 | $table->json('payload')->nullable(); 22 | $table->json('response')->nullable(); 23 | $table->integer('response_status')->nullable(); 24 | $table->text('response_message')->nullable(); 25 | $table->dateTime('date')->useCurrent(); 26 | }); 27 | } 28 | 29 | public function down(): void 30 | { 31 | Schema::dropIfExists('logger'); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /app/Http/Requests/UserCreateRequest.php: -------------------------------------------------------------------------------- 1 | 'required|string', 27 | 'last_name' => 'nullable|string', 28 | 'phone' => [ 29 | 'required', 30 | new PhoneRule(), 31 | new UniqueRule('users', 'phone'), 32 | ], 33 | 'password' => ['required', 'string', 'confirmed', Password::min(8)->letters()->numbers()], 34 | 'avatar' => 'image', 35 | 'roles' => 'array', 36 | 'roles.*' => 'nullable|exists:roles,name|not_in:superadmin', 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.docker/web.dev/app.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 443 ssl default_server; 3 | listen [::]:443 ssl default_server; 4 | root /var/www/public; 5 | server_name _; 6 | ssl_certificate _; 7 | ssl_certificate_key _; 8 | keepalive_timeout 70; 9 | 10 | add_header X-Frame-Options "SAMEORIGIN"; 11 | add_header X-Content-Type-Options "nosniff"; 12 | 13 | index index.php index.html; 14 | charset utf-8; 15 | 16 | location = /favicon.ico { access_log off; log_not_found off; } 17 | location = /robots.txt { access_log off; log_not_found off; } 18 | 19 | error_page 404 /index.php; 20 | 21 | location ~ \.php$ { 22 | try_files $uri =404; 23 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 24 | fastcgi_pass localhost:9000; 25 | fastcgi_index index.php; 26 | include fastcgi_params; 27 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 28 | fastcgi_param PATH_INFO $fastcgi_path_info; 29 | fastcgi_buffering off; 30 | } 31 | 32 | location / { 33 | try_files $uri $uri/ /index.php?$query_string; 34 | gzip_static on; 35 | } 36 | 37 | location ~ /\.(?!well-known).* { 38 | deny all; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.docker/web/app.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 443 ssl default_server; 3 | listen [::]:443 ssl default_server; 4 | root /var/www/public; 5 | server_name _; 6 | ssl_certificate _; 7 | ssl_certificate_key _; 8 | keepalive_timeout 70; 9 | 10 | add_header X-Frame-Options "SAMEORIGIN"; 11 | add_header X-Content-Type-Options "nosniff"; 12 | 13 | index index.php index.html; 14 | charset utf-8; 15 | 16 | location = /favicon.ico { access_log off; log_not_found off; } 17 | location = /robots.txt { access_log off; log_not_found off; } 18 | 19 | error_page 404 /index.php; 20 | 21 | location ~ \.php$ { 22 | try_files $uri =404; 23 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 24 | fastcgi_pass localhost:9000; 25 | fastcgi_index index.php; 26 | include fastcgi_params; 27 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 28 | fastcgi_param PATH_INFO $fastcgi_path_info; 29 | fastcgi_buffering off; 30 | } 31 | 32 | location / { 33 | try_files $uri $uri/ /index.php?$query_string; 34 | gzip_static on; 35 | } 36 | 37 | location ~ /\.(?!well-known).* { 38 | deny all; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.docker/web.debug/app.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 443 ssl default_server; 3 | listen [::]:443 ssl default_server; 4 | root /var/www/public; 5 | server_name laravel.api; 6 | ssl_certificate _; 7 | ssl_certificate_key _; 8 | keepalive_timeout 70; 9 | 10 | add_header X-Frame-Options "SAMEORIGIN"; 11 | add_header X-Content-Type-Options "nosniff"; 12 | 13 | index index.php index.html; 14 | charset utf-8; 15 | 16 | location = /favicon.ico { access_log off; log_not_found off; } 17 | location = /robots.txt { access_log off; log_not_found off; } 18 | 19 | error_page 404 /index.php; 20 | 21 | location ~ \.php$ { 22 | try_files $uri =404; 23 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 24 | fastcgi_pass localhost:9000; 25 | fastcgi_index index.php; 26 | include fastcgi_params; 27 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 28 | fastcgi_param PATH_INFO $fastcgi_path_info; 29 | fastcgi_buffering off; 30 | } 31 | 32 | location / { 33 | try_files $uri $uri/ /index.php?$query_string; 34 | gzip_static on; 35 | } 36 | 37 | location ~ /\.(?!well-known).* { 38 | deny all; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Core/Enums/CoreEnum.php: -------------------------------------------------------------------------------- 1 | getConstants()); 25 | } 26 | 27 | /** 28 | * Returns class constant keys 29 | */ 30 | public static function getKeys(): array 31 | { 32 | return array_keys(self::reflection()->getConstants()); 33 | } 34 | 35 | public function __toString(): string 36 | { 37 | return implode(',', static::toArray()); 38 | } 39 | 40 | /** 41 | * validator enum abilities 42 | */ 43 | public static function isValid(string $value): bool 44 | { 45 | return in_array($value, static::toArray()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/Services/RoleService.php: -------------------------------------------------------------------------------- 1 | repository->givePermissionTo($role, $request->get('permission')); 24 | } 25 | 26 | /** 27 | * Revoke permission to 28 | */ 29 | public function revokePermissionTo(Role $role, PermissionRequest $request): mixed 30 | { 31 | return $this->repository->revokePermissionTo($role, $request->get('permission')); 32 | } 33 | 34 | /** 35 | * Sync Role permissions 36 | */ 37 | public function syncPermissions(Role $role, SyncPermissionsRequest $request): mixed 38 | { 39 | return $this->repository->syncRolePermissions($role, $request->get('permissions')); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /public/web.config: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.docker/web/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user=root 3 | nodaemon=true 4 | logfile=/dev/stdout 5 | logfile_maxbytes=0 6 | pidfile=/var/run/supervisord.pid 7 | loglevel=INFO 8 | 9 | [program:php-fpm] 10 | command=/usr/local/sbin/php-fpm 11 | autostart=true 12 | autorestart=true 13 | priority=5 14 | stdout_logfile=/dev/stdout 15 | stdout_logfile_maxbytes=0 16 | stderr_logfile=/dev/stderr 17 | stderr_logfile_maxbytes=0 18 | 19 | [program:nginx] 20 | command=nginx -c /etc/nginx/nginx.conf -g 'daemon off;' 21 | process_name=%(program_name)s_%(process_num)02d 22 | numprocs=1 23 | autostart=true 24 | autorestart=false 25 | startsecs=0 26 | redirect_stderr=true 27 | stdout_logfile=/dev/stdout 28 | stdout_logfile_maxbytes=0 29 | 30 | [program:laravel-worker] 31 | process_name=%(program_name)s_%(process_num)02d 32 | command=php /var/www/artisan queue:work database --sleep=3 --tries=3 --max-time=3600 33 | autostart=true 34 | autorestart=true 35 | stopasgroup=true 36 | killasgroup=true 37 | user=www 38 | numprocs=1 39 | redirect_stderr=true 40 | stdout_logfile=/dev/stdout 41 | stdout_logfile_maxbytes=0 42 | stderr_logfile=/var/log/supervisor/laravel-queue-error.log 43 | stderr_logfile_maxbytes=0 44 | stopwaitsecs=3600 45 | 46 | [program:cron] 47 | command=/usr/sbin/crond -f 48 | user=root 49 | autostart=true 50 | autorestart=true 51 | -------------------------------------------------------------------------------- /app/Models/Resource.php: -------------------------------------------------------------------------------- 1 | path_original); 30 | } 31 | 32 | public function getUrl1024Attribute(): ?string 33 | { 34 | return $this->attributes['path_1024'] ? URL::to($this->attributes['path_1024']) : null; 35 | } 36 | 37 | public function getUrl512Attribute(): ?string 38 | { 39 | return $this->attributes['path_512'] ? URL::to($this->attributes['path_512']) : null; 40 | } 41 | 42 | public function resource(): MorphTo 43 | { 44 | return $this->morphTo(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/Repositories/LoggerRepository.php: -------------------------------------------------------------------------------- 1 | create(['ip' => request()->getClientIp(), 21 | 'user_agent' => request()->header('User-Agent'), 22 | "user_id" => auth()->user()?->id, 23 | 'action' => request()->route()->getAction(), 24 | 'uri' => request()->url(), 25 | 'method' => request()->method(), 26 | 'headers' => request()->header(), 27 | 'payload' => request()->input(), 28 | 'response_headers' => request()->header(), 29 | 'response_message' => $message, 30 | 'response_status' => $status, 31 | 'response' => $response]); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Rules/ExistsRule.php: -------------------------------------------------------------------------------- 1 | table) 18 | ->when($this->softDeletes, function ($query) { 19 | $query->whereNull('deleted_at'); 20 | }) 21 | ->when($this->pivotColumn && $this->pivotValue, function ($query) { 22 | $query->where($this->pivotColumn, $this->pivotValue); 23 | }) 24 | ->where($this->column, $value) 25 | ->exists(); 26 | } 27 | 28 | /** 29 | * Create a new rule instance. 30 | */ 31 | public function __construct( 32 | protected string $table, 33 | protected string $column, 34 | protected string|null $pivotColumn = null, 35 | protected string|null $pivotValue = null, 36 | protected bool $softDeletes = true 37 | ) { 38 | } 39 | 40 | /** 41 | * Get the validation error message. 42 | */ 43 | public function message(): string 44 | { 45 | return __('validation.exists', ['attribute' => $this->column]); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lang/en/messages.php: -------------------------------------------------------------------------------- 1 | 'Success', 5 | 'fail' => 'fail', 6 | 'invalid_login' => 'login or password is incorrect', 7 | 'cant_logged' => 'Sorry, user cannot be logged out', 8 | 'phone_invalid_format' => 'The phone format is invalid.', 9 | 'invalid_password' => 'password is incorrect', 10 | 'cannot_delete_using_element' => 'You cannot delete the used element,but can edit', 11 | 'cannot_change_admin' => "You don't have access to delete or update Admin", 12 | 'not_found' => 'Не найдено', 13 | 'not_access' => 'Нет доступа', 14 | 'attribute_not_found' => ':attribute не найдено', 15 | 'account_not_active' => 'Your account is not active', 16 | 'position_deleted' => "':attribute' position removed", 17 | 'employee_deleted' => "Director ':attribute' removed", 18 | 'user_deleted' => "':attribute' User removed", 19 | 'company_deleted' => "':attribute' company removed", 20 | 'isnt_your_worker' => "This is not your worker", 21 | 'product_not_found' => "Product not found", 22 | 'logout' => 'Logged out', 23 | 'error_type' => "Error type", 24 | ]; 25 | -------------------------------------------------------------------------------- /lang/uz/messages.php: -------------------------------------------------------------------------------- 1 | 'Success', 5 | 'fail' => 'fail', 6 | 'invalid_login' => 'login or password is incorrect', 7 | 'cant_logged' => 'Sorry, user cannot be logged out', 8 | 'phone_invalid_format' => 'The phone format is invalid.', 9 | 'invalid_password' => 'password is incorrect', 10 | 'cannot_delete_using_element' => 'You cannot delete the used element,but can edit', 11 | 'cannot_change_admin' => "You don't have access to delete or update Admin", 12 | 'not_found' => 'Не найдено', 13 | 'not_access' => 'Нет доступа', 14 | 'attribute_not_found' => ':attribute не найдено', 15 | 'account_not_active' => 'Sizning accountinggiz faol emas', 16 | 'position_deleted' => "':attribute' position removed", 17 | 'employee_deleted' => "Director ':attribute' removed", 18 | 'user_deleted' => "':attribute' User removed", 19 | 'company_deleted' => "':attribute' company removed", 20 | 'isnt_your_worker' => "This is not your worker", 21 | 'product_not_found' => "Product not found", 22 | 'logout' => 'Logged out', 23 | 'error_type' => "Error type", 24 | ]; 25 | -------------------------------------------------------------------------------- /app/Core/Contracts/CoreRepositoryContract.php: -------------------------------------------------------------------------------- 1 | where(function ($query) use ($columns, $search) { 13 | foreach ($columns as $column) { 14 | $query->orWhere($column, like(), "%$search%"); 15 | } 16 | }); 17 | 18 | return $this; 19 | }); 20 | 21 | $builder::macro('orWhereLike', function (array|string $columns, string $search) { 22 | $search = rtrim($search, " \t."); 23 | $columns = \Arr::wrap($columns); 24 | $this->orWhere(function ($query) use ($columns, $search) { 25 | foreach ($columns as $column) { 26 | $query->orWhere($column, like(), "%$search%"); 27 | } 28 | }); 29 | 30 | return $this; 31 | }); 32 | } 33 | 34 | EloquentBuilder::macro('orWhereLikeRelation', function (string $relation, string $column, string $search) { 35 | $search = rtrim($search, " \t."); 36 | $this->orWhereRelation($relation, $column, like(), "%$search%"); 37 | 38 | return $this; 39 | }); 40 | -------------------------------------------------------------------------------- /.docker/web.debug/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user=root 3 | nodaemon=true 4 | logfile=/dev/stdout 5 | logfile_maxbytes=0 6 | pidfile=/var/run/supervisord.pid 7 | loglevel=INFO 8 | 9 | [program:php-fpm] 10 | command=/usr/local/sbin/php-fpm 11 | autostart=true 12 | autorestart=true 13 | priority=5 14 | stdout_logfile=/dev/stdout 15 | stdout_logfile_maxbytes=0 16 | stderr_logfile=/var/log/supervisor/php-fpm-error.log 17 | stderr_logfile_maxbytes=0 18 | 19 | [program:nginx] 20 | command=nginx -c /etc/nginx/nginx.conf -g 'daemon off;' 21 | process_name=%(program_name)s_%(process_num)02d 22 | numprocs=1 23 | autostart=true 24 | autorestart=true 25 | startsecs=0 26 | stdout_logfile=/dev/stdout 27 | stdout_logfile_maxbytes=0 28 | stderr_logfile=/var/log/supervisor/php-fpm-error.log 29 | stderr_logfile_maxbytes=0 30 | 31 | [program:laravel-worker] 32 | process_name=%(program_name)s_%(process_num)02d 33 | command=php /var/www/artisan queue:work database --sleep=3 --tries=3 --max-time=3600 34 | autostart=true 35 | autorestart=true 36 | stopasgroup=true 37 | killasgroup=true 38 | user=www 39 | numprocs=1 40 | redirect_stderr=true 41 | stdout_logfile=/dev/stdout 42 | stdout_logfile_maxbytes=0 43 | stderr_logfile=/var/log/supervisor/laravel-queue-error.log 44 | stderr_logfile_maxbytes=0 45 | stopwaitsecs=3600 46 | 47 | [program:cron] 48 | command=/usr/sbin/crond -f 49 | user=root 50 | autostart=true 51 | autorestart=true 52 | -------------------------------------------------------------------------------- /.docker/web.dev/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user=root 3 | nodaemon=true 4 | logfile=/dev/stdout 5 | logfile_maxbytes=0 6 | pidfile=/var/run/supervisord.pid 7 | loglevel=INFO 8 | 9 | [program:php-fpm] 10 | command=/usr/local/sbin/php-fpm 11 | autostart=true 12 | autorestart=true 13 | priority=5 14 | stdout_logfile=/dev/stdout 15 | stdout_logfile_maxbytes=0 16 | stderr_logfile=/var/log/supervisor/php-fpm-error.log 17 | stderr_logfile_maxbytes=0 18 | 19 | [program:nginx] 20 | command=nginx -c /etc/nginx/nginx.conf -g 'daemon off;' 21 | process_name=%(program_name)s_%(process_num)02d 22 | numprocs=1 23 | autostart=true 24 | autorestart=true 25 | startsecs=0 26 | stdout_logfile=/dev/stdout 27 | stdout_logfile_maxbytes=0 28 | stderr_logfile=/var/log/supervisor/php-fpm-error.log 29 | stderr_logfile_maxbytes=0 30 | 31 | [program:laravel-worker] 32 | process_name=%(program_name)s_%(process_num)02d 33 | command=php /var/www/artisan queue:work database --sleep=3 --tries=3 --max-time=3600 34 | autostart=true 35 | autorestart=true 36 | stopasgroup=true 37 | killasgroup=true 38 | user=www 39 | numprocs=1 40 | redirect_stderr=true 41 | stdout_logfile=/dev/stdout 42 | stdout_logfile_maxbytes=0 43 | stderr_logfile=/var/log/supervisor/laravel-queue-error.log 44 | stderr_logfile_maxbytes=0 45 | stopwaitsecs=3600 46 | 47 | [program:cron] 48 | command=/usr/sbin/crond -f 49 | user=root 50 | autostart=true 51 | autorestart=true 52 | -------------------------------------------------------------------------------- /.docker/web.loc/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | user=root 3 | nodaemon=true 4 | logfile=/dev/stdout 5 | logfile_maxbytes=0 6 | pidfile=/var/run/supervisord.pid 7 | loglevel=INFO 8 | 9 | [program:php-fpm] 10 | command=/usr/local/sbin/php-fpm 11 | autostart=true 12 | autorestart=true 13 | priority=5 14 | stdout_logfile=/dev/stdout 15 | stdout_logfile_maxbytes=0 16 | stderr_logfile=/var/log/supervisor/php-fpm-error.log 17 | stderr_logfile_maxbytes=0 18 | 19 | [program:nginx] 20 | command=nginx -c /etc/nginx/nginx.conf -g 'daemon off;' 21 | process_name=%(program_name)s_%(process_num)02d 22 | numprocs=1 23 | autostart=true 24 | autorestart=true 25 | startsecs=0 26 | stdout_logfile=/dev/stdout 27 | stdout_logfile_maxbytes=0 28 | stderr_logfile=/var/log/supervisor/php-fpm-error.log 29 | stderr_logfile_maxbytes=0 30 | 31 | [program:laravel-worker] 32 | process_name=%(program_name)s_%(process_num)02d 33 | command=php /var/www/artisan queue:work database --sleep=3 --tries=3 --max-time=3600 34 | autostart=true 35 | autorestart=true 36 | stopasgroup=true 37 | killasgroup=true 38 | user=www 39 | numprocs=1 40 | redirect_stderr=true 41 | stdout_logfile=/dev/stdout 42 | stdout_logfile_maxbytes=0 43 | stderr_logfile=/var/log/supervisor/laravel-queue-error.log 44 | stderr_logfile_maxbytes=0 45 | stopwaitsecs=3600 46 | 47 | [program:cron] 48 | command=/usr/sbin/crond -f 49 | user=root 50 | autostart=true 51 | autorestart=true 52 | -------------------------------------------------------------------------------- /app/Providers/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 15 | LoggerListener::class, 16 | ], 17 | UpdateImage::class => [ 18 | UpdateImageListener::class, 19 | ], 20 | UpdateFile::class => [ 21 | UpdateFileListener::class, 22 | ], 23 | AttachImages::class => [ 24 | AttachImagesListener::class, 25 | ], 26 | DestroyFiles::class => [ 27 | DestroyFilesListener::class, 28 | ], 29 | ]; 30 | 31 | protected $observers = [ 32 | Category::class => [CategoryObserver::class], 33 | User::class => [UserObserver::class], 34 | Product::class => [ProductObserver::class], 35 | ]; 36 | 37 | /** 38 | * Register any events for your application. 39 | */ 40 | public function boot(): void { } 41 | } 42 | -------------------------------------------------------------------------------- /lang/ru/messages.php: -------------------------------------------------------------------------------- 1 | 'Success', 5 | 'fail' => 'fail', 6 | 'invalid_login' => 'login or password is incorrect', 7 | 'cant_logged' => 'Sorry, user cannot be logged out', 8 | 'phone_invalid_format' => 'The phone format is invalid.', 9 | 'invalid_password' => 'password is incorrect', 10 | 'cannot_delete_using_element' => 'You cannot delete the used element,but can edit', 11 | 'cannot_change_admin' => "You don't have access to delete or update Admin", 12 | 'not_found' => 'Не найдено', 13 | 'not_access' => 'Нет доступа', 14 | 'attribute_not_found' => ':attribute не найдено', 15 | 'account_not_active' => 'Ваш аккаунт не активен', 16 | 'inactive' => ':attribute неактивен', 17 | 'position_deleted' => "':attribute' position removed", 18 | 'employee_deleted' => "Director ':attribute' removed", 19 | 'user_deleted' => "':attribute' User removed", 20 | 'company_deleted' => "':attribute' company removed", 21 | 'isnt_your_worker' => "This is not your worker", 22 | 'product_not_found' => "Product not found", 23 | 'logout' => 'Logged out', 24 | 'error_type' => "Error type", 25 | ]; 26 | -------------------------------------------------------------------------------- /app/Models/RefreshToken.php: -------------------------------------------------------------------------------- 1 | 'datetime:Y.m.d H:i:s', 21 | 'refresh_expired_at' => 'datetime:Y.m.d H:i:s', 22 | ]; 23 | protected $with = ['user']; 24 | 25 | /** 26 | * to set creator_id 27 | */ 28 | protected static function boot(): void 29 | { 30 | parent::boot(); 31 | static::creating(function ($query) { 32 | $query->refresh_token = RefreshTokenGenerator::tokenGenerate(); 33 | $query->expired_at = now()->addMinutes(config('sanctum.expiration')); 34 | $query->refresh_expired_at = now()->addMinutes(config('sanctum.refresh_expiration')); 35 | }); 36 | } 37 | 38 | protected $hidden = ['user_id', 'user_type', 'created_at', 'updated_at']; 39 | 40 | public function getRefreshTokenAttribute(): string 41 | { 42 | return encrypt($this->attributes['refresh_token']); 43 | } 44 | 45 | public function user(): MorphTo 46 | { 47 | return $this->morphTo(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/Repositories/RoleRepository.php: -------------------------------------------------------------------------------- 1 | when(notSystem(), 23 | function (Builder $query) { 24 | $query->whereDoesntHave('permissions', function ($query) { 25 | $query->where('name', 'system'); 26 | })->whereNotIn('name', ['owner', 'demo']); 27 | }); 28 | } 29 | 30 | public function givePermissionTo(Model $role, int|string $permission): mixed 31 | { 32 | return $role->givePermissionTo($permission); 33 | } 34 | 35 | public function revokePermissionTo(Model $role, int|string $permission): mixed 36 | { 37 | return $role->revokePermissionTo($permission); 38 | } 39 | 40 | /** 41 | * Sync Role permissions 42 | */ 43 | public function syncRolePermissions(Model $role, array $permissions): mixed 44 | { 45 | return $role->syncPermissions($permissions); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/Core/Test/Feature/CoreTest.php: -------------------------------------------------------------------------------- 1 | getDatabaseName()); 24 | //$this->seed(TestSeeder::class); 25 | 26 | [$this->user, $this->phone, $this->pass] = $this->createUser($this->roles); 27 | } 28 | 29 | protected function tearDown(): void 30 | { 31 | $this->deleteUser($this->user); 32 | parent::tearDown(); 33 | } 34 | 35 | public function createUser(string|array $roles = null) 36 | { 37 | $roles = $roles ?? 'customer'; 38 | $user = User::factory() 39 | ->create(['phone' => $phone = '998' . rand(100000000, 999999999), 40 | 'password' => $password = $this->faker->password(8) . '1a', 41 | 'is_active' => 1, 42 | ])->assignRole(Arr::wrap($roles)); 43 | 44 | 45 | return [$user, $phone, $password]; 46 | } 47 | 48 | public function deleteUser(User $user) 49 | { 50 | $user->forceDelete(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/Core/Macros/Namespaces/QueryBuilder.php: -------------------------------------------------------------------------------- 1 | configureRateLimiting(); 18 | $this->aliasMiddleware('setAppLocale', SetAppLocale::class); 19 | $this->aliasMiddleware('isActive', IsActive::class); 20 | 21 | $routeFiles = array_slice(scandir(base_path('routes/api')), 2); 22 | foreach ($routeFiles as $file) { 23 | Route::middleware(['api', 'setAppLocale', 'isActive', 'bindings']) 24 | ->namespace('App\Http\Controllers') 25 | ->group(base_path('routes/api/' . $file)); 26 | } 27 | 28 | $this->routes(function () { 29 | Route::middleware('web') 30 | ->group(base_path('routes/web.php')); 31 | }); 32 | } 33 | 34 | /** 35 | * Configure the rate limiters for the application. 36 | */ 37 | protected function configureRateLimiting() 38 | { 39 | RateLimiter::for('api', function (Request $request) { 40 | return Limit::perMinute(60)->by(optional($request->user())->id ? : $request->ip()); 41 | }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /.env.testing.fail: -------------------------------------------------------------------------------- 1 | APP_NAME=LaravelApi 2 | APP_ENV=local 3 | APP_KEY=base64:8KfiBETfRmptGovlHVXHJ/Xzq3t0ABWjeoyNGEI6VC4= 4 | APP_DEBUG=true 5 | APP_URL=http://localhost 6 | APP_DOMAIN=localhost 7 | TELESCOPE_ENABLED=true 8 | APP_PORT=80 9 | PHP_IDE_CONFIG="serverName=localhost" 10 | PHP_IDE_KEY="PHPSTORM" 11 | WWWUSER=overlord 12 | WWWUSERID=1000 13 | SAIL_XDEBUG=true 14 | 15 | LOG_CHANNEL=stack 16 | LOG_DEPRECATIONS_CHANNEL=null 17 | LOG_LEVEL=debug 18 | 19 | FORWARD_DB_PORT=8011 20 | FORWARD_TEST_DB_PORT=8011 21 | FORWARD_REDIS_PORT=6380 22 | 23 | DB_CONNECTION=mysql 24 | DB_HOST=mariadb-testing 25 | DB_PORT=3306 26 | DB_DATABASE=laravel 27 | DB_USERNAME=sail 28 | DB_PASSWORD=password 29 | DATABASE_PATH=/etc/docker/laravel-api/laravel-api-database:/var/lib/mysql_2 30 | 31 | BROADCAST_DRIVER=log 32 | CACHE_DRIVER=redis 33 | FILESYSTEM_DRIVER=local 34 | QUEUE_CONNECTION=sync 35 | SESSION_DRIVER=file 36 | SESSION_LIFETIME=120 37 | 38 | MEMCACHED_HOST=127.0.0.1 39 | 40 | REDIS_HOST=redis 41 | REDIS_PASSWORD=null 42 | REDIS_PORT=6379 43 | 44 | MAIL_MAILER=smtp 45 | MAIL_HOST=mailhog 46 | MAIL_PORT=1025 47 | MAIL_USERNAME=null 48 | MAIL_PASSWORD=null 49 | MAIL_ENCRYPTION=null 50 | MAIL_FROM_ADDRESS=null 51 | MAIL_FROM_NAME="${APP_NAME}" 52 | 53 | AWS_ACCESS_KEY_ID= 54 | AWS_SECRET_ACCESS_KEY= 55 | AWS_DEFAULT_REGION=us-east-1 56 | AWS_BUCKET= 57 | AWS_USE_PATH_STYLE_ENDPOINT=false 58 | 59 | PUSHER_APP_ID= 60 | PUSHER_APP_KEY= 61 | PUSHER_APP_SECRET= 62 | PUSHER_APP_CLUSTER=mt1 63 | 64 | MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" 65 | MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" 66 | -------------------------------------------------------------------------------- /app/Http/Requests/ProductUpdateRequest.php: -------------------------------------------------------------------------------- 1 | ['filled', 14 | new checkActiveRule('categories', request('category_id'), 'category')], 15 | 'name' => 'filled|array', 16 | 'name.' . config('laravel_api.main_locale') => 'filled|string', 17 | 'name.*' => 'nullable|string', 18 | 'description' => 'nullable|array', 19 | 'description.' . config('laravel_api.main_locale') => 'required_with:description|string', 20 | 'description.*' => 'nullable|string', 21 | 'position' => 'integer', 22 | 'main_image' => 'nullable|image|max:10000', 23 | 'images.*' => 'nullable|image|max:10000', 24 | 'video' => 'nullable|mimetypes:video/*|max:20000', 25 | ]; 26 | } 27 | 28 | public function authorize(): bool 29 | { 30 | return true; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.env.example.docker: -------------------------------------------------------------------------------- 1 | APP_NAME=LaravelApi 2 | APP_ENV=local 3 | APP_KEY=base64:8KfiBETfRmptGovlHVXHJ/Xzq3t0ABWjeoyNGEI6VC4= 4 | APP_DEBUG=true 5 | APP_URL=http://localhost 6 | APP_DOMAIN=localhost 7 | TELESCOPE_ENABLED=${APP_DEBUG} 8 | APP_PORT=80 9 | FORWARD_DB_PORT=3306 10 | FORWARD_REDIS_PORT=6380 11 | PHP_IDE_CONFIG="serverName=localhost" 12 | PHP_IDE_KEY="PHPSTORM" 13 | WWWUSER=overlord 14 | WWWUSERID=1000 15 | 16 | LOG_CHANNEL=stack 17 | LOG_DEPRECATIONS_CHANNEL=null 18 | LOG_LEVEL=debug 19 | 20 | FORWARD_DB_PORT=3307 21 | FORWARD_REDIS_PORT=6380 22 | 23 | DB_HOST=mysql 24 | DB_PORT=3306 25 | DB_DATABASE=test_db 26 | DB_USERNAME=test_user 27 | DB_PASSWORD=secret 28 | DATABASE_PATH = /etc/docker/laravel-api/laravel-api-database:/var/lib/mysql_1 29 | 30 | BROADCAST_DRIVER=log 31 | CACHE_DRIVER=redis 32 | FILESYSTEM_DRIVER=local 33 | QUEUE_CONNECTION=sync 34 | SESSION_DRIVER=file 35 | SESSION_LIFETIME=120 36 | 37 | MEMCACHED_HOST=127.0.0.1 38 | 39 | REDIS_HOST=redis 40 | REDIS_PORT=6379 41 | REDIS_PASSWORD=null 42 | REDIS_PORT=6379 43 | 44 | MAIL_MAILER=smtp 45 | MAIL_HOST=mailhog 46 | MAIL_PORT=1025 47 | MAIL_USERNAME=null 48 | MAIL_PASSWORD=null 49 | MAIL_ENCRYPTION=null 50 | MAIL_FROM_ADDRESS=null 51 | MAIL_FROM_NAME="${APP_NAME}" 52 | 53 | AWS_ACCESS_KEY_ID= 54 | AWS_SECRET_ACCESS_KEY= 55 | AWS_DEFAULT_REGION=us-east-1 56 | AWS_BUCKET= 57 | AWS_USE_PATH_STYLE_ENDPOINT=false 58 | 59 | PUSHER_APP_ID= 60 | PUSHER_APP_KEY= 61 | PUSHER_APP_SECRET= 62 | PUSHER_APP_CLUSTER=mt1 63 | 64 | MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" 65 | MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" 66 | -------------------------------------------------------------------------------- /app/Http/Requests/ProductCreateRequest.php: -------------------------------------------------------------------------------- 1 | ['required', 14 | new checkActiveRule('categories', request('category_id'), 'category')], 15 | 'name' => 'required|array', 16 | 'name.' . config('laravel_api.main_locale') => 'required|string', 17 | 'name.*' => 'nullable|string', 18 | 'description' => 'nullable|array', 19 | 'description.' . config('laravel_api.main_locale') => 'required_with:description|string', 20 | 'description.*' => 'nullable|string', 21 | 'position' => 'integer', 22 | 'main_image' => 'nullable|image|max:10000', 23 | 'images.*' => 'nullable|image|max:10000', 24 | 'video' => 'nullable|mimetypes:video/*|max:20000', 25 | ]; 26 | } 27 | 28 | public function authorize(): bool 29 | { 30 | return true; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Repositories/PermissionRepository.php: -------------------------------------------------------------------------------- 1 | when(notSystem(), 20 | function (Builder $query) { 21 | $query->whereIn('name', auth()->user()->getAllPermissions()->pluck('name')); 22 | }); 23 | } 24 | 25 | public function appends(Builder $query): void 26 | { 27 | $this->role($query, request('role')); 28 | } 29 | 30 | public function attachToAdmin(Permission|int|string $permission) 31 | { 32 | $this->firstBy('superadmin', 'name', Role::query())->givePermissionTo($permission); 33 | } 34 | 35 | public function role( 36 | Builder $query, 37 | $role = null, 38 | ) { 39 | return $query->when($role, function (Builder $query) use ($role) { 40 | $query->whereHas('roles', function (Builder $query) use ($role) { 41 | if (is_array($role)) { 42 | $query->whereIn('name', $role); 43 | } else { 44 | $query->where('name', $role); 45 | } 46 | }); 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - develop 8 | - '*.x' 9 | pull_request: 10 | branches: 11 | - master 12 | - develop 13 | schedule: 14 | - cron: '0 0 * * *' 15 | 16 | # Allows you to run this workflow manually from the Actions tab 17 | workflow_dispatch: 18 | 19 | jobs: 20 | phpunit: 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | php-versions: [ 'kirschbaumdevelopment/laravel-test-runner:8.1','kirschbaumdevelopment/laravel-test-runner:8.2'] 25 | runs-on: ubuntu-latest 26 | container: 27 | image: ${{ matrix.php-versions }} 28 | 29 | services: 30 | mysql: 31 | image: mysql:8.0 32 | env: 33 | MYSQL_ROOT_PASSWORD: password 34 | MYSQL_DATABASE: test 35 | ports: 36 | - 3306:3306 37 | options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 38 | 39 | steps: 40 | - uses: actions/checkout@v1 41 | with: 42 | fetch-depth: 1 43 | 44 | - name: Install composer dependencies 45 | run: | 46 | composer install --no-scripts 47 | 48 | - name: Prepare Laravel Application 49 | run: | 50 | cp .env.example .env 51 | php artisan key:generate 52 | - name: Migrate seed 53 | run: | 54 | php artisan migrate --force 55 | php artisan db:seed --class=TestSeeder 56 | 57 | - name: Run Testsuite 58 | run: php artisan test 59 | -------------------------------------------------------------------------------- /app/Models/Logger.php: -------------------------------------------------------------------------------- 1 | 'datetime:Y-m-d H:i:s', 29 | 'action' => 'array', 30 | 'headers' => 'array', 31 | 'payload' => 'array', 32 | 'response' => 'array', 33 | ]; 34 | 35 | protected $dates = ['date']; 36 | 37 | public $timestamps = false; 38 | 39 | protected array $searchable = [ 40 | 'uri', 41 | 'user_agent', 42 | 'response_message', 43 | ]; 44 | 45 | protected $appends = ['happened']; 46 | 47 | //show diff with date at now method Happened 48 | public function getHappenedAttribute() 49 | { 50 | return $this->date->diffForHumans(); 51 | } 52 | 53 | public function user(): BelongsTo 54 | { 55 | return $this->belongsTo(User::class); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/Core/Macros/GetBy.php: -------------------------------------------------------------------------------- 1 | config('laravel_api.request.getBy', 'getBy'), 25 | 'other' => implode(',', $inGetBy)])); 26 | } 27 | 28 | return match ($getBy) { 29 | 'sum' => (float)$this->sum(DB::raw($column)),//sum:price*quantity 30 | 'avg' => (float)$this->avg(DB::raw($column)), 31 | 'count' => (int)$this->count($column ?? "*"), 32 | 'max' => (float)$this->max(DB::raw($column)), 33 | 'min' => (float)$this->min(DB::raw($column)), 34 | 'exists' => (boolean)$this->exists(), 35 | 'collection' => $this->collection(), 36 | default => $this->pagination(), 37 | }; 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /app/Core/Policies/CorePolicy.php: -------------------------------------------------------------------------------- 1 | name}", $user) 23 | ? Response::allow() 24 | : Response::deny(__('messages.not_access')); 25 | } 26 | 27 | public function view(User $user, Model $model): Response 28 | { 29 | return hasPermission("read-{$this->name}", $user) 30 | ? Response::allow() 31 | : Response::deny(__('messages.not_access')); 32 | } 33 | 34 | public function create(User $user): Response 35 | { 36 | return hasPermission("create-{$this->name}", $user) 37 | ? Response::allow() 38 | : Response::deny(__('messages.not_access')); 39 | } 40 | 41 | public function update(User $user, Model $model) 42 | { 43 | $this->updateDelete($user, $model); 44 | 45 | return hasPermission("update-{$this->name}", $user) 46 | ? Response::allow() 47 | : Response::deny(__('messages.not_access')); 48 | } 49 | 50 | public function delete(User $user, Model $model) 51 | { 52 | $this->updateDelete($user, $model); 53 | 54 | return hasPermission("delete-{$this->name}", $user) 55 | ? Response::allow() 56 | : Response::deny(__('messages.not_access')); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/Observers/ProductObserver.php: -------------------------------------------------------------------------------- 1 | hasFile('mainImage')) { 18 | UpdateImage::dispatch(request('mainImage'), $model->mainImage(), $model->getFilePath(), $model::MAIN_IMAGE); 19 | } 20 | if (request()->hasFile('video')) { 21 | UpdateFile::dispatch(request('mainImage'), $model->video(), $model->getFilePath(), $model::VIDEO); 22 | } 23 | if (request()->hasFile('images')) { 24 | AttachImages::dispatch(request('images'), $model->images(), $model->getFilePath(), $model::IMAGES); 25 | } 26 | } 27 | 28 | public function creating(Product $product) 29 | { 30 | $barcode = BarcodeService::generate('products', 'barcode', $product->getFilePath()); 31 | $product['barcode'] = $barcode['barcode']; 32 | $product['barcode_path'] = $barcode['barcode_path']; 33 | } 34 | 35 | public function created(Product $product) 36 | { 37 | $this->checkFile($product); 38 | } 39 | 40 | public function updated(Product $product) 41 | { 42 | $this->checkFile($product); 43 | } 44 | 45 | public function deleting(Product $product) 46 | { 47 | DestroyFiles::dispatch($product->images?->pluck('id')->toArray()); 48 | DestroyFiles::dispatch($product->mainImage?->id); 49 | DestroyFiles::dispatch($product->video?->id); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/Repositories/UserRepository.php: -------------------------------------------------------------------------------- 1 | selfExclude($query, request('self_exclude', false)); 21 | 22 | $this->filterByRole($query, request('role')); 23 | } 24 | 25 | public function selfExclude( 26 | Builder $query, 27 | bool $selfExclude = false 28 | ) { 29 | return $query->when($selfExclude, fn($q) => $q->where('id', '!=', auth()->id())); 30 | } 31 | 32 | public function filterByRole( 33 | Builder $query, 34 | array|string $role = null, 35 | ) { 36 | return $query->when($role, function ($query) use ($role) { 37 | $query->role($role); 38 | }); 39 | } 40 | 41 | public function generateRefreshToken(User $user): RefreshToken 42 | { 43 | $token = $user->createToken('user_' . $user->phone)->plainTextToken; 44 | 45 | return $user->token()->create(['token' => $token])->load('user.avatar'); 46 | } 47 | 48 | public function firstByRefreshToken(Request $request): ?RefreshToken 49 | { 50 | return RefreshToken::firstWhere('refresh_token', decrypt($request->bearerToken())); 51 | } 52 | 53 | public function firstByToken(Request $request): ?RefreshToken 54 | { 55 | return RefreshToken::firstWhere('token', $request->bearerToken()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /config/hashing.php: -------------------------------------------------------------------------------- 1 | 'bcrypt', 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Bcrypt Options 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may specify the configuration options that should be used when 26 | | passwords are hashed using the Bcrypt algorithm. This will allow you 27 | | to control the amount of time it takes to hash the given password. 28 | | 29 | */ 30 | 31 | 'bcrypt' => [ 32 | 'rounds' => env('BCRYPT_ROUNDS', 10), 33 | ], 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Argon Options 38 | |-------------------------------------------------------------------------- 39 | | 40 | | Here you may specify the configuration options that should be used when 41 | | passwords are hashed using the Argon algorithm. These will allow you 42 | | to control the amount of time it takes to hash the given password. 43 | | 44 | */ 45 | 46 | 'argon' => [ 47 | 'memory' => 1024, 48 | 'threads' => 2, 49 | 'time' => 2, 50 | ], 51 | 52 | ]; 53 | -------------------------------------------------------------------------------- /app/Repositories/ResourceRepository.php: -------------------------------------------------------------------------------- 1 | delete($model->path_original); 21 | \Storage::disk('public')->delete($model->path_1024); 22 | \Storage::disk('public')->delete($model->path_512); 23 | } 24 | 25 | public function create( 26 | $relation, 27 | $type, 28 | $identifier, 29 | $path_original, 30 | $displayName = null, 31 | $path_1024 = null, 32 | $path_512 = null 33 | ) { 34 | $relation->create(['path_original' => $path_original, 35 | 'path_1024' => $path_1024, 36 | 'path_512' => $path_512, 37 | 'type' => $type, 38 | 'additional_identifier' => $identifier, 39 | 'display_name' => $displayName, 40 | ]); 41 | } 42 | 43 | public function firstBy(int $modelId): ?Model 44 | { 45 | return $this->model->findOrFail($modelId); 46 | } 47 | 48 | public function destroy(array $images) 49 | { 50 | foreach ($images as $image) { 51 | $this->delete($this->firstBy($image)); 52 | } 53 | } 54 | 55 | public function delete(Model $image) 56 | { 57 | $this->removeFile($image); 58 | $image->delete(); 59 | } 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /docker-compose-mysql.yaml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | web: 4 | container_name: laravel-api-db 5 | restart: always 6 | build: 7 | context: . 8 | dockerfile: ./.docker/web.loc/Dockerfile 9 | environment: 10 | TZ: 'Asia/Tashkent' 11 | PHP_IDE_CONFIG: "serverName=localhost" 12 | PHP_MEMORY_LIMIT: "2048M" 13 | volumes: 14 | - '.:/var/www/' 15 | - './.docker/web.loc/supervisord.conf:/etc/supervisord.conf' 16 | - './.docker/web.loc/nginx.conf:/etc/nginx/nginx.conf' 17 | - './.docker/web.loc/app.conf:/etc/nginx/conf.d/default.conf' 18 | - './.docker/web.loc/php-fpm.conf:/usr/local/etc/php-fpm.d/www.conf' 19 | - '/etc/docker/laravel-api/uploads:/var/www/storage/app/public/uploads' 20 | extra_hosts: 21 | - 'host.docker.internal:host-gateway' 22 | ports: 23 | - '${APP_PORT:-80}:80' 24 | networks: 25 | - 'laravel-api-network' 26 | depends_on: 27 | - mariadb 28 | mariadb: 29 | container_name: laravel-api-database 30 | restart: always 31 | image: 'mariadb:10' 32 | ports: 33 | - '${FORWARD_DB_PORT:-3307}:3306' 34 | environment: 35 | MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' 36 | MYSQL_DATABASE: '${DB_DATABASE}' 37 | MYSQL_USER: '${DB_USERNAME}' 38 | MYSQL_PASSWORD: '${DB_PASSWORD}' 39 | MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' 40 | TZ: 'Asia/Tashkent' 41 | volumes: 42 | - '${DATABASE_PATH:-/etc/docker/laravel-api/laravel-api-database:/var/lib/mysql}' 43 | networks: 44 | - 'laravel-api-network' 45 | healthcheck: 46 | test: [ "CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}" ] 47 | retries: 3 48 | timeout: 5s 49 | networks: 50 | laravel-api-network: 51 | driver: bridge 52 | volumes: 53 | laravel-api-database: 54 | driver: local 55 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | singleton( 30 | Illuminate\Contracts\Http\Kernel::class, 31 | App\Http\Kernel::class 32 | ); 33 | 34 | $app->singleton( 35 | Illuminate\Contracts\Console\Kernel::class, 36 | App\Console\Kernel::class 37 | ); 38 | 39 | $app->singleton( 40 | Illuminate\Contracts\Debug\ExceptionHandler::class, 41 | App\Exceptions\Handler::class 42 | ); 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Return The Application 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This script returns the application instance. The instance is given to 50 | | the calling script so we can separate the building of the instances 51 | | from the actual running of the application and sending responses. 52 | | 53 | */ 54 | 55 | return $app; 56 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # For more information: https://laravel.com/docs/sail 2 | version: '3' 3 | services: 4 | laravel.test: 5 | build: 6 | context: ./vendor/laravel/sail/runtimes/8.1 7 | dockerfile: Dockerfile 8 | args: 9 | WWWGROUP: '${WWWGROUP}' 10 | image: sail-8.1/app 11 | ports: 12 | - '${APP_PORT:-80}:80' 13 | environment: 14 | WWWUSER: '${WWWUSER}' 15 | LARAVEL_SAIL: 1 16 | volumes: 17 | - '.:/var/www/html' 18 | networks: 19 | - sail 20 | depends_on: 21 | - mariadb 22 | - rediska 23 | mariadb: 24 | image: 'mariadb:10' 25 | ports: 26 | - '${FORWARD_DB_PORT:-3306}:3306' 27 | environment: 28 | MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' 29 | MYSQL_DATABASE: '${DB_DATABASE}' 30 | MYSQL_USER: '${DB_USERNAME}' 31 | MYSQL_PASSWORD: '${DB_PASSWORD}' 32 | MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' 33 | volumes: 34 | - 'sailmariadb:/var/lib/mysql' 35 | networks: 36 | - sail 37 | healthcheck: 38 | test: [ "CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}" ] 39 | retries: 3 40 | timeout: 5s 41 | rediska: 42 | image: 'redis:alpine' 43 | ports: 44 | - '${FORWARD_REDIS_PORT:-6379}:6379' 45 | volumes: 46 | - 'sailredis:/data' 47 | networks: 48 | - sail 49 | healthcheck: 50 | test: [ "CMD", "redis-cli", "ping" ] 51 | retries: 3 52 | timeout: 5s 53 | networks: 54 | sail: 55 | driver: bridge 56 | volumes: 57 | sailmariadb: 58 | driver: local 59 | sailredis: 60 | driver: local 61 | -------------------------------------------------------------------------------- /app/Http/Controllers/AuthController.php: -------------------------------------------------------------------------------- 1 | service->register($request); 22 | 23 | return $this->responseWith(compact('result'), 201); 24 | } 25 | 26 | public function login(LoginRequest $request): JsonResponse 27 | { 28 | try { 29 | $result = $this->service->login($request); 30 | 31 | return $this->responseWith(compact('result')); 32 | } catch (\Exception $e) { 33 | return $this->responseWith(code: $e->getCode(), message: $e->getMessage()); 34 | } 35 | } 36 | 37 | public function refresh(Request $request) 38 | { 39 | try { 40 | $result = $this->service->refresh($request); 41 | 42 | return $this->responseWith(compact('result')); 43 | } catch (\Exception $e) { 44 | return $this->responseWith(code: $e->getCode(), message: $e->getMessage()); 45 | } 46 | } 47 | 48 | public function logout(Request $request): JsonResponse 49 | { 50 | try { 51 | $this->service->logout($request); 52 | 53 | return $this->responseWith(message: __('messages.logout')); 54 | } catch (\Exception $e) { 55 | return $this->responseWith(code: $e->getCode(), message: $e->getMessage()); 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /app/Core/Traits/Responsable.php: -------------------------------------------------------------------------------- 1 | message = empty($message) ? Response::$statusTexts[$code] : $message; 42 | $this->data = $data; 43 | $this->statusCode = match ($code) { 44 | 200 => 200, 45 | 204 => 204, 46 | 201 => 201, 47 | 401 => 401, 48 | 403 => 403, 49 | 404 => 404, 50 | 422 => 422, 51 | 500, 0 => 500, 52 | }; 53 | 54 | if ($logging) { 55 | event(new LoggerEvent(response: $this->data, response_status: $this->statusCode, 56 | response_message: $this->message)); 57 | } 58 | 59 | return $this->response(); 60 | } 61 | 62 | /** 63 | * Send response 64 | */ 65 | private function response(): JsonResponse 66 | { 67 | $data = ['code' => $this->statusCode, 'message' => $this->message, 'data' => $this->data]; 68 | 69 | return response()->json($data, $this->statusCode); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | make(Illuminate\Contracts\Console\Kernel::class); 34 | 35 | $status = $kernel->handle( 36 | $input = new Symfony\Component\Console\Input\ArgvInput, 37 | new Symfony\Component\Console\Output\ConsoleOutput 38 | ); 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Shutdown The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once Artisan has finished running, we will fire off the shutdown events 46 | | so that any final work may be done by the application before we shut 47 | | down the process. This is the last thing to happen to the request. 48 | | 49 | */ 50 | 51 | $kernel->terminate($input, $status); 52 | 53 | exit($status); 54 | -------------------------------------------------------------------------------- /docker-compose-prod.yaml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | web: 4 | container_name: laravel-api-web 5 | restart: always 6 | build: 7 | context: . 8 | dockerfile: ./.docker/web/Dockerfile 9 | environment: 10 | TZ: 'Asia/Tashkent' 11 | ports: 12 | - '443:443' 13 | volumes: 14 | - '/etc/letsencrypt:/etc/letsencrypt' 15 | - '/etc/docker/laravel-api/uploads:/var/www/storage/app/public/uploads' 16 | networks: 17 | - 'laravel-api-network' 18 | depends_on: 19 | - mariadb 20 | - redis 21 | mariadb: 22 | container_name: laravel-api-database 23 | restart: always 24 | image: 'mariadb:10' 25 | ports: 26 | - '${FORWARD_DB_PORT:-3307}:3306' 27 | environment: 28 | MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' 29 | MYSQL_DATABASE: '${DB_DATABASE}' 30 | MYSQL_USER: '${DB_USERNAME}' 31 | MYSQL_PASSWORD: '${DB_PASSWORD}' 32 | MYSQL_ALLOW_EMPTY_PASSWORD: 'no' 33 | TZ: 'Asia/Tashkent' 34 | volumes: 35 | - '/etc/docker/laravel-api/laravel-api-database:/var/lib/mysql' 36 | networks: 37 | - 'laravel-api-network' 38 | healthcheck: 39 | test: [ "CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}" ] 40 | retries: 3 41 | timeout: 5s 42 | redis: 43 | container_name: laravel-api-redis 44 | restart: always 45 | image: 'redis:alpine' 46 | ports: 47 | - '${FORWARD_REDIS_PORT:-6380}:6379' 48 | environment: 49 | TZ: 'Asia/Tashkent' 50 | volumes: 51 | - '/etc/docker/laravel-api/laravel-api-redis:/data' 52 | networks: 53 | - 'laravel-api-network' 54 | healthcheck: 55 | test: [ "CMD", "redis-cli", "ping" ] 56 | retries: 3 57 | timeout: 5s 58 | networks: 59 | laravel-api-network: 60 | driver: bridge 61 | volumes: 62 | laravel-api-database: 63 | driver: local 64 | laravel-api-redis: 65 | driver: local 66 | -------------------------------------------------------------------------------- /app/Core/Macros/EloquentQuery.php: -------------------------------------------------------------------------------- 1 | make(request()->all(), [ 12 | // //config('laravel_api.request.columns', 'columns') => 'string', 13 | // //config('laravel_api.request.relations', 'relations') => 'string', 14 | // //config('laravel_api.request.only_deleted', 'only_deleted') => ['bool', 15 | // // function ($attribute, $value, $fail) { 16 | // // if (!hasPermission('system')) { 17 | // // $fail(__('messages.you_havnt_permission')); 18 | // // } 19 | // // }], 20 | //]); 21 | // 22 | //if ($validator->fails()) { 23 | // throw ValidationException::withMessages($validator->messages()->toArray()); 24 | //} 25 | //$trashed = $trashed ?? request(config('laravel_api.request.only_deleted', 'only_deleted'), false); 26 | 27 | $requestColumns = request(config('laravel_api.request.columns', 'columns'), ['*']); 28 | 29 | if (is_string($requestColumns)) { 30 | $requestColumns = explode(',', $requestColumns); 31 | } 32 | 33 | $columns = $columns ?? $requestColumns; 34 | 35 | 36 | return ($query ?? $this) 37 | ->select($columns); 38 | //->when($trashed, fn($query) => $query->onlyTrashed()) 39 | }); 40 | -------------------------------------------------------------------------------- /app/Core/Services/CoreService.php: -------------------------------------------------------------------------------- 1 | repository->index(query: $query); 21 | } 22 | 23 | public function indexDb( 24 | QueryBuilder $query 25 | ): mixed { 26 | return $this->repository->indexDb(query: $query); 27 | } 28 | 29 | public function dbFirstBy( 30 | QueryBuilder $query, 31 | mixed $value, 32 | string $column = 'id', 33 | ) { 34 | return $this->repository->dbFirstBy($query, $value, $column); 35 | } 36 | 37 | public function show(Model|int $model, Builder|Relation $query = null): ?Model 38 | { 39 | return $this->repository->show($model, query: $query); 40 | } 41 | 42 | public function create(FormRequest|Validator $request): mixed 43 | { 44 | return $this->repository->create($request->validated()); 45 | } 46 | 47 | public function update(Model|int $model, FormRequest|Validator $request): bool 48 | { 49 | $model = $this->repository->show($model); 50 | $this->repository->update($model, $request->validated()); 51 | 52 | return true; 53 | } 54 | 55 | public function delete(Model|int $model): bool 56 | { 57 | $model = $this->repository->show($model); 58 | 59 | return $this->repository->delete($model); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/Core/Http/Requests/GetAllFilteredRecordsRequest.php: -------------------------------------------------------------------------------- 1 | 'in:pagination,collection', 15 | 'columns' => 'array', 16 | 'relations' => 'array', 17 | 'appends' => 'array', 18 | 'limit' => [function ($attribute, $value, $fail) { 19 | is_numeric($value) || $value === 'all' ? : $fail("$attribute must be numeric or 'all'"); 20 | }], 21 | 'per_page' => 'integer', 22 | 'is_active' => 'boolean', 23 | 'search' => 'nullable|string', 24 | 'search_by' => 'nullable|array', 25 | 'conditions' => 'array', 26 | 'not_conditions' => 'array', 27 | 'or_conditions' => 'array', 28 | 'pluck' => !is_array($this->get('pluck')) ? 'string' : "array|required_array_keys:column", 29 | 'only_deleted' => ['bool', 30 | function ($attribute, $value, $fail) { 31 | if (!hasPermission('system')) { 32 | $fail(__('messages.you_havnt_permission')); 33 | } 34 | }], 35 | 'order' => 'string', 36 | 'sort' => 'in:desc,asc', 37 | ]; 38 | } 39 | 40 | /** 41 | * Determine if the user is authorized to make this request. 42 | */ 43 | public function authorize(): bool 44 | { 45 | return true; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class); 50 | 51 | $response = tap($kernel->handle( 52 | $request = Request::capture() 53 | ))->send(); 54 | 55 | $kernel->terminate($request, $response); 56 | --------------------------------------------------------------------------------