#1 - project skeleton (#7)

* #1 - project skeleton

* #1 - composer fix

* #1 - add app key to phpunit config

* #1 - change default session driver

* #1 - add EXTERNAL_WEBSERVER_PORT variable to .env.example
This commit is contained in:
Adrian Hopek 2022-01-10 13:28:18 +01:00 committed by GitHub
parent 683b2367ea
commit 8f5f2b88f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
90 changed files with 25980 additions and 1 deletions

46
.env.example Normal file
View File

@ -0,0 +1,46 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=toby-db
DB_PORT=3306
DB_DATABASE=toby
DB_USERNAME=toby
DB_PASSWORD=password
DB_ROOT_PASSWORD=example
DOCKER_DB_EXTERNAL_PORT=3306
DOCKER_DB_DATABASE=${DB_DATABASE}
DOCKER_DB_USERNAME=${DB_USERNAME}
DOCKER_DB_PASSWORD=${DB_PASSWORD}
DOCKER_DB_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
EXTERNAL_WEBSERVER_PORT=
XDG_CONFIG_HOME=/tmp
BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
FILESYSTEM_DRIVER=local
MAILHOG_PORT=1025
MAILHOG_DASHBOARD_PORT=8025
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=${MAILHOG_PORT}
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
DOCKER_INSTALL_XDEBUG=false

20
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,20 @@
version: 2
updates:
- package-ecosystem: composer
directory: "/"
schedule:
interval: monthly
time: "06:30"
commit-message:
prefix: "#5 - (php) "
target-branch: main
open-pull-requests-limit: 1
- package-ecosystem: npm
directory: "/"
schedule:
interval: monthly
time: "06:30"
commit-message:
prefix: "#5 - (js) "
target-branch: main
open-pull-requests-limit: 1

16
.github/workflows/check-pr-title.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Check PR Title
on:
pull_request:
types:
- opened
- edited
- synchronize
- labeled
- unlabeled
jobs:
check-pr-title:
name: Check PR title
runs-on: ubuntu-20.04
steps:
- uses: blumilksoftware/action-pr-title@v1.2.0

40
.github/workflows/test-and-lint.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: Build & Test
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Validate composer.json and composer.lock
run: composer validate
- name: Cache dependencies
uses: actions/cache@v2
with:
path: vendor
key: ${{ runner.os }}-composer-dependencies-${{ hashFiles('composer.lock') }}
restore-keys: ${{ runner.os }}-composer-dependencies
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, intl
coverage: none
- name: Install Composer dependencies
run: composer install --prefer-dist --no-interaction --no-suggest
- name: Run linter
run: composer ecs
- name: Execute tests
run: php artisan test --env=ci

19
.gitignore vendored Normal file
View File

@ -0,0 +1,19 @@
/node_modules
/public/hot
/public/storage
/public/js/
/public/css/
/public/mix-manifest.json
/storage/*.key
/storage/*.sqlite
/vendor
.env
.env.backup
.phpunit.result.cache
docker-compose.override.yml
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
.idea/
.composer

View File

@ -1,2 +1,83 @@
# toby # Toby
HR software you love to hate HR software you love to hate
## Local setup
> `dcr` is an alias to `docker-compose run --rm -u "$(id -u):$(id -g)"`
- clone the repository
- initialize `.env` file and customize if needed
cp .env.example .env
- build containers
docker-compose build --no-cache --pull
- run containers
docker-compose up -d
- install composer packages
dcr php composer install
- generate app key
dcr php php artisan key:generate
- generate storage link
dcr php php artisan storage:link
- migrate and seed database
dcr php php artisan migrate --seed
## Available containers (local)
- **php** - php and composer stuff
- **node** - npm stuff
- **mysql** - database for local development
- **mailhog** - for emails preview
## Running tests
If xDebug is installed, set environment variable **XDEBUG_MODE=off** to improve performance
dcr -e XDEBUG_MODE=off php php artisan test
## Code style check
dcr php php vendor/bin/ecs check
dcr php composer ecs
dcr php php vendor/bin/ecs check --fix
dcr php composer ecsf
## xDebug
To use xDebug you need to set `DOCKER_INSTALL_XDEBUG` to `true` in `.env` file.\
Then rebuild php container `docker-compose up --build -d php`.\
You can also set up xDebug params (see docs https://xdebug.org/docs/all_settings) in `docker/dev/php/php.ini` file:
Default values for xDebug:
```
xdebug.client_host=host.docker.internal
xdebug.client_port=9003
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.log_level=0
```
### Disable xDebug
it is possible to disable the Xdebug completely by setting the option **xdebug.mode** to **off**,
or by setting the environment variable **XDEBUG_MODE=off**\
See docs (https://xdebug.org/docs/all_settings#mode)
CLI:
```
XDEBUG_MODE=off php artisan test
```
Docker container:
```
docker-compose run --rm -e XDEBUG_MODE=off php php artisan test
```

20
app/Console/Kernel.php Normal file
View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Toby\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
protected function schedule(Schedule $schedule): void
{
}
protected function commands(): void
{
$this->load(__DIR__ . "/Commands");
}
}

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Toby\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
{
protected $dontFlash = [
"current_password",
"password",
"password_confirmation",
];
}

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Toby\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests;
use DispatchesJobs;
use ValidatesRequests;
}

72
app/Http/Kernel.php Normal file
View File

@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace Toby\Http;
use Fruitcake\Cors\HandleCors;
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
use Illuminate\Auth\Middleware\Authorize;
use Illuminate\Auth\Middleware\EnsureEmailIsVerified;
use Illuminate\Auth\Middleware\RequirePassword;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance;
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Http\Middleware\SetCacheHeaders;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Routing\Middleware\ValidateSignature;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
use Toby\Http\Middleware\Authenticate;
use Toby\Http\Middleware\RedirectIfAuthenticated;
use Toby\Http\Middleware\TrimStrings;
use Toby\Http\Middleware\TrustProxies;
class Kernel extends HttpKernel
{
protected $middleware = [
TrustProxies::class,
HandleCors::class,
PreventRequestsDuringMaintenance::class,
ValidatePostSize::class,
TrimStrings::class,
ConvertEmptyStringsToNull::class,
];
protected $middlewareGroups = [
"web" => [
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
],
"api" => [
EnsureFrontendRequestsAreStateful::class,
"throttle:api",
SubstituteBindings::class,
],
];
protected $routeMiddleware = [
"auth" => Authenticate::class,
"auth.basic" => AuthenticateWithBasicAuth::class,
"cache.headers" => SetCacheHeaders::class,
"can" => Authorize::class,
"guest" => RedirectIfAuthenticated::class,
"password.confirm" => RequirePassword::class,
"signed" => ValidateSignature::class,
"throttle" => ThrottleRequests::class,
"verified" => EnsureEmailIsVerified::class,
];
}

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Toby\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
protected function redirectTo($request)
{
if (!$request->expectsJson()) {
return route("login");
}
}
}

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Toby\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
use Toby\Providers\RouteServiceProvider;
class RedirectIfAuthenticated
{
public function handle(Request $request, Closure $next, ...$guards): Response
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Toby\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
protected $except = [
"current_password",
"password",
"password_confirmation",
];
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Toby\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Symfony\Component\HttpFoundation\Request;
class TrustProxies extends Middleware
{
protected $proxies;
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}

32
app/Models/User.php Normal file
View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Toby\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens;
use HasFactory;
use Notifiable;
protected $fillable = [
"name",
"email",
"password",
];
protected $hidden = [
"password",
"remember_token",
];
protected $casts = [
"email_verified_at" => "datetime",
];
}

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Toby\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
}

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Toby\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [];
public function boot(): void
{
$this->registerPolicies();
}
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Toby\Providers;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
protected $listen = [];
}

View File

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Toby\Providers;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
public const HOME = "/home";
public function boot(): void
{
$this->configureRateLimiting();
$this->routes(function (): void {
Route::prefix("api")
->middleware("api")
->group(base_path("routes/api.php"));
Route::middleware("web")
->group(base_path("routes/web.php"));
});
}
protected function configureRateLimiting(): void
{
RateLimiter::for("api", fn(Request $request) => Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()));
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Toby\Providers;
use Illuminate\Support\ServiceProvider;
use Laravel\Telescope\TelescopeApplicationServiceProvider;
use Laravel\Telescope\TelescopeServiceProvider as BaseTelescopeServiceProvider;
class TelescopeServiceProvider extends ServiceProvider
{
public function register(): void
{
if ($this->app->environment("local")) {
$this->app->register(BaseTelescopeServiceProvider::class);
$this->app->register(TelescopeApplicationServiceProvider::class);
}
}
}

53
artisan Executable file
View File

@ -0,0 +1,53 @@
#!/usr/bin/env php
<?php
define('LARAVEL_START', microtime(true));
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any of our classes manually. It's great to relax.
|
*/
require __DIR__.'/vendor/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArgvInput,
new Symfony\Component\Console\Output\ConsoleOutput
);
/*
|--------------------------------------------------------------------------
| Shutdown The Application
|--------------------------------------------------------------------------
|
| Once Artisan has finished running, we will fire off the shutdown events
| so that any final work may be done by the application before we shut
| down the process. This is the last thing to happen to the request.
|
*/
$kernel->terminate($input, $status);
exit($status);

