├── .editorconfig ├── .env.example ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── app ├── Http │ └── Controllers │ │ ├── Controller.php │ │ └── IssueController.php ├── Livewire │ └── ShowIssues.php ├── Models │ ├── Issue.php │ ├── Label.php │ ├── Repository.php │ └── User.php ├── Providers │ └── AppServiceProvider.php ├── Services │ ├── IssueService.php │ ├── LabelService.php │ └── RepositoryService.php └── Tasks │ └── UpdateRecentIssues.php ├── artisan ├── bootstrap ├── app.php ├── cache │ └── .gitignore └── providers.php ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── cache.php ├── database.php ├── filesystems.php ├── logging.php ├── mail.php ├── queue.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_07_27_190100_create_repositories_table.php │ ├── 2024_07_27_190200_create_issues_table.php │ ├── 2024_07_27_190345_create_labels_table.php │ └── 2024_07_28_154451_create_issue_label_table.php └── seeders │ └── DatabaseSeeder.php ├── docker-compose-prod.yml ├── docker-compose.yml ├── docker ├── 8.0 │ ├── Dockerfile │ ├── php.ini │ ├── start-container │ └── supervisord.conf ├── 8.1 │ ├── Dockerfile │ ├── php.ini │ ├── start-container │ └── supervisord.conf ├── 8.2-prod │ ├── Dockerfile │ ├── nginx.conf │ ├── php.ini │ ├── start-container │ └── supervisord.conf ├── 8.2 │ ├── Dockerfile │ ├── php.ini │ ├── start-container │ └── supervisord.conf ├── 8.3 │ ├── Dockerfile │ ├── php.ini │ ├── start-container │ └── supervisord.conf ├── mariadb │ └── create-testing-database.sh ├── mysql │ └── create-testing-database.sh └── pgsql │ └── create-testing-database.sql ├── package-lock.json ├── package.json ├── phpstan.neon ├── phpunit.xml ├── postcss.config.js ├── public ├── .htaccess ├── favicon.ico ├── images │ └── logo.png ├── index.php └── robots.txt ├── resources ├── css │ └── app.css ├── js │ ├── app.js │ └── bootstrap.js └── views │ ├── index.blade.php │ ├── layouts │ └── app.blade.php │ └── livewire │ └── show-issues.blade.php ├── routes ├── console.php └── web.php ├── storage ├── app │ ├── .gitignore │ └── public │ │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ ├── .gitignore │ │ └── data │ │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ ├── testing │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore ├── tailwind.config.js ├── tests ├── Feature │ └── ExampleTest.php ├── TestCase.php └── Unit │ └── ExampleTest.php └── vite.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Laravel 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_TIMEZONE=UTC 6 | APP_URL=http://localhost 7 | 8 | APP_LOCALE=en 9 | APP_FALLBACK_LOCALE=en 10 | APP_FAKER_LOCALE=en_US 11 | 12 | APP_MAINTENANCE_DRIVER=file 13 | # APP_MAINTENANCE_STORE=database 14 | 15 | BCRYPT_ROUNDS=12 16 | 17 | LOG_CHANNEL=stack 18 | LOG_STACK=single 19 | LOG_DEPRECATIONS_CHANNEL=null 20 | LOG_LEVEL=debug 21 | 22 | DB_CONNECTION=mysql 23 | DB_HOST=mysql 24 | DB_PORT=3306 25 | DB_DATABASE=laravel 26 | DB_USERNAME=sail 27 | DB_PASSWORD=password 28 | 29 | SESSION_DRIVER=database 30 | SESSION_LIFETIME=120 31 | SESSION_ENCRYPT=false 32 | SESSION_PATH=/ 33 | SESSION_DOMAIN=null 34 | 35 | BROADCAST_CONNECTION=log 36 | FILESYSTEM_DISK=local 37 | QUEUE_CONNECTION=database 38 | 39 | CACHE_STORE=database 40 | CACHE_PREFIX= 41 | 42 | MEMCACHED_HOST=127.0.0.1 43 | 44 | REDIS_CLIENT=phpredis 45 | REDIS_HOST=127.0.0.1 46 | REDIS_PASSWORD=null 47 | REDIS_PORT=6379 48 | 49 | MAIL_MAILER=log 50 | MAIL_HOST=127.0.0.1 51 | MAIL_PORT=2525 52 | MAIL_USERNAME=null 53 | MAIL_PASSWORD=null 54 | MAIL_ENCRYPTION=null 55 | MAIL_FROM_ADDRESS="hello@example.com" 56 | MAIL_FROM_NAME="${APP_NAME}" 57 | 58 | AWS_ACCESS_KEY_ID= 59 | AWS_SECRET_ACCESS_KEY= 60 | AWS_DEFAULT_REGION=us-east-1 61 | AWS_BUCKET= 62 | AWS_USE_PATH_STYLE_ENDPOINT=false 63 | 64 | VITE_APP_NAME="${APP_NAME}" 65 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.blade.php diff=html 4 | *.css diff=css 5 | *.html diff=html 6 | *.md diff=markdown 7 | *.php diff=php 8 | 9 | /.github export-ignore 10 | CHANGELOG.md export-ignore 11 | .styleci.yml export-ignore 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.phpunit.cache 2 | /node_modules 3 | /public/build 4 | /public/hot 5 | /public/storage 6 | /storage/*.key 7 | /vendor 8 | .env 9 | .env.backup 10 | .env.production 11 | .phpactor.json 12 | .phpunit.result.cache 13 | Homestead.json 14 | Homestead.yaml 15 | auth.json 16 | npm-debug.log 17 | yarn-error.log 18 | /.fleet 19 | /.idea 20 | /.vscode 21 | config/github.php -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Daniel Lopes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is provided to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the Software or the use or other dealings in the Software. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Contributing [WIP] 2 | 3 |
4 | golang 5 |
6 | Welcome to the PHP Contributing project! This project aims to provide a friendly way to find new open source tasks to contribute to. 7 | 8 | ## Table of Contents 9 | 10 | - [About the Project](#about-the-project) 11 | - [Getting Started](#getting-started) 12 | - [Local Setup](#local-setup) 13 | - [Docker Setup with Sail](#docker-setup-with-sail) 14 | - [Authentication Configuration (GitHub)](#authentication-configuration-github) 15 | - [Contributing](#contributing) 16 | - [License](#license) 17 | - [Contact](#contact) 18 | 19 | ## About the Project 20 | 21 | PHP Contributing is designed to help new contributors find beginner-friendly issues in open source projects, making it easier to start contributing to the open source community. 22 | 23 | ## Getting Started 24 | 1. Clone the repository: 25 | ```sh 26 | git clone https://github.com/Danielopes7/php-contributing.git 27 | ``` 28 | ```sh 29 | cd php-contributing 30 | ``` 31 | 32 | Copy the example environment file and set up the environment variables: 33 | 2. **Copy the example environment file and set up the environment variables:** 34 | 35 | ```sh 36 | cp .env.example .env 37 | ``` 38 | 3. **Install dependencies using Composer:** 39 | 40 | ```sh 41 | composer install 42 | ``` 43 | ### Local Setup🔧 44 | 5. **Generate the application key and run database migrations:** 45 | ```sh 46 | php artisan key:generate 47 | ``` 48 | ```sh 49 | php artisan migrate 50 | ``` 51 | 6. **Install front-end dependencies and start the development environment:** 52 | 53 | ```sh 54 | npm install 55 | ``` 56 | ```sh 57 | npm run dev 58 | ``` 59 | 6. **Start the Laravel development server:** 60 | ```sh 61 | php artisan serve 62 | ``` 63 | Visit http://localhost:8000 to see the application in action. 64 | ### Docker Setup with Sail🐳 65 | If you prefer running the project in Docker using Laravel Sail, follow these steps: 66 | 67 | 5. **Build and start the Docker container using Laravel Sail:** 68 | 69 | ```sh 70 | ./vendor/bin/sail up 71 | ``` 72 | ```sh 73 | ./vendor/bin/sail artisan key:generate 74 | ``` 75 | ```sh 76 | ./vendor/bin/sail artisan migrate 77 | ``` 78 | ```sh 79 | ./vendor/bin/sail npm install 80 | ``` 81 | ```sh 82 | ./vendor/bin/sail npm run dev 83 | ``` 84 | Visit http://localhost to see the application in action. 85 | 86 | ### Authentication Configuration (GitHub)🔐 87 | Before running the project, you need to configure authentication settings for GitHub. 88 | 89 | This project uses **[Laravel GitHub](https://github.com/GrahamCampbell/Laravel-GitHub)** to interact with the GitHub API. 90 | Laravel GitHub requires connection configuration. 91 | 92 | To get started, you'll need to publish all vendor assets: 93 | 94 | ```bash 95 | $ php artisan vendor:publish --provider="GrahamCampbell\GitHub\GitHubServiceProvider" 96 | ``` 97 | 98 | This will create a `config/github.php` file in your app that you can modify to set your configuration. Also, make sure you check for changes to the original config file in this package between releases. 99 | 100 | There are two config options: 101 | 102 | ##### Default Connection Name 103 | 104 | This option (`'default'`) is where you may specify which of the connections below you wish to use as your default connection for all work. Of course, you may use many connections at once using the manager class. The default value for this setting is `'main'`. 105 | 106 | ##### GitHub Connections 107 | 108 | This option (`'connections'`) is where each of the connections are setup for your application. Example configuration has been included, but you may add as many connections as you would like. Note that the 5 supported authentication methods are: `"application"`, `"jwt"`, `"none"`, `"private"`, and `"token"`. 109 | 110 | ##### HTTP Cache 111 | 112 | This option (`'cache'`) is where each of the cache configurations setup for your application. Only the "illuminate" driver is provided out of the box. Example configuration has been included. 113 | 114 | 115 | 116 | ## Contributing 117 | We welcome contributions from the community! 118 | 119 | ## License 120 | This project is licensed under the MIT License. See the LICENSE file for details. 121 | 122 | ## Contact 123 | If you have any questions or feedback, feel free to reach out by creating an issue in the repository. 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | issueService = $issueService; 14 | } 15 | 16 | public function index() 17 | { 18 | if (empty($this->issueService->index()->all())) { 19 | $this->issueService->processUpdateSchedule(); 20 | } 21 | 22 | return view('index'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Livewire/ShowIssues.php: -------------------------------------------------------------------------------- 1 | issues = $issueService->index(); 19 | } 20 | 21 | public function render() 22 | { 23 | return view('livewire.show-issues'); 24 | } 25 | 26 | public function reloadIssues($event, IssueService $issueService) 27 | { 28 | $this->filters_label = in_array($event, $this->filters_label) 29 | ? array_diff($this->filters_label, [$event]) 30 | : array_merge($this->filters_label, [$event]); 31 | 32 | $this->issues = $issueService->index($this->filters_label, $this->filter_size); 33 | } 34 | 35 | public function reloadIssuesBySize($event, IssueService $issueService) 36 | { 37 | $this->filter_size = $this->filter_size == $event ? '' : $event; 38 | $this->issues = $issueService->index($this->filters_label, $this->filter_size); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Models/Issue.php: -------------------------------------------------------------------------------- 1 | belongsTo(Repository::class); 29 | } 30 | 31 | /** 32 | * The labels that belong to the issue. 33 | * 34 | * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany 35 | */ 36 | public function labels() 37 | { 38 | return $this->belongsToMany(Label::class, 'issue_label'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Models/Label.php: -------------------------------------------------------------------------------- 1 | belongsTo(Issue::class); 23 | } 24 | 25 | /** 26 | * The issues that belong to the label. 27 | * 28 | * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany 29 | */ 30 | public function Issues() 31 | { 32 | return $this->belongsToMany(Issue::class, 'issue_label'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/Models/Repository.php: -------------------------------------------------------------------------------- 1 | hasMany(Issue::class); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/Models/User.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | protected $fillable = [ 21 | 'name', 22 | 'email', 23 | 'password', 24 | ]; 25 | 26 | /** 27 | * The attributes that should be hidden for serialization. 28 | * 29 | * @var array 30 | */ 31 | protected $hidden = [ 32 | 'password', 33 | 'remember_token', 34 | ]; 35 | 36 | /** 37 | * Get the attributes that should be cast. 38 | * 39 | * @return array 40 | */ 41 | protected function casts(): array 42 | { 43 | return [ 44 | 'email_verified_at' => 'datetime', 45 | 'password' => 'hashed', 46 | ]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | repositoryService = $repositoryService; 24 | $this->labelService = $labelService; 25 | $this->client = $client; 26 | } 27 | 28 | public function index(array $filters_label = [], ?string $filter_size = null) 29 | { 30 | $query = Issue::select('issues.*') 31 | ->join('issue_label', 'issues.id', '=', 'issue_label.issue_id') 32 | ->join('labels', 'labels.id', '=', 'issue_label.label_id') 33 | ->join('repositories', 'repositories.id', '=', 'issues.repository_id'); 34 | if (! empty($filters_label)) { 35 | $query->whereIn('labels.name', $filters_label) 36 | ->havingRaw('COUNT(DISTINCT labels.name) = ?', [count($filters_label)]); 37 | } 38 | 39 | $array_size = $this->repositoryService->metricsSizeProject($filter_size); 40 | if (! empty($array_size)) { 41 | $query->whereBetween('stargazers_count', [$array_size['stargazers_count_ini'], $array_size['stargazers_count_end']]) 42 | ->whereBetween('forks', [$array_size['forks_count_ini'], $array_size['forks_count_end']]); 43 | } 44 | $filteredIssues = $query->groupBy('issues.id') 45 | ->get(); 46 | 47 | return $filteredIssues; 48 | } 49 | 50 | public function processUpdateSchedule() 51 | { 52 | $threeMonthsAgo = date('Y-m-d', strtotime('-3 months')); 53 | $parameters = ['created:>'.$threeMonthsAgo.' label:"good first issue" language:PHP is:open']; 54 | $issueList = $this->getIssueFromGit($parameters); 55 | $this->execSchedule($issueList); 56 | } 57 | 58 | public function getIssueFromGit(array $parameters): array 59 | { 60 | try { 61 | $pager = new ResultPager($this->client, 30); 62 | 63 | return $pager->fetch($this->client->search(), $this->method, $parameters); 64 | } catch (\Exception $e) { 65 | 66 | Log::error('Error fetching issue from GitHub: '.$e->getMessage()); 67 | 68 | return []; 69 | } 70 | } 71 | 72 | public function createIssueWithDetails(array $issueData, array $repositoryData, array $labelsData): void 73 | { 74 | try { 75 | $repository = $this->repositoryService->createIfNotExists($repositoryData); 76 | 77 | $issue = Issue::create(array_merge($issueData, ['repository_id' => $repository->id])); 78 | 79 | foreach ($labelsData as $labelData) { 80 | $label = $this->labelService->createIfNotExists($labelData); 81 | $issue->labels()->attach($label); 82 | } 83 | } catch (\Exception $e) { 84 | Log::error('Error creating issue with details: '.$e->getMessage()); 85 | } 86 | } 87 | 88 | public function execSchedule(array $issue): void 89 | { 90 | try { 91 | DB::transaction(function () use ($issue) { 92 | $this->deleteAll(); 93 | $this->repositoryService->deleteAll(); 94 | $this->labelService->deleteAll(); 95 | DB::table('issue_label')->delete(); 96 | 97 | foreach ($issue['items'] as $issue) { 98 | $issueData = $this->mountIssueData($issue); 99 | $labelsData = $this->labelService->mountLabelsData($issue['labels']); 100 | $repositoryData = $this->repositoryService->mountRepositoryDataFromUrl($issue['repository_url']); 101 | $this->createIssueWithDetails($issueData, $repositoryData, $labelsData); 102 | } 103 | }); 104 | } catch (\Exception $e) { 105 | Log::error('Error exec schedule: '.$e->getMessage()); 106 | } 107 | } 108 | 109 | protected function mountIssueData(array $issue): array 110 | { 111 | if (empty($issue)) { 112 | return []; 113 | } 114 | 115 | return [ 116 | 'url' => $issue['url'] ?? null, 117 | 'html_url' => $issue['html_url'] ?? null, 118 | 'issue_id' => $issue['id'] ?? null, 119 | 'number' => $issue['number'] ?? null, 120 | 'title' => $issue['title'] ?? null, 121 | 'user_login' => $issue['user']['login'] ?? null, 122 | 'user_avatar_url' => $issue['user']['avatar_url'] ?? null, 123 | 'state' => $issue['state'] ?? null, 124 | 'comments' => $issue['comments'] ?? 0, 125 | 'created_at' => $issue['created_at'] ?? null, 126 | 'updated_at' => $issue['updated_at'] ?? null, 127 | 'closed_at' => $issue['closed_at'] ?? null, 128 | 'body' => $issue['body'] ?? null, 129 | ]; 130 | } 131 | 132 | public function deleteAll(): void 133 | { 134 | Issue::query()->delete(); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /app/Services/LabelService.php: -------------------------------------------------------------------------------- 1 | $labelName], 13 | ['name' => $labelName] 14 | ); 15 | } 16 | 17 | public function mountLabelsData(array $labels): array 18 | { 19 | $labelsList = []; 20 | foreach ($labels as $label) { 21 | $labelsList[] = $label['name']; 22 | } 23 | 24 | return $labelsList; 25 | } 26 | 27 | public function deleteAll(): void 28 | { 29 | Label::query()->delete(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Services/RepositoryService.php: -------------------------------------------------------------------------------- 1 | client = $client; 15 | } 16 | 17 | public function updateOrCreate(array $repositoryData) 18 | { 19 | return Repository::updateOrCreate( 20 | ['repository_git_id' => $repositoryData['id']], 21 | $repositoryData 22 | ); 23 | } 24 | 25 | public function createIfNotExists(array $repositoryData) 26 | { 27 | return Repository::firstOrCreate( 28 | ['repository_git_id' => $repositoryData['repository_git_id']], 29 | $repositoryData 30 | ); 31 | } 32 | 33 | public function getRepositoryFromGit(string $owner = '', string $repo_name = '') 34 | { 35 | if (! empty($owner) && ! empty($repo_name)) { 36 | return $this->client->api('repo')->show($owner, $repo_name); 37 | } 38 | 39 | return []; 40 | } 41 | 42 | public function mountRepositoryDataFromUrl(string $url) 43 | { 44 | $repository = $this->getRepositoryFromGit(...$this->getOwnerRepoFromUrl($url)); 45 | if (isset($repository)) { 46 | return [ 47 | 'repository_git_id' => $repository['id'], 48 | 'language' => $repository['language'], 49 | 'full_name' => $repository['full_name'], 50 | 'description' => $repository['description'], 51 | 'stargazers_count' => $repository['stargazers_count'], 52 | 'forks' => $repository['forks'], 53 | 'open_issues' => $repository['open_issues'], 54 | 'watchers' => $repository['watchers'], 55 | ]; 56 | } 57 | 58 | return []; 59 | } 60 | 61 | protected function getOwnerRepoFromUrl(string $url): array 62 | { 63 | if (preg_match('/repos\/([^\/]+)\/([^\/]+)/', $url, $matches)) { 64 | $owner = $matches[1]; 65 | $repo = $matches[2]; 66 | 67 | return [$owner, $repo]; 68 | } 69 | 70 | return []; 71 | } 72 | 73 | public function deleteAll(): void 74 | { 75 | Repository::query()->delete(); 76 | } 77 | 78 | public function metricsSizeProject(mixed $type_size) 79 | { 80 | if ($type_size == 'small') { 81 | return [ 82 | 'stargazers_count_ini' => 0, 83 | 'stargazers_count_end' => 50, 84 | 'forks_count_ini' => 0, 85 | 'forks_count_end' => 10, 86 | ]; 87 | } 88 | if ($type_size == 'medium') { 89 | return [ 90 | 'stargazers_count_ini' => 50, 91 | 'stargazers_count_end' => 500, 92 | 'forks_count_ini' => 10, 93 | 'forks_count_end' => 100, 94 | ]; 95 | } 96 | if ($type_size == 'big') { 97 | return [ 98 | 'stargazers_count_ini' => 500, 99 | 'stargazers_count_end' => 999999999, 100 | 'forks_count_ini' => 100, 101 | 'forks_count_end' => 999999999, 102 | ]; 103 | } 104 | 105 | return []; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /app/Tasks/UpdateRecentIssues.php: -------------------------------------------------------------------------------- 1 | issueService = $issueService; 14 | } 15 | 16 | public function __invoke() 17 | { 18 | $this->issueService->processUpdateSchedule(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | handleCommand(new ArgvInput); 14 | 15 | exit($status); 16 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | withRouting( 9 | web: __DIR__.'/../routes/web.php', 10 | commands: __DIR__.'/../routes/console.php', 11 | health: '/up', 12 | ) 13 | ->withMiddleware(function (Middleware $middleware) { 14 | // 15 | }) 16 | ->withExceptions(function (Exceptions $exceptions) { 17 | // 18 | })->create(); 19 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /bootstrap/providers.php: -------------------------------------------------------------------------------- 1 | env('APP_NAME', 'Laravel'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Application Environment 21 | |-------------------------------------------------------------------------- 22 | | 23 | | This value determines the "environment" your application is currently 24 | | running in. This may determine how you prefer to configure various 25 | | services the application utilizes. Set this in your ".env" file. 26 | | 27 | */ 28 | 29 | 'env' => env('APP_ENV', 'production'), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Application Debug Mode 34 | |-------------------------------------------------------------------------- 35 | | 36 | | When your application is in debug mode, detailed error messages with 37 | | stack traces will be shown on every error that occurs within your 38 | | application. If disabled, a simple generic error page is shown. 39 | | 40 | */ 41 | 42 | 'debug' => (bool) env('APP_DEBUG', false), 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Application URL 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This URL is used by the console to properly generate URLs when using 50 | | the Artisan command line tool. You should set this to the root of 51 | | the application so that it's available within Artisan commands. 52 | | 53 | */ 54 | 55 | 'url' => env('APP_URL', 'http://localhost'), 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | Application Timezone 60 | |-------------------------------------------------------------------------- 61 | | 62 | | Here you may specify the default timezone for your application, which 63 | | will be used by the PHP date and date-time functions. The timezone 64 | | is set to "UTC" by default as it is suitable for most use cases. 65 | | 66 | */ 67 | 68 | 'timezone' => env('APP_TIMEZONE', 'UTC'), 69 | 70 | /* 71 | |-------------------------------------------------------------------------- 72 | | Application Locale Configuration 73 | |-------------------------------------------------------------------------- 74 | | 75 | | The application locale determines the default locale that will be used 76 | | by Laravel's translation / localization methods. This option can be 77 | | set to any locale for which you plan to have translation strings. 78 | | 79 | */ 80 | 81 | 'locale' => env('APP_LOCALE', 'en'), 82 | 83 | 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), 84 | 85 | 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), 86 | 87 | /* 88 | |-------------------------------------------------------------------------- 89 | | Encryption Key 90 | |-------------------------------------------------------------------------- 91 | | 92 | | This key is utilized by Laravel's encryption services and should be set 93 | | to a random, 32 character string to ensure that all encrypted values 94 | | are secure. You should do this prior to deploying the application. 95 | | 96 | */ 97 | 98 | 'cipher' => 'AES-256-CBC', 99 | 100 | 'key' => env('APP_KEY'), 101 | 102 | 'previous_keys' => [ 103 | ...array_filter( 104 | explode(',', env('APP_PREVIOUS_KEYS', '')) 105 | ), 106 | ], 107 | 108 | /* 109 | |-------------------------------------------------------------------------- 110 | | Maintenance Mode Driver 111 | |-------------------------------------------------------------------------- 112 | | 113 | | These configuration options determine the driver used to determine and 114 | | manage Laravel's "maintenance mode" status. The "cache" driver will 115 | | allow maintenance mode to be controlled across multiple machines. 116 | | 117 | | Supported drivers: "file", "cache" 118 | | 119 | */ 120 | 121 | 'maintenance' => [ 122 | 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), 123 | 'store' => env('APP_MAINTENANCE_STORE', 'database'), 124 | ], 125 | 126 | ]; 127 | -------------------------------------------------------------------------------- /config/auth.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'guard' => env('AUTH_GUARD', 'web'), 18 | 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), 19 | ], 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Authentication Guards 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Next, you may define every authentication guard for your application. 27 | | Of course, a great default configuration has been defined for you 28 | | which utilizes session storage plus the Eloquent user provider. 29 | | 30 | | All authentication guards have a user provider, which defines how the 31 | | users are actually retrieved out of your database or other storage 32 | | system used by the application. Typically, Eloquent is utilized. 33 | | 34 | | Supported: "session" 35 | | 36 | */ 37 | 38 | 'guards' => [ 39 | 'web' => [ 40 | 'driver' => 'session', 41 | 'provider' => 'users', 42 | ], 43 | ], 44 | 45 | /* 46 | |-------------------------------------------------------------------------- 47 | | User Providers 48 | |-------------------------------------------------------------------------- 49 | | 50 | | All authentication guards have a user provider, which defines how the 51 | | users are actually retrieved out of your database or other storage 52 | | system used by the application. Typically, Eloquent is utilized. 53 | | 54 | | If you have multiple user tables or models you may configure multiple 55 | | providers to represent the model / table. These providers may then 56 | | be assigned to any extra authentication guards you have defined. 57 | | 58 | | Supported: "database", "eloquent" 59 | | 60 | */ 61 | 62 | 'providers' => [ 63 | 'users' => [ 64 | 'driver' => 'eloquent', 65 | 'model' => env('AUTH_MODEL', App\Models\User::class), 66 | ], 67 | 68 | // 'users' => [ 69 | // 'driver' => 'database', 70 | // 'table' => 'users', 71 | // ], 72 | ], 73 | 74 | /* 75 | |-------------------------------------------------------------------------- 76 | | Resetting Passwords 77 | |-------------------------------------------------------------------------- 78 | | 79 | | These configuration options specify the behavior of Laravel's password 80 | | reset functionality, including the table utilized for token storage 81 | | and the user provider that is invoked to actually retrieve users. 82 | | 83 | | The expiry time is the number of minutes that each reset token will be 84 | | considered valid. This security feature keeps tokens short-lived so 85 | | they have less time to be guessed. You may change this as needed. 86 | | 87 | | The throttle setting is the number of seconds a user must wait before 88 | | generating more password reset tokens. This prevents the user from 89 | | quickly generating a very large amount of password reset tokens. 90 | | 91 | */ 92 | 93 | 'passwords' => [ 94 | 'users' => [ 95 | 'provider' => 'users', 96 | 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), 97 | 'expire' => 60, 98 | 'throttle' => 60, 99 | ], 100 | ], 101 | 102 | /* 103 | |-------------------------------------------------------------------------- 104 | | Password Confirmation Timeout 105 | |-------------------------------------------------------------------------- 106 | | 107 | | Here you may define the amount of seconds before a password confirmation 108 | | window expires and users are asked to re-enter their password via the 109 | | confirmation screen. By default, the timeout lasts for three hours. 110 | | 111 | */ 112 | 113 | 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), 114 | 115 | ]; 116 | -------------------------------------------------------------------------------- /config/cache.php: -------------------------------------------------------------------------------- 1 | env('CACHE_STORE', 'database'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Cache Stores 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the cache "stores" for your application as 26 | | well as their drivers. You may even define multiple stores for the 27 | | same cache driver to group types of items stored in your caches. 28 | | 29 | | Supported drivers: "array", "database", "file", "memcached", 30 | | "redis", "dynamodb", "octane", "null" 31 | | 32 | */ 33 | 34 | 'stores' => [ 35 | 36 | 'array' => [ 37 | 'driver' => 'array', 38 | 'serialize' => false, 39 | ], 40 | 41 | 'database' => [ 42 | 'driver' => 'database', 43 | 'connection' => env('DB_CACHE_CONNECTION'), 44 | 'table' => env('DB_CACHE_TABLE', 'cache'), 45 | 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), 46 | 'lock_table' => env('DB_CACHE_LOCK_TABLE'), 47 | ], 48 | 49 | 'file' => [ 50 | 'driver' => 'file', 51 | 'path' => storage_path('framework/cache/data'), 52 | 'lock_path' => storage_path('framework/cache/data'), 53 | ], 54 | 55 | 'memcached' => [ 56 | 'driver' => 'memcached', 57 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), 58 | 'sasl' => [ 59 | env('MEMCACHED_USERNAME'), 60 | env('MEMCACHED_PASSWORD'), 61 | ], 62 | 'options' => [ 63 | // Memcached::OPT_CONNECT_TIMEOUT => 2000, 64 | ], 65 | 'servers' => [ 66 | [ 67 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 68 | 'port' => env('MEMCACHED_PORT', 11211), 69 | 'weight' => 100, 70 | ], 71 | ], 72 | ], 73 | 74 | 'redis' => [ 75 | 'driver' => 'redis', 76 | 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), 77 | 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), 78 | ], 79 | 80 | 'dynamodb' => [ 81 | 'driver' => 'dynamodb', 82 | 'key' => env('AWS_ACCESS_KEY_ID'), 83 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 84 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 85 | 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), 86 | 'endpoint' => env('DYNAMODB_ENDPOINT'), 87 | ], 88 | 89 | 'octane' => [ 90 | 'driver' => 'octane', 91 | ], 92 | 93 | ], 94 | 95 | /* 96 | |-------------------------------------------------------------------------- 97 | | Cache Key Prefix 98 | |-------------------------------------------------------------------------- 99 | | 100 | | When utilizing the APC, database, memcached, Redis, and DynamoDB cache 101 | | stores, there might be other applications using the same cache. For 102 | | that reason, you may prefix every cache key to avoid collisions. 103 | | 104 | */ 105 | 106 | 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), 107 | 108 | ]; 109 | -------------------------------------------------------------------------------- /config/database.php: -------------------------------------------------------------------------------- 1 | env('DB_CONNECTION', 'sqlite'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Database Connections 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Below are all of the database connections defined for your application. 27 | | An example configuration is provided for each database system which 28 | | is supported by Laravel. You're free to add / remove connections. 29 | | 30 | */ 31 | 32 | 'connections' => [ 33 | 34 | 'sqlite' => [ 35 | 'driver' => 'sqlite', 36 | 'url' => env('DB_URL'), 37 | 'database' => env('DB_DATABASE', database_path('database.sqlite')), 38 | 'prefix' => '', 39 | 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), 40 | 'busy_timeout' => null, 41 | 'journal_mode' => null, 42 | 'synchronous' => null, 43 | ], 44 | 45 | 'mysql' => [ 46 | 'driver' => 'mysql', 47 | 'url' => env('DB_URL'), 48 | 'host' => env('DB_HOST', '127.0.0.1'), 49 | 'port' => env('DB_PORT', '3306'), 50 | 'database' => env('DB_DATABASE', 'laravel'), 51 | 'username' => env('DB_USERNAME', 'root'), 52 | 'password' => env('DB_PASSWORD', ''), 53 | 'unix_socket' => env('DB_SOCKET', ''), 54 | 'charset' => env('DB_CHARSET', 'utf8mb4'), 55 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 56 | 'prefix' => '', 57 | 'prefix_indexes' => true, 58 | 'strict' => true, 59 | 'engine' => null, 60 | 'options' => extension_loaded('pdo_mysql') ? array_filter([ 61 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), 62 | ]) : [], 63 | ], 64 | 65 | 'mariadb' => [ 66 | 'driver' => 'mariadb', 67 | 'url' => env('DB_URL'), 68 | 'host' => env('DB_HOST', '127.0.0.1'), 69 | 'port' => env('DB_PORT', '3306'), 70 | 'database' => env('DB_DATABASE', 'laravel'), 71 | 'username' => env('DB_USERNAME', 'root'), 72 | 'password' => env('DB_PASSWORD', ''), 73 | 'unix_socket' => env('DB_SOCKET', ''), 74 | 'charset' => env('DB_CHARSET', 'utf8mb4'), 75 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 76 | 'prefix' => '', 77 | 'prefix_indexes' => true, 78 | 'strict' => true, 79 | 'engine' => null, 80 | 'options' => extension_loaded('pdo_mysql') ? array_filter([ 81 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), 82 | ]) : [], 83 | ], 84 | 85 | 'pgsql' => [ 86 | 'driver' => 'pgsql', 87 | 'url' => env('DB_URL'), 88 | 'host' => env('DB_HOST', '127.0.0.1'), 89 | 'port' => env('DB_PORT', '5432'), 90 | 'database' => env('DB_DATABASE', 'laravel'), 91 | 'username' => env('DB_USERNAME', 'root'), 92 | 'password' => env('DB_PASSWORD', ''), 93 | 'charset' => env('DB_CHARSET', 'utf8'), 94 | 'prefix' => '', 95 | 'prefix_indexes' => true, 96 | 'search_path' => 'public', 97 | 'sslmode' => 'prefer', 98 | ], 99 | 100 | 'sqlsrv' => [ 101 | 'driver' => 'sqlsrv', 102 | 'url' => env('DB_URL'), 103 | 'host' => env('DB_HOST', 'localhost'), 104 | 'port' => env('DB_PORT', '1433'), 105 | 'database' => env('DB_DATABASE', 'laravel'), 106 | 'username' => env('DB_USERNAME', 'root'), 107 | 'password' => env('DB_PASSWORD', ''), 108 | 'charset' => env('DB_CHARSET', 'utf8'), 109 | 'prefix' => '', 110 | 'prefix_indexes' => true, 111 | // 'encrypt' => env('DB_ENCRYPT', 'yes'), 112 | // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), 113 | ], 114 | 115 | ], 116 | 117 | /* 118 | |-------------------------------------------------------------------------- 119 | | Migration Repository Table 120 | |-------------------------------------------------------------------------- 121 | | 122 | | This table keeps track of all the migrations that have already run for 123 | | your application. Using this information, we can determine which of 124 | | the migrations on disk haven't actually been run on the database. 125 | | 126 | */ 127 | 128 | 'migrations' => [ 129 | 'table' => 'migrations', 130 | 'update_date_on_publish' => true, 131 | ], 132 | 133 | /* 134 | |-------------------------------------------------------------------------- 135 | | Redis Databases 136 | |-------------------------------------------------------------------------- 137 | | 138 | | Redis is an open source, fast, and advanced key-value store that also 139 | | provides a richer body of commands than a typical key-value system 140 | | such as Memcached. You may define your connection settings here. 141 | | 142 | */ 143 | 144 | 'redis' => [ 145 | 146 | 'client' => env('REDIS_CLIENT', 'phpredis'), 147 | 148 | 'options' => [ 149 | 'cluster' => env('REDIS_CLUSTER', 'redis'), 150 | 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), 151 | ], 152 | 153 | 'default' => [ 154 | 'url' => env('REDIS_URL'), 155 | 'host' => env('REDIS_HOST', '127.0.0.1'), 156 | 'username' => env('REDIS_USERNAME'), 157 | 'password' => env('REDIS_PASSWORD'), 158 | 'port' => env('REDIS_PORT', '6379'), 159 | 'database' => env('REDIS_DB', '0'), 160 | ], 161 | 162 | 'cache' => [ 163 | 'url' => env('REDIS_URL'), 164 | 'host' => env('REDIS_HOST', '127.0.0.1'), 165 | 'username' => env('REDIS_USERNAME'), 166 | 'password' => env('REDIS_PASSWORD'), 167 | 'port' => env('REDIS_PORT', '6379'), 168 | 'database' => env('REDIS_CACHE_DB', '1'), 169 | ], 170 | 171 | ], 172 | 173 | ]; 174 | -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DISK', 'local'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Filesystem Disks 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Below you may configure as many filesystem disks as necessary, and you 24 | | may even configure multiple disks for the same driver. Examples for 25 | | most supported storage drivers are configured here for reference. 26 | | 27 | | Supported drivers: "local", "ftp", "sftp", "s3" 28 | | 29 | */ 30 | 31 | 'disks' => [ 32 | 33 | 'local' => [ 34 | 'driver' => 'local', 35 | 'root' => storage_path('app'), 36 | 'throw' => false, 37 | ], 38 | 39 | 'public' => [ 40 | 'driver' => 'local', 41 | 'root' => storage_path('app/public'), 42 | 'url' => env('APP_URL').'/storage', 43 | 'visibility' => 'public', 44 | 'throw' => false, 45 | ], 46 | 47 | 's3' => [ 48 | 'driver' => 's3', 49 | 'key' => env('AWS_ACCESS_KEY_ID'), 50 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 51 | 'region' => env('AWS_DEFAULT_REGION'), 52 | 'bucket' => env('AWS_BUCKET'), 53 | 'url' => env('AWS_URL'), 54 | 'endpoint' => env('AWS_ENDPOINT'), 55 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), 56 | 'throw' => false, 57 | ], 58 | 59 | ], 60 | 61 | /* 62 | |-------------------------------------------------------------------------- 63 | | Symbolic Links 64 | |-------------------------------------------------------------------------- 65 | | 66 | | Here you may configure the symbolic links that will be created when the 67 | | `storage:link` Artisan command is executed. The array keys should be 68 | | the locations of the links and the values should be their targets. 69 | | 70 | */ 71 | 72 | 'links' => [ 73 | public_path('storage') => storage_path('app/public'), 74 | ], 75 | 76 | ]; 77 | -------------------------------------------------------------------------------- /config/logging.php: -------------------------------------------------------------------------------- 1 | env('LOG_CHANNEL', 'stack'), 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Deprecations Log Channel 26 | |-------------------------------------------------------------------------- 27 | | 28 | | This option controls the log channel that should be used to log warnings 29 | | regarding deprecated PHP and library features. This allows you to get 30 | | your application ready for upcoming major versions of dependencies. 31 | | 32 | */ 33 | 34 | 'deprecations' => [ 35 | 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), 36 | 'trace' => env('LOG_DEPRECATIONS_TRACE', false), 37 | ], 38 | 39 | /* 40 | |-------------------------------------------------------------------------- 41 | | Log Channels 42 | |-------------------------------------------------------------------------- 43 | | 44 | | Here you may configure the log channels for your application. Laravel 45 | | utilizes the Monolog PHP logging library, which includes a variety 46 | | of powerful log handlers and formatters that you're free to use. 47 | | 48 | | Available drivers: "single", "daily", "slack", "syslog", 49 | | "errorlog", "monolog", "custom", "stack" 50 | | 51 | */ 52 | 53 | 'channels' => [ 54 | 55 | 'stack' => [ 56 | 'driver' => 'stack', 57 | 'channels' => explode(',', env('LOG_STACK', 'single')), 58 | 'ignore_exceptions' => false, 59 | ], 60 | 61 | 'single' => [ 62 | 'driver' => 'single', 63 | 'path' => storage_path('logs/laravel.log'), 64 | 'level' => env('LOG_LEVEL', 'debug'), 65 | 'replace_placeholders' => true, 66 | ], 67 | 68 | 'daily' => [ 69 | 'driver' => 'daily', 70 | 'path' => storage_path('logs/laravel.log'), 71 | 'level' => env('LOG_LEVEL', 'debug'), 72 | 'days' => env('LOG_DAILY_DAYS', 14), 73 | 'replace_placeholders' => true, 74 | ], 75 | 76 | 'slack' => [ 77 | 'driver' => 'slack', 78 | 'url' => env('LOG_SLACK_WEBHOOK_URL'), 79 | 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), 80 | 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), 81 | 'level' => env('LOG_LEVEL', 'critical'), 82 | 'replace_placeholders' => true, 83 | ], 84 | 85 | 'papertrail' => [ 86 | 'driver' => 'monolog', 87 | 'level' => env('LOG_LEVEL', 'debug'), 88 | 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), 89 | 'handler_with' => [ 90 | 'host' => env('PAPERTRAIL_URL'), 91 | 'port' => env('PAPERTRAIL_PORT'), 92 | 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), 93 | ], 94 | 'processors' => [PsrLogMessageProcessor::class], 95 | ], 96 | 97 | 'stderr' => [ 98 | 'driver' => 'monolog', 99 | 'level' => env('LOG_LEVEL', 'debug'), 100 | 'handler' => StreamHandler::class, 101 | 'formatter' => env('LOG_STDERR_FORMATTER'), 102 | 'with' => [ 103 | 'stream' => 'php://stderr', 104 | ], 105 | 'processors' => [PsrLogMessageProcessor::class], 106 | ], 107 | 108 | 'syslog' => [ 109 | 'driver' => 'syslog', 110 | 'level' => env('LOG_LEVEL', 'debug'), 111 | 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), 112 | 'replace_placeholders' => true, 113 | ], 114 | 115 | 'errorlog' => [ 116 | 'driver' => 'errorlog', 117 | 'level' => env('LOG_LEVEL', 'debug'), 118 | 'replace_placeholders' => true, 119 | ], 120 | 121 | 'null' => [ 122 | 'driver' => 'monolog', 123 | 'handler' => NullHandler::class, 124 | ], 125 | 126 | 'emergency' => [ 127 | 'path' => storage_path('logs/laravel.log'), 128 | ], 129 | 130 | ], 131 | 132 | ]; 133 | -------------------------------------------------------------------------------- /config/mail.php: -------------------------------------------------------------------------------- 1 | env('MAIL_MAILER', 'log'), 18 | 19 | /* 20 | |-------------------------------------------------------------------------- 21 | | Mailer Configurations 22 | |-------------------------------------------------------------------------- 23 | | 24 | | Here you may configure all of the mailers used by your application plus 25 | | their respective settings. Several examples have been configured for 26 | | you and you are free to add your own as your application requires. 27 | | 28 | | Laravel supports a variety of mail "transport" drivers that can be used 29 | | when delivering an email. You may specify which one you're using for 30 | | your mailers below. You may also add additional mailers if needed. 31 | | 32 | | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", 33 | | "postmark", "resend", "log", "array", 34 | | "failover", "roundrobin" 35 | | 36 | */ 37 | 38 | 'mailers' => [ 39 | 40 | 'smtp' => [ 41 | 'transport' => 'smtp', 42 | 'url' => env('MAIL_URL'), 43 | 'host' => env('MAIL_HOST', '127.0.0.1'), 44 | 'port' => env('MAIL_PORT', 2525), 45 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 46 | 'username' => env('MAIL_USERNAME'), 47 | 'password' => env('MAIL_PASSWORD'), 48 | 'timeout' => null, 49 | 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url(env('APP_URL', 'http://localhost'), PHP_URL_HOST)), 50 | ], 51 | 52 | 'ses' => [ 53 | 'transport' => 'ses', 54 | ], 55 | 56 | 'postmark' => [ 57 | 'transport' => 'postmark', 58 | // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), 59 | // 'client' => [ 60 | // 'timeout' => 5, 61 | // ], 62 | ], 63 | 64 | 'resend' => [ 65 | 'transport' => 'resend', 66 | ], 67 | 68 | 'sendmail' => [ 69 | 'transport' => 'sendmail', 70 | 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), 71 | ], 72 | 73 | 'log' => [ 74 | 'transport' => 'log', 75 | 'channel' => env('MAIL_LOG_CHANNEL'), 76 | ], 77 | 78 | 'array' => [ 79 | 'transport' => 'array', 80 | ], 81 | 82 | 'failover' => [ 83 | 'transport' => 'failover', 84 | 'mailers' => [ 85 | 'smtp', 86 | 'log', 87 | ], 88 | ], 89 | 90 | 'roundrobin' => [ 91 | 'transport' => 'roundrobin', 92 | 'mailers' => [ 93 | 'ses', 94 | 'postmark', 95 | ], 96 | ], 97 | 98 | ], 99 | 100 | /* 101 | |-------------------------------------------------------------------------- 102 | | Global "From" Address 103 | |-------------------------------------------------------------------------- 104 | | 105 | | You may wish for all emails sent by your application to be sent from 106 | | the same address. Here you may specify a name and address that is 107 | | used globally for all emails that are sent by your application. 108 | | 109 | */ 110 | 111 | 'from' => [ 112 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), 113 | 'name' => env('MAIL_FROM_NAME', 'Example'), 114 | ], 115 | 116 | ]; 117 | -------------------------------------------------------------------------------- /config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_CONNECTION', 'database'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Queue Connections 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may configure the connection options for every queue backend 24 | | used by your application. An example configuration is provided for 25 | | each backend supported by Laravel. You're also free to add more. 26 | | 27 | | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'sync' => [ 34 | 'driver' => 'sync', 35 | ], 36 | 37 | 'database' => [ 38 | 'driver' => 'database', 39 | 'connection' => env('DB_QUEUE_CONNECTION'), 40 | 'table' => env('DB_QUEUE_TABLE', 'jobs'), 41 | 'queue' => env('DB_QUEUE', 'default'), 42 | 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), 43 | 'after_commit' => false, 44 | ], 45 | 46 | 'beanstalkd' => [ 47 | 'driver' => 'beanstalkd', 48 | 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), 49 | 'queue' => env('BEANSTALKD_QUEUE', 'default'), 50 | 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), 51 | 'block_for' => 0, 52 | 'after_commit' => false, 53 | ], 54 | 55 | 'sqs' => [ 56 | 'driver' => 'sqs', 57 | 'key' => env('AWS_ACCESS_KEY_ID'), 58 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 59 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 60 | 'queue' => env('SQS_QUEUE', 'default'), 61 | 'suffix' => env('SQS_SUFFIX'), 62 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 63 | 'after_commit' => false, 64 | ], 65 | 66 | 'redis' => [ 67 | 'driver' => 'redis', 68 | 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), 69 | 'queue' => env('REDIS_QUEUE', 'default'), 70 | 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), 71 | 'block_for' => null, 72 | 'after_commit' => false, 73 | ], 74 | 75 | ], 76 | 77 | /* 78 | |-------------------------------------------------------------------------- 79 | | Job Batching 80 | |-------------------------------------------------------------------------- 81 | | 82 | | The following options configure the database and table that store job 83 | | batching information. These options can be updated to any database 84 | | connection and table which has been defined by your application. 85 | | 86 | */ 87 | 88 | 'batching' => [ 89 | 'database' => env('DB_CONNECTION', 'sqlite'), 90 | 'table' => 'job_batches', 91 | ], 92 | 93 | /* 94 | |-------------------------------------------------------------------------- 95 | | Failed Queue Jobs 96 | |-------------------------------------------------------------------------- 97 | | 98 | | These options configure the behavior of failed queue job logging so you 99 | | can control how and where failed jobs are stored. Laravel ships with 100 | | support for storing failed jobs in a simple file or in a database. 101 | | 102 | | Supported drivers: "database-uuids", "dynamodb", "file", "null" 103 | | 104 | */ 105 | 106 | 'failed' => [ 107 | 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), 108 | 'database' => env('DB_CONNECTION', 'sqlite'), 109 | 'table' => 'failed_jobs', 110 | ], 111 | 112 | ]; 113 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'token' => env('POSTMARK_TOKEN'), 19 | ], 20 | 21 | 'ses' => [ 22 | 'key' => env('AWS_ACCESS_KEY_ID'), 23 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 24 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 25 | ], 26 | 27 | 'resend' => [ 28 | 'key' => env('RESEND_KEY'), 29 | ], 30 | 31 | 'slack' => [ 32 | 'notifications' => [ 33 | 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), 34 | 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), 35 | ], 36 | ], 37 | 38 | ]; 39 | -------------------------------------------------------------------------------- /config/session.php: -------------------------------------------------------------------------------- 1 | env('SESSION_DRIVER', 'database'), 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Session Lifetime 26 | |-------------------------------------------------------------------------- 27 | | 28 | | Here you may specify the number of minutes that you wish the session 29 | | to be allowed to remain idle before it expires. If you want them 30 | | to expire immediately when the browser is closed then you may 31 | | indicate that via the expire_on_close configuration option. 32 | | 33 | */ 34 | 35 | 'lifetime' => env('SESSION_LIFETIME', 120), 36 | 37 | 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), 38 | 39 | /* 40 | |-------------------------------------------------------------------------- 41 | | Session Encryption 42 | |-------------------------------------------------------------------------- 43 | | 44 | | This option allows you to easily specify that all of your session data 45 | | should be encrypted before it's stored. All encryption is performed 46 | | automatically by Laravel and you may use the session like normal. 47 | | 48 | */ 49 | 50 | 'encrypt' => env('SESSION_ENCRYPT', false), 51 | 52 | /* 53 | |-------------------------------------------------------------------------- 54 | | Session File Location 55 | |-------------------------------------------------------------------------- 56 | | 57 | | When utilizing the "file" session driver, the session files are placed 58 | | on disk. The default storage location is defined here; however, you 59 | | are free to provide another location where they should be stored. 60 | | 61 | */ 62 | 63 | 'files' => storage_path('framework/sessions'), 64 | 65 | /* 66 | |-------------------------------------------------------------------------- 67 | | Session Database Connection 68 | |-------------------------------------------------------------------------- 69 | | 70 | | When using the "database" or "redis" session drivers, you may specify a 71 | | connection that should be used to manage these sessions. This should 72 | | correspond to a connection in your database configuration options. 73 | | 74 | */ 75 | 76 | 'connection' => env('SESSION_CONNECTION'), 77 | 78 | /* 79 | |-------------------------------------------------------------------------- 80 | | Session Database Table 81 | |-------------------------------------------------------------------------- 82 | | 83 | | When using the "database" session driver, you may specify the table to 84 | | be used to store sessions. Of course, a sensible default is defined 85 | | for you; however, you're welcome to change this to another table. 86 | | 87 | */ 88 | 89 | 'table' => env('SESSION_TABLE', 'sessions'), 90 | 91 | /* 92 | |-------------------------------------------------------------------------- 93 | | Session Cache Store 94 | |-------------------------------------------------------------------------- 95 | | 96 | | When using one of the framework's cache driven session backends, you may 97 | | define the cache store which should be used to store the session data 98 | | between requests. This must match one of your defined cache stores. 99 | | 100 | | Affects: "apc", "dynamodb", "memcached", "redis" 101 | | 102 | */ 103 | 104 | 'store' => env('SESSION_STORE'), 105 | 106 | /* 107 | |-------------------------------------------------------------------------- 108 | | Session Sweeping Lottery 109 | |-------------------------------------------------------------------------- 110 | | 111 | | Some session drivers must manually sweep their storage location to get 112 | | rid of old sessions from storage. Here are the chances that it will 113 | | happen on a given request. By default, the odds are 2 out of 100. 114 | | 115 | */ 116 | 117 | 'lottery' => [2, 100], 118 | 119 | /* 120 | |-------------------------------------------------------------------------- 121 | | Session Cookie Name 122 | |-------------------------------------------------------------------------- 123 | | 124 | | Here you may change the name of the session cookie that is created by 125 | | the framework. Typically, you should not need to change this value 126 | | since doing so does not grant a meaningful security improvement. 127 | | 128 | */ 129 | 130 | 'cookie' => env( 131 | 'SESSION_COOKIE', 132 | Str::slug(env('APP_NAME', 'laravel'), '_').'_session' 133 | ), 134 | 135 | /* 136 | |-------------------------------------------------------------------------- 137 | | Session Cookie Path 138 | |-------------------------------------------------------------------------- 139 | | 140 | | The session cookie path determines the path for which the cookie will 141 | | be regarded as available. Typically, this will be the root path of 142 | | your application, but you're free to change this when necessary. 143 | | 144 | */ 145 | 146 | 'path' => env('SESSION_PATH', '/'), 147 | 148 | /* 149 | |-------------------------------------------------------------------------- 150 | | Session Cookie Domain 151 | |-------------------------------------------------------------------------- 152 | | 153 | | This value determines the domain and subdomains the session cookie is 154 | | available to. By default, the cookie will be available to the root 155 | | domain and all subdomains. Typically, this shouldn't be changed. 156 | | 157 | */ 158 | 159 | 'domain' => env('SESSION_DOMAIN'), 160 | 161 | /* 162 | |-------------------------------------------------------------------------- 163 | | HTTPS Only Cookies 164 | |-------------------------------------------------------------------------- 165 | | 166 | | By setting this option to true, session cookies will only be sent back 167 | | to the server if the browser has a HTTPS connection. This will keep 168 | | the cookie from being sent to you when it can't be done securely. 169 | | 170 | */ 171 | 172 | 'secure' => env('SESSION_SECURE_COOKIE'), 173 | 174 | /* 175 | |-------------------------------------------------------------------------- 176 | | HTTP Access Only 177 | |-------------------------------------------------------------------------- 178 | | 179 | | Setting this value to true will prevent JavaScript from accessing the 180 | | value of the cookie and the cookie will only be accessible through 181 | | the HTTP protocol. It's unlikely you should disable this option. 182 | | 183 | */ 184 | 185 | 'http_only' => env('SESSION_HTTP_ONLY', true), 186 | 187 | /* 188 | |-------------------------------------------------------------------------- 189 | | Same-Site Cookies 190 | |-------------------------------------------------------------------------- 191 | | 192 | | This option determines how your cookies behave when cross-site requests 193 | | take place, and can be used to mitigate CSRF attacks. By default, we 194 | | will set this value to "lax" to permit secure cross-site requests. 195 | | 196 | | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value 197 | | 198 | | Supported: "lax", "strict", "none", null 199 | | 200 | */ 201 | 202 | 'same_site' => env('SESSION_SAME_SITE', 'lax'), 203 | 204 | /* 205 | |-------------------------------------------------------------------------- 206 | | Partitioned Cookies 207 | |-------------------------------------------------------------------------- 208 | | 209 | | Setting this value to true will tie the cookie to the top-level site for 210 | | a cross-site context. Partitioned cookies are accepted by the browser 211 | | when flagged "secure" and the Same-Site attribute is set to "none". 212 | | 213 | */ 214 | 215 | 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), 216 | 217 | ]; 218 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class UserFactory extends Factory 13 | { 14 | /** 15 | * The current password being used by the factory. 16 | */ 17 | protected static ?string $password; 18 | 19 | /** 20 | * Define the model's default state. 21 | * 22 | * @return array 23 | */ 24 | public function definition(): array 25 | { 26 | return [ 27 | 'name' => fake()->name(), 28 | 'email' => fake()->unique()->safeEmail(), 29 | 'email_verified_at' => now(), 30 | 'password' => static::$password ??= Hash::make('password'), 31 | 'remember_token' => Str::random(10), 32 | ]; 33 | } 34 | 35 | /** 36 | * Indicate that the model's email address should be unverified. 37 | */ 38 | public function unverified(): static 39 | { 40 | return $this->state(fn (array $attributes) => [ 41 | 'email_verified_at' => null, 42 | ]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('name'); 17 | $table->string('email')->unique(); 18 | $table->timestamp('email_verified_at')->nullable(); 19 | $table->string('password'); 20 | $table->rememberToken(); 21 | $table->timestamps(); 22 | }); 23 | 24 | Schema::create('password_reset_tokens', function (Blueprint $table) { 25 | $table->string('email')->primary(); 26 | $table->string('token'); 27 | $table->timestamp('created_at')->nullable(); 28 | }); 29 | 30 | Schema::create('sessions', function (Blueprint $table) { 31 | $table->string('id')->primary(); 32 | $table->foreignId('user_id')->nullable()->index(); 33 | $table->string('ip_address', 45)->nullable(); 34 | $table->text('user_agent')->nullable(); 35 | $table->longText('payload'); 36 | $table->integer('last_activity')->index(); 37 | }); 38 | } 39 | 40 | /** 41 | * Reverse the migrations. 42 | */ 43 | public function down(): void 44 | { 45 | Schema::dropIfExists('users'); 46 | Schema::dropIfExists('password_reset_tokens'); 47 | Schema::dropIfExists('sessions'); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000001_create_cache_table.php: -------------------------------------------------------------------------------- 1 | string('key')->primary(); 16 | $table->mediumText('value'); 17 | $table->integer('expiration'); 18 | }); 19 | 20 | Schema::create('cache_locks', function (Blueprint $table) { 21 | $table->string('key')->primary(); 22 | $table->string('owner'); 23 | $table->integer('expiration'); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | */ 30 | public function down(): void 31 | { 32 | Schema::dropIfExists('cache'); 33 | Schema::dropIfExists('cache_locks'); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /database/migrations/0001_01_01_000002_create_jobs_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('queue')->index(); 17 | $table->longText('payload'); 18 | $table->unsignedTinyInteger('attempts'); 19 | $table->unsignedInteger('reserved_at')->nullable(); 20 | $table->unsignedInteger('available_at'); 21 | $table->unsignedInteger('created_at'); 22 | }); 23 | 24 | Schema::create('job_batches', function (Blueprint $table) { 25 | $table->string('id')->primary(); 26 | $table->string('name'); 27 | $table->integer('total_jobs'); 28 | $table->integer('pending_jobs'); 29 | $table->integer('failed_jobs'); 30 | $table->longText('failed_job_ids'); 31 | $table->mediumText('options')->nullable(); 32 | $table->integer('cancelled_at')->nullable(); 33 | $table->integer('created_at'); 34 | $table->integer('finished_at')->nullable(); 35 | }); 36 | 37 | Schema::create('failed_jobs', function (Blueprint $table) { 38 | $table->id(); 39 | $table->string('uuid')->unique(); 40 | $table->text('connection'); 41 | $table->text('queue'); 42 | $table->longText('payload'); 43 | $table->longText('exception'); 44 | $table->timestamp('failed_at')->useCurrent(); 45 | }); 46 | } 47 | 48 | /** 49 | * Reverse the migrations. 50 | */ 51 | public function down(): void 52 | { 53 | Schema::dropIfExists('jobs'); 54 | Schema::dropIfExists('job_batches'); 55 | Schema::dropIfExists('failed_jobs'); 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /database/migrations/2024_07_27_190100_create_repositories_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->unsignedBigInteger('repository_git_id'); 17 | $table->string('language'); 18 | $table->string('full_name'); 19 | $table->text('description')->nullable(); 20 | $table->integer('stargazers_count'); 21 | $table->integer('forks'); 22 | $table->integer('open_issues'); 23 | $table->integer('watchers'); 24 | $table->timestamps(); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | */ 31 | public function down(): void 32 | { 33 | Schema::dropIfExists('repositories'); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /database/migrations/2024_07_27_190200_create_issues_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('url'); 17 | $table->unsignedBigInteger('repository_id'); // Foreign key to repositories 18 | $table->string('html_url'); 19 | $table->unsignedBigInteger('issue_id'); 20 | $table->integer('number'); 21 | $table->string('title')->nullable(); 22 | $table->string('user_login'); // Reference to the login field in the user array in the JSON response from the issues API. 23 | $table->string('user_avatar_url')->nullable(); // Reference to the avatar_url field in the user array in the JSON response from the issues API. 24 | $table->string('state'); 25 | $table->integer('comments'); 26 | $table->timestamp('created_at'); 27 | $table->timestamp('updated_at')->nullable(); 28 | $table->timestamp('closed_at')->nullable(); 29 | $table->text('body')->nullable(); 30 | 31 | // Define a foreign key constraint for repository_id 32 | $table->foreign('repository_id')->references('id')->on('repositories')->onDelete('cascade'); 33 | }); 34 | } 35 | 36 | /** 37 | * Reverse the migrations. 38 | */ 39 | public function down(): void 40 | { 41 | Schema::dropIfExists('issues'); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /database/migrations/2024_07_27_190345_create_labels_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('name'); 17 | $table->string('color')->nullable(); 18 | $table->timestamps(); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | */ 25 | public function down(): void 26 | { 27 | Schema::dropIfExists('labels'); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /database/migrations/2024_07_28_154451_create_issue_label_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->unsignedBigInteger('issue_id'); 17 | $table->unsignedBigInteger('label_id'); 18 | $table->timestamps(); 19 | 20 | $table->foreign('issue_id')->references('id')->on('issues')->onDelete('cascade'); 21 | $table->foreign('label_id')->references('id')->on('labels')->onDelete('cascade'); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::dropIfExists('issue_label'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 17 | 18 | User::factory()->create([ 19 | 'name' => 'Test User', 20 | 'email' => 'test@example.com', 21 | ]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docker-compose-prod.yml: -------------------------------------------------------------------------------- 1 | # For more information: https://laravel.com/docs/sail 2 | version: '3' 3 | services: 4 | laravel.test: 5 | build: 6 | context: ./ 7 | dockerfile: ./docker/8.2-prod/Dockerfile 8 | image: sail-8.2/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 | environment: 15 | LARAVEL_SAIL: 1 16 | XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' 17 | XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' 18 | volumes: 19 | - '.:/var/www/html' 20 | networks: 21 | - sail 22 | depends_on: 23 | - mysql 24 | mysql: 25 | image: 'mysql/mysql-server:8.0' 26 | ports: 27 | - '${FORWARD_DB_PORT:-3306}:3306' 28 | environment: 29 | MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' 30 | MYSQL_ROOT_HOST: '%' 31 | MYSQL_DATABASE: '${DB_DATABASE}' 32 | MYSQL_USER: '${DB_USERNAME}' 33 | MYSQL_PASSWORD: '${DB_PASSWORD}' 34 | MYSQL_ALLOW_EMPTY_PASSWORD: 1 35 | volumes: 36 | - 'sail-mysql:/var/lib/mysql' 37 | - './docker/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh' 38 | networks: 39 | - sail 40 | healthcheck: 41 | test: 42 | - CMD 43 | - mysqladmin 44 | - ping 45 | - '-p${DB_PASSWORD}' 46 | retries: 3 47 | timeout: 5s 48 | networks: 49 | sail: 50 | driver: bridge 51 | volumes: 52 | sail-mysql: 53 | driver: local -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | laravel.test: 3 | build: 4 | context: ./docker/8.3 5 | dockerfile: Dockerfile 6 | args: 7 | WWWGROUP: '${WWWGROUP}' 8 | image: sail-8.3/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 | environment: 15 | WWWUSER: '${WWWUSER}' 16 | LARAVEL_SAIL: 1 17 | XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' 18 | XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' 19 | IGNITION_LOCAL_SITES_PATH: '${PWD}' 20 | volumes: 21 | - '.:/var/www/html' 22 | networks: 23 | - sail 24 | depends_on: 25 | - mysql 26 | - redis 27 | mysql: 28 | image: 'mysql/mysql-server:8.0' 29 | ports: 30 | - '${FORWARD_DB_PORT:-3306}:3306' 31 | environment: 32 | MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' 33 | MYSQL_ROOT_HOST: '%' 34 | MYSQL_DATABASE: '${DB_DATABASE}' 35 | MYSQL_USER: '${DB_USERNAME}' 36 | MYSQL_PASSWORD: '${DB_PASSWORD}' 37 | MYSQL_ALLOW_EMPTY_PASSWORD: 1 38 | volumes: 39 | - 'sail-mysql:/var/lib/mysql' 40 | - './docker/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh' 41 | networks: 42 | - sail 43 | healthcheck: 44 | test: 45 | - CMD 46 | - mysqladmin 47 | - ping 48 | - '-p${DB_PASSWORD}' 49 | retries: 3 50 | timeout: 5s 51 | redis: 52 | image: 'redis:alpine' 53 | ports: 54 | - '${FORWARD_REDIS_PORT:-6379}:6379' 55 | volumes: 56 | - 'sail-redis:/data' 57 | networks: 58 | - sail 59 | healthcheck: 60 | test: 61 | - CMD 62 | - redis-cli 63 | - ping 64 | retries: 3 65 | timeout: 5s 66 | networks: 67 | sail: 68 | driver: bridge 69 | volumes: 70 | sail-mysql: 71 | driver: local 72 | sail-redis: 73 | driver: local 74 | -------------------------------------------------------------------------------- /docker/8.0/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | LABEL maintainer="Taylor Otwell" 4 | 5 | ARG WWWGROUP 6 | ARG NODE_VERSION=20 7 | ARG POSTGRES_VERSION=13 8 | 9 | WORKDIR /var/www/html 10 | 11 | ENV DEBIAN_FRONTEND noninteractive 12 | ENV TZ=UTC 13 | ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80" 14 | ENV SUPERVISOR_PHP_USER="sail" 15 | 16 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 17 | 18 | RUN apt-get update \ 19 | && mkdir -p /etc/apt/keyrings \ 20 | && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch ffmpeg nano \ 21 | && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /usr/share/keyrings/ppa_ondrej_php.gpg > /dev/null \ 22 | && echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu focal main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ 23 | && apt-get update \ 24 | && apt-get install -y php8.0-cli php8.0-dev \ 25 | php8.0-pgsql php8.0-sqlite3 php8.0-gd php8.0-imagick \ 26 | php8.0-curl php8.0-memcached \ 27 | php8.0-imap php8.0-mysql php8.0-mbstring \ 28 | php8.0-xml php8.0-zip php8.0-bcmath php8.0-soap \ 29 | php8.0-intl php8.0-readline php8.0-pcov \ 30 | php8.0-msgpack php8.0-igbinary php8.0-ldap \ 31 | php8.0-redis php8.0-swoole php8.0-xdebug \ 32 | && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ 33 | && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ 34 | && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ 35 | && apt-get update \ 36 | && apt-get install -y nodejs \ 37 | && npm install -g npm \ 38 | && npm install -g bun \ 39 | && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarnkey.gpg >/dev/null \ 40 | && echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ 41 | && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/pgdg.gpg >/dev/null \ 42 | && echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt focal-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ 43 | && apt-get update \ 44 | && apt-get install -y yarn \ 45 | && apt-get install -y mysql-client \ 46 | && apt-get install -y postgresql-client-$POSTGRES_VERSION \ 47 | && apt-get -y autoremove \ 48 | && apt-get clean \ 49 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 50 | 51 | RUN update-alternatives --set php /usr/bin/php8.0 52 | 53 | RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.0 54 | 55 | RUN groupadd --force -g $WWWGROUP sail 56 | RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail 57 | 58 | COPY start-container /usr/local/bin/start-container 59 | COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf 60 | COPY php.ini /etc/php/8.0/cli/conf.d/99-sail.ini 61 | RUN chmod +x /usr/local/bin/start-container 62 | 63 | EXPOSE 80/tcp 64 | 65 | ENTRYPOINT ["start-container"] 66 | -------------------------------------------------------------------------------- /docker/8.0/php.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | post_max_size = 100M 3 | upload_max_filesize = 100M 4 | variables_order = EGPCS 5 | pcov.directory = . 6 | -------------------------------------------------------------------------------- /docker/8.0/start-container: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "$SUPERVISOR_PHP_USER" != "root" ] && [ "$SUPERVISOR_PHP_USER" != "sail" ]; then 4 | echo "You should set SUPERVISOR_PHP_USER to either 'sail' or 'root'." 5 | exit 1 6 | fi 7 | 8 | if [ ! -z "$WWWUSER" ]; then 9 | usermod -u $WWWUSER sail 10 | fi 11 | 12 | if [ ! -d /.composer ]; then 13 | mkdir /.composer 14 | fi 15 | 16 | chmod -R ugo+rw /.composer 17 | 18 | if [ $# -gt 0 ]; then 19 | if [ "$SUPERVISOR_PHP_USER" = "root" ]; then 20 | exec "$@" 21 | else 22 | exec gosu $WWWUSER "$@" 23 | fi 24 | else 25 | exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf 26 | fi 27 | -------------------------------------------------------------------------------- /docker/8.0/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [program:php] 8 | command=%(ENV_SUPERVISOR_PHP_COMMAND)s 9 | user=%(ENV_SUPERVISOR_PHP_USER)s 10 | environment=LARAVEL_SAIL="1" 11 | stdout_logfile=/dev/stdout 12 | stdout_logfile_maxbytes=0 13 | stderr_logfile=/dev/stderr 14 | stderr_logfile_maxbytes=0 15 | -------------------------------------------------------------------------------- /docker/8.1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | LABEL maintainer="Taylor Otwell" 4 | 5 | ARG WWWGROUP 6 | ARG NODE_VERSION=20 7 | ARG POSTGRES_VERSION=15 8 | 9 | WORKDIR /var/www/html 10 | 11 | ENV DEBIAN_FRONTEND noninteractive 12 | ENV TZ=UTC 13 | ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80" 14 | ENV SUPERVISOR_PHP_USER="sail" 15 | 16 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 17 | 18 | RUN apt-get update \ 19 | && mkdir -p /etc/apt/keyrings \ 20 | && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch ffmpeg nano \ 21 | && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /usr/share/keyrings/ppa_ondrej_php.gpg > /dev/null \ 22 | && echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ 23 | && apt-get update \ 24 | && apt-get install -y php8.1-cli php8.1-dev \ 25 | php8.1-pgsql php8.1-sqlite3 php8.1-gd php8.1-imagick \ 26 | php8.1-curl \ 27 | php8.1-imap php8.1-mysql php8.1-mbstring \ 28 | php8.1-xml php8.1-zip php8.1-bcmath php8.1-soap \ 29 | php8.1-intl php8.1-readline \ 30 | php8.1-ldap \ 31 | php8.1-msgpack php8.1-igbinary php8.1-redis php8.1-swoole \ 32 | php8.1-memcached php8.1-pcov php8.1-xdebug \ 33 | && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ 34 | && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ 35 | && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ 36 | && apt-get update \ 37 | && apt-get install -y nodejs \ 38 | && npm install -g npm \ 39 | && npm install -g bun \ 40 | && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarn.gpg >/dev/null \ 41 | && echo "deb [signed-by=/usr/share/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ 42 | && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/pgdg.gpg >/dev/null \ 43 | && echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ 44 | && apt-get update \ 45 | && apt-get install -y yarn \ 46 | && apt-get install -y mysql-client \ 47 | && apt-get install -y postgresql-client-$POSTGRES_VERSION \ 48 | && apt-get -y autoremove \ 49 | && apt-get clean \ 50 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 51 | 52 | RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.1 53 | 54 | RUN groupadd --force -g $WWWGROUP sail 55 | RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail 56 | 57 | COPY start-container /usr/local/bin/start-container 58 | COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf 59 | COPY php.ini /etc/php/8.1/cli/conf.d/99-sail.ini 60 | RUN chmod +x /usr/local/bin/start-container 61 | 62 | EXPOSE 80/tcp 63 | 64 | ENTRYPOINT ["start-container"] 65 | -------------------------------------------------------------------------------- /docker/8.1/php.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | post_max_size = 100M 3 | upload_max_filesize = 100M 4 | variables_order = EGPCS 5 | pcov.directory = . 6 | -------------------------------------------------------------------------------- /docker/8.1/start-container: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "$SUPERVISOR_PHP_USER" != "root" ] && [ "$SUPERVISOR_PHP_USER" != "sail" ]; then 4 | echo "You should set SUPERVISOR_PHP_USER to either 'sail' or 'root'." 5 | exit 1 6 | fi 7 | 8 | if [ ! -z "$WWWUSER" ]; then 9 | usermod -u $WWWUSER sail 10 | fi 11 | 12 | if [ ! -d /.composer ]; then 13 | mkdir /.composer 14 | fi 15 | 16 | chmod -R ugo+rw /.composer 17 | 18 | if [ $# -gt 0 ]; then 19 | if [ "$SUPERVISOR_PHP_USER" = "root" ]; then 20 | exec "$@" 21 | else 22 | exec gosu $WWWUSER "$@" 23 | fi 24 | else 25 | exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf 26 | fi 27 | -------------------------------------------------------------------------------- /docker/8.1/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [program:php] 8 | command=%(ENV_SUPERVISOR_PHP_COMMAND)s 9 | user=%(ENV_SUPERVISOR_PHP_USER)s 10 | environment=LARAVEL_SAIL="1" 11 | stdout_logfile=/dev/stdout 12 | stdout_logfile_maxbytes=0 13 | stderr_logfile=/dev/stderr 14 | stderr_logfile_maxbytes=0 15 | -------------------------------------------------------------------------------- /docker/8.2-prod/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | ARG NODE_VERSION=18 4 | ARG POSTGRES_VERSION=14 5 | 6 | WORKDIR /var/www/html 7 | 8 | ENV DEBIAN_FRONTEND noninteractive 9 | ENV TZ=UTC 10 | 11 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 12 | 13 | RUN apt-get update \ 14 | && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils \ 15 | && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /usr/share/keyrings/ppa_ondrej_php.gpg > /dev/null \ 16 | && echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ 17 | && apt-get update \ 18 | && apt-get install -y php8.2-cli php8.2-dev \ 19 | php8.2-pgsql php8.2-sqlite3 php8.2-gd \ 20 | php8.2-curl \ 21 | php8.2-imap php8.2-mysql php8.2-mbstring \ 22 | php8.2-xml php8.2-zip php8.2-bcmath php8.2-soap \ 23 | php8.2-intl php8.2-readline \ 24 | php8.2-ldap \ 25 | php8.2-msgpack php8.2-igbinary php8.2-redis php8.2-swoole \ 26 | php8.2-memcached php8.2-pcov php8.2-xdebug \ 27 | php8.2-fpm \ 28 | nginx \ 29 | supervisor \ 30 | && php -r "readfile('https://getcomposer.org/installer');" | php -- --install-dir=/usr/bin/ --filename=composer \ 31 | && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \ 32 | && apt-get install -y nodejs \ 33 | && npm install -g npm \ 34 | && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarn.gpg >/dev/null \ 35 | && echo "deb [signed-by=/usr/share/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ 36 | && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/pgdg.gpg >/dev/null \ 37 | && echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ 38 | && apt-get update \ 39 | && apt-get install -y postgresql-client-$POSTGRES_VERSION \ 40 | && apt-get -y autoremove \ 41 | && apt-get clean \ 42 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 43 | 44 | RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2 45 | 46 | COPY . /var/www/html 47 | COPY docker/8.2-prod/start-container /usr/local/bin/start-container 48 | COPY docker/8.2-prod/supervisord.conf /etc/supervisor/conf.d/supervisord.conf 49 | COPY docker/8.2-prod/php.ini /etc/php/8.2/cli/conf.d/99-sail.ini 50 | COPY docker/8.2-prod/nginx.conf /etc/nginx/sites-enabled/default 51 | RUN chmod +x /usr/local/bin/start-container 52 | 53 | RUN chown -R www-data:www-data /var/www/html 54 | 55 | RUN composer install --optimize-autoloader --no-dev 56 | RUN npm ci 57 | RUN npm run build 58 | 59 | EXPOSE 80 60 | 61 | ENTRYPOINT ["start-container"] -------------------------------------------------------------------------------- /docker/8.2-prod/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | listen [::]:80; 4 | root /var/www/html/public; 5 | server_name phpcontributing.com www.phpcontributing.com; 6 | 7 | add_header X-Frame-Options "SAMEORIGIN"; 8 | add_header X-Content-Type-Options "nosniff"; 9 | 10 | index index.php; 11 | 12 | charset utf-8; 13 | 14 | location / { 15 | try_files $uri $uri/ /index.php?$query_string; 16 | } 17 | 18 | location = /favicon.ico { access_log off; log_not_found off; } 19 | location = /robots.txt { access_log off; log_not_found off; } 20 | 21 | error_page 404 /index.php; 22 | 23 | location ~ \.php$ { 24 | fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; 25 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; 26 | include fastcgi_params; 27 | } 28 | 29 | location ~ /\.(?!well-known).* { 30 | deny all; 31 | } 32 | } -------------------------------------------------------------------------------- /docker/8.2-prod/php.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | post_max_size = 100M 3 | upload_max_filesize = 100M 4 | variables_order = EGPCS -------------------------------------------------------------------------------- /docker/8.2-prod/start-container: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | chown -R www-data:www-data "$PWD" 5 | 6 | exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf -------------------------------------------------------------------------------- /docker/8.2-prod/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [group:laravel-worker] 8 | priority=999 9 | programs=nginx 10 | #,laravel-schedule,laravel-notification,laravel-queue 11 | 12 | [program:nginx] 13 | priority=10 14 | autostart=true 15 | autorestart=true 16 | stderr_logfile_maxbytes=0 17 | stdout_logfile_maxbytes=0 18 | stdout_events_enabled=true 19 | stderr_events_enabled=true 20 | command=/usr/sbin/nginx -g 'daemon off;' 21 | stderr_logfile=/var/log/nginx/error.log 22 | stdout_logfile=/var/log/nginx/access.log 23 | 24 | [program:php8-fpm] 25 | priority=5 26 | autostart=true 27 | autorestart=true 28 | stderr_logfile_maxbytes=0 29 | stdout_logfile_maxbytes=0 30 | command=service php8.2-fpm start 31 | stderr_logfile=/var/log/nginx/php-error.log 32 | stdout_logfile=/var/log/nginx/php-access.log -------------------------------------------------------------------------------- /docker/8.2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | LABEL maintainer="Taylor Otwell" 4 | 5 | ARG WWWGROUP 6 | ARG NODE_VERSION=20 7 | ARG POSTGRES_VERSION=15 8 | 9 | WORKDIR /var/www/html 10 | 11 | ENV DEBIAN_FRONTEND noninteractive 12 | ENV TZ=UTC 13 | ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80" 14 | ENV SUPERVISOR_PHP_USER="sail" 15 | 16 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 17 | 18 | RUN apt-get update \ 19 | && mkdir -p /etc/apt/keyrings \ 20 | && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch ffmpeg nano \ 21 | && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \ 22 | && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ 23 | && apt-get update \ 24 | && apt-get install -y php8.2-cli php8.2-dev \ 25 | php8.2-pgsql php8.2-sqlite3 php8.2-gd php8.2-imagick \ 26 | php8.2-curl \ 27 | php8.2-imap php8.2-mysql php8.2-mbstring \ 28 | php8.2-xml php8.2-zip php8.2-bcmath php8.2-soap \ 29 | php8.2-intl php8.2-readline \ 30 | php8.2-ldap \ 31 | php8.2-msgpack php8.2-igbinary php8.2-redis php8.2-swoole \ 32 | php8.2-memcached php8.2-pcov php8.2-xdebug \ 33 | && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ 34 | && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ 35 | && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ 36 | && apt-get update \ 37 | && apt-get install -y nodejs \ 38 | && npm install -g npm \ 39 | && npm install -g pnpm \ 40 | && npm install -g bun \ 41 | && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \ 42 | && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ 43 | && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \ 44 | && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ 45 | && apt-get update \ 46 | && apt-get install -y yarn \ 47 | && apt-get install -y mysql-client \ 48 | && apt-get install -y postgresql-client-$POSTGRES_VERSION \ 49 | && apt-get -y autoremove \ 50 | && apt-get clean \ 51 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 52 | 53 | RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2 54 | 55 | RUN groupadd --force -g $WWWGROUP sail 56 | RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail 57 | 58 | COPY start-container /usr/local/bin/start-container 59 | COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf 60 | COPY php.ini /etc/php/8.2/cli/conf.d/99-sail.ini 61 | RUN chmod +x /usr/local/bin/start-container 62 | 63 | EXPOSE 80/tcp 64 | 65 | ENTRYPOINT ["start-container"] 66 | -------------------------------------------------------------------------------- /docker/8.2/php.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | post_max_size = 100M 3 | upload_max_filesize = 100M 4 | variables_order = EGPCS 5 | pcov.directory = . 6 | -------------------------------------------------------------------------------- /docker/8.2/start-container: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "$SUPERVISOR_PHP_USER" != "root" ] && [ "$SUPERVISOR_PHP_USER" != "sail" ]; then 4 | echo "You should set SUPERVISOR_PHP_USER to either 'sail' or 'root'." 5 | exit 1 6 | fi 7 | 8 | if [ ! -z "$WWWUSER" ]; then 9 | usermod -u $WWWUSER sail 10 | fi 11 | 12 | if [ ! -d /.composer ]; then 13 | mkdir /.composer 14 | fi 15 | 16 | chmod -R ugo+rw /.composer 17 | 18 | if [ $# -gt 0 ]; then 19 | if [ "$SUPERVISOR_PHP_USER" = "root" ]; then 20 | exec "$@" 21 | else 22 | exec gosu $WWWUSER "$@" 23 | fi 24 | else 25 | exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf 26 | fi 27 | -------------------------------------------------------------------------------- /docker/8.2/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [program:php] 8 | command=%(ENV_SUPERVISOR_PHP_COMMAND)s 9 | user=%(ENV_SUPERVISOR_PHP_USER)s 10 | environment=LARAVEL_SAIL="1" 11 | stdout_logfile=/dev/stdout 12 | stdout_logfile_maxbytes=0 13 | stderr_logfile=/dev/stderr 14 | stderr_logfile_maxbytes=0 15 | -------------------------------------------------------------------------------- /docker/8.3/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | LABEL maintainer="Taylor Otwell" 4 | 5 | ARG WWWGROUP 6 | ARG NODE_VERSION=20 7 | ARG MYSQL_CLIENT="mysql-client" 8 | ARG POSTGRES_VERSION=15 9 | 10 | WORKDIR /var/www/html 11 | 12 | ENV DEBIAN_FRONTEND noninteractive 13 | ENV TZ=UTC 14 | ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80" 15 | ENV SUPERVISOR_PHP_USER="sail" 16 | 17 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 18 | 19 | RUN apt-get update \ 20 | && mkdir -p /etc/apt/keyrings \ 21 | && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch ffmpeg nano \ 22 | && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \ 23 | && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ 24 | && apt-get update \ 25 | && apt-get install -y php8.3-cli php8.3-dev \ 26 | php8.3-pgsql php8.3-sqlite3 php8.3-gd \ 27 | php8.3-curl \ 28 | php8.3-imap php8.3-mysql php8.3-mbstring \ 29 | php8.3-xml php8.3-zip php8.3-bcmath php8.3-soap \ 30 | php8.3-intl php8.3-readline \ 31 | php8.3-ldap \ 32 | php8.3-msgpack php8.3-igbinary php8.3-redis php8.3-swoole \ 33 | php8.3-memcached php8.3-pcov php8.3-imagick php8.3-xdebug \ 34 | && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ 35 | && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ 36 | && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ 37 | && apt-get update \ 38 | && apt-get install -y nodejs \ 39 | && npm install -g npm \ 40 | && npm install -g pnpm \ 41 | && npm install -g bun \ 42 | && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \ 43 | && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ 44 | && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \ 45 | && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ 46 | && apt-get update \ 47 | && apt-get install -y yarn \ 48 | && apt-get install -y $MYSQL_CLIENT \ 49 | && apt-get install -y postgresql-client-$POSTGRES_VERSION \ 50 | && apt-get -y autoremove \ 51 | && apt-get clean \ 52 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 53 | 54 | RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.3 55 | 56 | RUN groupadd --force -g $WWWGROUP sail 57 | RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail 58 | 59 | COPY start-container /usr/local/bin/start-container 60 | COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf 61 | COPY php.ini /etc/php/8.3/cli/conf.d/99-sail.ini 62 | RUN chmod +x /usr/local/bin/start-container 63 | 64 | EXPOSE 80/tcp 65 | 66 | ENTRYPOINT ["start-container"] 67 | -------------------------------------------------------------------------------- /docker/8.3/php.ini: -------------------------------------------------------------------------------- 1 | [PHP] 2 | post_max_size = 100M 3 | upload_max_filesize = 100M 4 | variables_order = EGPCS 5 | pcov.directory = . 6 | -------------------------------------------------------------------------------- /docker/8.3/start-container: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "$SUPERVISOR_PHP_USER" != "root" ] && [ "$SUPERVISOR_PHP_USER" != "sail" ]; then 4 | echo "You should set SUPERVISOR_PHP_USER to either 'sail' or 'root'." 5 | exit 1 6 | fi 7 | 8 | if [ ! -z "$WWWUSER" ]; then 9 | usermod -u $WWWUSER sail 10 | fi 11 | 12 | if [ ! -d /.composer ]; then 13 | mkdir /.composer 14 | fi 15 | 16 | chmod -R ugo+rw /.composer 17 | 18 | if [ $# -gt 0 ]; then 19 | if [ "$SUPERVISOR_PHP_USER" = "root" ]; then 20 | exec "$@" 21 | else 22 | exec gosu $WWWUSER "$@" 23 | fi 24 | else 25 | exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf 26 | fi 27 | -------------------------------------------------------------------------------- /docker/8.3/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | logfile=/var/log/supervisor/supervisord.log 5 | pidfile=/var/run/supervisord.pid 6 | 7 | [program:php] 8 | command=%(ENV_SUPERVISOR_PHP_COMMAND)s 9 | user=%(ENV_SUPERVISOR_PHP_USER)s 10 | environment=LARAVEL_SAIL="1" 11 | stdout_logfile=/dev/stdout 12 | stdout_logfile_maxbytes=0 13 | stderr_logfile=/dev/stderr 14 | stderr_logfile_maxbytes=0 15 | -------------------------------------------------------------------------------- /docker/mariadb/create-testing-database.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | /usr/bin/mariadb --user=root --password="$MYSQL_ROOT_PASSWORD" <<-EOSQL 4 | CREATE DATABASE IF NOT EXISTS testing; 5 | GRANT ALL PRIVILEGES ON \`testing%\`.* TO '$MYSQL_USER'@'%'; 6 | EOSQL 7 | -------------------------------------------------------------------------------- /docker/mysql/create-testing-database.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" <<-EOSQL 4 | CREATE DATABASE IF NOT EXISTS testing; 5 | GRANT ALL PRIVILEGES ON \`testing%\`.* TO '$MYSQL_USER'@'%'; 6 | EOSQL 7 | -------------------------------------------------------------------------------- /docker/pgsql/create-testing-database.sql: -------------------------------------------------------------------------------- 1 | SELECT 'CREATE DATABASE testing' 2 | WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'testing')\gexec 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build" 7 | }, 8 | "devDependencies": { 9 | "autoprefixer": "^10.4.19", 10 | "axios": "^1.6.4", 11 | "laravel-vite-plugin": "^1.0", 12 | "postcss": "^8.4.40", 13 | "tailwindcss": "^3.4.7", 14 | "vite": "^5.0" 15 | }, 16 | "name": "html", 17 | "version": "1.0.0", 18 | "description": "

