├── .gitignore ├── README.md ├── api ├── .editorconfig ├── .env.example ├── .gitattributes ├── .gitignore ├── .phpactor.json ├── README.md ├── app │ ├── Console │ │ └── Commands │ │ │ └── BustCacheCommand.php │ ├── Enums │ │ └── Cache │ │ │ ├── CacheKey.php │ │ │ └── CacheTtl.php │ ├── Http │ │ ├── Controllers │ │ │ ├── Api │ │ │ │ ├── Auth │ │ │ │ │ └── UserController.php │ │ │ │ └── Users │ │ │ │ │ └── IndexController.php │ │ │ ├── Auth │ │ │ │ ├── AuthenticatedSessionController.php │ │ │ │ ├── EmailVerificationNotificationController.php │ │ │ │ ├── NewPasswordController.php │ │ │ │ ├── PasswordResetLinkController.php │ │ │ │ ├── RegisteredUserController.php │ │ │ │ └── VerifyEmailController.php │ │ │ └── Controller.php │ │ ├── Factories │ │ │ └── HeaderFactory.php │ │ ├── Middleware │ │ │ └── EnsureEmailIsVerified.php │ │ ├── Requests │ │ │ └── Auth │ │ │ │ └── LoginRequest.php │ │ ├── Resources │ │ │ ├── DateResource.php │ │ │ ├── PermissionResource.php │ │ │ ├── RoleResource.php │ │ │ └── UserResource.php │ │ └── Responses │ │ │ └── CollectionResponse.php │ ├── Models │ │ ├── Permission.php │ │ ├── Role.php │ │ └── User.php │ ├── Providers │ │ └── AppServiceProvider.php │ └── Services │ │ ├── CacheService.php │ │ └── UserService.php ├── artisan ├── bootstrap │ ├── app.php │ ├── cache │ │ └── .gitignore │ └── providers.php ├── composer.json ├── composer.lock ├── config │ ├── app.php │ ├── auth.php │ ├── cache.php │ ├── cors.php │ ├── database.php │ ├── filesystems.php │ ├── logging.php │ ├── mail.php │ ├── queue.php │ ├── sanctum.php │ ├── services.php │ └── session.php ├── database │ ├── .gitignore │ ├── factories │ │ └── UserFactory.php │ ├── migrations │ │ ├── 0001_01_01_000000_create_users_table.php │ │ ├── 0001_01_01_000001_create_cache_table.php │ │ ├── 0001_01_01_000002_create_jobs_table.php │ │ ├── 2014_10_12_200000_create_authorization_tables.php │ │ └── 2024_05_01_173951_create_personal_access_tokens_table.php │ └── seeders │ │ └── DatabaseSeeder.php ├── phpstan.neon ├── phpunit.xml ├── pint.json ├── public │ ├── .htaccess │ ├── favicon.ico │ ├── index.php │ └── robots.txt ├── resources │ └── views │ │ └── .gitkeep ├── routes │ ├── api.php │ ├── auth.php │ ├── console.php │ └── web.php ├── storage │ ├── app │ │ ├── .gitignore │ │ └── public │ │ │ └── .gitignore │ ├── framework │ │ ├── .gitignore │ │ ├── cache │ │ │ ├── .gitignore │ │ │ └── data │ │ │ │ └── .gitignore │ │ ├── sessions │ │ │ └── .gitignore │ │ ├── testing │ │ │ └── .gitignore │ │ └── views │ │ │ └── .gitignore │ └── logs │ │ └── .gitignore └── tests │ ├── Feature │ ├── Auth │ │ ├── AuthenticationTest.php │ │ ├── EmailVerificationTest.php │ │ ├── PasswordResetTest.php │ │ └── RegistrationTest.php │ └── ExampleTest.php │ ├── Pest.php │ ├── TestCase.php │ └── Unit │ └── ExampleTest.php └── web ├── .gitignore ├── .npmrc ├── README.md ├── app.config.ts ├── app.vue ├── layouts ├── default.vue └── guest.vue ├── nuxt.config.ts ├── package.json ├── pages ├── auth │ └── login.vue ├── index.vue └── users │ └── index.vue ├── pnpm-lock.yaml ├── public └── favicon.ico ├── server └── tsconfig.json ├── tailwind.config.js └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Nuxt 2 | 3 | This repo contains the source code for a template project using Laravel PHP alongside NuxtJS. 4 | -------------------------------------------------------------------------------- /api/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml,json}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /api/.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Laravel 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_TIMEZONE=UTC 6 | APP_URL=http://localhost 7 | 8 | APP_LOCALE=en 9 | APP_FALLBACK_LOCALE=en 10 | APP_FAKER_LOCALE=en_US 11 | 12 | APP_MAINTENANCE_DRIVER=file 13 | APP_MAINTENANCE_STORE=database 14 | 15 | BCRYPT_ROUNDS=12 16 | 17 | LOG_CHANNEL=stack 18 | LOG_STACK=single 19 | LOG_DEPRECATIONS_CHANNEL=null 20 | LOG_LEVEL=debug 21 | 22 | DB_CONNECTION=pgsql 23 | DB_HOST=127.0.0.1 24 | DB_PORT=5432 25 | DB_DATABASE=api 26 | DB_USERNAME=root 27 | DB_PASSWORD= 28 | 29 | SESSION_DRIVER=database 30 | SESSION_LIFETIME=120 31 | SESSION_ENCRYPT=false 32 | SESSION_PATH=/ 33 | SESSION_DOMAIN=null 34 | 35 | BROADCAST_CONNECTION=log 36 | FILESYSTEM_DISK=local 37 | QUEUE_CONNECTION=database 38 | 39 | CACHE_STORE=database 40 | CACHE_PREFIX= 41 | 42 | MEMCACHED_HOST=127.0.0.1 43 | 44 | REDIS_CLIENT=phpredis 45 | REDIS_HOST=127.0.0.1 46 | REDIS_PASSWORD=null 47 | REDIS_PORT=6379 48 | 49 | MAIL_MAILER=log 50 | MAIL_HOST=127.0.0.1 51 | MAIL_PORT=2525 52 | MAIL_USERNAME=null 53 | MAIL_PASSWORD=null 54 | MAIL_ENCRYPTION=null 55 | MAIL_FROM_ADDRESS="hello@example.com" 56 | MAIL_FROM_NAME="${APP_NAME}" 57 | 58 | AWS_ACCESS_KEY_ID= 59 | AWS_SECRET_ACCESS_KEY= 60 | AWS_DEFAULT_REGION=us-east-1 61 | AWS_BUCKET= 62 | AWS_USE_PATH_STYLE_ENDPOINT=false 63 | 64 | VITE_APP_NAME="${APP_NAME}" 65 | -------------------------------------------------------------------------------- /api/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.blade.php diff=html 4 | *.css diff=css 5 | *.html diff=html 6 | *.md diff=markdown 7 | *.php diff=php 8 | 9 | /.github export-ignore 10 | CHANGELOG.md export-ignore 11 | .styleci.yml export-ignore 12 | -------------------------------------------------------------------------------- /api/.gitignore: -------------------------------------------------------------------------------- 1 | /.phpunit.cache 2 | /node_modules 3 | /public/build 4 | /public/hot 5 | /public/storage 6 | /storage/*.key 7 | /vendor 8 | .env 9 | .env.backup 10 | .env.production 11 | .phpunit.result.cache 12 | Homestead.json 13 | Homestead.yaml 14 | auth.json 15 | npm-debug.log 16 | yarn-error.log 17 | /.fleet 18 | /.idea 19 | /.vscode 20 | -------------------------------------------------------------------------------- /api/.phpactor.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "/Applications/Tinkerwell.app/Contents/Resources/phpactor/phpactor.schema.json", 3 | "language_server_phpstan.enabled": false 4 | } -------------------------------------------------------------------------------- /api/README.md: -------------------------------------------------------------------------------- 1 |

Laravel Logo

2 | 3 |

4 | Build Status 5 | Total Downloads 6 | Latest Stable Version 7 | License 8 |