24
bootstrap/app.php Normal file
View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
$app = new Illuminate\Foundation\Application(
$_ENV["APP_BASE_PATH"] ?? dirname(__DIR__),
);
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
Toby\Http\Kernel::class,
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
Toby\Console\Kernel::class,
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
Toby\Exceptions\Handler::class,
);
return $app;

2
bootstrap/cache/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

66
composer.json Normal file
View File

@ -0,0 +1,66 @@
{
"name": "blumilksoftware/toby",
"type": "project",
"description": "HR software you love to hate",
"keywords": ["toby", "laravel", "hr"],
"license": "MIT",
"require": {
"php": "^8.1",
"ext-pdo": "*",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"laravel/framework": "^8.75",
"laravel/sanctum": "^2.11",
"laravel/telescope": "^4.6",
"laravel/tinker": "^2.5"
},
"require-dev": {
"blumilksoftware/codestyle": "^0.9.0",
"facade/ignition": "^2.5",
"fakerphp/faker": "^1.9.1",
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^5.10",
"phpunit/phpunit": "^9.5.10"
},
"autoload": {
"psr-4": {
"Toby\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"ecs": "./vendor/bin/ecs check --clear-cache",
"ecsf": "./vendor/bin/ecs check --clear-cache --fix",
"test": "@php artisan test",
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
]
},
"extra": {
"laravel": {
"dont-discover": [
"laravel/telescope"
]
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}

8012
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

46
config/app.php Normal file
View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
return [
"name" => env("APP_NAME", "Laravel"),
"env" => env("APP_ENV", "production"),
"debug" => (bool)env("APP_DEBUG", false),
"url" => env("APP_URL", "http://localhost"),
"asset_url" => env("ASSET_URL"),
"timezone" => "Europe/Warsaw",
"locale" => "pl",
"fallback_locale" => "en",
"faker_locale" => "pl_PL",
"key" => env("APP_KEY"),
"cipher" => "AES-256-CBC",
"providers" => [
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
Illuminate\Cookie\CookieServiceProvider::class,
Illuminate\Database\DatabaseServiceProvider::class,
Illuminate\Encryption\EncryptionServiceProvider::class,
Illuminate\Filesystem\FilesystemServiceProvider::class,
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Mail\MailServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
Toby\Providers\AppServiceProvider::class,
Toby\Providers\AuthServiceProvider::class,
Toby\Providers\EventServiceProvider::class,
Toby\Providers\RouteServiceProvider::class,
Toby\Providers\TelescopeServiceProvider::class,
],
];

31
config/auth.php Normal file
View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
return [
"defaults" => [
"guard" => "web",
"passwords" => "users",
],
"guards" => [
"web" => [
"driver" => "session",
"provider" => "users",
],
],
"providers" => [
"users" => [
"driver" => "eloquent",
"model" => Toby\Models\User::class,
],
],
"passwords" => [
"users" => [
"provider" => "users",
"table" => "password_resets",
"expire" => 60,
"throttle" => 60,
],
],
"password_timeout" => 10800,
];

15
config/broadcasting.php Normal file
View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
return [
"default" => env("BROADCAST_DRIVER", "null"),
"connections" => [
"log" => [
"driver" => "log",
],
"null" => [
"driver" => "null",
],
],
];

26
config/cache.php Normal file
View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
use Illuminate\Support\Str;
return [
"default" => env("CACHE_DRIVER", "file"),
"stores" => [
"array" => [
"driver" => "array",
"serialize" => false,
],
"database" => [
"driver" => "database",
"table" => "cache",
"connection" => null,
"lock_connection" => null,
],
"file" => [
"driver" => "file",
"path" => storage_path("framework/cache/data"),
],
],
"prefix" => env("CACHE_PREFIX", Str::slug(env("APP_NAME", "laravel"), "_") . "_cache"),
];

14
config/cors.php Normal file
View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
return [
"paths" => ["api/*", "sanctum/csrf-cookie"],
"allowed_methods" => ["*"],
"allowed_origins" => ["*"],
"allowed_origins_patterns" => [],
"allowed_headers" => ["*"],
"exposed_headers" => [],
"max_age" => 0,
"supports_credentials" => false,
];

34
config/database.php Normal file
View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
return [
"default" => env("DB_CONNECTION", "mysql"),
"connections" => [
"testing" => [
"driver" => "sqlite",
"database" => ":memory:",
"prefix" => "",
],
"mysql" => [
"driver" => "mysql",
"url" => env("DATABASE_URL"),
"host" => env("DB_HOST", "127.0.0.1"),
"port" => env("DB_PORT", "3306"),
"database" => env("DB_DATABASE", "forge"),
"username" => env("DB_USERNAME", "forge"),
"password" => env("DB_PASSWORD", ""),
"unix_socket" => env("DB_SOCKET", ""),
"charset" => "utf8mb4",
"collation" => "utf8mb4_unicode_ci",
"prefix" => "",
"prefix_indexes" => true,
"strict" => true,
"engine" => null,
"options" => extension_loaded("pdo_mysql") ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env("MYSQL_ATTR_SSL_CA"),
]) : [],
],
],
"migrations" => "migrations",
];