\"Laravel

", 19 | "main": "vite.config.js", 20 | "directories": { 21 | "test": "tests" 22 | }, 23 | "author": "", 24 | "license": "ISC" 25 | } 26 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | includes: 2 | - vendor/nunomaduro/larastan/extension.neon 3 | parameters: 4 | level: 4 5 | paths: 6 | - app 7 | - database 8 | - routes 9 | - database 10 | - public 11 | - resources/views 12 | - tests 13 | excludePaths: 14 | - vendor -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /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/Danielopes7/php-contributing/9b5d218e2da9c1df3b4183995a8fdaf899b63186/public/favicon.ico -------------------------------------------------------------------------------- /public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Danielopes7/php-contributing/9b5d218e2da9c1df3b4183995a8fdaf899b63186/public/images/logo.png -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | handleRequest(Request::capture()); 18 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /resources/css/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- 1 | import './bootstrap'; 2 | -------------------------------------------------------------------------------- /resources/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | window.axios = axios; 3 | 4 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 5 | -------------------------------------------------------------------------------- /resources/views/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('title', 'PHP contributing') 4 | 5 | @section('content') 6 | 7 | @endsection 8 | -------------------------------------------------------------------------------- /resources/views/layouts/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | PHP Contributing 9 | 10 | @vite('resources/css/app.css') 11 | 12 | @livewireStyles 13 | 14 | 15 |
16 |
17 | 23 |
24 |
25 | @yield('content') 26 |
27 |
28 | 29 | 34 | 35 | @livewireScripts 36 | 37 | 38 | -------------------------------------------------------------------------------- /resources/views/livewire/show-issues.blade.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |

