├── .editorconfig
├── .env.example
├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── app
├── Broadcasting
│ ├── PrivateUserChannel.php
│ └── PublicUserChannel.php
├── Console
│ └── Commands
│ │ └── Broadcasting
│ │ └── TriggerEvent.php
├── Events
│ ├── PrivateMessageTriggered.php
│ └── PublicMessageTriggered.php
├── Http
│ ├── Controllers
│ │ ├── Api
│ │ │ ├── AuthenticatedUserController.php
│ │ │ ├── QuoteController.php
│ │ │ └── TokenAuthenticationController.php
│ │ ├── Auth
│ │ │ ├── AuthenticatedSessionController.php
│ │ │ ├── EmailVerificationNotificationController.php
│ │ │ ├── NewPasswordController.php
│ │ │ ├── PasswordResetLinkController.php
│ │ │ ├── RegisteredUserController.php
│ │ │ └── VerifyEmailController.php
│ │ └── Controller.php
│ ├── Middleware
│ │ └── EnsureEmailIsVerified.php
│ └── Requests
│ │ └── Auth
│ │ ├── LoginRequest.php
│ │ └── RegisterRequest.php
├── Models
│ └── User.php
└── Providers
│ └── AppServiceProvider.php
├── artisan
├── bootstrap
├── app.php
├── cache
│ └── .gitignore
└── providers.php
├── composer.json
├── composer.lock
├── config
├── app.php
├── auth.php
├── broadcasting.php
├── cache.php
├── cors.php
├── database.php
├── filesystems.php
├── logging.php
├── mail.php
├── queue.php
├── reverb.php
├── sanctum.php
├── services.php
└── session.php
├── database
├── .gitignore
├── factories
│ └── UserFactory.php
├── migrations
│ ├── 0001_01_01_000000_create_users_table.php
│ ├── 0001_01_01_000001_create_cache_table.php
│ ├── 0001_01_01_000002_create_jobs_table.php
│ └── 2024_05_01_115424_create_personal_access_tokens_table.php
└── seeders
│ └── DatabaseSeeder.php
├── docker-compose.yml
├── justfile
├── phpstan.neon
├── phpunit.xml
├── pint.json
├── public
├── .htaccess
├── favicon.ico
├── index.php
└── robots.txt
├── routes
├── api.php
├── auth.php
├── channels.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
├── TestCase.php
└── Unit
└── ExampleTest.php
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 4
7 | indent_style = space
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.{yml,yaml}]
15 | indent_size = 2
16 |
17 | [docker-compose.yml]
18 | indent_size = 4
19 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME=Laravel
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_TIMEZONE=UTC
6 | APP_URL=http://localhost
7 | FRONTEND_URL=http://localhost:3000
8 |
9 | APP_LOCALE=en
10 | APP_FALLBACK_LOCALE=en
11 | APP_FAKER_LOCALE=en_US
12 |
13 | APP_MAINTENANCE_DRIVER=file
14 | APP_MAINTENANCE_STORE=database
15 |
16 | BCRYPT_ROUNDS=12
17 |
18 | LOG_CHANNEL=stack
19 | LOG_STACK=single
20 | LOG_DEPRECATIONS_CHANNEL=null
21 | LOG_LEVEL=debug
22 |
23 | DB_CONNECTION=sqlite
24 | # DB_HOST=127.0.0.1
25 | # DB_PORT=3306
26 | # DB_DATABASE=laravel
27 | # DB_USERNAME=root
28 | # DB_PASSWORD=
29 |
30 | SESSION_DRIVER=database
31 | SESSION_LIFETIME=120
32 | SESSION_ENCRYPT=false
33 | SESSION_PATH=/
34 | SESSION_DOMAIN=null
35 |
36 | BROADCAST_CONNECTION=log
37 | FILESYSTEM_DISK=local
38 | QUEUE_CONNECTION=database
39 |
40 | CACHE_STORE=database
41 | CACHE_PREFIX=
42 |
43 | MEMCACHED_HOST=127.0.0.1
44 |
45 | REDIS_CLIENT=phpredis
46 | REDIS_HOST=127.0.0.1
47 | REDIS_PASSWORD=null
48 | REDIS_PORT=6379
49 |
50 | MAIL_MAILER=log
51 | MAIL_HOST=127.0.0.1
52 | MAIL_PORT=2525
53 | MAIL_USERNAME=null
54 | MAIL_PASSWORD=null
55 | MAIL_ENCRYPTION=null
56 | MAIL_FROM_ADDRESS="hello@example.com"
57 | MAIL_FROM_NAME="${APP_NAME}"
58 |
59 | AWS_ACCESS_KEY_ID=
60 | AWS_SECRET_ACCESS_KEY=
61 | AWS_DEFAULT_REGION=us-east-1
62 | AWS_BUCKET=
63 | AWS_USE_PATH_STYLE_ENDPOINT=false
64 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 |
3 | *.blade.php diff=html
4 | *.css diff=css
5 | *.html diff=html
6 | *.md diff=markdown
7 | *.php diff=php
8 |
9 | /.github export-ignore
10 | CHANGELOG.md export-ignore
11 | .styleci.yml export-ignore
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.phpunit.cache
2 | /node_modules
3 | /public/build
4 | /public/hot
5 | /public/storage
6 | /storage/*.key
7 | /vendor
8 | .env
9 | .env.backup
10 | .env.production
11 | .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 | .DS_Store
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Artem Manchenkov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |

2 |
3 | # Laravel Breeze API template
4 |
5 | This repository is a template for Laravel projects based on Sail environment to integrate with the Sanctum authentication.
6 |
7 | You can use this project to integrate with one of these frontend applications:
8 |
9 | - [Breeze Next](https://github.com/laravel/breeze-next/) (React/Next)
10 | - [Breeze Nuxt](https://github.com/manchenkoff/breeze-nuxt) (Vue/Nuxt)
11 |
12 | ## Prerequisites
13 |
14 | To work with this project you will also need to install the following software:
15 |
16 | - [Git](https://git-scm.com/)
17 | - [Docker](https://docker.com/)
18 | - [Justfile](https://just.systems/)
19 |
20 | ## Features
21 |
22 | - Laravel 12
23 | - Breeze API with Sanctum
24 | - Laravel Pint code formatter
25 | - Larastan static analysis rules
26 | - IDE helper for Laravel (Stubs generation)
27 |
28 | ## Installation
29 |
30 | 1. Clone the repository:
31 |
32 | ```bash
33 | git clone https://github.com/manchenkoff/breeze-api
34 | ```
35 |
36 | 2. Build the project and install dependencies:
37 |
38 | ```bash
39 | just build
40 | ```
41 |
42 | 3. Start the project:
43 |
44 | ```bash
45 | just start
46 | ```
47 |
48 | Once the project is started, you can access it at [http://localhost](http://localhost).
49 |
50 | ## Development
51 |
52 | To get more details about available commands in `justfile`, run the following command:
53 |
54 | ```bash
55 | just help
56 | ```
57 |
58 | To auto-format your code use `just fmt` command and also `just lint` to check the code quality by running Larastan checks.
59 |
60 | ## Production
61 |
62 | **Environment**
63 |
64 | To make sure that Laravel Sanctum will work on your production instance, make sure that you defined properly the following environment variables:
65 |
66 | ```dotenv
67 | APP_KEY=base64:your_key_here # Generate a new key using `php artisan key:generate --show`
68 | FRONTEND_URL=https://domain.com # Your frontend Nuxt application URL
69 | SESSION_DOMAIN=.domain.com # Your domain should start with a dot to support all subdomains like www.* or frontend.*
70 | ```
71 |
72 | _💡 Keep in mind, that `SESSION_DOMAIN` is not applicable for `localhost` and should not be used during development with the value other than `null`._
73 |
74 | **Multiple apps**
75 |
76 | If you have multiple frontend applications (e.g. public and admin apps), you can define the `SANCTUM_STATEFUL_DOMAINS` environment variable to allow multiple domains to access the same session.
77 |
78 | ```dotenv
79 | SANCTUM_STATEFUL_DOMAINS=domain.com,backoffice.domain.com,admin.domain.com
80 | ```
81 |
--------------------------------------------------------------------------------
/app/Broadcasting/PrivateUserChannel.php:
--------------------------------------------------------------------------------
1 | option('private')
34 | ? new PrivateMessageTriggered
35 | : new PublicMessageTriggered;
36 |
37 | broadcast($event);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/Events/PrivateMessageTriggered.php:
--------------------------------------------------------------------------------
1 |
32 | */
33 | public function broadcastOn(): array
34 | {
35 | return [
36 | new PrivateChannel(PrivateUserChannel::ROUTE),
37 | ];
38 | }
39 |
40 | public function broadcastAs(): string
41 | {
42 | return self::NAME;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/Events/PublicMessageTriggered.php:
--------------------------------------------------------------------------------
1 |
32 | */
33 | public function broadcastOn(): array
34 | {
35 | return [
36 | new Channel(PublicUserChannel::ROUTE),
37 | ];
38 | }
39 |
40 | public function broadcastAs(): string
41 | {
42 | return self::NAME;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Api/AuthenticatedUserController.php:
--------------------------------------------------------------------------------
1 | user();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Api/QuoteController.php:
--------------------------------------------------------------------------------
1 | random();
17 | [$text, $author] = explode(' - ', $quote);
18 |
19 | $data = [
20 | 'text' => $text,
21 | 'author' => $author,
22 | ];
23 |
24 | return response()->json($data);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Api/TokenAuthenticationController.php:
--------------------------------------------------------------------------------
1 | validate([
24 | 'email' => 'required|email',
25 | 'password' => 'required',
26 | ]);
27 |
28 | $user = User::whereEmail($request->email)->first();
29 |
30 | if (!$user) {
31 | throw ValidationException::withMessages([
32 | 'email' => ['The provided credentials are incorrect.'],
33 | ]);
34 | }
35 |
36 | /** @var string */
37 | $userPassword = $user->password;
38 | $requestPassword = $request->string('password')->toString();
39 |
40 | if (!Hash::check($requestPassword, $userPassword)) {
41 | throw ValidationException::withMessages([
42 | 'email' => ['The provided credentials are incorrect.'],
43 | ]);
44 | }
45 |
46 | $response = [
47 | 'token' => $user->createToken('web')->plainTextToken,
48 | ];
49 |
50 | return response()->json($response);
51 | }
52 |
53 | /**
54 | * Destroy an authenticated session.
55 | */
56 | public function destroy(Request $request): Response
57 | {
58 | /** @var User */
59 | $user = $request->user();
60 | /** @var PersonalAccessToken */
61 | $token = $user->currentAccessToken();
62 |
63 | $token->delete();
64 |
65 | return response()->noContent();
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/AuthenticatedSessionController.php:
--------------------------------------------------------------------------------
1 | authenticate();
21 | $request->session()->regenerate();
22 |
23 | return response()->noContent();
24 | }
25 |
26 | /**
27 | * Destroy an authenticated session.
28 | */
29 | public function destroy(Request $request): Response
30 | {
31 | Auth::guard('web')->logout();
32 |
33 | $request->session()->invalidate();
34 | $request->session()->regenerateToken();
35 |
36 | return response()->noContent();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/EmailVerificationNotificationController.php:
--------------------------------------------------------------------------------
1 | user();
21 |
22 | /** @var string $frontendUrl */
23 | $frontendUrl = config('app.frontend_url');
24 |
25 | if ($user->hasVerifiedEmail()) {
26 | return redirect()->intended(
27 | $frontendUrl . '/dashboard?verified=1'
28 | );
29 | }
30 |
31 | $user->sendEmailVerificationNotification();
32 |
33 | return response()->json(['status' => 'verification-link-sent']);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/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 |
36 | /** @var string */
37 | $status = Password::reset(
38 | $request->only('email', 'password', 'password_confirmation', 'token'),
39 | function ($user) use ($request) {
40 | /** @var string */
41 | $password = $request->password;
42 |
43 | $user
44 | ->forceFill([
45 | 'password' => Hash::make($password),
46 | 'remember_token' => Str::random(60),
47 | ])
48 | ->save();
49 |
50 | event(new PasswordReset($user));
51 | }
52 | );
53 |
54 | if ($status != Password::PASSWORD_RESET) {
55 | throw ValidationException::withMessages([
56 | 'email' => [__($status)],
57 | ]);
58 | }
59 |
60 | return response()->json(['status' => __($status)]);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/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 ($status != Password::RESET_LINK_SENT) {
34 | throw ValidationException::withMessages([
35 | 'email' => [__($status)],
36 | ]);
37 | }
38 |
39 | return response()->json(['status' => __($status)]);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/RegisteredUserController.php:
--------------------------------------------------------------------------------
1 | password;
26 |
27 | $user = User::create([
28 | 'name' => $request->name,
29 | 'email' => $request->email,
30 | 'password' => Hash::make($password),
31 | ]);
32 |
33 | event(new Registered($user));
34 |
35 | Auth::login($user);
36 |
37 | return response()->noContent();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Auth/VerifyEmailController.php:
--------------------------------------------------------------------------------
1 | user();
21 |
22 | /** @var string $frontendUrl */
23 | $frontendUrl = config('app.frontend_url');
24 |
25 | if ($user->hasVerifiedEmail()) {
26 | return redirect()->intended(
27 | $frontendUrl . '/dashboard?verified=1'
28 | );
29 | }
30 |
31 | if ($user->markEmailAsVerified()) {
32 | event(new Verified($user));
33 | }
34 |
35 | return redirect()->intended(
36 | $frontendUrl . '/dashboard?verified=1'
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Controller.php:
--------------------------------------------------------------------------------
1 | user();
23 |
24 | if (!$user || ($user instanceof MustVerifyEmail && !$user->hasVerifiedEmail())
25 | ) {
26 | return response()->json(['message' => 'Your email address is not verified.'], 409);
27 | }
28 |
29 | return $next($request);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/Http/Requests/Auth/LoginRequest.php:
--------------------------------------------------------------------------------
1 | |string>
28 | */
29 | public function rules(): array
30 | {
31 | return [
32 | 'email' => ['required', 'string', 'email'],
33 | 'password' => ['required', 'string'],
34 | 'remember' => ['boolean'],
35 | ];
36 | }
37 |
38 | /**
39 | * Attempt to authenticate the request's credentials.
40 | *
41 | * @throws \Illuminate\Validation\ValidationException
42 | */
43 | public function authenticate(): void
44 | {
45 | $this->ensureIsNotRateLimited();
46 |
47 | if (!Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
48 | RateLimiter::hit($this->throttleKey());
49 |
50 | throw ValidationException::withMessages([
51 | 'email' => __('auth.failed'),
52 | ]);
53 | }
54 |
55 | RateLimiter::clear($this->throttleKey());
56 | }
57 |
58 | /**
59 | * Ensure the login request is not rate limited.
60 | *
61 | * @throws \Illuminate\Validation\ValidationException
62 | */
63 | public function ensureIsNotRateLimited(): void
64 | {
65 | if (!RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
66 | return;
67 | }
68 |
69 | event(new Lockout($this));
70 |
71 | $seconds = RateLimiter::availableIn($this->throttleKey());
72 |
73 | throw ValidationException::withMessages([
74 | 'email' => trans('auth.throttle', [
75 | 'seconds' => $seconds,
76 | 'minutes' => ceil($seconds / 60),
77 | ]),
78 | ]);
79 | }
80 |
81 | /**
82 | * Get the rate limiting throttle key for the request.
83 | */
84 | public function throttleKey(): string
85 | {
86 | /** @var string */
87 | $email = $this->input('email');
88 |
89 | return Str::transliterate(Str::lower($email) . '|' . $this->ip());
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/Http/Requests/Auth/RegisterRequest.php:
--------------------------------------------------------------------------------
1 | |string>
25 | */
26 | public function rules(): array
27 | {
28 | return [
29 | 'name' => ['required', 'string', 'max:255'],
30 | 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:' . User::class],
31 | 'password' => ['required', 'confirmed', Password::defaults()],
32 | ];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/Models/User.php:
--------------------------------------------------------------------------------
1 | $notifications
24 | * @property-read int|null $notifications_count
25 | * @property-read \Illuminate\Database\Eloquent\Collection $tokens
26 | * @property-read int|null $tokens_count
27 | *
28 | * @method static \Database\Factories\UserFactory factory($count = null, $state = [])
29 | * @method static \Illuminate\Database\Eloquent\Builder|User newModelQuery()
30 | * @method static \Illuminate\Database\Eloquent\Builder|User newQuery()
31 | * @method static \Illuminate\Database\Eloquent\Builder|User query()
32 | * @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value)
33 | * @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value)
34 | * @method static \Illuminate\Database\Eloquent\Builder|User whereEmailVerifiedAt($value)
35 | * @method static \Illuminate\Database\Eloquent\Builder|User whereId($value)
36 | * @method static \Illuminate\Database\Eloquent\Builder|User whereName($value)
37 | * @method static \Illuminate\Database\Eloquent\Builder|User wherePassword($value)
38 | * @method static \Illuminate\Database\Eloquent\Builder|User whereRememberToken($value)
39 | * @method static \Illuminate\Database\Eloquent\Builder|User whereUpdatedAt($value)
40 | *
41 | * @mixin \Eloquent
42 | */
43 | final class User extends Authenticatable implements MustVerifyEmail
44 | {
45 | use HasApiTokens;
46 |
47 | /** @use HasFactory */
48 | use HasFactory;
49 |
50 | use Notifiable;
51 |
52 | protected $fillable = [
53 | 'name',
54 | 'email',
55 | 'password',
56 | ];
57 |
58 | protected $hidden = [
59 | 'password',
60 | 'remember_token',
61 | ];
62 |
63 | /**
64 | * Get the attributes that should be cast.
65 | *
66 | * @return array
67 | */
68 | protected function casts(): array
69 | {
70 | return [
71 | 'email_verified_at' => 'datetime',
72 | 'password' => 'hashed',
73 | ];
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | getEmailForPasswordReset()}";
33 | }
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | handleCommand(new ArgvInput);
14 |
15 | exit($status);
16 |
--------------------------------------------------------------------------------
/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | withRouting(
12 | web: __DIR__ . '/../routes/web.php',
13 | api: __DIR__ . '/../routes/api.php',
14 | commands: __DIR__ . '/../routes/console.php',
15 | health: '/up',
16 | )
17 | ->withBroadcasting(
18 | __DIR__ . '/../routes/channels.php',
19 | [
20 | 'middleware' => ['api', 'auth:sanctum'],
21 | ]
22 | )
23 | ->withMiddleware(function (Middleware $middleware) {
24 | $middleware->alias([
25 | 'verified' => EnsureEmailIsVerified::class,
26 | ]);
27 |
28 | $middleware->statefulApi();
29 | })
30 | ->withExceptions(function (Exceptions $exceptions) {
31 | //
32 | })
33 | ->create();
34 |
--------------------------------------------------------------------------------
/bootstrap/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/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 | 'frontend_url' => env('FRONTEND_URL', 'http://localhost:3000'),
60 |
61 | /*
62 | |--------------------------------------------------------------------------
63 | | Application Timezone
64 | |--------------------------------------------------------------------------
65 | |
66 | | Here you may specify the default timezone for your application, which
67 | | will be used by the PHP date and date-time functions. The timezone
68 | | is set to "UTC" by default as it is suitable for most use cases.
69 | |
70 | */
71 |
72 | 'timezone' => env('APP_TIMEZONE', 'UTC'),
73 |
74 | /*
75 | |--------------------------------------------------------------------------
76 | | Application Locale Configuration
77 | |--------------------------------------------------------------------------
78 | |
79 | | The application locale determines the default locale that will be used
80 | | by Laravel's translation / localization methods. This option can be
81 | | set to any locale for which you plan to have translation strings.
82 | |
83 | */
84 |
85 | 'locale' => env('APP_LOCALE', 'en'),
86 |
87 | 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
88 |
89 | 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
90 |
91 | /*
92 | |--------------------------------------------------------------------------
93 | | Encryption Key
94 | |--------------------------------------------------------------------------
95 | |
96 | | This key is utilized by Laravel's encryption services and should be set
97 | | to a random, 32 character string to ensure that all encrypted values
98 | | are secure. You should do this prior to deploying the application.
99 | |
100 | */
101 |
102 | 'cipher' => 'AES-256-CBC',
103 |
104 | 'key' => env('APP_KEY'),
105 |
106 | 'previous_keys' => [
107 | ...array_filter(
108 | explode(',', env('APP_PREVIOUS_KEYS', ''))
109 | ),
110 | ],
111 |
112 | /*
113 | |--------------------------------------------------------------------------
114 | | Maintenance Mode Driver
115 | |--------------------------------------------------------------------------
116 | |
117 | | These configuration options determine the driver used to determine and
118 | | manage Laravel's "maintenance mode" status. The "cache" driver will
119 | | allow maintenance mode to be controlled across multiple machines.
120 | |
121 | | Supported drivers: "file", "cache"
122 | |
123 | */
124 |
125 | 'maintenance' => [
126 | 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
127 | 'store' => env('APP_MAINTENANCE_STORE', 'database'),
128 | ],
129 |
130 | ];
131 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/config/broadcasting.php:
--------------------------------------------------------------------------------
1 | env('BROADCAST_CONNECTION', 'null'),
21 |
22 | /*
23 | |--------------------------------------------------------------------------
24 | | Broadcast Connections
25 | |--------------------------------------------------------------------------
26 | |
27 | | Here you may define all of the broadcast connections that will be used
28 | | to broadcast events to other systems or over WebSockets. Samples of
29 | | each available type of connection are provided inside this array.
30 | |
31 | */
32 |
33 | 'connections' => [
34 |
35 | 'reverb' => [
36 | 'driver' => 'reverb',
37 | 'key' => env('REVERB_APP_KEY'),
38 | 'secret' => env('REVERB_APP_SECRET'),
39 | 'app_id' => env('REVERB_APP_ID'),
40 | 'options' => [
41 | 'host' => env('REVERB_HOST'),
42 | 'port' => env('REVERB_PORT', 443),
43 | 'scheme' => env('REVERB_SCHEME', 'https'),
44 | 'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
45 | ],
46 | 'client_options' => [
47 | // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
48 | ],
49 | ],
50 |
51 | 'pusher' => [
52 | 'driver' => 'pusher',
53 | 'key' => env('PUSHER_APP_KEY'),
54 | 'secret' => env('PUSHER_APP_SECRET'),
55 | 'app_id' => env('PUSHER_APP_ID'),
56 | 'options' => [
57 | 'cluster' => env('PUSHER_APP_CLUSTER'),
58 | 'host' => env('PUSHER_HOST') ?: 'api-' . env('PUSHER_APP_CLUSTER', 'mt1') . '.pusher.com',
59 | 'port' => env('PUSHER_PORT', 443),
60 | 'scheme' => env('PUSHER_SCHEME', 'https'),
61 | 'encrypted' => true,
62 | 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
63 | ],
64 | 'client_options' => [
65 | // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
66 | ],
67 | ],
68 |
69 | 'ably' => [
70 | 'driver' => 'ably',
71 | 'key' => env('ABLY_KEY'),
72 | ],
73 |
74 | 'log' => [
75 | 'driver' => 'log',
76 | ],
77 |
78 | 'null' => [
79 | 'driver' => 'null',
80 | ],
81 |
82 | ],
83 |
84 | ];
85 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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' => ['Precognition', 'Precognition-Success'],
31 |
32 | 'max_age' => 0,
33 |
34 | 'supports_credentials' => true,
35 |
36 | ];
37 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/config/reverb.php:
--------------------------------------------------------------------------------
1 | env('REVERB_SERVER', 'reverb'),
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Reverb Servers
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may define details for each of the supported Reverb servers.
26 | | Each server has its own configuration options that are defined in
27 | | the array below. You should ensure all the options are present.
28 | |
29 | */
30 |
31 | 'servers' => [
32 |
33 | 'reverb' => [
34 | 'host' => env('REVERB_SERVER_HOST', '0.0.0.0'),
35 | 'port' => env('REVERB_SERVER_PORT', 8080),
36 | 'hostname' => env('REVERB_HOST'),
37 | 'options' => [
38 | 'tls' => [],
39 | ],
40 | 'max_request_size' => env('REVERB_MAX_REQUEST_SIZE', 10_000),
41 | 'scaling' => [
42 | 'enabled' => env('REVERB_SCALING_ENABLED', false),
43 | 'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'),
44 | 'server' => [
45 | 'url' => env('REDIS_URL'),
46 | 'host' => env('REDIS_HOST', '127.0.0.1'),
47 | 'port' => env('REDIS_PORT', '6379'),
48 | 'username' => env('REDIS_USERNAME'),
49 | 'password' => env('REDIS_PASSWORD'),
50 | 'database' => env('REDIS_DB', '0'),
51 | ],
52 | ],
53 | 'pulse_ingest_interval' => env('REVERB_PULSE_INGEST_INTERVAL', 15),
54 | 'telescope_ingest_interval' => env('REVERB_TELESCOPE_INGEST_INTERVAL', 15),
55 | ],
56 |
57 | ],
58 |
59 | /*
60 | |--------------------------------------------------------------------------
61 | | Reverb Applications
62 | |--------------------------------------------------------------------------
63 | |
64 | | Here you may define how Reverb applications are managed. If you choose
65 | | to use the "config" provider, you may define an array of apps which
66 | | your server will support, including their connection credentials.
67 | |
68 | */
69 |
70 | 'apps' => [
71 |
72 | 'provider' => 'config',
73 |
74 | 'apps' => [
75 | [
76 | 'key' => env('REVERB_APP_KEY'),
77 | 'secret' => env('REVERB_APP_SECRET'),
78 | 'app_id' => env('REVERB_APP_ID'),
79 | 'options' => [
80 | 'host' => env('REVERB_HOST'),
81 | 'port' => env('REVERB_PORT', 443),
82 | 'scheme' => env('REVERB_SCHEME', 'https'),
83 | 'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
84 | ],
85 | 'allowed_origins' => ['localhost'],
86 | 'ping_interval' => env('REVERB_APP_PING_INTERVAL', 60),
87 | 'max_message_size' => env('REVERB_APP_MAX_MESSAGE_SIZE', 10_000),
88 | ],
89 | ],
90 |
91 | ],
92 |
93 | ];
94 |
--------------------------------------------------------------------------------
/config/sanctum.php:
--------------------------------------------------------------------------------
1 | explode(
21 | ',',
22 | env('SANCTUM_STATEFUL_DOMAINS', sprintf(
23 | '%s%s%s',
24 | 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
25 | Sanctum::currentApplicationUrlWithPort(),
26 | env('FRONTEND_URL') ? ',' . parse_url(env('FRONTEND_URL'), PHP_URL_HOST) : ''
27 | ))
28 | ),
29 |
30 | /*
31 | |--------------------------------------------------------------------------
32 | | Sanctum Guards
33 | |--------------------------------------------------------------------------
34 | |
35 | | This array contains the authentication guards that will be checked when
36 | | Sanctum is trying to authenticate a request. If none of these guards
37 | | are able to authenticate the request, Sanctum will use the bearer
38 | | token that's present on an incoming request for authentication.
39 | |
40 | */
41 |
42 | 'guard' => ['web'],
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | Expiration Minutes
47 | |--------------------------------------------------------------------------
48 | |
49 | | This value controls the number of minutes until an issued token will be
50 | | considered expired. This will override any values set in the token's
51 | | "expires_at" attribute, but first-party sessions are not affected.
52 | |
53 | */
54 |
55 | 'expiration' => null,
56 |
57 | /*
58 | |--------------------------------------------------------------------------
59 | | Token Prefix
60 | |--------------------------------------------------------------------------
61 | |
62 | | Sanctum can prefix new tokens in order to take advantage of numerous
63 | | security scanning initiatives maintained by open source platforms
64 | | that notify developers if they commit tokens into repositories.
65 | |
66 | | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning
67 | |
68 | */
69 |
70 | 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),
71 |
72 | /*
73 | |--------------------------------------------------------------------------
74 | | Sanctum Middleware
75 | |--------------------------------------------------------------------------
76 | |
77 | | When authenticating your first-party SPA with Sanctum you may need to
78 | | customize some of the middleware Sanctum uses while processing the
79 | | request. You may change the middleware listed below as required.
80 | |
81 | */
82 |
83 | 'middleware' => [
84 | 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
85 | 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class,
86 | 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
87 | ],
88 |
89 | ];
90 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/database/.gitignore:
--------------------------------------------------------------------------------
1 | *.sqlite*
2 |
--------------------------------------------------------------------------------
/database/factories/UserFactory.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | final class UserFactory extends Factory
15 | {
16 | /**
17 | * The current password being used by the factory.
18 | */
19 | protected static ?string $password;
20 |
21 | /**
22 | * Define the model's default state.
23 | *
24 | * @return array
25 | */
26 | public function definition(): array
27 | {
28 | return [
29 | 'name' => fake()->name(),
30 | 'email' => fake()->unique()->safeEmail(),
31 | 'email_verified_at' => now(),
32 | 'password' => self::$password ??= Hash::make('password'),
33 | 'remember_token' => Str::random(10),
34 | ];
35 | }
36 |
37 | /**
38 | * Indicate that the model's email address should be unverified.
39 | */
40 | public function unverified(): static
41 | {
42 | return $this->state(fn (array $attributes) => [
43 | 'email_verified_at' => null,
44 | ]);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/database/migrations/0001_01_01_000000_create_users_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('name');
19 | $table->string('email')->unique();
20 | $table->timestamp('email_verified_at')->nullable();
21 | $table->string('password');
22 | $table->rememberToken();
23 | $table->timestamps();
24 | });
25 |
26 | Schema::create('password_reset_tokens', function (Blueprint $table) {
27 | $table->string('email')->primary();
28 | $table->string('token');
29 | $table->timestamp('created_at')->nullable();
30 | });
31 |
32 | Schema::create('sessions', function (Blueprint $table) {
33 | $table->string('id')->primary();
34 | $table->foreignId('user_id')->nullable()->index();
35 | $table->string('ip_address', 45)->nullable();
36 | $table->text('user_agent')->nullable();
37 | $table->longText('payload');
38 | $table->integer('last_activity')->index();
39 | });
40 | }
41 |
42 | /**
43 | * Reverse the migrations.
44 | */
45 | public function down(): void
46 | {
47 | Schema::dropIfExists('users');
48 | Schema::dropIfExists('password_reset_tokens');
49 | Schema::dropIfExists('sessions');
50 | }
51 | };
52 |
--------------------------------------------------------------------------------
/database/migrations/0001_01_01_000001_create_cache_table.php:
--------------------------------------------------------------------------------
1 | string('key')->primary();
18 | $table->mediumText('value');
19 | $table->integer('expiration');
20 | });
21 |
22 | Schema::create('cache_locks', function (Blueprint $table) {
23 | $table->string('key')->primary();
24 | $table->string('owner');
25 | $table->integer('expiration');
26 | });
27 | }
28 |
29 | /**
30 | * Reverse the migrations.
31 | */
32 | public function down(): void
33 | {
34 | Schema::dropIfExists('cache');
35 | Schema::dropIfExists('cache_locks');
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/database/migrations/0001_01_01_000002_create_jobs_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('queue')->index();
19 | $table->longText('payload');
20 | $table->unsignedTinyInteger('attempts');
21 | $table->unsignedInteger('reserved_at')->nullable();
22 | $table->unsignedInteger('available_at');
23 | $table->unsignedInteger('created_at');
24 | });
25 |
26 | Schema::create('job_batches', function (Blueprint $table) {
27 | $table->string('id')->primary();
28 | $table->string('name');
29 | $table->integer('total_jobs');
30 | $table->integer('pending_jobs');
31 | $table->integer('failed_jobs');
32 | $table->longText('failed_job_ids');
33 | $table->mediumText('options')->nullable();
34 | $table->integer('cancelled_at')->nullable();
35 | $table->integer('created_at');
36 | $table->integer('finished_at')->nullable();
37 | });
38 |
39 | Schema::create('failed_jobs', function (Blueprint $table) {
40 | $table->id();
41 | $table->string('uuid')->unique();
42 | $table->text('connection');
43 | $table->text('queue');
44 | $table->longText('payload');
45 | $table->longText('exception');
46 | $table->timestamp('failed_at')->useCurrent();
47 | });
48 | }
49 |
50 | /**
51 | * Reverse the migrations.
52 | */
53 | public function down(): void
54 | {
55 | Schema::dropIfExists('jobs');
56 | Schema::dropIfExists('job_batches');
57 | Schema::dropIfExists('failed_jobs');
58 | }
59 | };
60 |
--------------------------------------------------------------------------------
/database/migrations/2024_05_01_115424_create_personal_access_tokens_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->morphs('tokenable');
19 | $table->string('name');
20 | $table->string('token', 64)->unique();
21 | $table->text('abilities')->nullable();
22 | $table->timestamp('last_used_at')->nullable();
23 | $table->timestamp('expires_at')->nullable();
24 | $table->timestamps();
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | */
31 | public function down(): void
32 | {
33 | Schema::dropIfExists('personal_access_tokens');
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/database/seeders/DatabaseSeeder.php:
--------------------------------------------------------------------------------
1 | create();
21 |
22 | User::factory()->create([
23 | 'name' => 'Test User',
24 | 'email' => 'test@example.com',
25 | ]);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | laravel.test:
3 | build:
4 | context: ./vendor/laravel/sail/runtimes/8.4
5 | dockerfile: Dockerfile
6 | args:
7 | WWWGROUP: "${WWWGROUP}"
8 | image: sail-8.4/app
9 | extra_hosts:
10 | - "host.docker.internal:host-gateway"
11 | ports:
12 | - "${APP_PORT:-80}:80"
13 | - "${VITE_PORT:-5173}:${VITE_PORT:-5173}"
14 | - 8080:8080
15 | environment:
16 | WWWUSER: "${WWWUSER}"
17 | LARAVEL_SAIL: 1
18 | XDEBUG_MODE: "${SAIL_XDEBUG_MODE:-off}"
19 | XDEBUG_CONFIG: "${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}"
20 | IGNITION_LOCAL_SITES_PATH: "${PWD}"
21 | volumes:
22 | - ".:/var/www/html"
23 | networks:
24 | - sail
25 | depends_on:
26 | - mysql
27 | - redis
28 | - meilisearch
29 | - mailpit
30 | - selenium
31 | mysql:
32 | image: "mysql/mysql-server:8.0"
33 | ports:
34 | - "${FORWARD_DB_PORT:-3306}:3306"
35 | environment:
36 | MYSQL_ROOT_PASSWORD: "${DB_PASSWORD}"
37 | MYSQL_ROOT_HOST: "%"
38 | MYSQL_DATABASE: "${DB_DATABASE}"
39 | MYSQL_USER: "${DB_USERNAME}"
40 | MYSQL_PASSWORD: "${DB_PASSWORD}"
41 | MYSQL_ALLOW_EMPTY_PASSWORD: 1
42 | volumes:
43 | - "sail-mysql:/var/lib/mysql"
44 | - "./vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh"
45 | networks:
46 | - sail
47 | healthcheck:
48 | test:
49 | - CMD
50 | - mysqladmin
51 | - ping
52 | - "-p${DB_PASSWORD}"
53 | retries: 3
54 | timeout: 5s
55 | redis:
56 | image: "redis:alpine"
57 | ports:
58 | - "${FORWARD_REDIS_PORT:-6379}:6379"
59 | volumes:
60 | - "sail-redis:/data"
61 | networks:
62 | - sail
63 | healthcheck:
64 | test:
65 | - CMD
66 | - redis-cli
67 | - ping
68 | retries: 3
69 | timeout: 5s
70 | meilisearch:
71 | image: "getmeili/meilisearch:latest"
72 | ports:
73 | - "${FORWARD_MEILISEARCH_PORT:-7700}:7700"
74 | environment:
75 | MEILI_NO_ANALYTICS: "${MEILISEARCH_NO_ANALYTICS:-false}"
76 | volumes:
77 | - "sail-meilisearch:/meili_data"
78 | networks:
79 | - sail
80 | healthcheck:
81 | test:
82 | - CMD
83 | - wget
84 | - "--no-verbose"
85 | - "--spider"
86 | - "http://localhost:7700/health"
87 | retries: 3
88 | timeout: 5s
89 | mailpit:
90 | image: "axllent/mailpit:latest"
91 | ports:
92 | - "${FORWARD_MAILPIT_PORT:-1025}:1025"
93 | - "${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}:8025"
94 | networks:
95 | - sail
96 | selenium:
97 | image: seleniarm/standalone-chromium
98 | extra_hosts:
99 | - "host.docker.internal:host-gateway"
100 | volumes:
101 | - "/dev/shm:/dev/shm"
102 | networks:
103 | - sail
104 | networks:
105 | sail:
106 | driver: bridge
107 | volumes:
108 | sail-mysql:
109 | driver: local
110 | sail-redis:
111 | driver: local
112 | sail-meilisearch:
113 | driver: local
114 |
--------------------------------------------------------------------------------
/justfile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env just --justfile
2 |
3 | set quiet := true
4 |
5 | sail := "./vendor/bin/sail"
6 |
7 | # show help
8 | help:
9 | just --list
10 |
11 | # Install Composer dependencies (w/o Sail)
12 | [group('project')]
13 | _composer-install:
14 | test -d 'vendor' && echo "Vendor directory exists, skip" || docker run --rm \
15 | -u "$(id -u):$(id -g)" \
16 | -v "$(pwd):/var/www/html" \
17 | -w /var/www/html \
18 | laravelsail/php82-composer:latest \
19 | composer install --ignore-platform-reqs
20 |
21 | # Build Laravel Sail application image
22 | [group('project')]
23 | build:
24 | test -f '.env' && echo "Env file exists, skip" || cp .env.example .env
25 | just _composer-install
26 | {{sail}} build --no-cache
27 | just start
28 | just deps
29 | just migrate
30 | just stop
31 |
32 | # Start Laravel Sail containers
33 | [group('project')]
34 | start:
35 | {{sail}} up -d
36 |
37 | # Restart Laravel Sail containers
38 | [group('project')]
39 | restart:
40 | {{sail}} restart
41 |
42 | # Stop Laravel Sail containers
43 | [group('project')]
44 | stop:
45 | {{sail}} down
46 |
47 | # Stop Laravel Sail containers and remove volumes
48 | [group('project')]
49 | purge:
50 | {{sail}} down -v
51 |
52 | # Install Composer dependencies
53 | [group('sail')]
54 | deps:
55 | {{sail}} composer install
56 |
57 | # Attach PHP container console
58 | [group('sail')]
59 | shell:
60 | {{sail}} shell
61 |
62 | # Run Laravel scheduler (cron)
63 | [group('sail')]
64 | schedule:
65 | {{sail}} artisan schedule:work
66 |
67 | # Run Laravel worker (queue)
68 | [group('sail')]
69 | queue:
70 | {{sail}} artisan queue:listen -v --timeout=0
71 |
72 | # Run command inside of Laravel Sail PHP container, e.g. [just sail artisan help]
73 | [group('sail')]
74 | sail +command:
75 | {{sail}} {{command}}
76 |
77 | # Optimize Laravel cache
78 | [group('sail')]
79 | cache:
80 | {{sail}} artisan optimize
81 | {{sail}} artisan event:cache
82 | {{sail}} artisan config:cache
83 | {{sail}} artisan route:cache
84 | {{sail}} artisan view:cache
85 | {{sail}} artisan storage:link
86 |
87 | # Clear Laravel cache
88 | [group('sail')]
89 | cache-clear:
90 | {{sail}} artisan cache:clear
91 | {{sail}} artisan config:clear
92 | {{sail}} artisan event:clear
93 | {{sail}} artisan optimize:clear
94 | {{sail}} artisan route:clear
95 | {{sail}} artisan view:clear
96 |
97 | # Run Laravel Sail application tests
98 | [group('sail')]
99 | test:
100 | {{sail}} test
101 |
102 | # Run database migrations
103 | [group('sql')]
104 | migrate:
105 | {{sail}} artisan migrate
106 |
107 | # Attach SQL container console
108 | [group('sql')]
109 | sql:
110 | {{sail}} artisan db
111 |
112 | # Recreate database from scratch
113 | [group('sql')]
114 | migrate-fresh:
115 | {{sail}} artisan migrate:fresh
116 |
117 | # Seed initial records into database
118 | [group('sql')]
119 | seed:
120 | {{sail}} artisan migrate:fresh --seed
121 |
122 | # Generate Laravel stubs and model comments
123 | [group('tools')]
124 | stubs:
125 | {{sail}} artisan clear-compiled
126 | {{sail}} artisan ide-helper:models -W
127 | just fmt
128 |
129 | # Run Code Style formatter
130 | [group('tools')]
131 | fmt:
132 | {{sail}} composer fmt
133 |
134 | # Run static analysis
135 | [group('tools')]
136 | lint:
137 | {{sail}} composer validate
138 | {{sail}} composer lint
139 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | includes:
2 | - ./vendor/larastan/larastan/extension.neon
3 | parameters:
4 | level: 9
5 | paths:
6 | - app
7 | - database/factories
8 | - database/seeders
9 | ignoreErrors: []
10 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/pint.json:
--------------------------------------------------------------------------------
1 | {
2 | "preset": "laravel",
3 | "rules": {
4 | "concat_space": {
5 | "spacing": "one"
6 | },
7 | "declare_strict_types": true,
8 | "not_operator_with_successor_space": false,
9 | "final_class": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 |
3 | Options -MultiViews -Indexes
4 |
5 |
6 | RewriteEngine On
7 |
8 | # Handle Authorization Header
9 | RewriteCond %{HTTP:Authorization} .
10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
11 |
12 | # Redirect Trailing Slashes If Not A Folder...
13 | RewriteCond %{REQUEST_FILENAME} !-d
14 | RewriteCond %{REQUEST_URI} (.+)/$
15 | RewriteRule ^ %1 [L,R=301]
16 |
17 | # Send Requests To Front Controller...
18 | RewriteCond %{REQUEST_FILENAME} !-d
19 | RewriteCond %{REQUEST_FILENAME} !-f
20 | RewriteRule ^ index.php [L]
21 |
22 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manchenkoff/breeze-api/c656e49672bf597f37528e0abc673a4472b4047d/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | handleRequest(Request::capture());
20 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/routes/api.php:
--------------------------------------------------------------------------------
1 | get('/user', AuthenticatedUserController::class);
11 |
12 | Route::middleware(['guest'])->post('/login', [TokenAuthenticationController::class, 'store']);
13 | Route::middleware(['auth:sanctum'])->post('/logout', [TokenAuthenticationController::class, 'destroy']);
14 |
15 | Route::get('/quote', QuoteController::class);
16 |
--------------------------------------------------------------------------------
/routes/auth.php:
--------------------------------------------------------------------------------
1 | middleware(['guest', HandlePrecognitiveRequests::class])
16 | ->name('register');
17 |
18 | Route::post('/login', [AuthenticatedSessionController::class, 'store'])
19 | ->middleware('guest')
20 | ->name('login');
21 |
22 | Route::post('/forgot-password', [PasswordResetLinkController::class, 'store'])
23 | ->middleware('guest')
24 | ->name('password.email');
25 |
26 | Route::post('/reset-password', [NewPasswordController::class, 'store'])
27 | ->middleware('guest')
28 | ->name('password.store');
29 |
30 | Route::get('/verify-email/{id}/{hash}', VerifyEmailController::class)
31 | ->middleware(['auth', 'signed', 'throttle:6,1'])
32 | ->name('verification.verify');
33 |
34 | Route::post('/email/verification-notification', [EmailVerificationNotificationController::class, 'store'])
35 | ->middleware(['auth', 'throttle:6,1'])
36 | ->name('verification.send');
37 |
38 | Route::post('/logout', [AuthenticatedSessionController::class, 'destroy'])
39 | ->middleware('auth')
40 | ->name('logout');
41 |
--------------------------------------------------------------------------------
/routes/channels.php:
--------------------------------------------------------------------------------
1 | comment(Inspiring::quote());
12 | }
13 | )->purpose('Display an inspiring quote')->hourly();
14 |
--------------------------------------------------------------------------------
/routes/web.php:
--------------------------------------------------------------------------------
1 | app()->version()];
9 | });
10 |
11 | require __DIR__ . '/auth.php';
12 |
--------------------------------------------------------------------------------
/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !public/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/storage/app/public/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/storage/framework/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !data/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/storage/framework/cache/data/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/sessions/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/testing/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/tests/Feature/Auth/AuthenticationTest.php:
--------------------------------------------------------------------------------
1 | create();
18 |
19 | $response = $this->post('/login', [
20 | 'email' => $user->email,
21 | 'password' => 'password',
22 | ]);
23 |
24 | $this->assertAuthenticated();
25 | $response->assertNoContent();
26 | }
27 |
28 | public function test_users_can_not_authenticate_with_invalid_password(): void
29 | {
30 | $user = User::factory()->create();
31 |
32 | $this->post('/login', [
33 | 'email' => $user->email,
34 | 'password' => 'wrong-password',
35 | ]);
36 |
37 | $this->assertGuest();
38 | }
39 |
40 | public function test_users_can_logout(): void
41 | {
42 | $user = User::factory()->create();
43 |
44 | $response = $this->actingAs($user)->post('/logout');
45 |
46 | $this->assertGuest();
47 | $response->assertNoContent();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/Feature/Auth/EmailVerificationTest.php:
--------------------------------------------------------------------------------
1 | unverified()->create();
21 |
22 | Event::fake();
23 |
24 | $verificationUrl = URL::temporarySignedRoute(
25 | 'verification.verify',
26 | now()->addMinutes(60),
27 | ['id' => $user->id, 'hash' => sha1($user->email)]
28 | );
29 |
30 | $response = $this->actingAs($user)->get($verificationUrl);
31 |
32 | Event::assertDispatched(Verified::class);
33 | $this->assertTrue($user->fresh()->hasVerifiedEmail());
34 | $response->assertRedirect(config('app.frontend_url') . '/dashboard?verified=1');
35 | }
36 |
37 | public function test_email_is_not_verified_with_invalid_hash(): void
38 | {
39 | $user = User::factory()->unverified()->create();
40 |
41 | $verificationUrl = URL::temporarySignedRoute(
42 | 'verification.verify',
43 | now()->addMinutes(60),
44 | ['id' => $user->id, 'hash' => sha1('wrong-email')]
45 | );
46 |
47 | $this->actingAs($user)->get($verificationUrl);
48 |
49 | $this->assertFalse($user->fresh()->hasVerifiedEmail());
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tests/Feature/Auth/PasswordResetTest.php:
--------------------------------------------------------------------------------
1 | create();
22 |
23 | $this->post('/forgot-password', ['email' => $user->email]);
24 |
25 | Notification::assertSentTo($user, ResetPassword::class);
26 | }
27 |
28 | public function test_password_can_be_reset_with_valid_token(): void
29 | {
30 | Notification::fake();
31 |
32 | $user = User::factory()->create();
33 |
34 | $this->post('/forgot-password', ['email' => $user->email]);
35 |
36 | Notification::assertSentTo($user, ResetPassword::class, function (object $notification) use ($user) {
37 | $response = $this->post('/reset-password', [
38 | 'token' => $notification->token,
39 | 'email' => $user->email,
40 | 'password' => 'password',
41 | 'password_confirmation' => 'password',
42 | ]);
43 |
44 | $response
45 | ->assertSessionHasNoErrors()
46 | ->assertStatus(200);
47 |
48 | return true;
49 | });
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tests/Feature/Auth/RegistrationTest.php:
--------------------------------------------------------------------------------
1 | post('/register', [
17 | 'name' => 'Test User',
18 | 'email' => 'test@example.com',
19 | 'password' => 'password',
20 | 'password_confirmation' => 'password',
21 | ]);
22 |
23 | $this->assertAuthenticated();
24 | $response->assertNoContent();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tests/Feature/ExampleTest.php:
--------------------------------------------------------------------------------
1 | get('/');
18 |
19 | $response->assertStatus(200);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | assertTrue(true);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------