22
config/filesystems.php Normal file
View File

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
return [
"default" => env("FILESYSTEM_DRIVER", "local"),
"disks" => [
"local" => [
"driver" => "local",
"root" => storage_path("app"),
],
"public" => [
"driver" => "local",
"root" => storage_path("app/public"),
"url" => env("APP_URL") . "/storage",
"visibility" => "public",
],
],
"links" => [
public_path("storage") => storage_path("app/public"),
],
];

15
config/hashing.php Normal file
View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
return [
"driver" => "bcrypt",
"bcrypt" => [
"rounds" => env("BCRYPT_ROUNDS", 10),
],
"argon" => [
"memory" => 1024,
"threads" => 2,
"time" => 2,
],
];

54
config/logging.php Normal file
View File

@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
return [
"default" => env("LOG_CHANNEL", "stack"),
"deprecations" => env("LOG_DEPRECATIONS_CHANNEL", "null"),
"channels" => [
"stack" => [
"driver" => "stack",
"channels" => ["single"],
"ignore_exceptions" => false,
],
"single" => [
"driver" => "single",
"path" => storage_path("logs/laravel.log"),
"level" => env("LOG_LEVEL", "debug"),
],
"daily" => [
"driver" => "daily",
"path" => storage_path("logs/laravel.log"),
"level" => env("LOG_LEVEL", "debug"),
"days" => 14,
],
"stderr" => [
"driver" => "monolog",
"level" => env("LOG_LEVEL", "debug"),
"handler" => StreamHandler::class,
"formatter" => env("LOG_STDERR_FORMATTER"),
"with" => [
"stream" => "php://stderr",
],
],
"syslog" => [
"driver" => "syslog",
"level" => env("LOG_LEVEL", "debug"),
],
"errorlog" => [
"driver" => "errorlog",
"level" => env("LOG_LEVEL", "debug"),
],
"null" => [
"driver" => "monolog",
"handler" => NullHandler::class,
],
"emergency" => [
"path" => storage_path("logs/laravel.log"),
],
],
];

43
config/mail.php Normal file
View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
return [
"default" => env("MAIL_MAILER", "smtp"),
"mailers" => [
"smtp" => [
"transport" => "smtp",
"host" => env("MAIL_HOST", "smtp.mailgun.org"),
"port" => env("MAIL_PORT", 587),
"encryption" => env("MAIL_ENCRYPTION", "tls"),
"username" => env("MAIL_USERNAME"),
"password" => env("MAIL_PASSWORD"),
"timeout" => null,
"auth_mode" => null,
],
"log" => [
"transport" => "log",
"channel" => env("MAIL_LOG_CHANNEL"),
],
"array" => [
"transport" => "array",
],
"failover" => [
"transport" => "failover",
"mailers" => [
"smtp",
"log",
],
],
],
"from" => [
"address" => env("MAIL_FROM_ADDRESS", "hello@example.com"),
"name" => env("MAIL_FROM_NAME", "Example"),
],
"markdown" => [
"theme" => "default",
"paths" => [
resource_path("views/vendor/mail"),
],
],
];

17
config/queue.php Normal file
View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
return [
"default" => env("QUEUE_CONNECTION", "sync"),
"connections" => [
"sync" => [
"driver" => "sync",
],
],
"failed" => [
"driver" => env("QUEUE_FAILED_DRIVER", "database-uuids"),
"database" => env("DB_CONNECTION", "mysql"),
"table" => "failed_jobs",
],
];

17
config/sanctum.php Normal file
View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
return [
"stateful" => explode(",", env("SANCTUM_STATEFUL_DOMAINS", sprintf(
"%s%s",
"localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1",
env("APP_URL") ? "," . parse_url(env("APP_URL"), PHP_URL_HOST) : "",
))),
"guard" => ["web"],
"expiration" => null,
"middleware" => [
"verify_csrf_token" => Toby\Http\Middleware\VerifyCsrfToken::class,
"encrypt_cookies" => Toby\Http\Middleware\EncryptCookies::class,
],
];

5
config/services.php Normal file
View File

@ -0,0 +1,5 @@
<?php
declare(strict_types=1);
return [];

26
config/session.php Normal file
View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
use Illuminate\Support\Str;
return [
"driver" => env("SESSION_DRIVER", "file"),
"lifetime" => env("SESSION_LIFETIME", 120),
"expire_on_close" => false,
"encrypt" => false,
"files" => storage_path("framework/sessions"),
"connection" => env("SESSION_CONNECTION"),
"table" => "sessions",
"store" => env("SESSION_STORE"),
"lottery" => [2, 100],
"cookie" => env(
"SESSION_COOKIE",
Str::slug(env("APP_NAME", "laravel"), "_") . "_session",
),
"path" => "/",
"domain" => env("SESSION_DOMAIN"),
"secure" => env("SESSION_SECURE_COOKIE"),
"http_only" => true,
"same_site" => "lax",
];