About

6 |

PHP contributing curates beginner-friendly tasks from PHP open-source projects, making it easier for you to make your first contribution to the PHP community.

7 |
8 |
9 |

Browse

10 |

by type of issue

11 |
12 | 16 | 19 | 22 | 25 | 28 |
29 |

by type of project

30 |
31 | 35 | 38 | 41 |
42 |
43 | 51 |
52 |
53 | @foreach($issues as $issue) 54 |
55 |
56 |
57 | {{ $issue->repository->full_name }} 58 | 59 |
60 | 💬 comments: {{ $issue->comments }} 61 |
62 | @if (!empty($filter_size)) 63 |
64 | 65 | @if ($filter_size == 'small') 66 | 😃 67 | @elseif ($filter_size == 'medium') 68 | 😎 69 | @else 70 | 🤑 71 | @endif 72 | 73 |
74 | @endif 75 |
76 |
77 | {{ $issue->title }} 78 | 79 |
80 |
81 | {{ Str::limit($issue->body, 200, '[click to know more]') }} 82 |
83 |
    84 |
  • 85 | 86 | {{$issue->user_login}} 87 |
  • 88 | 99 |
100 |
101 | @foreach($issue->labels as $label) 102 |
103 | 104 |
105 | @endforeach 106 |
107 |
108 |
109 | @endforeach 110 |
111 | 112 |
113 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | hourly(); 12 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | name('issues'); 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: [ 4 | "./resources/**/*.blade.php", 5 | "./resources/**/*.js", 6 | "./resources/**/*.vue", 7 | ], 8 | theme: { 9 | extend: { 10 | colors: { 11 | ink: { 12 | 200: '#2d3039', 13 | 400: '#16181d', 14 | }, 15 | }, 16 | }, 17 | }, 18 | plugins: [], 19 | } -------------------------------------------------------------------------------- /tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 16 | 17 | $response->assertStatus(200); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import laravel from 'laravel-vite-plugin'; 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | laravel({ 7 | input: ['resources/css/app.css', 'resources/js/app.js'], 8 | refresh: true, 9 | }), 10 | ], 11 | }); 12 | --------------------------------------------------------------------------------