9 | 10 | ## About Laravel 11 | 12 | Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: 13 | 14 | - [Simple, fast routing engine](https://laravel.com/docs/routing). 15 | - [Powerful dependency injection container](https://laravel.com/docs/container). 16 | - Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. 17 | - Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). 18 | - Database agnostic [schema migrations](https://laravel.com/docs/migrations). 19 | - [Robust background job processing](https://laravel.com/docs/queues). 20 | - [Real-time event broadcasting](https://laravel.com/docs/broadcasting). 21 | 22 | Laravel is accessible, powerful, and provides tools required for large, robust applications. 23 | 24 | ## Learning Laravel 25 | 26 | Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. 27 | 28 | You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch. 29 | 30 | If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. 31 | 32 | ## Laravel Sponsors 33 | 34 | We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com). 35 | 36 | ### Premium Partners 37 | 38 | - **[Vehikl](https://vehikl.com/)** 39 | - **[Tighten Co.](https://tighten.co)** 40 | - **[WebReinvent](https://webreinvent.com/)** 41 | - **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)** 42 | - **[64 Robots](https://64robots.com)** 43 | - **[Curotec](https://www.curotec.com/services/technologies/laravel/)** 44 | - **[Cyber-Duck](https://cyber-duck.co.uk)** 45 | - **[DevSquad](https://devsquad.com/hire-laravel-developers)** 46 | - **[Jump24](https://jump24.co.uk)** 47 | - **[Redberry](https://redberry.international/laravel/)** 48 | - **[Active Logic](https://activelogic.com)** 49 | - **[byte5](https://byte5.de)** 50 | - **[OP.GG](https://op.gg)** 51 | 52 | ## Contributing 53 | 54 | Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). 55 | 56 | ## Code of Conduct 57 | 58 | In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). 59 | 60 | ## Security Vulnerabilities 61 | 62 | If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. 63 | 64 | ## License 65 | 66 | The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). 67 | -------------------------------------------------------------------------------- /api/app/Console/Commands/BustCacheCommand.php: -------------------------------------------------------------------------------- 1 | forget($key->value); 20 | } 21 | 22 | return SymfonyCommand::SUCCESS; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /api/app/Enums/Cache/CacheKey.php: -------------------------------------------------------------------------------- 1 | with( 24 | relations: ['roles.permissions'], 25 | )->where( 26 | column: 'id', 27 | operator: '=', 28 | value: $this->auth->id(), 29 | )->firstOrFail(), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api/app/Http/Controllers/Api/Users/IndexController.php: -------------------------------------------------------------------------------- 1 | service->all(), 25 | ) 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /api/app/Http/Controllers/Auth/AuthenticatedSessionController.php: -------------------------------------------------------------------------------- 1 | authenticate(); 21 | 22 | $request->session()->regenerate(); 23 | 24 | return response()->noContent(); 25 | } 26 | 27 | /** 28 | * Destroy an authenticated session. 29 | */ 30 | public function destroy(Request $request): Response 31 | { 32 | Auth::guard('web')->logout(); 33 | 34 | $request->session()->invalidate(); 35 | 36 | $request->session()->regenerateToken(); 37 | 38 | return response()->noContent(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /api/app/Http/Controllers/Auth/EmailVerificationNotificationController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 20 | return redirect()->intended('/dashboard'); 21 | } 22 | 23 | $request->user()->sendEmailVerificationNotification(); 24 | 25 | return response()->json(['status' => 'verification-link-sent']); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /api/app/Http/Controllers/Auth/NewPasswordController.php: -------------------------------------------------------------------------------- 1 | validate([ 27 | 'token' => ['required'], 28 | 'email' => ['required', 'email'], 29 | 'password' => ['required', 'confirmed', Rules\Password::defaults()], 30 | ]); 31 | 32 | // Here we will attempt to reset the user's password. If it is successful we 33 | // will update the password on an actual user model and persist it to the 34 | // database. Otherwise we will parse the error and return the response. 35 | $status = Password::reset( 36 | $request->only('email', 'password', 'password_confirmation', 'token'), 37 | function ($user) use ($request): void { 38 | $user->forceFill([ 39 | 'password' => Hash::make($request->password), 40 | 'remember_token' => Str::random(60), 41 | ])->save(); 42 | 43 | event(new PasswordReset($user)); 44 | }, 45 | ); 46 | 47 | if (Password::PASSWORD_RESET !== $status) { 48 | throw ValidationException::withMessages([ 49 | 'email' => [__($status)], 50 | ]); 51 | } 52 | 53 | return response()->json(['status' => __($status)]); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /api/app/Http/Controllers/Auth/PasswordResetLinkController.php: -------------------------------------------------------------------------------- 1 | validate([ 23 | 'email' => ['required', 'email'], 24 | ]); 25 | 26 | // We will send the password reset link to this user. Once we have attempted 27 | // to send the link, we will examine the response then see the message we 28 | // need to show to the user. Finally, we'll send out a proper response. 29 | $status = Password::sendResetLink( 30 | $request->only('email'), 31 | ); 32 | 33 | if (Password::RESET_LINK_SENT !== $status) { 34 | throw ValidationException::withMessages([ 35 | 'email' => [__($status)], 36 | ]); 37 | } 38 | 39 | return response()->json(['status' => __($status)]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /api/app/Http/Controllers/Auth/RegisteredUserController.php: -------------------------------------------------------------------------------- 1 | validate([ 26 | 'name' => ['required', 'string', 'max:255'], 27 | 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:' . User::class], 28 | 'password' => ['required', 'confirmed', Rules\Password::defaults()], 29 | ]); 30 | 31 | $user = User::create([ 32 | 'name' => $request->name, 33 | 'email' => $request->email, 34 | 'password' => Hash::make($request->password), 35 | ]); 36 | 37 | event(new Registered($user)); 38 | 39 | Auth::login($user); 40 | 41 | return response()->noContent(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /api/app/Http/Controllers/Auth/VerifyEmailController.php: -------------------------------------------------------------------------------- 1 | user()->hasVerifiedEmail()) { 20 | return redirect()->intended( 21 | config('app.frontend_url') . '/dashboard?verified=1', 22 | ); 23 | } 24 | 25 | if ($request->user()->markEmailAsVerified()) { 26 | event(new Verified($request->user())); 27 | } 28 | 29 | return redirect()->intended( 30 | config('app.frontend_url') . '/dashboard?verified=1', 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /api/app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | 'application/json', 13 | 'Accept' => 'application/json', 14 | 'X-CUSTOM-HEADER' => 'boop', 15 | ]; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /api/app/Http/Middleware/EnsureEmailIsVerified.php: -------------------------------------------------------------------------------- 1 | user() || 22 | ($request->user() instanceof MustVerifyEmail && 23 | ! $request->user()->hasVerifiedEmail())) { 24 | return response()->json(['message' => 'Your email address is not verified.'], 409); 25 | } 26 | 27 | return $next($request); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /api/app/Http/Requests/Auth/LoginRequest.php: -------------------------------------------------------------------------------- 1 | 28 | */ 29 | public function rules(): array 30 | { 31 | return [ 32 | 'email' => ['required', 'string', 'email'], 33 | 'password' => ['required', 'string'], 34 | ]; 35 | } 36 | 37 | /** 38 | * Attempt to authenticate the request's credentials. 39 | * 40 | * @throws ValidationException 41 | */ 42 | public function authenticate(): void 43 | { 44 | $this->ensureIsNotRateLimited(); 45 | 46 | if ( ! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) { 47 | RateLimiter::hit($this->throttleKey()); 48 | 49 | throw ValidationException::withMessages([ 50 | 'email' => __('auth.failed'), 51 | ]); 52 | } 53 | 54 | RateLimiter::clear($this->throttleKey()); 55 | } 56 | 57 | /** 58 | * Ensure the login request is not rate limited. 59 | * 60 | * @throws ValidationException 61 | */ 62 | public function ensureIsNotRateLimited(): void 63 | { 64 | if ( ! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { 65 | return; 66 | } 67 | 68 | event(new Lockout($this)); 69 | 70 | $seconds = RateLimiter::availableIn($this->throttleKey()); 71 | 72 | throw ValidationException::withMessages([ 73 | 'email' => trans('auth.throttle', [ 74 | 'seconds' => $seconds, 75 | 'minutes' => ceil($seconds / 60), 76 | ]), 77 | ]); 78 | } 79 | 80 | /** 81 | * Get the rate limiting throttle key for the request. 82 | */ 83 | public function throttleKey(): string 84 | { 85 | return Str::transliterate(Str::lower($this->input('email')) . '|' . $this->ip()); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /api/app/Http/Resources/DateResource.php: -------------------------------------------------------------------------------- 1 | $this->resource->diffForHumans(), 20 | 'string' => $this->resource->toDateTimeString(), 21 | 'local' => $this->resource->toDateTimeLocalString(), 22 | 'timestamp' => $this->resource->timestamp, 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /api/app/Http/Resources/PermissionResource.php: -------------------------------------------------------------------------------- 1 | $this->resource->id, 20 | 'name' => $this->resource->name, 21 | 'label' => $this->resource->label, 22 | ]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /api/app/Http/Resources/RoleResource.php: -------------------------------------------------------------------------------- 1 | $this->resource->id, 20 | 'name' => $this->resource->name, 21 | 'label' => $this->resource->label, 22 | 'permission' => PermissionResource::collection( 23 | resource: $this->whenLoaded( 24 | relationship: 'permissions', 25 | ), 26 | ), 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /api/app/Http/Resources/UserResource.php: -------------------------------------------------------------------------------- 1 | $this->resource->id, 21 | 'name' => $this->resource->name, 22 | 'email' => $this->resource->email, 23 | 'roles' => RoleResource::collection( 24 | resource: $this->whenLoaded( 25 | relationship: 'roles', 26 | ), 27 | ), 28 | 'created' => new DateResource( 29 | resource: $this->resource->created_at, 30 | ), 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /api/app/Http/Responses/CollectionResponse.php: -------------------------------------------------------------------------------- 1 | data, 24 | headers: HeaderFactory::default(), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /api/app/Models/Permission.php: -------------------------------------------------------------------------------- 1 | $users 22 | * @property Collection $roles 23 | */ 24 | final class Permission extends DirectoryPermission 25 | { 26 | use ClearsCachedPermissions; 27 | use HasRoles; 28 | use HasUsers; 29 | use HasUuids; 30 | } 31 | -------------------------------------------------------------------------------- /api/app/Models/Role.php: -------------------------------------------------------------------------------- 1 | $users 20 | * @property Collection $permissions 21 | */ 22 | final class Role extends DirectoryRole 23 | { 24 | use HasUuids; 25 | use ManagesPermissions; 26 | } 27 | -------------------------------------------------------------------------------- /api/app/Models/User.php: -------------------------------------------------------------------------------- 1 | $tokens 30 | * @property Collection $roles 31 | * @property Collection $permissions 32 | */ 33 | final class User extends Authenticatable implements MustVerifyEmail 34 | { 35 | use Authorizable; 36 | use HasApiTokens; 37 | use HasFactory; 38 | use HasUuids; 39 | use Notifiable; 40 | use SoftDeletes; 41 | 42 | /** @var array */ 43 | protected $fillable = [ 44 | 'name', 45 | 'email', 46 | 'password', 47 | 'remember_token', 48 | 'email_verified_at', 49 | ]; 50 | 51 | /** @var array */ 52 | protected $hidden = [ 53 | 'password', 54 | 'remember_token', 55 | ]; 56 | 57 | /** @return array */ 58 | protected function casts(): array 59 | { 60 | return [ 61 | 'email_verified_at' => 'datetime', 62 | 'password' => 'hashed', 63 | ]; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /api/app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | config('app.frontend_url') . "/password-reset/{$token}?email={$notifiable->getEmailForPasswordReset()}"); 25 | 26 | Authorization::useUserModel(User::class); 27 | Authorization::useRoleModel(Role::class); 28 | Authorization::usePermissionModel(Permission::class); 29 | 30 | Authorization::cacheKey('auth:'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api/app/Services/CacheService.php: -------------------------------------------------------------------------------- 1 | cache->remember( 22 | key: $key->value, 23 | ttl: $ttl->value, 24 | callback: $callback, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /api/app/Services/UserService.php: -------------------------------------------------------------------------------- 1 | cache->remember( 25 | key: CacheKey::UsersAll, 26 | ttl: CacheTtl::TenMinutes, 27 | callback: static fn () => User::query()->get(), 28 | ); 29 | } 30 | 31 | public function create(array $data): User|Model 32 | { 33 | return $this->database->transaction( 34 | callback: fn () => User::query()->create($data), 35 | attempts: 3, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /api/artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | handleCommand(new ArgvInput); 14 | 15 | exit($status); 16 | -------------------------------------------------------------------------------- /api/bootstrap/app.php: -------------------------------------------------------------------------------- 1 | withRouting( 15 | web: __DIR__ . '/../routes/web.php', 16 | api: __DIR__ . '/../routes/api.php', 17 | commands: __DIR__ . '/../routes/console.php', 18 | health: '/up', 19 | ) 20 | ->withMiddleware(function (Middleware $middleware): void { 21 | $middleware->api(prepend: [ 22 | EnsureFrontendRequestsAreStateful::class, 23 | ]); 24 | 25 | $middleware->alias([ 26 | 'role' => RoleMiddleware::class, 27 | 'permission' => PermissionMiddleware::class, 28 | 'verified' => EnsureEmailIsVerified::class, 29 | ]); 30 | 31 | 32 | }) 33 | ->withExceptions(function (Exceptions $exceptions): void {})->create(); 34 | -------------------------------------------------------------------------------- /api/bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /api/bootstrap/providers.php: -------------------------------------------------------------------------------- 1 | env('APP_NAME', 'Laravel'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Application Environment 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This value determines the "environment" your application is currently 26 | | running in. This may determine how you prefer to configure various 27 | | services the application utilizes. Set this in your ".env" file. 28 | | 29 | */ 30 | 31 | 'env' => env('APP_ENV', 'production'), 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Application Debug Mode 36 | |-------------------------------------------------------------------------- 37 | | 38 | | When your application is in debug mode, detailed error messages with 39 | | stack traces will be shown on every error that occurs within your 40 | | application. If disabled, a simple generic error page is shown. 41 | | 42 | */ 43 | 44 | 'debug' => (bool) env('APP_DEBUG', false), 45 | 46 | /* 47 | |-------------------------------------------------------------------------- 48 | | Application URL 49 | |-------------------------------------------------------------------------- 50 | | 51 | | This URL is used by the console to properly generate URLs when using 52 | | the Artisan command line tool. You should set this to the root of 53 | | the application so that it's available within Artisan commands. 54 | | 55 | */ 56 | 57 | 'url' => env('APP_URL', 'http://localhost'), 58 | 59 | /* 60 | |-------------------------------------------------------------------------- 61 | | Application Timezone 62 | |-------------------------------------------------------------------------- 63 | | 64 | | Here you may specify the default timezone for your application, which 65 | | will be used by the PHP date and date-time functions. The timezone 66 | | is set to "UTC" by default as it is suitable for most use cases. 67 | | 68 | */ 69 | 70 | 'timezone' => env('APP_TIMEZONE', 'UTC'), 71 | 72 | /* 73 | |-------------------------------------------------------------------------- 74 | | Application Locale Configuration 75 | |-------------------------------------------------------------------------- 76 | | 77 | | The application locale determines the default locale that will be used 78 | | by Laravel's translation / localization methods. This option can be 79 | | set to any locale for which you plan to have translation strings. 80 | | 81 | */ 82 | 83 | 'locale' => env('APP_LOCALE', 'en'), 84 | 85 | 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), 86 | 87 | 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), 88 | 89 | /* 90 | |-------------------------------------------------------------------------- 91 | | Encryption Key 92 | |-------------------------------------------------------------------------- 93 | | 94 | | This key is utilized by Laravel's encryption services and should be set 95 | | to a random, 32 character string to ensure that all encrypted values 96 | | are secure. You should do this prior to deploying the application. 97 | | 98 | */ 99 | 100 | 'cipher' => 'AES-256-CBC', 101 | 102 | 'key' => env('APP_KEY'), 103 | 104 | 'previous_keys' => [ 105 | ...array_filter( 106 | explode(',', env('APP_PREVIOUS_KEYS', '')), 107 | ), 108 | ], 109 | 110 | /* 111 | |-------------------------------------------------------------------------- 112 | | Maintenance Mode Driver 113 | |-------------------------------------------------------------------------- 114 | | 115 | | These configuration options determine the driver used to determine and 116 | | manage Laravel's "maintenance mode" status. The "cache" driver will 117 | | allow maintenance mode to be controlled across multiple machines. 118 | | 119 | | Supported drivers: "file", "cache" 120 | | 121 | */ 122 | 123 | 'maintenance' => [ 124 | 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), 125 | 'store' => env('APP_MAINTENANCE_STORE', 'database'), 126 | ], 127 | 128 | ]; 129 | -------------------------------------------------------------------------------- /api/config/auth.php: -------------------------------------------------------------------------------- 1 | [ 19 | 'guard' => env('AUTH_GUARD', 'web'), 20 | 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), 21 | ], 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Authentication Guards 26 | |-------------------------------------------------------------------------- 27 | | 28 | | Next, you may define every authentication guard for your application. 29 | | Of course, a great default configuration has been defined for you 30 | | which utilizes session storage plus the Eloquent user provider. 31 | | 32 | | All authentication guards have a user provider, which defines how the 33 | | users are actually retrieved out of your database or other storage 34 | | system used by the application. Typically, Eloquent is utilized. 35 | | 36 | | Supported: "session" 37 | | 38 | */ 39 | 40 | 'guards' => [ 41 | 'web' => [ 42 | 'driver' => 'session', 43 | 'provider' => 'users', 44 | ], 45 | ], 46 | 47 | /* 48 | |-------------------------------------------------------------------------- 49 | | User Providers 50 | |-------------------------------------------------------------------------- 51 | | 52 | | All authentication guards have a user provider, which defines how the 53 | | users are actually retrieved out of your database or other storage 54 | | system used by the application. Typically, Eloquent is utilized. 55 | | 56 | | If you have multiple user tables or models you may configure multiple 57 | | providers to represent the model / table. These providers may then 58 | | be assigned to any extra authentication guards you have defined. 59 | | 60 | | Supported: "database", "eloquent" 61 | | 62 | */ 63 | 64 | 'providers' => [ 65 | 'users' => [ 66 | 'driver' => 'eloquent', 67 | 'model' => env('AUTH_MODEL', App\Models\User::class), 68 | ], 69 | 70 | // 'users' => [ 71 | // 'driver' => 'database', 72 | // 'table' => 'users', 73 | // ], 74 | ], 75 | 76 | /* 77 | |-------------------------------------------------------------------------- 78 | | Resetting Passwords 79 | |-------------------------------------------------------------------------- 80 | | 81 | | These configuration options specify the behavior of Laravel's password 82 | | reset functionality, including the table utilized for token storage 83 | | and the user provider that is invoked to actually retrieve users. 84 | | 85 | | The expiry time is the number of minutes that each reset token will be 86 | | considered valid. This security feature keeps tokens short-lived so 87 | | they have less time to be guessed. You may change this as needed. 88 | | 89 | | The throttle setting is the number of seconds a user must wait before 90 | | generating more password reset tokens. This prevents the user from 91 | | quickly generating a very large amount of password reset tokens. 92 | | 93 | */ 94 | 95 | 'passwords' => [ 96 | 'users' => [ 97 | 'provider' => 'users', 98 | 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), 99 | 'expire' => 60, 100 | 'throttle' => 60, 101 | ], 102 | ], 103 | 104 | /* 105 | |-------------------------------------------------------------------------- 106 | | Password Confirmation Timeout 107 | |-------------------------------------------------------------------------- 108 | | 109 | | Here you may define the amount of seconds before a password confirmation 110 | | window expires and users are asked to re-enter their password via the 111 | | confirmation screen. By default, the timeout lasts for three hours. 112 | | 113 | */ 114 | 115 | 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), 116 | 117 | ]; 118 | -------------------------------------------------------------------------------- /api/config/cache.php: -------------------------------------------------------------------------------- 1 | env('CACHE_STORE', 'database'), 21 | 22 | /* 23 | |-------------------------------------------------------------------------- 24 | | Cache Stores 25 | |-------------------------------------------------------------------------- 26 | | 27 | | Here you may define all of the cache "stores" for your application as 28 | | well as their drivers. You may even define multiple stores for the 29 | | same cache driver to group types of items stored in your caches. 30 | | 31 | | Supported drivers: "apc", "array", "database", "file", "memcached", 32 | | "redis", "dynamodb", "octane", "null" 33 | | 34 | */ 35 | 36 | 'stores' => [ 37 | 38 | 'array' => [ 39 | 'driver' => 'array', 40 | 'serialize' => false, 41 | ], 42 | 43 | 'database' => [ 44 | 'driver' => 'database', 45 | 'table' => env('DB_CACHE_TABLE', 'cache'), 46 | 'connection' => env('DB_CACHE_CONNECTION'), 47 | 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), 48 | ], 49 | 50 | 'file' => [ 51 | 'driver' => 'file', 52 | 'path' => storage_path('framework/cache/data'), 53 | 'lock_path' => storage_path('framework/cache/data'), 54 | ], 55 | 56 | 'memcached' => [ 57 | 'driver' => 'memcached', 58 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), 59 | 'sasl' => [ 60 | env('MEMCACHED_USERNAME'), 61 | env('MEMCACHED_PASSWORD'), 62 | ], 63 | 'options' => [ 64 | // Memcached::OPT_CONNECT_TIMEOUT => 2000, 65 | ], 66 | 'servers' => [ 67 | [ 68 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 69 | 'port' => env('MEMCACHED_PORT', 11211), 70 | 'weight' => 100, 71 | ], 72 | ], 73 | ], 74 | 75 | 'redis' => [ 76 | 'driver' => 'redis', 77 | 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), 78 | 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), 79 | ], 80 | 81 | 'dynamodb' => [ 82 | 'driver' => 'dynamodb', 83 | 'key' => env('AWS_ACCESS_KEY_ID'), 84 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 85 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 86 | 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), 87 | 'endpoint' => env('DYNAMODB_ENDPOINT'), 88 | ], 89 | 90 | 'octane' => [ 91 | 'driver' => 'octane', 92 | ], 93 | 94 | ], 95 | 96 | /* 97 | |-------------------------------------------------------------------------- 98 | | Cache Key Prefix 99 | |-------------------------------------------------------------------------- 100 | | 101 | | When utilizing the APC, database, memcached, Redis, and DynamoDB cache 102 | | stores, there might be other applications using the same cache. For 103 | | that reason, you may prefix every cache key to avoid collisions. 104 | | 105 | */ 106 | 107 | 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_cache_'), 108 | 109 | ]; 110 | -------------------------------------------------------------------------------- /api/config/cors.php: -------------------------------------------------------------------------------- 1 | ['*'], 21 | 22 | 'allowed_methods' => ['*'], 23 | 24 | 'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:3000')], 25 | 26 | 'allowed_origins_patterns' => [], 27 | 28 | 'allowed_headers' => ['*'], 29 | 30 | 'exposed_headers' => [], 31 | 32 | 'max_age' => 0, 33 | 34 | 'supports_credentials' => true, 35 | 36 | ]; 37 | -------------------------------------------------------------------------------- /api/config/database.php: -------------------------------------------------------------------------------- 1 | env('DB_CONNECTION', 'sqlite'), 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Database Connections 26 | |-------------------------------------------------------------------------- 27 | | 28 | | Below are all of the database connections defined for your application. 29 | | An example configuration is provided for each database system which 30 | | is supported by Laravel. You're free to add / remove connections. 31 | | 32 | */ 33 | 34 | 'connections' => [ 35 | 36 | 'sqlite' => [ 37 | 'driver' => 'sqlite', 38 | 'url' => env('DB_URL'), 39 | 'database' => env('DB_DATABASE', database_path('database.sqlite')), 40 | 'prefix' => '', 41 | 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), 42 | ], 43 | 44 | 'mysql' => [ 45 | 'driver' => 'mysql', 46 | 'url' => env('DB_URL'), 47 | 'host' => env('DB_HOST', '127.0.0.1'), 48 | 'port' => env('DB_PORT', '3306'), 49 | 'database' => env('DB_DATABASE', 'laravel'), 50 | 'username' => env('DB_USERNAME', 'root'), 51 | 'password' => env('DB_PASSWORD', ''), 52 | 'unix_socket' => env('DB_SOCKET', ''), 53 | 'charset' => env('DB_CHARSET', 'utf8mb4'), 54 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 55 | 'prefix' => '', 56 | 'prefix_indexes' => true, 57 | 'strict' => true, 58 | 'engine' => null, 59 | 'options' => extension_loaded('pdo_mysql') ? array_filter([ 60 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), 61 | ]) : [], 62 | ], 63 | 64 | 'mariadb' => [ 65 | 'driver' => 'mariadb', 66 | 'url' => env('DB_URL'), 67 | 'host' => env('DB_HOST', '127.0.0.1'), 68 | 'port' => env('DB_PORT', '3306'), 69 | 'database' => env('DB_DATABASE', 'laravel'), 70 | 'username' => env('DB_USERNAME', 'root'), 71 | 'password' => env('DB_PASSWORD', ''), 72 | 'unix_socket' => env('DB_SOCKET', ''), 73 | 'charset' => env('DB_CHARSET', 'utf8mb4'), 74 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 75 | 'prefix' => '', 76 | 'prefix_indexes' => true, 77 | 'strict' => true, 78 | 'engine' => null, 79 | 'options' => extension_loaded('pdo_mysql') ? array_filter([ 80 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), 81 | ]) : [], 82 | ], 83 | 84 | 'pgsql' => [ 85 | 'driver' => 'pgsql', 86 | 'url' => env('DB_URL'), 87 | 'host' => env('DB_HOST', '127.0.0.1'), 88 | 'port' => env('DB_PORT', '5432'), 89 | 'database' => env('DB_DATABASE', 'laravel'), 90 | 'username' => env('DB_USERNAME', 'root'), 91 | 'password' => env('DB_PASSWORD', ''), 92 | 'charset' => env('DB_CHARSET', 'utf8'), 93 | 'prefix' => '', 94 | 'prefix_indexes' => true, 95 | 'search_path' => 'public', 96 | 'sslmode' => 'prefer', 97 | ], 98 | 99 | 'sqlsrv' => [ 100 | 'driver' => 'sqlsrv', 101 | 'url' => env('DB_URL'), 102 | 'host' => env('DB_HOST', 'localhost'), 103 | 'port' => env('DB_PORT', '1433'), 104 | 'database' => env('DB_DATABASE', 'laravel'), 105 | 'username' => env('DB_USERNAME', 'root'), 106 | 'password' => env('DB_PASSWORD', ''), 107 | 'charset' => env('DB_CHARSET', 'utf8'), 108 | 'prefix' => '', 109 | 'prefix_indexes' => true, 110 | // 'encrypt' => env('DB_ENCRYPT', 'yes'), 111 | // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), 112 | ], 113 | 114 | ], 115 | 116 | /* 117 | |-------------------------------------------------------------------------- 118 | | Migration Repository Table 119 | |-------------------------------------------------------------------------- 120 | | 121 | | This table keeps track of all the migrations that have already run for 122 | | your application. Using this information, we can determine which of 123 | | the migrations on disk haven't actually been run on the database. 124 | | 125 | */ 126 | 127 | 'migrations' => [ 128 | 'table' => 'migrations', 129 | 'update_date_on_publish' => true, 130 | ], 131 | 132 | /* 133 | |-------------------------------------------------------------------------- 134 | | Redis Databases 135 | |-------------------------------------------------------------------------- 136 | | 137 | | Redis is an open source, fast, and advanced key-value store that also 138 | | provides a richer body of commands than a typical key-value system 139 | | such as Memcached. You may define your connection settings here. 140 | | 141 | */ 142 | 143 | 'redis' => [ 144 | 145 | 'client' => env('REDIS_CLIENT', 'phpredis'), 146 | 147 | 'options' => [ 148 | 'cluster' => env('REDIS_CLUSTER', 'redis'), 149 | 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_database_'), 150 | ], 151 | 152 | 'default' => [ 153 | 'url' => env('REDIS_URL'), 154 | 'host' => env('REDIS_HOST', '127.0.0.1'), 155 | 'username' => env('REDIS_USERNAME'), 156 | 'password' => env('REDIS_PASSWORD'), 157 | 'port' => env('REDIS_PORT', '6379'), 158 | 'database' => env('REDIS_DB', '0'), 159 | ], 160 | 161 | 'cache' => [ 162 | 'url' => env('REDIS_URL'), 163 | 'host' => env('REDIS_HOST', '127.0.0.1'), 164 | 'username' => env('REDIS_USERNAME'), 165 | 'password' => env('REDIS_PASSWORD'), 166 | 'port' => env('REDIS_PORT', '6379'), 167 | 'database' => env('REDIS_CACHE_DB', '1'), 168 | ], 169 | 170 | ], 171 | 172 | ]; 173 | -------------------------------------------------------------------------------- /api/config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DISK', 'local'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Filesystem Disks 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Below you may configure as many filesystem disks as necessary, and you 26 | | may even configure multiple disks for the same driver. Examples for 27 | | most supported storage drivers are configured here for reference. 28 | | 29 | | Supported Drivers: "local", "ftp", "sftp", "s3" 30 | | 31 | */ 32 | 33 | 'disks' => [ 34 | 35 | 'local' => [ 36 | 'driver' => 'local', 37 | 'root' => storage_path('app'), 38 | 'throw' => false, 39 | ], 40 | 41 | 'public' => [ 42 | 'driver' => 'local', 43 | 'root' => storage_path('app/public'), 44 | 'url' => env('APP_URL') . '/storage', 45 | 'visibility' => 'public', 46 | 'throw' => false, 47 | ], 48 | 49 | 's3' => [ 50 | 'driver' => 's3', 51 | 'key' => env('AWS_ACCESS_KEY_ID'), 52 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 53 | 'region' => env('AWS_DEFAULT_REGION'), 54 | 'bucket' => env('AWS_BUCKET'), 55 | 'url' => env('AWS_URL'), 56 | 'endpoint' => env('AWS_ENDPOINT'), 57 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), 58 | 'throw' => false, 59 | ], 60 | 61 | ], 62 | 63 | /* 64 | |-------------------------------------------------------------------------- 65 | | Symbolic Links 66 | |-------------------------------------------------------------------------- 67 | | 68 | | Here you may configure the symbolic links that will be created when the 69 | | `storage:link` Artisan command is executed. The array keys should be 70 | | the locations of the links and the values should be their targets. 71 | | 72 | */ 73 | 74 | 'links' => [ 75 | public_path('storage') => storage_path('app/public'), 76 | ], 77 | 78 | ]; 79 | -------------------------------------------------------------------------------- /api/config/logging.php: -------------------------------------------------------------------------------- 1 | env('LOG_CHANNEL', 'stack'), 24 | 25 | /* 26 | |-------------------------------------------------------------------------- 27 | | Deprecations Log Channel 28 | |-------------------------------------------------------------------------- 29 | | 30 | | This option controls the log channel that should be used to log warnings 31 | | regarding deprecated PHP and library features. This allows you to get 32 | | your application ready for upcoming major versions of dependencies. 33 | | 34 | */ 35 | 36 | 'deprecations' => [ 37 | 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), 38 | 'trace' => env('LOG_DEPRECATIONS_TRACE', false), 39 | ], 40 | 41 | /* 42 | |-------------------------------------------------------------------------- 43 | | Log Channels 44 | |-------------------------------------------------------------------------- 45 | | 46 | | Here you may configure the log channels for your application. Laravel 47 | | utilizes the Monolog PHP logging library, which includes a variety 48 | | of powerful log handlers and formatters that you're free to use. 49 | | 50 | | Available Drivers: "single", "daily", "slack", "syslog", 51 | | "errorlog", "monolog", "custom", "stack" 52 | | 53 | */ 54 | 55 | 'channels' => [ 56 | 57 | 'stack' => [ 58 | 'driver' => 'stack', 59 | 'channels' => explode(',', env('LOG_STACK', 'single')), 60 | 'ignore_exceptions' => false, 61 | ], 62 | 63 | 'single' => [ 64 | 'driver' => 'single', 65 | 'path' => storage_path('logs/laravel.log'), 66 | 'level' => env('LOG_LEVEL', 'debug'), 67 | 'replace_placeholders' => true, 68 | ], 69 | 70 | 'daily' => [ 71 | 'driver' => 'daily', 72 | 'path' => storage_path('logs/laravel.log'), 73 | 'level' => env('LOG_LEVEL', 'debug'), 74 | 'days' => env('LOG_DAILY_DAYS', 14), 75 | 'replace_placeholders' => true, 76 | ], 77 | 78 | 'slack' => [ 79 | 'driver' => 'slack', 80 | 'url' => env('LOG_SLACK_WEBHOOK_URL'), 81 | 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), 82 | 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), 83 | 'level' => env('LOG_LEVEL', 'critical'), 84 | 'replace_placeholders' => true, 85 | ], 86 | 87 | 'papertrail' => [ 88 | 'driver' => 'monolog', 89 | 'level' => env('LOG_LEVEL', 'debug'), 90 | 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), 91 | 'handler_with' => [ 92 | 'host' => env('PAPERTRAIL_URL'), 93 | 'port' => env('PAPERTRAIL_PORT'), 94 | 'connectionString' => 'tls://' . env('PAPERTRAIL_URL') . ':' . env('PAPERTRAIL_PORT'), 95 | ], 96 | 'processors' => [PsrLogMessageProcessor::class], 97 | ], 98 | 99 | 'stderr' => [ 100 | 'driver' => 'monolog', 101 | 'level' => env('LOG_LEVEL', 'debug'), 102 | 'handler' => StreamHandler::class, 103 | 'formatter' => env('LOG_STDERR_FORMATTER'), 104 | 'with' => [ 105 | 'stream' => 'php://stderr', 106 | ], 107 | 'processors' => [PsrLogMessageProcessor::class], 108 | ], 109 | 110 | 'syslog' => [ 111 | 'driver' => 'syslog', 112 | 'level' => env('LOG_LEVEL', 'debug'), 113 | 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), 114 | 'replace_placeholders' => true, 115 | ], 116 | 117 | 'errorlog' => [ 118 | 'driver' => 'errorlog', 119 | 'level' => env('LOG_LEVEL', 'debug'), 120 | 'replace_placeholders' => true, 121 | ], 122 | 123 | 'null' => [ 124 | 'driver' => 'monolog', 125 | 'handler' => NullHandler::class, 126 | ], 127 | 128 | 'emergency' => [ 129 | 'path' => storage_path('logs/laravel.log'), 130 | ], 131 | 132 | ], 133 | 134 | ]; 135 | -------------------------------------------------------------------------------- /api/config/mail.php: -------------------------------------------------------------------------------- 1 | env('MAIL_MAILER', 'log'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Mailer Configurations 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Here you may configure all of the mailers used by your application plus 27 | | their respective settings. Several examples have been configured for 28 | | you and you are free to add your own as your application requires. 29 | | 30 | | Laravel supports a variety of mail "transport" drivers that can be used 31 | | when delivering an email. You may specify which one you're using for 32 | | your mailers below. You may also add additional mailers if needed. 33 | | 34 | | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", 35 | | "postmark", "log", "array", "failover", "roundrobin" 36 | | 37 | */ 38 | 39 | 'mailers' => [ 40 | 41 | 'smtp' => [ 42 | 'transport' => 'smtp', 43 | 'url' => env('MAIL_URL'), 44 | 'host' => env('MAIL_HOST', '127.0.0.1'), 45 | 'port' => env('MAIL_PORT', 2525), 46 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 47 | 'username' => env('MAIL_USERNAME'), 48 | 'password' => env('MAIL_PASSWORD'), 49 | 'timeout' => null, 50 | 'local_domain' => env('MAIL_EHLO_DOMAIN'), 51 | ], 52 | 53 | 'ses' => [ 54 | 'transport' => 'ses', 55 | ], 56 | 57 | 'postmark' => [ 58 | 'transport' => 'postmark', 59 | // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), 60 | // 'client' => [ 61 | // 'timeout' => 5, 62 | // ], 63 | ], 64 | 65 | 'sendmail' => [ 66 | 'transport' => 'sendmail', 67 | 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), 68 | ], 69 | 70 | 'log' => [ 71 | 'transport' => 'log', 72 | 'channel' => env('MAIL_LOG_CHANNEL'), 73 | ], 74 | 75 | 'array' => [ 76 | 'transport' => 'array', 77 | ], 78 | 79 | 'failover' => [ 80 | 'transport' => 'failover', 81 | 'mailers' => [ 82 | 'smtp', 83 | 'log', 84 | ], 85 | ], 86 | 87 | 'roundrobin' => [ 88 | 'transport' => 'roundrobin', 89 | 'mailers' => [ 90 | 'ses', 91 | 'postmark', 92 | ], 93 | ], 94 | 95 | ], 96 | 97 | /* 98 | |-------------------------------------------------------------------------- 99 | | Global "From" Address 100 | |-------------------------------------------------------------------------- 101 | | 102 | | You may wish for all emails sent by your application to be sent from 103 | | the same address. Here you may specify a name and address that is 104 | | used globally for all emails that are sent by your application. 105 | | 106 | */ 107 | 108 | 'from' => [ 109 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), 110 | 'name' => env('MAIL_FROM_NAME', 'Example'), 111 | ], 112 | 113 | ]; 114 | -------------------------------------------------------------------------------- /api/config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_CONNECTION', 'database'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Queue Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may configure the connection options for every queue backend 26 | | used by your application. An example configuration is provided for 27 | | each backend supported by Laravel. You're also free to add more. 28 | | 29 | | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" 30 | | 31 | */ 32 | 33 | 'connections' => [ 34 | 35 | 'sync' => [ 36 | 'driver' => 'sync', 37 | ], 38 | 39 | 'database' => [ 40 | 'driver' => 'database', 41 | 'connection' => env('DB_QUEUE_CONNECTION'), 42 | 'table' => env('DB_QUEUE_TABLE', 'jobs'), 43 | 'queue' => env('DB_QUEUE', 'default'), 44 | 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), 45 | 'after_commit' => false, 46 | ], 47 | 48 | 'beanstalkd' => [ 49 | 'driver' => 'beanstalkd', 50 | 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), 51 | 'queue' => env('BEANSTALKD_QUEUE', 'default'), 52 | 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), 53 | 'block_for' => 0, 54 | 'after_commit' => false, 55 | ], 56 | 57 | 'sqs' => [ 58 | 'driver' => 'sqs', 59 | 'key' => env('AWS_ACCESS_KEY_ID'), 60 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 61 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 62 | 'queue' => env('SQS_QUEUE', 'default'), 63 | 'suffix' => env('SQS_SUFFIX'), 64 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 65 | 'after_commit' => false, 66 | ], 67 | 68 | 'redis' => [ 69 | 'driver' => 'redis', 70 | 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), 71 | 'queue' => env('REDIS_QUEUE', 'default'), 72 | 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), 73 | 'block_for' => null, 74 | 'after_commit' => false, 75 | ], 76 | 77 | ], 78 | 79 | /* 80 | |-------------------------------------------------------------------------- 81 | | Job Batching 82 | |-------------------------------------------------------------------------- 83 | | 84 | | The following options configure the database and table that store job 85 | | batching information. These options can be updated to any database 86 | | connection and table which has been defined by your application. 87 | | 88 | */ 89 | 90 | 'batching' => [ 91 | 'database' => env('DB_CONNECTION', 'sqlite'), 92 | 'table' => 'job_batches', 93 | ], 94 | 95 | /* 96 | |-------------------------------------------------------------------------- 97 | | Failed Queue Jobs 98 | |-------------------------------------------------------------------------- 99 | | 100 | | These options configure the behavior of failed queue job logging so you 101 | | can control how and where failed jobs are stored. Laravel ships with 102 | | support for storing failed jobs in a simple file or in a database. 103 | | 104 | | Supported drivers: "database-uuids", "dynamodb", "file", "null" 105 | | 106 | */ 107 | 108 | 'failed' => [ 109 | 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), 110 | 'database' => env('DB_CONNECTION', 'sqlite'), 111 | 'table' => 'failed_jobs', 112 | ], 113 | 114 | ]; 115 | -------------------------------------------------------------------------------- /api/config/sanctum.php: -------------------------------------------------------------------------------- 1 | explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( 21 | '%s%s%s', 22 | 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', 23 | Sanctum::currentApplicationUrlWithPort(), 24 | env('FRONTEND_URL') ? ',' . parse_url(env('FRONTEND_URL'), PHP_URL_HOST) : '', 25 | ))), 26 | 27 | /* 28 | |-------------------------------------------------------------------------- 29 | | Sanctum Guards 30 | |-------------------------------------------------------------------------- 31 | | 32 | | This array contains the authentication guards that will be checked when 33 | | Sanctum is trying to authenticate a request. If none of these guards 34 | | are able to authenticate the request, Sanctum will use the bearer 35 | | token that's present on an incoming request for authentication. 36 | | 37 | */ 38 | 39 | 'guard' => ['web'], 40 | 41 | /* 42 | |-------------------------------------------------------------------------- 43 | | Expiration Minutes 44 | |-------------------------------------------------------------------------- 45 | | 46 | | This value controls the number of minutes until an issued token will be 47 | | considered expired. This will override any values set in the token's 48 | | "expires_at" attribute, but first-party sessions are not affected. 49 | | 50 | */ 51 | 52 | 'expiration' => null, 53 | 54 | /* 55 | |-------------------------------------------------------------------------- 56 | | Token Prefix 57 | |-------------------------------------------------------------------------- 58 | | 59 | | Sanctum can prefix new tokens in order to take advantage of numerous 60 | | security scanning initiatives maintained by open source platforms 61 | | that notify developers if they commit tokens into repositories. 62 | | 63 | | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning 64 | | 65 | */ 66 | 67 | 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), 68 | 69 | /* 70 | |-------------------------------------------------------------------------- 71 | | Sanctum Middleware 72 | |-------------------------------------------------------------------------- 73 | | 74 | | When authenticating your first-party SPA with Sanctum you may need to 75 | | customize some of the middleware Sanctum uses while processing the 76 | | request. You may change the middleware listed below as required. 77 | | 78 | */ 79 | 80 | 'middleware' => [ 81 | 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, 82 | 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, 83 | 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, 84 | ], 85 | 86 | ]; 87 | -------------------------------------------------------------------------------- /api/config/services.php: -------------------------------------------------------------------------------- 1 | [ 20 | 'token' => env('POSTMARK_TOKEN'), 21 | ], 22 | 23 | 'ses' => [ 24 | 'key' => env('AWS_ACCESS_KEY_ID'), 25 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 26 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 27 | ], 28 | 29 | 'slack' => [ 30 | 'notifications' => [ 31 | 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), 32 | 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), 33 | ], 34 | ], 35 | 36 | ]; 37 | -------------------------------------------------------------------------------- /api/config/session.php: -------------------------------------------------------------------------------- 1 | env('SESSION_DRIVER', 'database'), 24 | 25 | /* 26 | |-------------------------------------------------------------------------- 27 | | Session Lifetime 28 | |-------------------------------------------------------------------------- 29 | | 30 | | Here you may specify the number of minutes that you wish the session 31 | | to be allowed to remain idle before it expires. If you want them 32 | | to expire immediately when the browser is closed then you may 33 | | indicate that via the expire_on_close configuration option. 34 | | 35 | */ 36 | 37 | 'lifetime' => env('SESSION_LIFETIME', 120), 38 | 39 | 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), 40 | 41 | /* 42 | |-------------------------------------------------------------------------- 43 | | Session Encryption 44 | |-------------------------------------------------------------------------- 45 | | 46 | | This option allows you to easily specify that all of your session data 47 | | should be encrypted before it's stored. All encryption is performed 48 | | automatically by Laravel and you may use the session like normal. 49 | | 50 | */ 51 | 52 | 'encrypt' => env('SESSION_ENCRYPT', false), 53 | 54 | /* 55 | |-------------------------------------------------------------------------- 56 | | Session File Location 57 | |-------------------------------------------------------------------------- 58 | | 59 | | When utilizing the "file" session driver, the session files are placed 60 | | on disk. The default storage location is defined here; however, you 61 | | are free to provide another location where they should be stored. 62 | | 63 | */ 64 | 65 | 'files' => storage_path('framework/sessions'), 66 | 67 | /* 68 | |-------------------------------------------------------------------------- 69 | | Session Database Connection 70 | |-------------------------------------------------------------------------- 71 | | 72 | | When using the "database" or "redis" session drivers, you may specify a 73 | | connection that should be used to manage these sessions. This should 74 | | correspond to a connection in your database configuration options. 75 | | 76 | */ 77 | 78 | 'connection' => env('SESSION_CONNECTION'), 79 | 80 | /* 81 | |-------------------------------------------------------------------------- 82 | | Session Database Table 83 | |-------------------------------------------------------------------------- 84 | | 85 | | When using the "database" session driver, you may specify the table to 86 | | be used to store sessions. Of course, a sensible default is defined 87 | | for you; however, you're welcome to change this to another table. 88 | | 89 | */ 90 | 91 | 'table' => env('SESSION_TABLE', 'sessions'), 92 | 93 | /* 94 | |-------------------------------------------------------------------------- 95 | | Session Cache Store 96 | |-------------------------------------------------------------------------- 97 | | 98 | | When using one of the framework's cache driven session backends, you may 99 | | define the cache store which should be used to store the session data 100 | | between requests. This must match one of your defined cache stores. 101 | | 102 | | Affects: "apc", "dynamodb", "memcached", "redis" 103 | | 104 | */ 105 | 106 | 'store' => env('SESSION_STORE'), 107 | 108 | /* 109 | |-------------------------------------------------------------------------- 110 | | Session Sweeping Lottery 111 | |-------------------------------------------------------------------------- 112 | | 113 | | Some session drivers must manually sweep their storage location to get 114 | | rid of old sessions from storage. Here are the chances that it will 115 | | happen on a given request. By default, the odds are 2 out of 100. 116 | | 117 | */ 118 | 119 | 'lottery' => [2, 100], 120 | 121 | /* 122 | |-------------------------------------------------------------------------- 123 | | Session Cookie Name 124 | |-------------------------------------------------------------------------- 125 | | 126 | | Here you may change the name of the session cookie that is created by 127 | | the framework. Typically, you should not need to change this value 128 | | since doing so does not grant a meaningful security improvement. 129 | | 130 | */ 131 | 132 | 'cookie' => env( 133 | 'SESSION_COOKIE', 134 | Str::slug(env('APP_NAME', 'laravel'), '_') . '_session', 135 | ), 136 | 137 | /* 138 | |-------------------------------------------------------------------------- 139 | | Session Cookie Path 140 | |-------------------------------------------------------------------------- 141 | | 142 | | The session cookie path determines the path for which the cookie will 143 | | be regarded as available. Typically, this will be the root path of 144 | | your application, but you're free to change this when necessary. 145 | | 146 | */ 147 | 148 | 'path' => env('SESSION_PATH', '/'), 149 | 150 | /* 151 | |-------------------------------------------------------------------------- 152 | | Session Cookie Domain 153 | |-------------------------------------------------------------------------- 154 | | 155 | | This value determines the domain and subdomains the session cookie is 156 | | available to. By default, the cookie will be available to the root 157 | | domain and all subdomains. Typically, this shouldn't be changed. 158 | | 159 | */ 160 | 161 | 'domain' => env('SESSION_DOMAIN'), 162 | 163 | /* 164 | |-------------------------------------------------------------------------- 165 | | HTTPS Only Cookies 166 | |-------------------------------------------------------------------------- 167 | | 168 | | By setting this option to true, session cookies will only be sent back 169 | | to the server if the browser has a HTTPS connection. This will keep 170 | | the cookie from being sent to you when it can't be done securely. 171 | | 172 | */ 173 | 174 | 'secure' => env('SESSION_SECURE_COOKIE'), 175 | 176 | /* 177 | |-------------------------------------------------------------------------- 178 | | HTTP Access Only 179 | |-------------------------------------------------------------------------- 180 | | 181 | | Setting this value to true will prevent JavaScript from accessing the 182 | | value of the cookie and the cookie will only be accessible through 183 | | the HTTP protocol. It's unlikely you should disable this option. 184 | | 185 | */ 186 | 187 | 'http_only' => env('SESSION_HTTP_ONLY', true), 188 | 189 | /* 190 | |-------------------------------------------------------------------------- 191 | | Same-Site Cookies 192 | |-------------------------------------------------------------------------- 193 | | 194 | | This option determines how your cookies behave when cross-site requests 195 | | take place, and can be used to mitigate CSRF attacks. By default, we 196 | | will set this value to "lax" to permit secure cross-site requests. 197 | | 198 | | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value 199 | | 200 | | Supported: "lax", "strict", "none", null 201 | | 202 | */ 203 | 204 | 'same_site' => env('SESSION_SAME_SITE', 'lax'), 205 | 206 | /* 207 | |-------------------------------------------------------------------------- 208 | | Partitioned Cookies 209 | |-------------------------------------------------------------------------- 210 | | 211 | | Setting this value to true will tie the cookie to the top-level site for 212 | | a cross-site context. Partitioned cookies are accepted by the browser 213 | | when flagged "secure" and the Same-Site attribute is set to "none". 214 | | 215 | */ 216 | 217 | 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), 218 | 219 | ]; 220 | -------------------------------------------------------------------------------- /api/database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /api/database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | */ 16 | protected $model = User::class; 17 | 18 | /** @return array */ 19 | public function definition(): array 20 | { 21 | return [ 22 | 'name' => $this->faker->name(), 23 | 'email' => $this->faker->unique()->safeEmail(), 24 | 'password' => Hash::make('password'), 25 | 'remember_token' => Str::random(10), 26 | 'email_verified_at' => now(), 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /api/database/migrations/0001_01_01_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | uuid('id')->primary(); 14 | 15 | $table->string('name'); 16 | $table->string('email')->unique(); 17 | $table->string('password'); 18 | $table->rememberToken(); 19 | 20 | $table->timestamp('email_verified_at')->nullable(); 21 | $table->timestamps(); 22 | $table->softDeletes(); 23 | }); 24 | 25 | Schema::create('password_reset_tokens', static function (Blueprint $table): void { 26 | $table->string('email')->primary(); 27 | $table->string('token'); 28 | $table->timestamp('created_at')->nullable(); 29 | }); 30 | 31 | Schema::create('sessions', static function (Blueprint $table): void { 32 | $table->string('id')->primary(); 33 | $table->foreignUuid('user_id')->nullable()->index()->constrained()->cascadeOnDelete(); 34 | $table->string('ip_address', 45)->nullable(); 35 | $table->text('user_agent')->nullable(); 36 | $table->longText('payload'); 37 | $table->integer('last_activity')->index(); 38 | }); 39 | } 40 | 41 | public function down(): void 42 | { 43 | Schema::dropIfExists('users'); 44 | Schema::dropIfExists('password_reset_tokens'); 45 | Schema::dropIfExists('sessions'); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /api/database/migrations/0001_01_01_000001_create_cache_table.php: -------------------------------------------------------------------------------- 1 | string('key')->primary(); 14 | $table->mediumText('value'); 15 | $table->integer('expiration'); 16 | }); 17 | 18 | Schema::create('cache_locks', static function (Blueprint $table): void { 19 | $table->string('key')->primary(); 20 | $table->string('owner'); 21 | $table->integer('expiration'); 22 | }); 23 | } 24 | 25 | public function down(): void 26 | { 27 | Schema::dropIfExists('cache'); 28 | Schema::dropIfExists('cache_locks'); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /api/database/migrations/0001_01_01_000002_create_jobs_table.php: -------------------------------------------------------------------------------- 1 | id(); 17 | $table->string('queue')->index(); 18 | $table->longText('payload'); 19 | $table->unsignedTinyInteger('attempts'); 20 | $table->unsignedInteger('reserved_at')->nullable(); 21 | $table->unsignedInteger('available_at'); 22 | $table->unsignedInteger('created_at'); 23 | }); 24 | 25 | Schema::create('job_batches', function (Blueprint $table): void { 26 | $table->string('id')->primary(); 27 | $table->string('name'); 28 | $table->integer('total_jobs'); 29 | $table->integer('pending_jobs'); 30 | $table->integer('failed_jobs'); 31 | $table->longText('failed_job_ids'); 32 | $table->mediumText('options')->nullable(); 33 | $table->integer('cancelled_at')->nullable(); 34 | $table->integer('created_at'); 35 | $table->integer('finished_at')->nullable(); 36 | }); 37 | 38 | Schema::create('failed_jobs', function (Blueprint $table): void { 39 | $table->id(); 40 | $table->string('uuid')->unique(); 41 | $table->text('connection'); 42 | $table->text('queue'); 43 | $table->longText('payload'); 44 | $table->longText('exception'); 45 | $table->timestamp('failed_at')->useCurrent(); 46 | }); 47 | } 48 | 49 | /** 50 | * Reverse the migrations. 51 | */ 52 | public function down(): void 53 | { 54 | Schema::dropIfExists('jobs'); 55 | Schema::dropIfExists('job_batches'); 56 | Schema::dropIfExists('failed_jobs'); 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /api/database/migrations/2014_10_12_200000_create_authorization_tables.php: -------------------------------------------------------------------------------- 1 | uuid('id')->primary(); 14 | 15 | $table->string('name')->unique()->index(); 16 | $table->string('label')->nullable(); 17 | 18 | $table->timestamps(); 19 | }); 20 | 21 | Schema::create('permissions', static function (Blueprint $table): void { 22 | $table->uuid('id')->primary(); 23 | 24 | $table->string('name')->unique()->index(); 25 | $table->string('label')->nullable(); 26 | 27 | $table->timestamps(); 28 | }); 29 | 30 | Schema::create('permission_role', static function (Blueprint $table): void { 31 | $table 32 | ->foreignUuid('permission_id') 33 | ->index() 34 | ->constrained() 35 | ->cascadeOnDelete(); 36 | 37 | $table 38 | ->foreignUuid('role_id') 39 | ->index() 40 | ->constrained() 41 | ->cascadeOnDelete(); 42 | 43 | $table->primary(['permission_id', 'role_id']); 44 | }); 45 | 46 | Schema::create('permission_user', static function (Blueprint $table): void { 47 | $table 48 | ->foreignUuid('permission_id') 49 | ->index() 50 | ->constrained() 51 | ->cascadeOnDelete(); 52 | 53 | $table 54 | ->foreignUuid('user_id') 55 | ->index() 56 | ->constrained() 57 | ->cascadeOnDelete(); 58 | 59 | $table->primary(['permission_id', 'user_id']); 60 | }); 61 | 62 | Schema::create('role_user', static function (Blueprint $table): void { 63 | $table 64 | ->foreignUuid('role_id') 65 | ->index() 66 | ->constrained() 67 | ->cascadeOnDelete(); 68 | 69 | $table 70 | ->foreignUuid('user_id') 71 | ->index() 72 | ->constrained() 73 | ->cascadeOnDelete(); 74 | 75 | $table->primary(['role_id', 'user_id']); 76 | }); 77 | } 78 | 79 | public function down(): void 80 | { 81 | Schema::dropIfExists('role_user'); 82 | Schema::dropIfExists('permission_role'); 83 | Schema::dropIfExists('permission_user'); 84 | Schema::dropIfExists('permissions'); 85 | Schema::dropIfExists('roles'); 86 | } 87 | }; 88 | -------------------------------------------------------------------------------- /api/database/migrations/2024_05_01_173951_create_personal_access_tokens_table.php: -------------------------------------------------------------------------------- 1 | id(); 14 | $table->uuidMorphs('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('personal_access_tokens'); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /api/database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create([ 17 | 'name' => 'Steve McDougall', 18 | 'email' => 'juststevemcd@gmail.com', 19 | ]); 20 | 21 | $member = Role::query()->create([ 22 | 'name' => 'Member', 23 | 'label' => 'member', 24 | ]); 25 | 26 | $admin = Role::query()->create([ 27 | 'name' => 'admin', 28 | 'label' => 'Admin', 29 | ]); 30 | 31 | $createUser = Permission::query()->create([ 32 | 'name' => 'users.create', 33 | 'label' => 'Create Users', 34 | ]); 35 | $deleteUser = Permission::query()->create([ 36 | 'name' => 'users.delete', 37 | 'label' => 'Delete Users', 38 | ]); 39 | 40 | $listUser = Permission::query()->create([ 41 | 'name' => 'users.list', 42 | 'label' => 'List Users', 43 | ]); 44 | 45 | $admin->permissions()->save($createUser); 46 | $admin->permissions()->save($deleteUser); 47 | $admin->permissions()->save($listUser); 48 | $member->permissions()->save($listUser); 49 | 50 | $user->roles()->save($admin); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /api/phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | paths: 3 | - app/ 4 | 5 | level: 9 6 | 7 | ignoreErrors: 8 | 9 | excludePaths: 10 | 11 | checkMissingIterableValueType: false 12 | checkGenericClassInNonGenericObjectType: false 13 | -------------------------------------------------------------------------------- /api/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | tests/Unit 10 | 11 | 12 | tests/Feature 13 | 14 | 15 | 16 | 17 | app 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /api/pint.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "per", 3 | "rules": { 4 | "align_multiline_comment": true, 5 | "array_indentation": true, 6 | "array_syntax": true, 7 | "blank_line_after_namespace": true, 8 | "blank_line_after_opening_tag": true, 9 | "combine_consecutive_issets": true, 10 | "combine_consecutive_unsets": true, 11 | "concat_space": { 12 | "spacing": "one" 13 | }, 14 | "declare_parentheses": true, 15 | "declare_strict_types": true, 16 | "explicit_string_variable": true, 17 | "final_class": true, 18 | "fully_qualified_strict_types": true, 19 | "global_namespace_import": { 20 | "import_classes": true, 21 | "import_constants": true, 22 | "import_functions": true 23 | }, 24 | "is_null": true, 25 | "lambda_not_used_import": true, 26 | "logical_operators": true, 27 | "mb_str_functions": true, 28 | "method_chaining_indentation": true, 29 | "modernize_strpos": true, 30 | "new_with_braces": true, 31 | "no_empty_comment": true, 32 | "not_operator_with_space": true, 33 | "ordered_traits": true, 34 | "protected_to_private": true, 35 | "simplified_if_return": true, 36 | "strict_comparison": true, 37 | "ternary_to_null_coalescing": true, 38 | "trim_array_spaces": true, 39 | "use_arrow_functions": true, 40 | "void_return": true, 41 | "yoda_style": true, 42 | "array_push": true, 43 | "assign_null_coalescing_to_coalesce_equal": true, 44 | "explicit_indirect_variable": true, 45 | "method_argument_space": { 46 | "on_multiline": "ensure_fully_multiline" 47 | }, 48 | "modernize_types_casting": true, 49 | "no_superfluous_elseif": true, 50 | "no_useless_else": true, 51 | "nullable_type_declaration_for_default_null_value": true, 52 | "ordered_imports": { 53 | "sort_algorithm": "alpha" 54 | }, 55 | "ordered_class_elements": { 56 | "order": [ 57 | "use_trait", 58 | "case", 59 | "constant", 60 | "constant_public", 61 | "constant_protected", 62 | "constant_private", 63 | "property_public", 64 | "property_protected", 65 | "property_private", 66 | "construct", 67 | "destruct", 68 | "magic", 69 | "phpunit", 70 | "method_abstract", 71 | "method_public_static", 72 | "method_public", 73 | "method_protected_static", 74 | "method_protected", 75 | "method_private_static", 76 | "method_private" 77 | ], 78 | "sort_algorithm": "none" 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /api/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 | -------------------------------------------------------------------------------- /api/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustSteveKing/laravel-nuxt/2fcc2a01abf589fb4295358f51962dd06c5ad83e/api/public/favicon.ico -------------------------------------------------------------------------------- /api/public/index.php: -------------------------------------------------------------------------------- 1 | handleRequest(Request::capture()); 20 | -------------------------------------------------------------------------------- /api/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /api/resources/views/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /api/routes/api.php: -------------------------------------------------------------------------------- 1 | group(static function (): void { 8 | Route::middleware(['auth:sanctum'])->group(static function (): void { 9 | Route::get('user', App\Http\Controllers\Api\Auth\UserController::class)->name('user'); 10 | 11 | Route::prefix('users')->as('users:')->group(static function (): void { 12 | Route::get('/', App\Http\Controllers\Api\Users\IndexController::class)->name('index')->middleware([ 13 | 'permission:users.list', 14 | ]); 15 | }); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /api/routes/auth.php: -------------------------------------------------------------------------------- 1 | middleware('guest') 15 | ->name('register'); 16 | 17 | Route::post('/login', [AuthenticatedSessionController::class, 'store']) 18 | ->middleware('guest') 19 | ->name('login'); 20 | 21 | Route::post('/forgot-password', [PasswordResetLinkController::class, 'store']) 22 | ->middleware('guest') 23 | ->name('password.email'); 24 | 25 | Route::post('/reset-password', [NewPasswordController::class, 'store']) 26 | ->middleware('guest') 27 | ->name('password.store'); 28 | 29 | Route::get('/verify-email/{id}/{hash}', VerifyEmailController::class) 30 | ->middleware(['auth', 'signed', 'throttle:6,1']) 31 | ->name('verification.verify'); 32 | 33 | Route::post('/email/verification-notification', [EmailVerificationNotificationController::class, 'store']) 34 | ->middleware(['auth', 'throttle:6,1']) 35 | ->name('verification.send'); 36 | 37 | Route::post('/logout', [AuthenticatedSessionController::class, 'destroy']) 38 | ->middleware('auth') 39 | ->name('logout'); 40 | -------------------------------------------------------------------------------- /api/routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 10 | })->purpose('Display an inspiring quote')->hourly(); 11 | -------------------------------------------------------------------------------- /api/routes/web.php: -------------------------------------------------------------------------------- 1 | ['Laravel' => app()->version()]); 8 | 9 | require __DIR__ . '/auth.php'; 10 | -------------------------------------------------------------------------------- /api/storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /api/storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /api/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 | -------------------------------------------------------------------------------- /api/storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /api/storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /api/storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /api/storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /api/storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /api/storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /api/tests/Feature/Auth/AuthenticationTest.php: -------------------------------------------------------------------------------- 1 | create(); 9 | 10 | $response = $this->post('/login', [ 11 | 'email' => $user->email, 12 | 'password' => 'password', 13 | ]); 14 | 15 | $this->assertAuthenticated(); 16 | $response->assertNoContent(); 17 | }); 18 | 19 | test('users can not authenticate with invalid password', function (): void { 20 | $user = User::factory()->create(); 21 | 22 | $this->post('/login', [ 23 | 'email' => $user->email, 24 | 'password' => 'wrong-password', 25 | ]); 26 | 27 | $this->assertGuest(); 28 | }); 29 | 30 | test('users can logout', function (): void { 31 | $user = User::factory()->create(); 32 | 33 | $response = $this->actingAs($user)->post('/logout'); 34 | 35 | $this->assertGuest(); 36 | $response->assertNoContent(); 37 | }); 38 | -------------------------------------------------------------------------------- /api/tests/Feature/Auth/EmailVerificationTest.php: -------------------------------------------------------------------------------- 1 | unverified()->create(); 12 | 13 | Event::fake(); 14 | 15 | $verificationUrl = URL::temporarySignedRoute( 16 | 'verification.verify', 17 | now()->addMinutes(60), 18 | ['id' => $user->id, 'hash' => sha1($user->email)], 19 | ); 20 | 21 | $response = $this->actingAs($user)->get($verificationUrl); 22 | 23 | Event::assertDispatched(Verified::class); 24 | expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); 25 | $response->assertRedirect(config('app.frontend_url') . '/dashboard?verified=1'); 26 | }); 27 | 28 | test('email is not verified with invalid hash', function (): void { 29 | $user = User::factory()->unverified()->create(); 30 | 31 | $verificationUrl = URL::temporarySignedRoute( 32 | 'verification.verify', 33 | now()->addMinutes(60), 34 | ['id' => $user->id, 'hash' => sha1('wrong-email')], 35 | ); 36 | 37 | $this->actingAs($user)->get($verificationUrl); 38 | 39 | expect($user->fresh()->hasVerifiedEmail())->toBeFalse(); 40 | }); 41 | -------------------------------------------------------------------------------- /api/tests/Feature/Auth/PasswordResetTest.php: -------------------------------------------------------------------------------- 1 | create(); 13 | 14 | $this->post('/forgot-password', ['email' => $user->email]); 15 | 16 | Notification::assertSentTo($user, ResetPassword::class); 17 | }); 18 | 19 | test('password can be reset with valid token', function (): void { 20 | Notification::fake(); 21 | 22 | $user = User::factory()->create(); 23 | 24 | $this->post('/forgot-password', ['email' => $user->email]); 25 | 26 | Notification::assertSentTo($user, ResetPassword::class, function (object $notification) use ($user) { 27 | $response = $this->post('/reset-password', [ 28 | 'token' => $notification->token, 29 | 'email' => $user->email, 30 | 'password' => 'password', 31 | 'password_confirmation' => 'password', 32 | ]); 33 | 34 | $response 35 | ->assertSessionHasNoErrors() 36 | ->assertStatus(200); 37 | 38 | return true; 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /api/tests/Feature/Auth/RegistrationTest.php: -------------------------------------------------------------------------------- 1 | post('/register', [ 7 | 'name' => 'Test User', 8 | 'email' => 'test@example.com', 9 | 'password' => 'password', 10 | 'password_confirmation' => 'password', 11 | ]); 12 | 13 | $this->assertAuthenticated(); 14 | $response->assertNoContent(); 15 | }); 16 | -------------------------------------------------------------------------------- /api/tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 7 | 8 | $response->assertStatus(200); 9 | }); 10 | -------------------------------------------------------------------------------- /api/tests/Pest.php: -------------------------------------------------------------------------------- 1 | in('Feature'); 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Expectations 24 | |-------------------------------------------------------------------------- 25 | | 26 | | When you're writing tests, you often need to check that values meet certain conditions. The 27 | | "expect()" function gives you access to a set of "expectations" methods that you can use 28 | | to assert different things. Of course, you may extend the Expectation API at any time. 29 | | 30 | */ 31 | 32 | expect()->extend('toBeOne', fn() => $this->toBe(1)); 33 | 34 | /* 35 | |-------------------------------------------------------------------------- 36 | | Functions 37 | |-------------------------------------------------------------------------- 38 | | 39 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your 40 | | project that you don't want to repeat in every file. Here you can also expose helpers as 41 | | global functions to help you to reduce the number of lines of code in your test files. 42 | | 43 | */ 44 | 45 | function something(): void 46 | { 47 | // .. 48 | } 49 | -------------------------------------------------------------------------------- /api/tests/TestCase.php: -------------------------------------------------------------------------------- 1 | register(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /api/tests/Unit/ExampleTest.php: -------------------------------------------------------------------------------- 1 | toBeTrue(); 7 | }); 8 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | -------------------------------------------------------------------------------- /web/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | # Nuxt UI Minimal Starter 2 | 3 | Look at [Nuxt docs](https://nuxt.com/docs/getting-started/introduction) and [Nuxt UI docs](https://ui.nuxt.com) to learn more. 4 | 5 | ## Setup 6 | 7 | Make sure to install the dependencies: 8 | 9 | ```bash 10 | # npm 11 | npm install 12 | 13 | # pnpm 14 | pnpm install 15 | 16 | # yarn 17 | yarn install 18 | 19 | # bun 20 | bun install 21 | ``` 22 | 23 | ## Development Server 24 | 25 | Start the development server on `http://localhost:3000`: 26 | 27 | ```bash 28 | # npm 29 | npm run dev 30 | 31 | # pnpm 32 | pnpm run dev 33 | 34 | # yarn 35 | yarn dev 36 | 37 | # bun 38 | bun run dev 39 | ``` 40 | 41 | ## Production 42 | 43 | Build the application for production: 44 | 45 | ```bash 46 | # npm 47 | npm run build 48 | 49 | # pnpm 50 | pnpm run build 51 | 52 | # yarn 53 | yarn build 54 | 55 | # bun 56 | bun run build 57 | ``` 58 | 59 | Locally preview production build: 60 | 61 | ```bash 62 | # npm 63 | npm run preview 64 | 65 | # pnpm 66 | pnpm run preview 67 | 68 | # yarn 69 | yarn preview 70 | 71 | # bun 72 | bun run preview 73 | ``` 74 | 75 | Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. 76 | -------------------------------------------------------------------------------- /web/app.config.ts: -------------------------------------------------------------------------------- 1 | export default defineAppConfig({ 2 | ui: { 3 | primary: 'indigo', 4 | gray: 'slate', 5 | } 6 | }) -------------------------------------------------------------------------------- /web/app.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | 20 | 31 | -------------------------------------------------------------------------------- /web/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | -------------------------------------------------------------------------------- /web/layouts/guest.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /web/nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | app: { 4 | layoutTransition: { 5 | name: 'layout', 6 | mode: 'out-in' 7 | }, 8 | pageTransition: { 9 | name: 'page', 10 | mode: 'out-in' 11 | } 12 | }, 13 | devtools: { enabled: true }, 14 | experimental: { 15 | viewTransition: true 16 | }, 17 | modules: [ 18 | "@pinia/nuxt", 19 | "@nuxt/ui", 20 | "nuxt-auth-sanctum" 21 | ], 22 | postcss: { 23 | plugins: { 24 | tailwindcss: {}, 25 | autoprefixer: {}, 26 | } 27 | }, 28 | sanctum: { 29 | baseUrl: 'http://localhost:8000', 30 | redirectIfAuthenticated: true, 31 | redirect: { 32 | onAuthOnly: '/auth/login' 33 | }, 34 | globalMiddleware: { 35 | enabled: false, 36 | }, 37 | }, 38 | ssr: false 39 | }) -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare" 11 | }, 12 | "devDependencies": { 13 | "@headlessui/vue": "^1.7.21", 14 | "@heroicons/vue": "^2.1.3", 15 | "@nuxt/devtools": "latest", 16 | "@nuxt/ui": "^2.13.0", 17 | "@pinia/nuxt": "^0.5.1", 18 | "nuxt": "^3.10.1", 19 | "nuxt-auth-sanctum": "^0.3.0", 20 | "zod": "^3.23.5" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /web/pages/auth/login.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 66 | -------------------------------------------------------------------------------- /web/pages/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /web/pages/users/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustSteveKing/laravel-nuxt/2fcc2a01abf589fb4295358f51962dd06c5ad83e/web/public/favicon.ico -------------------------------------------------------------------------------- /web/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /web/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: [ 4 | "./components/**/*.{js,vue,ts}", 5 | "./layouts/**/*.vue", 6 | "./pages/**/*.vue", 7 | "./plugins/**/*.{js,ts}", 8 | "./app.vue", 9 | "./error.vue", 10 | ], 11 | theme: { 12 | extend: {}, 13 | }, 14 | plugins: [], 15 | } 16 | 17 | -------------------------------------------------------------------------------- /web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | --------------------------------------------------------------------------------