82
config/telescope.php Normal file
View File

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
use Laravel\Telescope\Http\Middleware\Authorize;
use Laravel\Telescope\Watchers;
return [
"domain" => env("TELESCOPE_DOMAIN"),
"path" => env("TELESCOPE_PATH", "telescope"),
"driver" => env("TELESCOPE_DRIVER", "database"),
"storage" => [
"database" => [
"connection" => env("DB_CONNECTION", "mysql"),
"chunk" => 1000,
],
],
"enabled" => env("TELESCOPE_ENABLED", true),
"middleware" => [
"web",
Authorize::class,
],
"only_paths" => [],
"ignore_paths" => [
"nova-api*",
],
"ignore_commands" => [],
"watchers" => [
Watchers\BatchWatcher::class => env("TELESCOPE_BATCH_WATCHER", true),
Watchers\CacheWatcher::class => env("TELESCOPE_CACHE_WATCHER", true),
Watchers\ClientRequestWatcher::class => env("TELESCOPE_CLIENT_REQUEST_WATCHER", true),
Watchers\CommandWatcher::class => [
"enabled" => env("TELESCOPE_COMMAND_WATCHER", true),
"ignore" => [],
],
Watchers\DumpWatcher::class => env("TELESCOPE_DUMP_WATCHER", true),
Watchers\EventWatcher::class => [
"enabled" => env("TELESCOPE_EVENT_WATCHER", true),
"ignore" => [],
],
Watchers\ExceptionWatcher::class => env("TELESCOPE_EXCEPTION_WATCHER", true),
Watchers\GateWatcher::class => [
"enabled" => env("TELESCOPE_GATE_WATCHER", true),
"ignore_abilities" => [],
"ignore_packages" => true,
],
Watchers\JobWatcher::class => env("TELESCOPE_JOB_WATCHER", true),
Watchers\LogWatcher::class => env("TELESCOPE_LOG_WATCHER", true),
Watchers\MailWatcher::class => env("TELESCOPE_MAIL_WATCHER", true),
Watchers\ModelWatcher::class => [
"enabled" => env("TELESCOPE_MODEL_WATCHER", true),
"events" => ["eloquent.*"],
"hydrations" => true,
],
Watchers\NotificationWatcher::class => env("TELESCOPE_NOTIFICATION_WATCHER", true),
Watchers\QueryWatcher::class => [
"enabled" => env("TELESCOPE_QUERY_WATCHER", true),
"ignore_packages" => true,
"slow" => 100,
],
Watchers\RedisWatcher::class => env("TELESCOPE_REDIS_WATCHER", true),
Watchers\RequestWatcher::class => [
"enabled" => env("TELESCOPE_REQUEST_WATCHER", true),
"size_limit" => env("TELESCOPE_RESPONSE_SIZE_LIMIT", 64),
"ignore_status_codes" => [],
],
Watchers\ScheduleWatcher::class => env("TELESCOPE_SCHEDULE_WATCHER", true),
Watchers\ViewWatcher::class => env("TELESCOPE_VIEW_WATCHER", true),
],
];

13
config/view.php Normal file
View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
return [
"paths" => [
resource_path("views"),
],
"compiled" => env(
"VIEW_COMPILED_PATH",
realpath(storage_path("framework/views")),
),
];

1
database/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.sqlite*

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class UserFactory extends Factory
{
public function definition(): array
{
return [
"name" => $this->faker->name(),
"email" => $this->faker->unique()->safeEmail(),
"email_verified_at" => now(),
"password" => Hash::make("secret123"),
"remember_token" => Str::random(10),
];
}
}

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class() extends Migration {
public function up(): void
{
Schema::create("users", function (Blueprint $table): void {
$table->id();
$table->string("name");
$table->string("email")->unique();
$table->timestamp("email_verified_at")->nullable();
$table->string("password");
$table->rememberToken();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists("users");
}
};

View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class() extends Migration {
public function up(): void
{
Schema::create("password_resets", function (Blueprint $table): void {
$table->string("email")->index();
$table->string("token");
$table->timestamp("created_at")->nullable();
});
}
public function down(): void
{
Schema::dropIfExists("password_resets");
}
};

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class() extends Migration {
public function up(): void
{
Schema::create("failed_jobs", function (Blueprint $table): void {
$table->id();
$table->string("uuid")->unique();
$table->text("connection");
$table->text("queue");
$table->longText("payload");
$table->longText("exception");
$table->timestamp("failed_at")->useCurrent();
});
}
public function down(): void
{
Schema::dropIfExists("failed_jobs");
}
};

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class() extends Migration {
public function up(): void
{
Schema::create("personal_access_tokens", function (Blueprint $table): void {
$table->id();
$table->morphs("tokenable");
$table->string("name");
$table->string("token", 64)->unique();
$table->text("abilities")->nullable();
$table->timestamp("last_used_at")->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists("personal_access_tokens");
}
};

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Toby\Models\User;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
User::factory(10)->create();
}
}

79
docker-compose.yml Normal file
View File

@ -0,0 +1,79 @@
version: '3.8'
services:
web:
image: nginx:1.21-alpine
container_name: toby-web
working_dir: /application
volumes:
- ./docker/dev/nginx/nginx.conf:/etc/nginx/conf.d/default.conf
- .:/application
networks:
- toby-dev
ports:
- ${EXTERNAL_WEBSERVER_PORT:-80}:80
restart: unless-stopped
depends_on:
- php
- database
php:
build:
context: docker/dev/php
args:
INSTALL_XDEBUG: ${DOCKER_INSTALL_XDEBUG:-false}
container_name: toby-php
working_dir: /application
user: ${CURRENT_UID:-1000}
volumes:
- .:/application
- ./docker/dev/php/php.ini:/usr/local/etc/php/conf.d/php.ini
networks:
- toby-dev
extra_hosts:
- host.docker.internal:host-gateway
restart: unless-stopped
database:
image: mysql:8.0
container_name: toby-db
environment:
- MYSQL_ROOT_PASSWORD=${DOCKER_DB_ROOT_PASSWORD}
- MYSQL_DATABASE=${DOCKER_DB_DATABASE}
- MYSQL_USER=${DOCKER_DB_USERNAME}
- MYSQL_PASSWORD=${DOCKER_DB_PASSWORD}
ports:
- ${DOCKER_DB_EXTERNAL_PORT:-3306}:3306
volumes:
- toby-mysql-data:/var/lib/mysql
networks:
- toby-dev
restart: unless-stopped
node:
image: node:17.2.0-alpine3.14
container_name: toby-node
working_dir: /application
volumes:
- .:/application
networks:
- toby-dev
restart: unless-stopped
mailhog:
image: mailhog/mailhog:v1.0.1
container_name: toby-mailhog
ports:
- ${MAIL_PORT}:1025
- ${MAILHOG_DASHBOARD_PORT}:8025
networks:
- toby-dev
restart: unless-stopped
networks:
toby-dev:
driver: bridge
volumes:
toby-mysql-data:
name: toby-mysql-data

View File

@ -0,0 +1,24 @@
server {
listen 80 default;
server_name localhost;
client_max_body_size 108M;
access_log /dev/stdout;
root /application/public;
index index.php;
if (!-e $request_filename) {
rewrite ^.*$ /index.php last;
}
location ~ \.php$ {
fastcgi_pass toby-php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PHP_VALUE "error_log=/dev/stdout";
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
include fastcgi_params;
}
}

20
docker/dev/php/Dockerfile Normal file
View File

@ -0,0 +1,20 @@
FROM ghcr.io/blumilksoftware/php:8.1
ARG XDEBUG_VERSION=3.1.2
ARG INSTALL_XDEBUG=false
RUN if [ ${INSTALL_XDEBUG} = true ]; then \
apk --no-cache add $PHPIZE_DEPS \
&& pecl install xdebug-${XDEBUG_VERSION} \
&& docker-php-ext-enable xdebug \
;fi
RUN apk --no-cache add \
zip \
libzip-dev \
libpng-dev \
&& docker-php-ext-install \
zip \
gd \
&& docker-php-ext-configure \
zip

9
docker/dev/php/php.ini Normal file
View File

@ -0,0 +1,9 @@
[PHP]
memory_limit = 1G
[xdebug]
xdebug.client_host=host.docker.internal
xdebug.client_port=9003
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.log_level=0

13
ecs.php Normal file
View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
use Blumilk\Codestyle\Config;
use Blumilk\Codestyle\Configuration\Defaults\LaravelPaths;
$paths = (new LaravelPaths())->add("public", "bootstrap/app.php", "ecs.php");
$config = new Config(
paths: $paths,
);
return $config->config();

15966
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

18
package.json Normal file
View File

@ -0,0 +1,18 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production"
},
"devDependencies": {
"axios": "^0.21",
"laravel-mix": "^6.0.6",
"lodash": "^4.17.19",
"postcss": "^8.1.14"
}
}

