diff --git a/.env.example b/.env.example index a10ea7b6..1885d89c 100644 --- a/.env.example +++ b/.env.example @@ -59,3 +59,5 @@ TELESCOPE_EMAIL= DEMO_MODE=false DEMO_REPO_URL="https://github.com/JHWelch/ChoreManager" + +PROFILE_FILESYSTEM_DISK=public diff --git a/.github/workflows/dploy.yml b/.github/workflows/dploy.yml new file mode 100644 index 00000000..f83a7f40 --- /dev/null +++ b/.github/workflows/dploy.yml @@ -0,0 +1,53 @@ +name: Deploy to Production + +on: + pull_request: + types: [opened, synchronize, reopened] + +concurrency: + group: deploy-production + cancel-in-progress: true + +jobs: + deploy: + environment: Production + name: Deploy to Production + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - id: auth + name: 'Authenticate to Google Cloud' + uses: google-github-actions/auth@v1 + with: + create_credentials_file: true + export_environment_variables: true + service_account: ${{ secrets.GOOGLE_CLOUD_SERVICE_ACCOUNT }} + workload_identity_provider: ${{ secrets.GOOGLE_CLOUD_WORKLOAD_IDENTITY_PROVIDER }} + + - name: Place .env + run: echo ${{ secrets.ENV }} | base64 -d > .env + + - name: Build Docker image + run: | + docker build --platform linux/amd64 \ + -t ${{ vars.GOOGLE_CLOUD_REGION }}-docker.pkg.dev/$GCP_PROJECT/chore-manager/chore-manager:${{ github.sha }} \ + --file=php.dockerfile . + + - name: Push Docker image + run: | + gcloud config set account ${{ secrets.GOOGLE_CLOUD_SERVICE_ACCOUNT }} + gcloud auth configure-docker ${{ vars.GOOGLE_CLOUD_REGION }}-docker.pkg.dev + docker push ${{ vars.GOOGLE_CLOUD_REGION }}-docker.pkg.dev/$GCP_PROJECT/chore-manager/chore-manager:${{ github.sha }} + + - name: Deploy to Cloud Run + run: | + gcloud run deploy chore-manager \ + --image ${{ vars.GOOGLE_CLOUD_REGION }}-docker.pkg.dev/$GCP_PROJECT/chore-manager/chore-manager:${{ github.sha }} \ + --region ${{ vars.GOOGLE_CLOUD_REGION }} \ + --project $GCP_PROJECT diff --git a/.github/workflows/qc.yml b/.github/workflows/qc.yml deleted file mode 100644 index ede70f7c..00000000 --- a/.github/workflows/qc.yml +++ /dev/null @@ -1,100 +0,0 @@ -name: QC -run-name: 'QC (PR #${{ github.event.pull_request.number }})' - -on: - pull_request: - types: [opened, synchronize, reopened] - workflow_call: - workflow_dispatch: - -concurrency: - group: qc-${{ github.event.pull_request.number }} - cancel-in-progress: true - -jobs: - phpstan: - name: Static Type Analysis (PHPStan) - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.2' - coverage: none - - - name: Install dependencies - uses: ramsey/composer-install@v2 - - - name: run PHPStan - run: composer test:types - - phpunit: - name: Unit Tests (PHPUnit) - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.2' - coverage: none - - - name: Install dependencies - uses: ramsey/composer-install@v2 - - - name: enable mysql - run: sudo systemctl start mysql.service - - - name: create empty database - run: mysql --user=root --password=root --execute="CREATE DATABASE choremanager" - - - name: run phpunit - run: composer test:unit - env: - APP_KEY: base64:UmeUVSBI8wMTAzYlIWXQOf/SoBR116L3jozOtnnUAyY= - DB_DATABASE: choremanager - DB_USERNAME: root - DB_PASSWORD: root - - standards: - name: Code Style (Pint) - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.2' - coverage: none - - - name: Install dependencies - uses: ramsey/composer-install@v2 - - - name: Check style - run: composer test:lint - - autoloader: - name: PSR-4 Autoloader - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '8.2' - coverage: none - - - name: Install dependencies - uses: ramsey/composer-install@v2 - - - name: Check compliance with PSR-4 - run: composer test:psr4 diff --git a/.gitignore b/.gitignore index 433a5ff0..f2683080 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ npm-debug.log yarn-error.log .env.testing /.vscode +gcloud.json diff --git a/.phpactor.json b/.phpactor.json new file mode 100644 index 00000000..969af7a8 --- /dev/null +++ b/.phpactor.json @@ -0,0 +1,4 @@ +{ + "$schema": "/Applications/Tinkerwell.app/Contents/Resources/phpactor/phpactor.schema.json", + "language_server_phpstan.enabled": false +} \ No newline at end of file diff --git a/app/Models/User.php b/app/Models/User.php index 114eaca2..e4303cfb 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -12,6 +12,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Storage; use Laravel\Fortify\TwoFactorAuthenticatable; use Laravel\Jetstream\HasProfilePhoto; use Laravel\Jetstream\HasTeams; @@ -77,7 +78,9 @@ class User extends Authenticatable implements FilamentUser use HasApiTokens; use HasChoreStreaks; use HasFactory; - use HasProfilePhoto; + use HasProfilePhoto { + getProfilePhotoUrlAttribute as parentGetProfilePhotoUrlAttribute; + } use HasTeams; use HasUnfinishedChoreScopes; use Notifiable; @@ -113,12 +116,29 @@ public function canAccessFilament(): bool protected static function booted(): void { - static::addGlobalScope(new OrderByNameScope); + static::addGlobalScope(new OrderByNameScope()); static::created(function ($user) { UserSetting::create(['user_id' => $user->id]); }); } + /** + * Get the URL to the user's profile photo. + * + * @return string + */ + public function getProfilePhotoUrlAttribute() + { + if (config('jetstream.profile_photo_disk') !== 'gcs') { + return $this->parentGetProfilePhotoUrlAttribute(); + } + + return $this->profile_photo_path + ? Storage::disk($this->profilePhotoDisk()) + ->temporaryUrl($this->profile_photo_path, now()->addMinutes(30)) + : $this->defaultProfilePhotoUrl(); + } + /** * Get all users with a specific setting. * diff --git a/composer.json b/composer.json index a150cc79..37de9379 100644 --- a/composer.json +++ b/composer.json @@ -53,6 +53,7 @@ "league/commonmark": "^2.2", "livewire/livewire": "^2.11", "spatie/icalendar-generator": "^2.3", + "spatie/laravel-google-cloud-storage": "^2.2", "spatie/laravel-ray": "^1.32" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 40f2ab4f..a5605f00 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "aa7c7b949ca55fae4986dcc9e6d21197", + "content-hash": "e5aa5b51b6395542122614992f124344", "packages": [ { "name": "akaunting/laravel-money", @@ -4641,6 +4641,65 @@ ], "time": "2023-05-04T09:04:26+00:00" }, + { + "name": "league/flysystem-google-cloud-storage", + "version": "3.21.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-google-cloud-storage.git", + "reference": "de4d9e68433ca17b1dc3e28e3800148531ef85a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-google-cloud-storage/zipball/de4d9e68433ca17b1dc3e28e3800148531ef85a0", + "reference": "de4d9e68433ca17b1dc3e28e3800148531ef85a0", + "shasum": "" + }, + "require": { + "google/cloud-storage": "^1.23", + "league/flysystem": "^3.10.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\GoogleCloudStorage\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Google Cloud Storage adapter for Flysystem.", + "keywords": [ + "Flysystem", + "filesystem", + "gcs", + "google cloud storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem-google-cloud-storage/issues", + "source": "https://github.com/thephpleague/flysystem-google-cloud-storage/tree/3.21.0" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" + }, + { + "url": "https://github.com/frankdejonge", + "type": "github" + } + ], + "time": "2023-11-14T11:54:45+00:00" + }, { "name": "league/flysystem-local", "version": "3.15.0", @@ -7240,6 +7299,81 @@ ], "time": "2022-07-05T09:31:00+00:00" }, + { + "name": "spatie/laravel-google-cloud-storage", + "version": "2.2.4", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-google-cloud-storage.git", + "reference": "af43998f668bdc42a76c02d6f64f9c66d030786b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-google-cloud-storage/zipball/af43998f668bdc42a76c02d6f64f9c66d030786b", + "reference": "af43998f668bdc42a76c02d6f64f9c66d030786b", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.0|^10.0", + "illuminate/filesystem": "^9.0|^10.0", + "illuminate/support": "^9.0|^10.0", + "league/flysystem-google-cloud-storage": "^3.0.15", + "php": "^8.0" + }, + "require-dev": { + "brianium/paratest": "^6.4", + "nunomaduro/collision": "^6.1|^7.0", + "orchestra/testbench": "^7.0|^8.0", + "phpunit/phpunit": "^9.6|^10.0", + "spatie/laravel-ray": "^1.29", + "vimeo/psalm": "^4.18|^5.9" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\GoogleCloudStorage\\GoogleCloudStorageServiceProvider" + ], + "aliases": { + "GoogleCloudStorage": "Spatie\\GoogleCloudStorage\\GoogleCloudStorageFacade" + } + } + }, + "autoload": { + "psr-4": { + "Spatie\\GoogleCloudStorage\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Vanderbist", + "email": "alex@spatie.be", + "role": "Developer" + } + ], + "description": "Google Cloud Storage filesystem driver for Laravel", + "homepage": "https://github.com/spatie/laravel-google-cloud-storage", + "keywords": [ + "laravel", + "laravel-google-cloud-storage", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-google-cloud-storage/issues", + "source": "https://github.com/spatie/laravel-google-cloud-storage/tree/2.2.4" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2023-11-16T14:25:44+00:00" + }, { "name": "spatie/laravel-package-tools", "version": "1.16.1", diff --git a/config/filesystems.php b/config/filesystems.php index 4afc1fc6..5f5a105d 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -56,6 +56,19 @@ 'throw' => false, ], + 'gcs' => [ + 'driver' => 'gcs', + 'key_file_path' => env('GOOGLE_CLOUD_KEY_FILE', null), + 'project_id' => env('GOOGLE_CLOUD_PROJECT_ID'), + 'bucket' => env('GOOGLE_CLOUD_STORAGE_BUCKET'), + 'path_prefix' => env('GOOGLE_CLOUD_STORAGE_PATH_PREFIX', ''), + 'storage_api_uri' => env('GOOGLE_CLOUD_STORAGE_API_URI', null), + 'api_endpoint' => env('GOOGLE_CLOUD_STORAGE_API_ENDPOINT', null), + 'visibility' => 'private', + 'visibility_handler' => \League\Flysystem\GoogleCloudStorage\UniformBucketLevelAccessVisibility::class, + 'metadata' => ['cacheControl' => 'public,max-age=86400'], + ], + ], /* diff --git a/config/jetstream.php b/config/jetstream.php index 3fece93c..72497984 100644 --- a/config/jetstream.php +++ b/config/jetstream.php @@ -17,6 +17,8 @@ 'stack' => 'livewire', + 'profile_photo_disk' => env('PROFILE_FILESYSTEM_DISK', 'public'), + /* |-------------------------------------------------------------------------- | Jetstream Route Middleware