32
phpunit.xml Normal file
View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
>
<testsuites>
<testsuite name="Unit">
<directory>./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory>./tests/Feature</directory>
</testsuite>
</testsuites>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./app</directory>
</include>
</coverage>
<php>
<server name="APP_ENV" value="testing"/>
<server name="APP_KEY" value="base64:SKEJSy9oF9chQBCMbxqgj5zhtAvug9kwZ+cDiP1Y8A8="/>
<server name="BCRYPT_ROUNDS" value="4"/>
<server name="CACHE_DRIVER" value="array"/>
<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_DATABASE" value=":memory:"/>
<server name="MAIL_MAILER" value="array"/>
<server name="QUEUE_CONNECTION" value="sync"/>
<server name="SESSION_DRIVER" value="array"/>
<server name="TELESCOPE_ENABLED" value="false"/>
</php>
</phpunit>

21
public/.htaccess Normal file
View File

@ -0,0 +1,21 @@
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>

0
public/favicon.ico Normal file
View File

24
public/index.php Normal file
View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;
define("LARAVEL_START", microtime(true));
if (file_exists(__DIR__ . "/../storage/framework/maintenance.php")) {
require __DIR__ . "/../storage/framework/maintenance.php";
}
require __DIR__ . "/../vendor/autoload.php";
$app = require_once __DIR__ . "/../bootstrap/app.php";
$kernel = $app->make(Kernel::class);
$response = $kernel->handle(
$request = Request::capture(),
)->send();
$kernel->terminate($request, $response);

2
public/robots.txt Normal file
View File

@ -0,0 +1,2 @@
User-agent: *
Disallow:

7
public/vendor/telescope/app-dark.css vendored Normal file

File diff suppressed because one or more lines are too long

7
public/vendor/telescope/app.css vendored Normal file

File diff suppressed because one or more lines are too long

2
public/vendor/telescope/app.js vendored Normal file

File diff suppressed because one or more lines are too long

BIN
public/vendor/telescope/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -0,0 +1,5 @@
{
"/app.js": "/app.js?id=d3ff915bff3de87b87cc",
"/app-dark.css": "/app-dark.css?id=3ae28ef5f7b987d68dc6",
"/app.css": "/app.css?id=7c970f699ed9cf60d80b"
}

0
resources/css/app.css Normal file
View File

1
resources/js/app.js Normal file
View File

@ -0,0 +1 @@
require('./bootstrap');

28
resources/js/bootstrap.js vendored Normal file
View File

@ -0,0 +1,28 @@
window._ = require('lodash');
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
// import Echo from 'laravel-echo';
// window.Pusher = require('pusher-js');
// window.Echo = new Echo({
// broadcaster: 'pusher',
// key: process.env.MIX_PUSHER_APP_KEY,
// cluster: process.env.MIX_PUSHER_APP_CLUSTER,
// forceTLS: true
// });

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
return [
"failed" => "These credentials do not match our records.",
"password" => "The provided password is incorrect.",
"throttle" => "Too many login attempts. Please try again in :seconds seconds.",
];

View File

@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
return [
"previous" => "&laquo; Previous",
"next" => "Next &raquo;",
];

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
return [
"reset" => "Your password has been reset!",
"sent" => "We have emailed your password reset link!",
"throttled" => "Please wait before retrying.",
"token" => "This password reset token is invalid.",
"user" => "We can't find a user with that email address.",
];

View File

@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
return [
"accepted" => "The :attribute must be accepted.",
"accepted_if" => "The :attribute must be accepted when :other is :value.",
"active_url" => "The :attribute is not a valid URL.",
"after" => "The :attribute must be a date after :date.",
"after_or_equal" => "The :attribute must be a date after or equal to :date.",
"alpha" => "The :attribute must only contain letters.",
"alpha_dash" => "The :attribute must only contain letters, numbers, dashes and underscores.",
"alpha_num" => "The :attribute must only contain letters and numbers.",
"array" => "The :attribute must be an array.",
"before" => "The :attribute must be a date before :date.",
"before_or_equal" => "The :attribute must be a date before or equal to :date.",
"between" => [
"numeric" => "The :attribute must be between :min and :max.",
"file" => "The :attribute must be between :min and :max kilobytes.",
"string" => "The :attribute must be between :min and :max characters.",
"array" => "The :attribute must have between :min and :max items.",
],
"boolean" => "The :attribute field must be true or false.",
"confirmed" => "The :attribute confirmation does not match.",
"current_password" => "The password is incorrect.",
"date" => "The :attribute is not a valid date.",
"date_equals" => "The :attribute must be a date equal to :date.",
"date_format" => "The :attribute does not match the format :format.",
"declined" => "The :attribute must be declined.",
"declined_if" => "The :attribute must be declined when :other is :value.",
"different" => "The :attribute and :other must be different.",
"digits" => "The :attribute must be :digits digits.",
"digits_between" => "The :attribute must be between :min and :max digits.",
"dimensions" => "The :attribute has invalid image dimensions.",
"distinct" => "The :attribute field has a duplicate value.",
"email" => "The :attribute must be a valid email address.",
"ends_with" => "The :attribute must end with one of the following: :values.",
"enum" => "The selected :attribute is invalid.",
"exists" => "The selected :attribute is invalid.",
"file" => "The :attribute must be a file.",
"filled" => "The :attribute field must have a value.",
"gt" => [
"numeric" => "The :attribute must be greater than :value.",
"file" => "The :attribute must be greater than :value kilobytes.",
"string" => "The :attribute must be greater than :value characters.",
"array" => "The :attribute must have more than :value items.",
],
"gte" => [
"numeric" => "The :attribute must be greater than or equal to :value.",
"file" => "The :attribute must be greater than or equal to :value kilobytes.",
"string" => "The :attribute must be greater than or equal to :value characters.",
"array" => "The :attribute must have :value items or more.",
],
"image" => "The :attribute must be an image.",
"in" => "The selected :attribute is invalid.",
"in_array" => "The :attribute field does not exist in :other.",
"integer" => "The :attribute must be an integer.",
"ip" => "The :attribute must be a valid IP address.",
"ipv4" => "The :attribute must be a valid IPv4 address.",
"ipv6" => "The :attribute must be a valid IPv6 address.",
"mac_address" => "The :attribute must be a valid MAC address.",
"json" => "The :attribute must be a valid JSON string.",
"lt" => [
"numeric" => "The :attribute must be less than :value.",
"file" => "The :attribute must be less than :value kilobytes.",
"string" => "The :attribute must be less than :value characters.",
"array" => "The :attribute must have less than :value items.",
],
"lte" => [
"numeric" => "The :attribute must be less than or equal to :value.",
"file" => "The :attribute must be less than or equal to :value kilobytes.",
"string" => "The :attribute must be less than or equal to :value characters.",
"array" => "The :attribute must not have more than :value items.",
],
"max" => [
"numeric" => "The :attribute must not be greater than :max.",
"file" => "The :attribute must not be greater than :max kilobytes.",
"string" => "The :attribute must not be greater than :max characters.",
"array" => "The :attribute must not have more than :max items.",
],
"mimes" => "The :attribute must be a file of type: :values.",
"mimetypes" => "The :attribute must be a file of type: :values.",
"min" => [
"numeric" => "The :attribute must be at least :min.",
"file" => "The :attribute must be at least :min kilobytes.",
"string" => "The :attribute must be at least :min characters.",
"array" => "The :attribute must have at least :min items.",
],
"multiple_of" => "The :attribute must be a multiple of :value.",
"not_in" => "The selected :attribute is invalid.",
"not_regex" => "The :attribute format is invalid.",
"numeric" => "The :attribute must be a number.",
"password" => "The password is incorrect.",
"present" => "The :attribute field must be present.",
"prohibited" => "The :attribute field is prohibited.",
"prohibited_if" => "The :attribute field is prohibited when :other is :value.",
"prohibited_unless" => "The :attribute field is prohibited unless :other is in :values.",
"prohibits" => "The :attribute field prohibits :other from being present.",
"regex" => "The :attribute format is invalid.",
"required" => "The :attribute field is required.",
"required_if" => "The :attribute field is required when :other is :value.",
"required_unless" => "The :attribute field is required unless :other is in :values.",
"required_with" => "The :attribute field is required when :values is present.",
"required_with_all" => "The :attribute field is required when :values are present.",
"required_without" => "The :attribute field is required when :values is not present.",
"required_without_all" => "The :attribute field is required when none of :values are present.",
"same" => "The :attribute and :other must match.",
"size" => [
"numeric" => "The :attribute must be :size.",
"file" => "The :attribute must be :size kilobytes.",
"string" => "The :attribute must be :size characters.",
"array" => "The :attribute must contain :size items.",
],
"starts_with" => "The :attribute must start with one of the following: :values.",
"string" => "The :attribute must be a string.",
"timezone" => "The :attribute must be a valid timezone.",
"unique" => "The :attribute has already been taken.",
"uploaded" => "The :attribute failed to upload.",
"url" => "The :attribute must be a valid URL.",
"uuid" => "The :attribute must be a valid UUID.",
"attributes" => [],
];

View File

@ -0,0 +1,112 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
<style>
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-t{border-top-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.dark\:text-gray-500{--tw-text-opacity:1;color:#6b7280;color:rgba(107,114,128,var(--tw-text-opacity))}}
</style>
<style>
body {
font-family: 'Nunito', sans-serif;
}
</style>
</head>
<body class="antialiased">
<div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center py-4 sm:pt-0">
<div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
<div class="flex justify-center pt-8 sm:justify-start sm:pt-0">
<svg viewBox="0 0 651 192" fill="none" xmlns="http://www.w3.org/2000/svg" class="h-16 w-auto text-gray-700 sm:h-20">
<g clip-path="url(#clip0)" fill="#EF3B2D">
<path d="M248.032 44.676h-16.466v100.23h47.394v-14.748h-30.928V44.676zM337.091 87.202c-2.101-3.341-5.083-5.965-8.949-7.875-3.865-1.909-7.756-2.864-11.669-2.864-5.062 0-9.69.931-13.89 2.792-4.201 1.861-7.804 4.417-10.811 7.661-3.007 3.246-5.347 6.993-7.016 11.239-1.672 4.249-2.506 8.713-2.506 13.389 0 4.774.834 9.26 2.506 13.459 1.669 4.202 4.009 7.925 7.016 11.169 3.007 3.246 6.609 5.799 10.811 7.66 4.199 1.861 8.828 2.792 13.89 2.792 3.913 0 7.804-.955 11.669-2.863 3.866-1.908 6.849-4.533 8.949-7.875v9.021h15.607V78.182h-15.607v9.02zm-1.431 32.503c-.955 2.578-2.291 4.821-4.009 6.73-1.719 1.91-3.795 3.437-6.229 4.582-2.435 1.146-5.133 1.718-8.091 1.718-2.96 0-5.633-.572-8.019-1.718-2.387-1.146-4.438-2.672-6.156-4.582-1.719-1.909-3.032-4.152-3.938-6.73-.909-2.577-1.36-5.298-1.36-8.161 0-2.864.451-5.585 1.36-8.162.905-2.577 2.219-4.819 3.938-6.729 1.718-1.908 3.77-3.437 6.156-4.582 2.386-1.146 5.059-1.718 8.019-1.718 2.958 0 5.656.572 8.091 1.718 2.434 1.146 4.51 2.674 6.229 4.582 1.718 1.91 3.054 4.152 4.009 6.729.953 2.577 1.432 5.298 1.432 8.162-.001 2.863-.479 5.584-1.432 8.161zM463.954 87.202c-2.101-3.341-5.083-5.965-8.949-7.875-3.865-1.909-7.756-2.864-11.669-2.864-5.062 0-9.69.931-13.89 2.792-4.201 1.861-7.804 4.417-10.811 7.661-3.007 3.246-5.347 6.993-7.016 11.239-1.672 4.249-2.506 8.713-2.506 13.389 0 4.774.834 9.26 2.506 13.459 1.669 4.202 4.009 7.925 7.016 11.169 3.007 3.246 6.609 5.799 10.811 7.66 4.199 1.861 8.828 2.792 13.89 2.792 3.913 0 7.804-.955 11.669-2.863 3.866-1.908 6.849-4.533 8.949-7.875v9.021h15.607V78.182h-15.607v9.02zm-1.432 32.503c-.955 2.578-2.291 4.821-4.009 6.73-1.719 1.91-3.795 3.437-6.229 4.582-2.435 1.146-5.133 1.718-8.091 1.718-2.96 0-5.633-.572-8.019-1.718-2.387-1.146-4.438-2.672-6.156-4.582-1.719-1.909-3.032-4.152-3.938-6.73-.909-2.577-1.36-5.298-1.36-8.161 0-2.864.451-5.585 1.36-8.162.905-2.577 2.219-4.819 3.938-6.729 1.718-1.908 3.77-3.437 6.156-4.582 2.386-1.146 5.059-1.718 8.019-1.718 2.958 0 5.656.572 8.091 1.718 2.434 1.146 4.51 2.674 6.229 4.582 1.718 1.91 3.054 4.152 4.009 6.729.953 2.577 1.432 5.298 1.432 8.162 0 2.863-.479 5.584-1.432 8.161zM650.772 44.676h-15.606v100.23h15.606V44.676zM365.013 144.906h15.607V93.538h26.776V78.182h-42.383v66.724zM542.133 78.182l-19.616 51.096-19.616-51.096h-15.808l25.617 66.724h19.614l25.617-66.724h-15.808zM591.98 76.466c-19.112 0-34.239 15.706-34.239 35.079 0 21.416 14.641 35.079 36.239 35.079 12.088 0 19.806-4.622 29.234-14.688l-10.544-8.158c-.006.008-7.958 10.449-19.832 10.449-13.802 0-19.612-11.127-19.612-16.884h51.777c2.72-22.043-11.772-40.877-33.023-40.877zm-18.713 29.28c.12-1.284 1.917-16.884 18.589-16.884 16.671 0 18.697 15.598 18.813 16.884h-37.402zM184.068 43.892c-.024-.088-.073-.165-.104-.25-.058-.157-.108-.316-.191-.46-.056-.097-.137-.176-.203-.265-.087-.117-.161-.242-.265-.345-.085-.086-.194-.148-.29-.223-.109-.085-.206-.182-.327-.252l-.002-.001-.002-.002-35.648-20.524a2.971 2.971 0 00-2.964 0l-35.647 20.522-.002.002-.002.001c-.121.07-.219.167-.327.252-.096.075-.205.138-.29.223-.103.103-.178.228-.265.345-.066.089-.147.169-.203.265-.083.144-.133.304-.191.46-.031.085-.08.162-.104.25-.067.249-.103.51-.103.776v38.979l-29.706 17.103V24.493a3 3 0 00-.103-.776c-.024-.088-.073-.165-.104-.25-.058-.157-.108-.316-.191-.46-.056-.097-.137-.176-.203-.265-.087-.117-.161-.242-.265-.345-.085-.086-.194-.148-.29-.223-.109-.085-.206-.182-.327-.252l-.002-.001-.002-.002L40.098 1.396a2.971 2.971 0 00-2.964 0L1.487 21.919l-.002.002-.002.001c-.121.07-.219.167-.327.252-.096.075-.205.138-.29.223-.103.103-.178.228-.265.345-.066.089-.147.169-.203.265-.083.144-.133.304-.191.46-.031.085-.08.162-.104.25-.067.249-.103.51-.103.776v122.09c0 1.063.568 2.044 1.489 2.575l71.293 41.045c.156.089.324.143.49.202.078.028.15.074.23.095a2.98 2.98 0 001.524 0c.069-.018.132-.059.2-.083.176-.061.354-.119.519-.214l71.293-41.045a2.971 2.971 0 001.489-2.575v-38.979l34.158-19.666a2.971 2.971 0 001.489-2.575V44.666a3.075 3.075 0 00-.106-.774zM74.255 143.167l-29.648-16.779 31.136-17.926.001-.001 34.164-19.669 29.674 17.084-21.772 12.428-43.555 24.863zm68.329-76.259v33.841l-12.475-7.182-17.231-9.92V49.806l12.475 7.182 17.231 9.92zm2.97-39.335l29.693 17.095-29.693 17.095-29.693-17.095 29.693-17.095zM54.06 114.089l-12.475 7.182V46.733l17.231-9.92 12.475-7.182v74.537l-17.231 9.921zM38.614 7.398l29.693 17.095-29.693 17.095L8.921 24.493 38.614 7.398zM5.938 29.632l12.475 7.182 17.231 9.92v79.676l.001.005-.001.006c0 .114.032.221.045.333.017.146.021.294.059.434l.002.007c.032.117.094.222.14.334.051.124.088.255.156.371a.036.036 0 00.004.009c.061.105.149.191.222.288.081.105.149.22.244.314l.008.01c.084.083.19.142.284.215.106.083.202.178.32.247l.013.005.011.008 34.139 19.321v34.175L5.939 144.867V29.632h-.001zm136.646 115.235l-65.352 37.625V148.31l48.399-27.628 16.953-9.677v33.862zm35.646-61.22l-29.706 17.102V66.908l17.231-9.92 12.475-7.182v33.841z"/>
</g>
</svg>
</div>
<div class="mt-8 bg-white dark:bg-gray-800 overflow-hidden shadow sm:rounded-lg">
<div class="grid grid-cols-1 md:grid-cols-2">
<div class="p-6">
<div class="flex items-center">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-8 h-8 text-gray-500"><path d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path></svg>
<div class="ml-4 text-lg leading-7 font-semibold"><a href="https://laravel.com/docs" class="underline text-gray-900 dark:text-white">Documentation</a></div>
</div>
<div class="ml-12">
<div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">
Laravel has wonderful, thorough documentation covering every aspect of the framework. Whether you are new to the framework or have previous experience with Laravel, we recommend reading all of the documentation from beginning to end.
</div>
</div>
</div>
<div class="p-6 border-t border-gray-200 dark:border-gray-700 md:border-t-0 md:border-l">
<div class="flex items-center">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-8 h-8 text-gray-500"><path d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"></path><path d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>
<div class="ml-4 text-lg leading-7 font-semibold"><a href="https://laracasts.com" class="underline text-gray-900 dark:text-white">Laracasts</a></div>
</div>
<div class="ml-12">
<div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">
Laracasts offers thousands of video tutorials on Laravel, PHP, and JavaScript development. Check them out, see for yourself, and massively level up your development skills in the process.
</div>
</div>
</div>
<div class="p-6 border-t border-gray-200 dark:border-gray-700">
<div class="flex items-center">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-8 h-8 text-gray-500"><path d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"></path></svg>
<div class="ml-4 text-lg leading-7 font-semibold"><a href="https://laravel-news.com/" class="underline text-gray-900 dark:text-white">Laravel News</a></div>
</div>
<div class="ml-12">
<div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">
Laravel News is a community driven portal and newsletter aggregating all of the latest and most important news in the Laravel ecosystem, including new package releases and tutorials.
</div>
</div>
</div>
<div class="p-6 border-t border-gray-200 dark:border-gray-700 md:border-l">
<div class="flex items-center">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-8 h-8 text-gray-500"><path d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
<div class="ml-4 text-lg leading-7 font-semibold text-gray-900 dark:text-white">Vibrant Ecosystem</div>
</div>
<div class="ml-12">
<div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">
Laravel's robust library of first-party tools and libraries, such as <a href="https://forge.laravel.com" class="underline">Forge</a>, <a href="https://vapor.laravel.com" class="underline">Vapor</a>, <a href="https://nova.laravel.com" class="underline">Nova</a>, and <a href="https://envoyer.io" class="underline">Envoyer</a> help you take your projects to the next level. Pair them with powerful open source libraries like <a href="https://laravel.com/docs/billing" class="underline">Cashier</a>, <a href="https://laravel.com/docs/dusk" class="underline">Dusk</a>, <a href="https://laravel.com/docs/broadcasting" class="underline">Echo</a>, <a href="https://laravel.com/docs/horizon" class="underline">Horizon</a>, <a href="https://laravel.com/docs/sanctum" class="underline">Sanctum</a>, <a href="https://laravel.com/docs/telescope" class="underline">Telescope</a>, and more.
</div>
</div>
</div>
</div>
</div>
<div class="flex justify-center mt-4 sm:items-center sm:justify-between">
<div class="text-center text-sm text-gray-500 sm:text-left">
<div class="flex items-center">
<svg fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" stroke="currentColor" class="-mt-px w-5 h-5 text-gray-400">
<path d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>
<a href="https://laravel.bigcartel.com" class="ml-1 underline">
Shop
</a>
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="ml-4 -mt-px w-5 h-5 text-gray-400">
<path d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"></path>
</svg>
<a href="https://github.com/sponsors/taylorotwell" class="ml-1 underline">
Sponsor
</a>
</div>
</div>
<div class="ml-4 text-center text-sm text-gray-500 sm:text-right sm:ml-0">
Laravel v{{ Illuminate\Foundation\Application::VERSION }} (PHP v{{ PHP_VERSION }})
</div>
</div>
</div>
</div>
</body>
</html>

8
routes/api.php Normal file
View File

@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::middleware("auth:sanctum")->get("/user", fn(Request $request) => $request->user());

7
routes/web.php Normal file
View File

@ -0,0 +1,7 @@
<?php
declare(strict_types=1);
use Illuminate\Support\Facades\Route;
Route::get("/", fn() => view("welcome"));

21
server.php Normal file
View File

@ -0,0 +1,21 @@
<?php
/**
* Laravel - A PHP Framework For Web Artisans
*
* @package Laravel
* @author Taylor Otwell <taylor@laravel.com>
*/
$uri = urldecode(
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
);
// This file allows us to emulate Apache's "mod_rewrite" functionality from the
// built-in PHP web server. This provides a convenient way to test a Laravel
// application without having installed a "real" web server software here.
if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) {
return false;
}
require_once __DIR__.'/public/index.php';

3
storage/app/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*
!public/
!.gitignore

2
storage/app/public/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

9
storage/framework/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
compiled.php
config.php
down
events.scanned.php
maintenance.php
routes.php
routes.scanned.php
schedule-*
services.json

3
storage/framework/cache/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*
!data/
!.gitignore

View File

@ -0,0 +1,2 @@
*
!.gitignore

2
storage/framework/sessions/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
storage/framework/testing/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
storage/framework/views/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
storage/logs/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Tests;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Foundation\Application;
trait CreatesApplication
{
public function createApplication(): Application
{
$app = require __DIR__ . "/../bootstrap/app.php";
$app->make(Kernel::class)->bootstrap();
return $app;
}
}

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function testExample(): void
{
$response = $this->get("/");
$response->assertStatus(200);
}
}

12
tests/TestCase.php Normal file
View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
}

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class ExampleTest extends TestCase
{
public function testExample(): void
{
$this->assertTrue(true);
}
}

17
webpack.mix.js Normal file
View File

@ -0,0 +1,17 @@
const mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel applications. By default, we are compiling the CSS
| file for the application as well as bundling up all the JS files.
|
*/
mix.js('resources/js/app.js', 'public/js')
.postCss('resources/css/app.css', 'public/css', [
//
]);