From b8c76f7c8e0e5f8045b871466160774ac2886e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karlo=20Miku=C5=A1?= Date: Sat, 16 Mar 2024 10:31:44 +0100 Subject: [PATCH 01/11] Make search actions queueable --- app/Console/Commands/BarSearchRefresh.php | 27 +++-------- app/Jobs/RefreshSearchIndex.php | 55 +++++++++++++++++++++++ config/scout.php | 2 +- 3 files changed, 61 insertions(+), 23 deletions(-) create mode 100644 app/Jobs/RefreshSearchIndex.php diff --git a/app/Console/Commands/BarSearchRefresh.php b/app/Console/Commands/BarSearchRefresh.php index c77ba83f..a568f697 100644 --- a/app/Console/Commands/BarSearchRefresh.php +++ b/app/Console/Commands/BarSearchRefresh.php @@ -5,10 +5,7 @@ namespace Kami\Cocktail\Console\Commands; use Illuminate\Console\Command; -use Kami\Cocktail\Models\Cocktail; -use Kami\Cocktail\Models\Ingredient; -use Illuminate\Support\Facades\Artisan; -use Kami\Cocktail\Search\SearchActionsAdapter; +use Kami\Cocktail\Jobs\RefreshSearchIndex; class BarSearchRefresh extends Command { @@ -26,11 +23,6 @@ class BarSearchRefresh extends Command */ protected $description = 'Sync search engine index with the latest Bar Assistant data'; - public function __construct(private readonly SearchActionsAdapter $searchActions) - { - parent::__construct(); - } - /** * Execute the console command. * @@ -38,22 +30,13 @@ public function __construct(private readonly SearchActionsAdapter $searchActions */ public function handle() { - $searchActions = $this->searchActions->getActions(); - - // Clear indexes if ($this->option('clear')) { - $this->info('Flushing site search, cocktails and ingredients index...'); - Artisan::call('scout:flush', ['model' => Cocktail::class]); - Artisan::call('scout:flush', ['model' => Ingredient::class]); + $this->info('Clearing index and syncing...'); + } else { + $this->info('Syncing search index...'); } - // Update settings - $this->info('Updating search index settings...'); - $searchActions->updateIndexSettings(); - - $this->info('Syncing cocktails and ingredients to meilisearch...'); - Artisan::call('scout:import', ['model' => Cocktail::class]); - Artisan::call('scout:import', ['model' => Ingredient::class]); + RefreshSearchIndex::dispatch((bool) $this->option('clear')); return Command::SUCCESS; } diff --git a/app/Jobs/RefreshSearchIndex.php b/app/Jobs/RefreshSearchIndex.php new file mode 100644 index 00000000..b7a55162 --- /dev/null +++ b/app/Jobs/RefreshSearchIndex.php @@ -0,0 +1,55 @@ +getActions(); + + // Clear indexes + if ($this->shouldClearIndexes) { + Log::info('Clearing search indexes'); + Artisan::call('scout:flush', ['model' => Cocktail::class]); + Artisan::call('scout:flush', ['model' => Ingredient::class]); + } + + // Update settings + Log::info('Updating search settings'); + $searchActions->updateIndexSettings(); + + Log::info('Building search indexes'); + + Artisan::call('scout:import', ['model' => Cocktail::class]); + Artisan::call('scout:import', ['model' => Ingredient::class]); + + Log::info('Search indexes updated'); + } +} diff --git a/config/scout.php b/config/scout.php index 5c8b7d20..a49f105c 100644 --- a/config/scout.php +++ b/config/scout.php @@ -41,7 +41,7 @@ | */ - 'queue' => env('SCOUT_QUEUE', false), + 'queue' => env('SCOUT_QUEUE', true), /* |-------------------------------------------------------------------------- From 43306228644dda9392f9d2f1fc097737ccb77e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karlo=20Miku=C5=A1?= Date: Sat, 16 Mar 2024 10:32:05 +0100 Subject: [PATCH 02/11] Update recipe parser lib --- composer.lock | 156 +++++++++++++++++++++++++------------------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/composer.lock b/composer.lock index c8e090c9..73b336d7 100644 --- a/composer.lock +++ b/composer.lock @@ -1749,16 +1749,16 @@ }, { "name": "karlomikus/recipe-utils", - "version": "0.8.0", + "version": "0.8.1", "source": { "type": "git", "url": "https://github.com/karlomikus/recipe-utils.git", - "reference": "2144237444c0846c6ab229c83ad456ac2d5061f5" + "reference": "52a88b9aa4265c8c5967187159e75f95f18a111a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/karlomikus/recipe-utils/zipball/2144237444c0846c6ab229c83ad456ac2d5061f5", - "reference": "2144237444c0846c6ab229c83ad456ac2d5061f5", + "url": "https://api.github.com/repos/karlomikus/recipe-utils/zipball/52a88b9aa4265c8c5967187159e75f95f18a111a", + "reference": "52a88b9aa4265c8c5967187159e75f95f18a111a", "shasum": "" }, "require": { @@ -1790,9 +1790,9 @@ "description": "Utilities for extracting normalized ingredient data from recipes", "support": { "issues": "https://github.com/karlomikus/recipe-utils/issues", - "source": "https://github.com/karlomikus/recipe-utils/tree/0.8.0" + "source": "https://github.com/karlomikus/recipe-utils/tree/0.8.1" }, - "time": "2023-12-07T18:07:10+00:00" + "time": "2024-03-14T19:57:40+00:00" }, { "name": "laravel/cashier-paddle", @@ -1878,16 +1878,16 @@ }, { "name": "laravel/framework", - "version": "v10.47.0", + "version": "v10.48.2", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "fce29b8de62733cdecbe12e3bae801f83fff2ea4" + "reference": "32a8bb151d748b579c3794dea53b56bd40dfbbd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/fce29b8de62733cdecbe12e3bae801f83fff2ea4", - "reference": "fce29b8de62733cdecbe12e3bae801f83fff2ea4", + "url": "https://api.github.com/repos/laravel/framework/zipball/32a8bb151d748b579c3794dea53b56bd40dfbbd3", + "reference": "32a8bb151d748b579c3794dea53b56bd40dfbbd3", "shasum": "" }, "require": { @@ -1935,6 +1935,7 @@ "conflict": { "carbonphp/carbon-doctrine-types": ">=3.0", "doctrine/dbal": ">=4.0", + "mockery/mockery": "1.6.8", "phpunit/phpunit": ">=11.0.0", "tightenco/collect": "<5.5.33" }, @@ -2080,7 +2081,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-03-05T15:18:36+00:00" + "time": "2024-03-12T16:35:43+00:00" }, { "name": "laravel/horizon", @@ -2699,16 +2700,16 @@ }, { "name": "league/flysystem", - "version": "3.24.0", + "version": "3.25.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "b25a361508c407563b34fac6f64a8a17a8819675" + "reference": "4c44347133618cccd9b3df1729647a1577b4ad99" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/b25a361508c407563b34fac6f64a8a17a8819675", - "reference": "b25a361508c407563b34fac6f64a8a17a8819675", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4c44347133618cccd9b3df1729647a1577b4ad99", + "reference": "4c44347133618cccd9b3df1729647a1577b4ad99", "shasum": "" }, "require": { @@ -2736,7 +2737,7 @@ "friendsofphp/php-cs-fixer": "^3.5", "google/cloud-storage": "^1.23", "microsoft/azure-storage-blob": "^1.1", - "phpseclib/phpseclib": "^3.0.34", + "phpseclib/phpseclib": "^3.0.36", "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.5.11|^10.0", "sabre/dav": "^4.6.0" @@ -2773,7 +2774,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.24.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.25.0" }, "funding": [ { @@ -2785,7 +2786,7 @@ "type": "github" } ], - "time": "2024-02-04T12:10:17+00:00" + "time": "2024-03-09T17:06:45+00:00" }, { "name": "league/flysystem-local", @@ -2972,16 +2973,16 @@ }, { "name": "meilisearch/meilisearch-php", - "version": "v1.6.1", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/meilisearch/meilisearch-php.git", - "reference": "a0bcb5ae8e8e32dec1d28613b53dc0360d995983" + "reference": "cbaecb8c554fb7094b02f03ec55e077174869c93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/meilisearch/meilisearch-php/zipball/a0bcb5ae8e8e32dec1d28613b53dc0360d995983", - "reference": "a0bcb5ae8e8e32dec1d28613b53dc0360d995983", + "url": "https://api.github.com/repos/meilisearch/meilisearch-php/zipball/cbaecb8c554fb7094b02f03ec55e077174869c93", + "reference": "cbaecb8c554fb7094b02f03ec55e077174869c93", "shasum": "" }, "require": { @@ -2996,7 +2997,7 @@ "guzzlehttp/guzzle": "^7.1", "http-interop/http-factory-guzzle": "^1.0", "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "1.10.57", + "phpstan/phpstan": "1.10.59", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-strict-rules": "^1.1", @@ -3034,9 +3035,9 @@ ], "support": { "issues": "https://github.com/meilisearch/meilisearch-php/issues", - "source": "https://github.com/meilisearch/meilisearch-php/tree/v1.6.1" + "source": "https://github.com/meilisearch/meilisearch-php/tree/v1.7.0" }, - "time": "2024-02-16T13:42:26+00:00" + "time": "2024-03-11T13:12:36+00:00" }, { "name": "moneyphp/money", @@ -4912,34 +4913,33 @@ }, { "name": "spatie/laravel-responsecache", - "version": "7.4.10", + "version": "7.5.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-responsecache.git", - "reference": "cf0305f73fcc49dacfadd0f2228887a92fa736ac" + "reference": "c6880344402b53fbf32d4f8dd5df8bff9f725c36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-responsecache/zipball/cf0305f73fcc49dacfadd0f2228887a92fa736ac", - "reference": "cf0305f73fcc49dacfadd0f2228887a92fa736ac", + "url": "https://api.github.com/repos/spatie/laravel-responsecache/zipball/c6880344402b53fbf32d4f8dd5df8bff9f725c36", + "reference": "c6880344402b53fbf32d4f8dd5df8bff9f725c36", "shasum": "" }, "require": { - "illuminate/cache": "^8.71|^9.0|^10.0", - "illuminate/console": "^8.71|^9.0|^10.0", - "illuminate/container": "^8.71|^9.0|^10.0", - "illuminate/http": "^8.71|^9.0|^10.0", - "illuminate/support": "^8.71|^9.0|^10.0", + "illuminate/cache": "^10.0|^11.0", + "illuminate/console": "^10.0|^11.0", + "illuminate/container": "^10.0|^11.0", + "illuminate/http": "^10.0|^11.0", + "illuminate/support": "^10.0|^11.0", "nesbot/carbon": "^2.63", - "php": "^8.0", + "php": "^8.2", "spatie/laravel-package-tools": "^1.9" }, "require-dev": { - "laravel/framework": "^9.0|^10.0", + "laravel/framework": "^10.0|^11.0", "mockery/mockery": "^1.4", - "orchestra/testbench": "^6.23|^7.0|^8.0", - "pestphp/pest": "^1.22", - "phpunit/phpunit": "^9.4" + "orchestra/testbench": "^8.0|^9.0", + "pestphp/pest": "^2.22" }, "type": "library", "extra": { @@ -4980,7 +4980,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/laravel-responsecache/tree/7.4.10" + "source": "https://github.com/spatie/laravel-responsecache/tree/7.5.0" }, "funding": [ { @@ -4992,7 +4992,7 @@ "type": "github" } ], - "time": "2023-10-28T18:47:12+00:00" + "time": "2024-03-11T23:35:58+00:00" }, { "name": "spatie/laravel-sluggable", @@ -8477,23 +8477,23 @@ "packages-dev": [ { "name": "barryvdh/laravel-debugbar", - "version": "v3.10.6", + "version": "v3.12.2", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "1fcb37307ebb32207dce16fa160a92b14d8b671f" + "reference": "43555503052443964ce2c1c1f3b0378e58219eb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/1fcb37307ebb32207dce16fa160a92b14d8b671f", - "reference": "1fcb37307ebb32207dce16fa160a92b14d8b671f", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/43555503052443964ce2c1c1f3b0378e58219eb8", + "reference": "43555503052443964ce2c1c1f3b0378e58219eb8", "shasum": "" }, "require": { "illuminate/routing": "^9|^10|^11", "illuminate/session": "^9|^10|^11", "illuminate/support": "^9|^10|^11", - "maximebf/debugbar": "~1.20.1", + "maximebf/debugbar": "~1.21.0", "php": "^8.0", "symfony/finder": "^6|^7" }, @@ -8545,7 +8545,7 @@ ], "support": { "issues": "https://github.com/barryvdh/laravel-debugbar/issues", - "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.10.6" + "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.12.2" }, "funding": [ { @@ -8557,7 +8557,7 @@ "type": "github" } ], - "time": "2024-03-01T14:41:13+00:00" + "time": "2024-03-13T09:50:34+00:00" }, { "name": "cebe/php-openapi", @@ -9125,16 +9125,16 @@ }, { "name": "laravel/sail", - "version": "v1.28.2", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "057777403b8ab79222dcc04983beaab10b6de6a0" + "reference": "e40cc7ffb5186c45698dbd47e9477e0e429396d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/057777403b8ab79222dcc04983beaab10b6de6a0", - "reference": "057777403b8ab79222dcc04983beaab10b6de6a0", + "url": "https://api.github.com/repos/laravel/sail/zipball/e40cc7ffb5186c45698dbd47e9477e0e429396d0", + "reference": "e40cc7ffb5186c45698dbd47e9477e0e429396d0", "shasum": "" }, "require": { @@ -9183,20 +9183,20 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2024-03-04T14:58:29+00:00" + "time": "2024-03-08T16:32:33+00:00" }, { "name": "maximebf/debugbar", - "version": "v1.20.2", + "version": "v1.21.3", "source": { "type": "git", "url": "https://github.com/maximebf/php-debugbar.git", - "reference": "484625c23a4fa4f303617f29fcacd42951c9c01d" + "reference": "0b407703b08ea0cf6ebc61e267cc96ff7000911b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/484625c23a4fa4f303617f29fcacd42951c9c01d", - "reference": "484625c23a4fa4f303617f29fcacd42951c9c01d", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/0b407703b08ea0cf6ebc61e267cc96ff7000911b", + "reference": "0b407703b08ea0cf6ebc61e267cc96ff7000911b", "shasum": "" }, "require": { @@ -9216,7 +9216,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.20-dev" + "dev-master": "1.21-dev" } }, "autoload": { @@ -9247,13 +9247,13 @@ ], "support": { "issues": "https://github.com/maximebf/php-debugbar/issues", - "source": "https://github.com/maximebf/php-debugbar/tree/v1.20.2" + "source": "https://github.com/maximebf/php-debugbar/tree/v1.21.3" }, - "time": "2024-02-15T10:49:09+00:00" + "time": "2024-03-12T14:23:07+00:00" }, { "name": "mockery/mockery", - "version": "1.6.7", + "version": "1.6.9", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", @@ -9945,16 +9945,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.60", + "version": "1.10.62", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe" + "reference": "cd5c8a1660ed3540b211407c77abf4af193a6af9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/95dcea7d6c628a3f2f56d091d8a0219485a86bbe", - "reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd5c8a1660ed3540b211407c77abf4af193a6af9", + "reference": "cd5c8a1660ed3540b211407c77abf4af193a6af9", "shasum": "" }, "require": { @@ -10003,20 +10003,20 @@ "type": "tidelift" } ], - "time": "2024-03-07T13:30:19+00:00" + "time": "2024-03-13T12:27:20+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.12", + "version": "10.1.14", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "842f72662d6b9edda84c4b6f13885fd9cd53dc63" + "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/842f72662d6b9edda84c4b6f13885fd9cd53dc63", - "reference": "842f72662d6b9edda84c4b6f13885fd9cd53dc63", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", + "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", "shasum": "" }, "require": { @@ -10073,7 +10073,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.12" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14" }, "funding": [ { @@ -10081,7 +10081,7 @@ "type": "github" } ], - "time": "2024-03-02T07:22:05+00:00" + "time": "2024-03-12T15:33:41+00:00" }, { "name": "phpunit/php-file-iterator", @@ -10328,16 +10328,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.11", + "version": "10.5.13", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "0d968f6323deb3dbfeba5bfd4929b9415eb7a9a4" + "reference": "20a63fc1c6db29b15da3bd02d4b6cf59900088a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0d968f6323deb3dbfeba5bfd4929b9415eb7a9a4", - "reference": "0d968f6323deb3dbfeba5bfd4929b9415eb7a9a4", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/20a63fc1c6db29b15da3bd02d4b6cf59900088a7", + "reference": "20a63fc1c6db29b15da3bd02d4b6cf59900088a7", "shasum": "" }, "require": { @@ -10409,7 +10409,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.11" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.13" }, "funding": [ { @@ -10425,7 +10425,7 @@ "type": "tidelift" } ], - "time": "2024-02-25T14:05:00+00:00" + "time": "2024-03-12T15:37:41+00:00" }, { "name": "sebastian/cli-parser", From b0b889a6096ddfbb02ebf34be0fcf8b0d294d98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karlo=20Miku=C5=A1?= Date: Sat, 16 Mar 2024 10:32:36 +0100 Subject: [PATCH 03/11] Remove unused --- app/Providers/AuthServiceProvider.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 8db8fae2..349fcafa 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -2,10 +2,6 @@ namespace Kami\Cocktail\Providers; -// use Illuminate\Support\Facades\Gate; -use Illuminate\Http\Request; -use Kami\Cocktail\Models\User; -use Illuminate\Support\Facades\Auth; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider @@ -16,9 +12,6 @@ class AuthServiceProvider extends ServiceProvider * @var array */ protected $policies = [ - \Kami\Cocktail\Models\Cocktail::class => \Kami\Cocktail\Policies\CocktailPolicy::class, - \Kami\Cocktail\Models\Ingredient::class => \Kami\Cocktail\Policies\IngredientPolicy::class, - \Kami\Cocktail\Models\Image::class => \Kami\Cocktail\Policies\ImagePolicy::class, ]; /** @@ -28,8 +21,5 @@ class AuthServiceProvider extends ServiceProvider */ public function boot() { - Auth::viaRequest('no-auth', function (Request $request) { - return User::first(); - }); } } From 3955ee7c50a74113b0810146639490e27b08ce66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karlo=20Miku=C5=A1?= Date: Sun, 24 Mar 2024 15:27:13 +0100 Subject: [PATCH 04/11] Add calories info --- app/Http/Resources/CocktailResource.php | 3 +++ app/Models/Cocktail.php | 28 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/app/Http/Resources/CocktailResource.php b/app/Http/Resources/CocktailResource.php index f9176e1c..fc71b21f 100644 --- a/app/Http/Resources/CocktailResource.php +++ b/app/Http/Resources/CocktailResource.php @@ -51,6 +51,9 @@ public function toArray($request) 'updated_at' => $this->updated_at?->toJson(), 'method' => new CocktailMethodResource($this->whenLoaded('method')), 'abv' => $this->abv, + 'volume' => $this->getVolume(), + 'alcohol_units' => $this->getAlcoholUnits(), + 'calories' => $this->getCalories(), 'created_user' => new UserBasicResource($this->whenLoaded('createdUser')), 'updated_user' => new UserBasicResource($this->whenLoaded('updatedUser')), 'access' => [ diff --git a/app/Models/Cocktail.php b/app/Models/Cocktail.php index 3dc10e69..e1b8686c 100644 --- a/app/Models/Cocktail.php +++ b/app/Models/Cocktail.php @@ -151,6 +151,34 @@ public function getABV(): ?float return Utils::calculateAbv($ingredients, $this->method->dilution_percentage); } + public function getVolume(): float + { + return (float) $this->ingredients + ->filter(function ($cocktailIngredient) { + $normalizedUnits = mb_strtolower($cocktailIngredient->units, 'UTF-8'); + + return in_array($normalizedUnits, ['ml', 'oz', 'cl']); + }) + ->sum('amount'); + } + + public function getAlcoholUnits(): float + { + if ($this->cocktail_method_id === null) { + return 0.0; + } + + return (float) number_format(($this->getVolume() * $this->getABV()) / 1000, 2); + } + + public function getCalories(): int + { + // It's important to note that the calorie content of mixed drinks can vary significantly + // depending on the type and amount of mixers used. Drinks with sugary mixers or syrups + // will generally have higher calorie counts. + return (int) floor($this->getVolume() * ($this->getABV() / 100) * 7); + } + public function getMainIngredient(): ?CocktailIngredient { return $this->ingredients->first(); From 2d3a2d4fb8f39251210fb0e7a633ceb958a593d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zden=C4=9Bk=20Biberle?= Date: Mon, 1 Apr 2024 18:36:33 +0200 Subject: [PATCH 05/11] Fix Cocktail Party scraper Cocktail Party have changed their website. It now appears to be using something called Elementor. Quite possibly https://elementor.com/. This has caused the old scraper to stop working. This commit fixes the scraper. I've tried it with 192 cocktails from Cocktail Party and, except for a few, it worked fine. The ones that didn't work were using units that recipe-utils doesn't understand, such as "stick" or "half," and thus I assume that they wouldn't've worked with the old scraper as well. --- app/Scraper/AbstractSiteExtractor.php | 6 ++- app/Scraper/Sites/CocktailParty.php | 66 +++++++++++++++++---------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/app/Scraper/AbstractSiteExtractor.php b/app/Scraper/AbstractSiteExtractor.php index 95c52ffb..94632f45 100644 --- a/app/Scraper/AbstractSiteExtractor.php +++ b/app/Scraper/AbstractSiteExtractor.php @@ -156,6 +156,7 @@ public function toArray(): array return html_entity_decode($str, encoding: 'UTF-8'); }; + $ingredients = $this->ingredients(); return [ 'name' => $clean($this->name()), 'description' => $clean($this->description()), @@ -168,7 +169,7 @@ public function toArray(): array 'images' => [ $this->image() ], - 'ingredients' => array_map(function (RecipeIngredient $recipeIngredient) use ($clean) { + 'ingredients' => array_map(function (RecipeIngredient $recipeIngredient, int $sort) use ($clean) { return [ 'name' => $clean(ucfirst($recipeIngredient->name)), 'amount' => $recipeIngredient->amount, @@ -178,8 +179,9 @@ public function toArray(): array 'original_amount' => $recipeIngredient->originalAmount, 'source' => $clean($recipeIngredient->source), 'optional' => false, + 'sort' => $sort, ]; - }, $this->ingredients()), + }, $ingredients, array_keys($ingredients)), ]; } } diff --git a/app/Scraper/Sites/CocktailParty.php b/app/Scraper/Sites/CocktailParty.php index 421e6bbf..7de2949b 100644 --- a/app/Scraper/Sites/CocktailParty.php +++ b/app/Scraper/Sites/CocktailParty.php @@ -5,6 +5,7 @@ namespace Kami\Cocktail\Scraper\Sites; use Kami\RecipeUtils\RecipeIngredient; +use Symfony\Component\DomCrawler\Crawler; use Kami\Cocktail\Scraper\AbstractSiteExtractor; class CocktailParty extends AbstractSiteExtractor @@ -18,12 +19,12 @@ public static function getSupportedUrls(): array public function name(): string { - return $this->crawler->filter('.recipe-card h1')->first()->text(); + return $this->findDescriptionContainer()->filter('.elementor-widget-theme-post-title h1')->first()->text(); } public function description(): ?string { - return $this->crawler->filter('.recipe-card .recipe-description')->first()->text(); + return $this->joinParagraphs($this->findDescriptionContainer()->filter('.elementor-widget-theme-post-content div p')); } public function source(): ?string @@ -33,7 +34,7 @@ public function source(): ?string public function instructions(): ?string { - return $this->crawler->filter('.recipe-card .recipe-instructions')->first()->text(); + return $this->joinParagraphs($this->findRecipeContainer()->filter('.elementor-widget-text-editor p')); } public function tags(): array @@ -43,32 +44,51 @@ public function tags(): array public function ingredients(): array { - $result = []; - - $this->crawler->filter('.recipe-card .recipe-ingredients li')->each(function ($node) use (&$result) { - $amount = $node->filter('.amount')->text(); - $ingredient = $node->filter('.ingredient')->text(); - - $recipeIngredient = $this->ingredientParser->parseLine($amount); - $result[] = new RecipeIngredient( - $ingredient, - $recipeIngredient->amount, - $recipeIngredient->units, - $recipeIngredient->source, - $recipeIngredient->originalAmount, - $recipeIngredient->comment, - $recipeIngredient->amountMax - ); - }); - - return $result; + return $this->findRecipeContainer() + ->filter(".elementor-widget-shortcode li") + ->each(function ($node): RecipeIngredient { + $amount = $node->filter('.amount')->text(); + $ingredient = $node->filter('.ingredient')->text(); + $recipeIngredient = $this->ingredientParser->parseLine($amount); + return new RecipeIngredient( + $ingredient, + $recipeIngredient->amount, + $recipeIngredient->units, + $recipeIngredient->source, + $recipeIngredient->originalAmount, + $recipeIngredient->comment, + $recipeIngredient->amountMax + ); + }); } public function image(): ?array { return [ - 'url' => $this->crawler->filter('.recipe-card .recipe-thumb img')->first()->attr('src'), + 'url' => $this->findDescriptionContainer()->filter('.elementor-widget-image img')->first()->attr('src'), 'copyright' => 'CocktailParty', ]; } + + private function joinParagraphs(Crawler $nodes): string + { + $paragraphs = $nodes->each(function ($node): string { + return $node->text(); + }); + return implode("\n\n", $paragraphs); + } + + private function findDescriptionContainer(): Crawler + { + return $this->crawler + ->filter('.elementor-widget-heading h1') + ->closest('.elementor-element[data-element_type="container"]'); + } + + private function findRecipeContainer(): Crawler + { + return $this->crawler + ->filter('.elementor-widget-shortcode li .ingredient') + ->closest('.elementor-element[data-element_type="container"]'); + } } From d6451feb62ae87b026eab5967e16677640fa6a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karlo=20Miku=C5=A1?= Date: Tue, 2 Apr 2024 17:00:14 +0200 Subject: [PATCH 06/11] Add exports table --- app/Http/Controllers/ExportController.php | 39 +++ app/Http/Resources/ExportResource.php | 30 ++ app/Jobs/StartRecipesExport.php | 35 +++ app/Models/Cocktail.php | 8 +- app/Models/Export.php | 55 ++++ composer.lock | 261 +++++++++--------- ...2024_04_01_140655_create_exports_table.php | 31 +++ routes/api.php | 7 + 8 files changed, 336 insertions(+), 130 deletions(-) create mode 100644 app/Http/Controllers/ExportController.php create mode 100644 app/Http/Resources/ExportResource.php create mode 100644 app/Jobs/StartRecipesExport.php create mode 100644 app/Models/Export.php create mode 100644 database/migrations/2024_04_01_140655_create_exports_table.php diff --git a/app/Http/Controllers/ExportController.php b/app/Http/Controllers/ExportController.php new file mode 100644 index 00000000..576c48f0 --- /dev/null +++ b/app/Http/Controllers/ExportController.php @@ -0,0 +1,39 @@ +where('created_user_id', $request->user()->id)->get(); + + return ExportResource::collection($exports); + } + + public function store(Request $request, int|string $barId): ExportResource + { + $type = $request->query('type', 'json'); + $bar = Bar::findOrFail($barId); + + $export = new Export(); + $export->withFilename(); + $export->bar_id = $bar->id; + $export->is_done = false; + $export->created_user_id = $request->user()->id; + $export->save(); + + StartRecipesExport::dispatch($bar->id, $type, $export); + + return new ExportResource($export); + } +} diff --git a/app/Http/Resources/ExportResource.php b/app/Http/Resources/ExportResource.php new file mode 100644 index 00000000..486607a9 --- /dev/null +++ b/app/Http/Resources/ExportResource.php @@ -0,0 +1,30 @@ + + */ + public function toArray($request) + { + return [ + 'id' => $this->id, + 'filename' => $this->filename, + 'created_at' => $this->created_at, + 'bar_name' => $this->bar->name, + 'is_done' => (bool) $this->is_done, + ]; + } +} diff --git a/app/Jobs/StartRecipesExport.php b/app/Jobs/StartRecipesExport.php new file mode 100644 index 00000000..bd129d79 --- /dev/null +++ b/app/Jobs/StartRecipesExport.php @@ -0,0 +1,35 @@ +type) ?? ExportTypeEnum::JSON; + + $exporter->process($this->barId, $this->export->getFullPath(), $type); + + $this->export->markAsDone(); + } +} diff --git a/app/Models/Cocktail.php b/app/Models/Cocktail.php index e1b8686c..19b845f6 100644 --- a/app/Models/Cocktail.php +++ b/app/Models/Cocktail.php @@ -173,10 +173,16 @@ public function getAlcoholUnits(): float public function getCalories(): int { + if ($this->getABV() === null) { + return 0; + } + // It's important to note that the calorie content of mixed drinks can vary significantly // depending on the type and amount of mixers used. Drinks with sugary mixers or syrups // will generally have higher calorie counts. - return (int) floor($this->getVolume() * ($this->getABV() / 100) * 7); + $averageAlcCalories = 7; + + return (int) floor($this->getVolume() * ($this->getABV() / 100) * $averageAlcCalories); } public function getMainIngredient(): ?CocktailIngredient diff --git a/app/Models/Export.php b/app/Models/Export.php new file mode 100644 index 00000000..09eebae4 --- /dev/null +++ b/app/Models/Export.php @@ -0,0 +1,55 @@ + + */ + public function bar(): BelongsTo + { + return $this->belongsTo(Bar::class); + } + + public function markAsDone(): self + { + $this->is_done = true; + $this->save(); + + return $this; + } + + public function withFilename(): self + { + $this->filename = sprintf('%s_%s_%s.zip', Carbon::now()->format('Ymd'), 'recipes', Str::random(8)); + + return $this; + } + + public function getFullPath(): string + { + $folderPath = storage_path(sprintf('bar-assistant/backups/')); + + return $folderPath . $this->filename; + } + + protected function casts(): array + { + return [ + 'is_done' => 'boolean', + ]; + } +} diff --git a/composer.lock b/composer.lock index 73b336d7..a3a25c3f 100644 --- a/composer.lock +++ b/composer.lock @@ -1613,16 +1613,16 @@ }, { "name": "intervention/gif", - "version": "4.0.2", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/Intervention/gif.git", - "reference": "c2b07d1f69709e196c8b4ced423449a7e0f3b925" + "reference": "3a2b5f8a8856e8877cdab5c47e51aab2d4cb23a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Intervention/gif/zipball/c2b07d1f69709e196c8b4ced423449a7e0f3b925", - "reference": "c2b07d1f69709e196c8b4ced423449a7e0f3b925", + "url": "https://api.github.com/repos/Intervention/gif/zipball/3a2b5f8a8856e8877cdab5c47e51aab2d4cb23a3", + "reference": "3a2b5f8a8856e8877cdab5c47e51aab2d4cb23a3", "shasum": "" }, "require": { @@ -1630,7 +1630,7 @@ }, "require-dev": { "phpstan/phpstan": "^1", - "phpunit/phpunit": "^9", + "phpunit/phpunit": "^10.0", "slevomat/coding-standard": "~8.0", "squizlabs/php_codesniffer": "^3.8" }, @@ -1661,7 +1661,7 @@ ], "support": { "issues": "https://github.com/Intervention/gif/issues", - "source": "https://github.com/Intervention/gif/tree/4.0.2" + "source": "https://github.com/Intervention/gif/tree/4.1.0" }, "funding": [ { @@ -1673,20 +1673,20 @@ "type": "github" } ], - "time": "2024-02-18T15:36:58+00:00" + "time": "2024-03-26T17:23:47+00:00" }, { "name": "intervention/image", - "version": "3.4.0", + "version": "3.5.1", "source": { "type": "git", "url": "https://github.com/Intervention/image.git", - "reference": "fe1b0e2e64157133322974c28b44c25c2770a0c5" + "reference": "67be90e5700370c88833190d4edc07e4bb7d157b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Intervention/image/zipball/fe1b0e2e64157133322974c28b44c25c2770a0c5", - "reference": "fe1b0e2e64157133322974c28b44c25c2770a0c5", + "url": "https://api.github.com/repos/Intervention/image/zipball/67be90e5700370c88833190d4edc07e4bb7d157b", + "reference": "67be90e5700370c88833190d4edc07e4bb7d157b", "shasum": "" }, "require": { @@ -1697,7 +1697,7 @@ "require-dev": { "mockery/mockery": "^1.6", "phpstan/phpstan": "^1", - "phpunit/phpunit": "^9", + "phpunit/phpunit": "^10.0", "slevomat/coding-standard": "~8.0", "squizlabs/php_codesniffer": "^3.8" }, @@ -1733,7 +1733,7 @@ ], "support": { "issues": "https://github.com/Intervention/image/issues", - "source": "https://github.com/Intervention/image/tree/3.4.0" + "source": "https://github.com/Intervention/image/tree/3.5.1" }, "funding": [ { @@ -1745,7 +1745,7 @@ "type": "github" } ], - "time": "2024-02-14T15:11:21+00:00" + "time": "2024-03-22T07:12:19+00:00" }, { "name": "karlomikus/recipe-utils", @@ -1878,16 +1878,16 @@ }, { "name": "laravel/framework", - "version": "v10.48.2", + "version": "v10.48.4", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "32a8bb151d748b579c3794dea53b56bd40dfbbd3" + "reference": "7e0701bf59cb76a51f7c1f7bea51c0c0c29c0b72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/32a8bb151d748b579c3794dea53b56bd40dfbbd3", - "reference": "32a8bb151d748b579c3794dea53b56bd40dfbbd3", + "url": "https://api.github.com/repos/laravel/framework/zipball/7e0701bf59cb76a51f7c1f7bea51c0c0c29c0b72", + "reference": "7e0701bf59cb76a51f7c1f7bea51c0c0c29c0b72", "shasum": "" }, "require": { @@ -1992,7 +1992,7 @@ "league/flysystem-sftp-v3": "^3.0", "mockery/mockery": "^1.5.1", "nyholm/psr7": "^1.2", - "orchestra/testbench-core": "^8.18", + "orchestra/testbench-core": "^8.23.4", "pda/pheanstalk": "^4.0", "phpstan/phpstan": "^1.4.7", "phpunit/phpunit": "^10.0.7", @@ -2081,20 +2081,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-03-12T16:35:43+00:00" + "time": "2024-03-21T13:36:36+00:00" }, { "name": "laravel/horizon", - "version": "v5.23.1", + "version": "v5.23.2", "source": { "type": "git", "url": "https://github.com/laravel/horizon.git", - "reference": "7475de7eb5b465c2da84218002fe1a62b8175da0" + "reference": "96d154340f1223bcc161ea7cee355c8fc29ff81e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/horizon/zipball/7475de7eb5b465c2da84218002fe1a62b8175da0", - "reference": "7475de7eb5b465c2da84218002fe1a62b8175da0", + "url": "https://api.github.com/repos/laravel/horizon/zipball/96d154340f1223bcc161ea7cee355c8fc29ff81e", + "reference": "96d154340f1223bcc161ea7cee355c8fc29ff81e", "shasum": "" }, "require": { @@ -2107,6 +2107,7 @@ "nesbot/carbon": "^2.17|^3.0", "php": "^8.0", "ramsey/uuid": "^4.0", + "symfony/console": "^6.0|^7.0", "symfony/error-handler": "^6.0|^7.0", "symfony/process": "^6.0|^7.0" }, @@ -2157,22 +2158,22 @@ ], "support": { "issues": "https://github.com/laravel/horizon/issues", - "source": "https://github.com/laravel/horizon/tree/v5.23.1" + "source": "https://github.com/laravel/horizon/tree/v5.23.2" }, - "time": "2024-02-20T15:14:10+00:00" + "time": "2024-03-23T12:11:34+00:00" }, { "name": "laravel/prompts", - "version": "v0.1.16", + "version": "v0.1.17", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "ca6872ab6aec3ab61db3a61f83a6caf764ec7781" + "reference": "8ee9f87f7f9eadcbe21e9e72cd4176b2f06cd5b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/ca6872ab6aec3ab61db3a61f83a6caf764ec7781", - "reference": "ca6872ab6aec3ab61db3a61f83a6caf764ec7781", + "url": "https://api.github.com/repos/laravel/prompts/zipball/8ee9f87f7f9eadcbe21e9e72cd4176b2f06cd5b5", + "reference": "8ee9f87f7f9eadcbe21e9e72cd4176b2f06cd5b5", "shasum": "" }, "require": { @@ -2214,9 +2215,9 @@ ], "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.1.16" + "source": "https://github.com/laravel/prompts/tree/v0.1.17" }, - "time": "2024-02-21T19:25:27+00:00" + "time": "2024-03-13T16:05:43+00:00" }, { "name": "laravel/sanctum", @@ -2286,16 +2287,16 @@ }, { "name": "laravel/scout", - "version": "v10.8.3", + "version": "v10.8.4", "source": { "type": "git", "url": "https://github.com/laravel/scout.git", - "reference": "f2e20b0eb3355b555038581b52aec4bfe814c700" + "reference": "de4000a0044c0c13d513132af82562397da70d52" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/scout/zipball/f2e20b0eb3355b555038581b52aec4bfe814c700", - "reference": "f2e20b0eb3355b555038581b52aec4bfe814c700", + "url": "https://api.github.com/repos/laravel/scout/zipball/de4000a0044c0c13d513132af82562397da70d52", + "reference": "de4000a0044c0c13d513132af82562397da70d52", "shasum": "" }, "require": { @@ -2306,7 +2307,8 @@ "illuminate/pagination": "^9.0|^10.0|^11.0", "illuminate/queue": "^9.0|^10.0|^11.0", "illuminate/support": "^9.0|^10.0|^11.0", - "php": "^8.0" + "php": "^8.0", + "symfony/console": "^6.0|^7.0" }, "require-dev": { "algolia/algoliasearch-client-php": "^3.2", @@ -2359,7 +2361,7 @@ "issues": "https://github.com/laravel/scout/issues", "source": "https://github.com/laravel/scout" }, - "time": "2024-02-13T17:22:36+00:00" + "time": "2024-03-26T16:46:57+00:00" }, { "name": "laravel/serializable-closure", @@ -2700,16 +2702,16 @@ }, { "name": "league/flysystem", - "version": "3.25.0", + "version": "3.26.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "4c44347133618cccd9b3df1729647a1577b4ad99" + "reference": "072735c56cc0da00e10716dd90d5a7f7b40b36be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4c44347133618cccd9b3df1729647a1577b4ad99", - "reference": "4c44347133618cccd9b3df1729647a1577b4ad99", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/072735c56cc0da00e10716dd90d5a7f7b40b36be", + "reference": "072735c56cc0da00e10716dd90d5a7f7b40b36be", "shasum": "" }, "require": { @@ -2774,7 +2776,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.25.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.26.0" }, "funding": [ { @@ -2786,20 +2788,20 @@ "type": "github" } ], - "time": "2024-03-09T17:06:45+00:00" + "time": "2024-03-25T11:49:53+00:00" }, { "name": "league/flysystem-local", - "version": "3.23.1", + "version": "3.25.1", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "b884d2bf9b53bb4804a56d2df4902bb51e253f00" + "reference": "61a6a90d6e999e4ddd9ce5adb356de0939060b92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/b884d2bf9b53bb4804a56d2df4902bb51e253f00", - "reference": "b884d2bf9b53bb4804a56d2df4902bb51e253f00", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/61a6a90d6e999e4ddd9ce5adb356de0939060b92", + "reference": "61a6a90d6e999e4ddd9ce5adb356de0939060b92", "shasum": "" }, "require": { @@ -2833,8 +2835,7 @@ "local" ], "support": { - "issues": "https://github.com/thephpleague/flysystem-local/issues", - "source": "https://github.com/thephpleague/flysystem-local/tree/3.23.1" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.25.1" }, "funding": [ { @@ -2846,7 +2847,7 @@ "type": "github" } ], - "time": "2024-01-26T18:25:23+00:00" + "time": "2024-03-15T19:58:44+00:00" }, { "name": "league/mime-type-detection", @@ -3640,16 +3641,16 @@ }, { "name": "php-http/discovery", - "version": "1.19.2", + "version": "1.19.4", "source": { "type": "git", "url": "https://github.com/php-http/discovery.git", - "reference": "61e1a1eb69c92741f5896d9e05fb8e9d7e8bb0cb" + "reference": "0700efda8d7526335132360167315fdab3aeb599" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/discovery/zipball/61e1a1eb69c92741f5896d9e05fb8e9d7e8bb0cb", - "reference": "61e1a1eb69c92741f5896d9e05fb8e9d7e8bb0cb", + "url": "https://api.github.com/repos/php-http/discovery/zipball/0700efda8d7526335132360167315fdab3aeb599", + "reference": "0700efda8d7526335132360167315fdab3aeb599", "shasum": "" }, "require": { @@ -3673,7 +3674,8 @@ "php-http/httplug": "^1.0 || ^2.0", "php-http/message-factory": "^1.0", "phpspec/phpspec": "^5.1 || ^6.1 || ^7.3", - "symfony/phpunit-bridge": "^6.2" + "sebastian/comparator": "^3.0.5 || ^4.0.8", + "symfony/phpunit-bridge": "^6.4.4 || ^7.0.1" }, "type": "composer-plugin", "extra": { @@ -3712,9 +3714,9 @@ ], "support": { "issues": "https://github.com/php-http/discovery/issues", - "source": "https://github.com/php-http/discovery/tree/1.19.2" + "source": "https://github.com/php-http/discovery/tree/1.19.4" }, - "time": "2023-11-30T16:49:05+00:00" + "time": "2024-03-29T13:00:05+00:00" }, { "name": "php-http/httplug", @@ -3844,16 +3846,16 @@ }, { "name": "php-http/promise", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/php-http/promise.git", - "reference": "2916a606d3b390f4e9e8e2b8dd68581508be0f07" + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/promise/zipball/2916a606d3b390f4e9e8e2b8dd68581508be0f07", - "reference": "2916a606d3b390f4e9e8e2b8dd68581508be0f07", + "url": "https://api.github.com/repos/php-http/promise/zipball/fc85b1fba37c169a69a07ef0d5a8075770cc1f83", + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83", "shasum": "" }, "require": { @@ -3890,9 +3892,9 @@ ], "support": { "issues": "https://github.com/php-http/promise/issues", - "source": "https://github.com/php-http/promise/tree/1.3.0" + "source": "https://github.com/php-http/promise/tree/1.3.1" }, - "time": "2024-01-04T18:49:48+00:00" + "time": "2024-03-15T13:55:21+00:00" }, { "name": "phpoption/phpoption", @@ -4780,16 +4782,16 @@ }, { "name": "spatie/laravel-package-tools", - "version": "1.16.3", + "version": "1.16.4", "source": { "type": "git", "url": "https://github.com/spatie/laravel-package-tools.git", - "reference": "59db18c2e20d49a0b6d447bb1c654f6c123beb9e" + "reference": "ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/59db18c2e20d49a0b6d447bb1c654f6c123beb9e", - "reference": "59db18c2e20d49a0b6d447bb1c654f6c123beb9e", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53", + "reference": "ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53", "shasum": "" }, "require": { @@ -4828,7 +4830,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-package-tools/issues", - "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.3" + "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.4" }, "funding": [ { @@ -4836,7 +4838,7 @@ "type": "github" } ], - "time": "2024-03-07T07:35:57+00:00" + "time": "2024-03-20T07:29:11+00:00" }, { "name": "spatie/laravel-query-builder", @@ -4913,16 +4915,16 @@ }, { "name": "spatie/laravel-responsecache", - "version": "7.5.0", + "version": "7.5.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-responsecache.git", - "reference": "c6880344402b53fbf32d4f8dd5df8bff9f725c36" + "reference": "c7a173858088e95046faf785c4b69cea8cfa4f40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-responsecache/zipball/c6880344402b53fbf32d4f8dd5df8bff9f725c36", - "reference": "c6880344402b53fbf32d4f8dd5df8bff9f725c36", + "url": "https://api.github.com/repos/spatie/laravel-responsecache/zipball/c7a173858088e95046faf785c4b69cea8cfa4f40", + "reference": "c7a173858088e95046faf785c4b69cea8cfa4f40", "shasum": "" }, "require": { @@ -4931,7 +4933,7 @@ "illuminate/container": "^10.0|^11.0", "illuminate/http": "^10.0|^11.0", "illuminate/support": "^10.0|^11.0", - "nesbot/carbon": "^2.63", + "nesbot/carbon": "^2.63|^3.0", "php": "^8.2", "spatie/laravel-package-tools": "^1.9" }, @@ -4980,7 +4982,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/laravel-responsecache/tree/7.5.0" + "source": "https://github.com/spatie/laravel-responsecache/tree/7.5.1" }, "funding": [ { @@ -4992,7 +4994,7 @@ "type": "github" } ], - "time": "2024-03-11T23:35:58+00:00" + "time": "2024-03-23T06:44:45+00:00" }, { "name": "spatie/laravel-sluggable", @@ -8477,16 +8479,16 @@ "packages-dev": [ { "name": "barryvdh/laravel-debugbar", - "version": "v3.12.2", + "version": "v3.12.4", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "43555503052443964ce2c1c1f3b0378e58219eb8" + "reference": "e7a9a217512d8f1d07448fd241ce2ac0922ddc2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/43555503052443964ce2c1c1f3b0378e58219eb8", - "reference": "43555503052443964ce2c1c1f3b0378e58219eb8", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/e7a9a217512d8f1d07448fd241ce2ac0922ddc2c", + "reference": "e7a9a217512d8f1d07448fd241ce2ac0922ddc2c", "shasum": "" }, "require": { @@ -8545,7 +8547,7 @@ ], "support": { "issues": "https://github.com/barryvdh/laravel-debugbar/issues", - "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.12.2" + "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.12.4" }, "funding": [ { @@ -8557,7 +8559,7 @@ "type": "github" } ], - "time": "2024-03-13T09:50:34+00:00" + "time": "2024-04-01T09:14:15+00:00" }, { "name": "cebe/php-openapi", @@ -9059,16 +9061,16 @@ }, { "name": "laravel/pint", - "version": "v1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "6b127276e3f263f7bb17d5077e9e0269e61b2a0e" + "reference": "c52de679b3ac01207016c179d7ce173e4be128c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/6b127276e3f263f7bb17d5077e9e0269e61b2a0e", - "reference": "6b127276e3f263f7bb17d5077e9e0269e61b2a0e", + "url": "https://api.github.com/repos/laravel/pint/zipball/c52de679b3ac01207016c179d7ce173e4be128c4", + "reference": "c52de679b3ac01207016c179d7ce173e4be128c4", "shasum": "" }, "require": { @@ -9121,20 +9123,20 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-02-20T17:38:05+00:00" + "time": "2024-03-26T16:40:24+00:00" }, { "name": "laravel/sail", - "version": "v1.29.0", + "version": "v1.29.1", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "e40cc7ffb5186c45698dbd47e9477e0e429396d0" + "reference": "8be4a31150eab3b46af11a2e7b2c4632eefaad7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/e40cc7ffb5186c45698dbd47e9477e0e429396d0", - "reference": "e40cc7ffb5186c45698dbd47e9477e0e429396d0", + "url": "https://api.github.com/repos/laravel/sail/zipball/8be4a31150eab3b46af11a2e7b2c4632eefaad7e", + "reference": "8be4a31150eab3b46af11a2e7b2c4632eefaad7e", "shasum": "" }, "require": { @@ -9142,6 +9144,7 @@ "illuminate/contracts": "^9.52.16|^10.0|^11.0", "illuminate/support": "^9.52.16|^10.0|^11.0", "php": "^8.0", + "symfony/console": "^6.0|^7.0", "symfony/yaml": "^6.0|^7.0" }, "require-dev": { @@ -9183,7 +9186,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2024-03-08T16:32:33+00:00" + "time": "2024-03-20T20:09:31+00:00" }, { "name": "maximebf/debugbar", @@ -9253,16 +9256,16 @@ }, { "name": "mockery/mockery", - "version": "1.6.9", + "version": "1.6.11", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06" + "reference": "81a161d0b135df89951abd52296adf97deb0723d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", - "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", + "url": "https://api.github.com/repos/mockery/mockery/zipball/81a161d0b135df89951abd52296adf97deb0723d", + "reference": "81a161d0b135df89951abd52296adf97deb0723d", "shasum": "" }, "require": { @@ -9274,8 +9277,8 @@ "phpunit/phpunit": "<8.0" }, "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.6.10", - "symplify/easy-coding-standard": "^12.0.8" + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" }, "type": "library", "autoload": { @@ -9332,7 +9335,7 @@ "security": "https://github.com/mockery/mockery/security/advisories", "source": "https://github.com/mockery/mockery" }, - "time": "2023-12-10T02:24:34+00:00" + "time": "2024-03-21T18:34:15+00:00" }, { "name": "myclabs/deep-copy", @@ -9945,16 +9948,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.62", + "version": "1.10.66", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "cd5c8a1660ed3540b211407c77abf4af193a6af9" + "reference": "94779c987e4ebd620025d9e5fdd23323903950bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd5c8a1660ed3540b211407c77abf4af193a6af9", - "reference": "cd5c8a1660ed3540b211407c77abf4af193a6af9", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/94779c987e4ebd620025d9e5fdd23323903950bd", + "reference": "94779c987e4ebd620025d9e5fdd23323903950bd", "shasum": "" }, "require": { @@ -10003,7 +10006,7 @@ "type": "tidelift" } ], - "time": "2024-03-13T12:27:20+00:00" + "time": "2024-03-28T16:17:31+00:00" }, { "name": "phpunit/php-code-coverage", @@ -10328,16 +10331,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.13", + "version": "10.5.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "20a63fc1c6db29b15da3bd02d4b6cf59900088a7" + "reference": "18f8d4a5f52b61fdd9370aaae3167daa0eeb69cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/20a63fc1c6db29b15da3bd02d4b6cf59900088a7", - "reference": "20a63fc1c6db29b15da3bd02d4b6cf59900088a7", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/18f8d4a5f52b61fdd9370aaae3167daa0eeb69cd", + "reference": "18f8d4a5f52b61fdd9370aaae3167daa0eeb69cd", "shasum": "" }, "require": { @@ -10409,7 +10412,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.13" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.16" }, "funding": [ { @@ -10425,7 +10428,7 @@ "type": "tidelift" } ], - "time": "2024-03-12T15:37:41+00:00" + "time": "2024-03-28T10:08:10+00:00" }, { "name": "sebastian/cli-parser", @@ -10799,16 +10802,16 @@ }, { "name": "sebastian/environment", - "version": "6.0.1", + "version": "6.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951" + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/43c751b41d74f96cbbd4e07b7aec9675651e2951", - "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", "shasum": "" }, "require": { @@ -10823,7 +10826,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "6.1-dev" } }, "autoload": { @@ -10851,7 +10854,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.0.1" + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" }, "funding": [ { @@ -10859,7 +10862,7 @@ "type": "github" } ], - "time": "2023-04-11T05:39:26+00:00" + "time": "2024-03-23T08:47:14+00:00" }, { "name": "sebastian/exporter", @@ -11476,16 +11479,16 @@ }, { "name": "spatie/ignition", - "version": "1.12.0", + "version": "1.13.1", "source": { "type": "git", "url": "https://github.com/spatie/ignition.git", - "reference": "5b6f801c605a593106b623e45ca41496a6e7d56d" + "reference": "889bf1dfa59e161590f677728b47bf4a6893983b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ignition/zipball/5b6f801c605a593106b623e45ca41496a6e7d56d", - "reference": "5b6f801c605a593106b623e45ca41496a6e7d56d", + "url": "https://api.github.com/repos/spatie/ignition/zipball/889bf1dfa59e161590f677728b47bf4a6893983b", + "reference": "889bf1dfa59e161590f677728b47bf4a6893983b", "shasum": "" }, "require": { @@ -11555,20 +11558,20 @@ "type": "github" } ], - "time": "2024-01-03T15:49:39+00:00" + "time": "2024-03-29T14:03:47+00:00" }, { "name": "spatie/laravel-ignition", - "version": "2.4.2", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "351504f4570e32908839fc5a2dc53bf77d02f85e" + "reference": "e23f4e8ce6644dc3d68b9d8a0aed3beaca0d6ada" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/351504f4570e32908839fc5a2dc53bf77d02f85e", - "reference": "351504f4570e32908839fc5a2dc53bf77d02f85e", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/e23f4e8ce6644dc3d68b9d8a0aed3beaca0d6ada", + "reference": "e23f4e8ce6644dc3d68b9d8a0aed3beaca0d6ada", "shasum": "" }, "require": { @@ -11578,7 +11581,7 @@ "illuminate/support": "^10.0|^11.0", "php": "^8.1", "spatie/flare-client-php": "^1.3.5", - "spatie/ignition": "^1.9", + "spatie/ignition": "^1.13", "symfony/console": "^6.2.3|^7.0", "symfony/var-dumper": "^6.2.3|^7.0" }, @@ -11647,7 +11650,7 @@ "type": "github" } ], - "time": "2024-02-09T16:08:40+00:00" + "time": "2024-03-29T14:14:55+00:00" }, { "name": "symplify/easy-coding-standard", diff --git a/database/migrations/2024_04_01_140655_create_exports_table.php b/database/migrations/2024_04_01_140655_create_exports_table.php new file mode 100644 index 00000000..83dcf8c9 --- /dev/null +++ b/database/migrations/2024_04_01_140655_create_exports_table.php @@ -0,0 +1,31 @@ +id(); + $table->foreignId('bar_id'); + $table->string('filename'); + $table->foreignId('created_user_id'); + $table->boolean('is_done')->default(false); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('exports'); + } +}; diff --git a/routes/api.php b/routes/api.php index 336a2c73..5d55887b 100644 --- a/routes/api.php +++ b/routes/api.php @@ -14,6 +14,7 @@ use Kami\Cocktail\Http\Controllers\ShelfController; use Kami\Cocktail\Http\Controllers\StatsController; use Kami\Cocktail\Http\Controllers\UsersController; +use Kami\Cocktail\Http\Controllers\ExportController; use Kami\Cocktail\Http\Controllers\ImportController; use Kami\Cocktail\Http\Controllers\RatingController; use Kami\Cocktail\Http\Controllers\ServerController; @@ -231,6 +232,12 @@ Route::post('/', [PATController::class, 'store']); Route::delete('/{id}', [PATController::class, 'delete']); })->middleware(['ability:*']); + + Route::prefix('exports')->group(function() { + Route::get('/', [ExportController::class, 'index']); + Route::post('/{barId}', [ExportController::class, 'store']); + Route::delete('/{id}', [ExportController::class, 'delete']); + })->middleware(['ability:*']); }); Route::post('/billing/webhook', WebhookController::class); From 8ca2e2c5ea6d30805793cbd9c32f2e1745311594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karlo=20Miku=C5=A1?= Date: Wed, 3 Apr 2024 18:58:24 +0200 Subject: [PATCH 07/11] Add export policies --- app/Http/Controllers/ExportController.php | 40 ++++++++++++++++++++--- app/Models/Export.php | 10 ++++++ app/Policies/BarPolicy.php | 5 +++ app/Policies/ExportPolicy.php | 24 ++++++++++++++ app/Scraper/AbstractSiteExtractor.php | 1 + app/Scraper/Sites/CocktailParty.php | 1 + routes/api.php | 3 +- 7 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 app/Policies/ExportPolicy.php diff --git a/app/Http/Controllers/ExportController.php b/app/Http/Controllers/ExportController.php index 576c48f0..4c9efab9 100644 --- a/app/Http/Controllers/ExportController.php +++ b/app/Http/Controllers/ExportController.php @@ -5,25 +5,33 @@ namespace Kami\Cocktail\Http\Controllers; use Illuminate\Http\Request; +use Illuminate\Http\Response; use Kami\Cocktail\Models\Bar; use Kami\Cocktail\Models\Export; use Kami\Cocktail\Jobs\StartRecipesExport; use Illuminate\Http\Resources\Json\JsonResource; use Kami\Cocktail\Http\Resources\ExportResource; +use Symfony\Component\HttpFoundation\BinaryFileResponse; class ExportController extends Controller { public function index(Request $request): JsonResource { - $exports = Export::orderBy('created_at')->where('created_user_id', $request->user()->id)->get(); + $exports = Export::orderBy('created_at', 'desc') + ->where('created_user_id', $request->user()->id) + ->get(); return ExportResource::collection($exports); } - public function store(Request $request, int|string $barId): ExportResource + public function store(Request $request): ExportResource { - $type = $request->query('type', 'json'); - $bar = Bar::findOrFail($barId); + $type = $request->post('type', 'json'); + $bar = Bar::findOrFail($request->post('bar_id')); + + if ($request->user()->cannot('createExport', $bar)) { + abort(403); + } $export = new Export(); $export->withFilename(); @@ -36,4 +44,28 @@ public function store(Request $request, int|string $barId): ExportResource return new ExportResource($export); } + + public function delete(Request $request, int $id): Response + { + $export = Export::findOrFail($id); + + if ($request->user()->cannot('delete', $export)) { + abort(403); + } + + $export->delete(); + + return response(null, 204); + } + + public function download(Request $request, int $id): BinaryFileResponse + { + $export = Export::findOrFail($id); + + if ($request->user()->cannot('download', $export)) { + abort(403); + } + + return response()->download($export->getFullPath()); + } } diff --git a/app/Models/Export.php b/app/Models/Export.php index 09eebae4..53c44a48 100644 --- a/app/Models/Export.php +++ b/app/Models/Export.php @@ -6,6 +6,7 @@ use Carbon\Carbon; use Illuminate\Support\Str; +use Illuminate\Support\Facades\File; use Illuminate\Database\Eloquent\Model; use Kami\Cocktail\Models\Concerns\HasAuthors; use Kami\Cocktail\Models\Concerns\HasBarAwareScope; @@ -24,6 +25,15 @@ public function bar(): BelongsTo return $this->belongsTo(Bar::class); } + public function delete(): bool + { + if (File::exists($this->getFullPath())) { + File::delete($this->getFullPath()); + } + + return parent::delete(); + } + public function markAsDone(): self { $this->is_done = true; diff --git a/app/Policies/BarPolicy.php b/app/Policies/BarPolicy.php index cec36e31..c8e6efd5 100644 --- a/app/Policies/BarPolicy.php +++ b/app/Policies/BarPolicy.php @@ -59,4 +59,9 @@ public function deactivate(User $user, Bar $bar): bool { return $user->id === $bar->owner()->id; } + + public function createExport(User $user, Bar $bar): bool + { + return $user->id === $bar->created_user_id; + } } diff --git a/app/Policies/ExportPolicy.php b/app/Policies/ExportPolicy.php new file mode 100644 index 00000000..b4d5de2c --- /dev/null +++ b/app/Policies/ExportPolicy.php @@ -0,0 +1,24 @@ +created_user_id === $user->id; + } + + public function download(User $user, Export $token): bool + { + return $token->created_user_id === $user->id; + } +} diff --git a/app/Scraper/AbstractSiteExtractor.php b/app/Scraper/AbstractSiteExtractor.php index 94632f45..1051b501 100644 --- a/app/Scraper/AbstractSiteExtractor.php +++ b/app/Scraper/AbstractSiteExtractor.php @@ -157,6 +157,7 @@ public function toArray(): array }; $ingredients = $this->ingredients(); + return [ 'name' => $clean($this->name()), 'description' => $clean($this->description()), diff --git a/app/Scraper/Sites/CocktailParty.php b/app/Scraper/Sites/CocktailParty.php index 7de2949b..ac06b421 100644 --- a/app/Scraper/Sites/CocktailParty.php +++ b/app/Scraper/Sites/CocktailParty.php @@ -75,6 +75,7 @@ private function joinParagraphs(Crawler $nodes): string $paragraphs = $nodes->each(function ($node): string { return $node->text(); }); + return implode("\n\n", $paragraphs); } diff --git a/routes/api.php b/routes/api.php index 5d55887b..b0c919a0 100644 --- a/routes/api.php +++ b/routes/api.php @@ -235,8 +235,9 @@ Route::prefix('exports')->group(function() { Route::get('/', [ExportController::class, 'index']); - Route::post('/{barId}', [ExportController::class, 'store']); + Route::post('/', [ExportController::class, 'store']); Route::delete('/{id}', [ExportController::class, 'delete']); + Route::get('/{id}/download', [ExportController::class, 'download']); })->middleware(['ability:*']); }); From ffef99d96934aa853e27397ca0ca9863fa05ecf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karlo=20Miku=C5=A1?= Date: Thu, 4 Apr 2024 20:21:42 +0200 Subject: [PATCH 08/11] Add filtering by specific_ingredients --- app/Http/Filters/CocktailQueryFilter.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/Http/Filters/CocktailQueryFilter.php b/app/Http/Filters/CocktailQueryFilter.php index d70cd3ce..41a10874 100644 --- a/app/Http/Filters/CocktailQueryFilter.php +++ b/app/Http/Filters/CocktailQueryFilter.php @@ -128,6 +128,22 @@ public function __construct(CocktailRepository $cocktailRepo) $query->having('missing_ingredients', (int) $value); } }), + AllowedFilter::callback('specific_ingredients', function ($query, $value) use ($barMembership) { + if (!is_array($value)) { + $value = [$value]; + } + + $query->whereIn('cocktails.id', function ($query) use ($barMembership, $value) { + $query + ->select('cocktails.id') + ->from('cocktails') + ->where('cocktails.bar_id', $barMembership->bar_id) + ->join('cocktail_ingredients', 'cocktail_ingredients.cocktail_id', '=', 'cocktails.id') + ->whereIn('cocktail_ingredients.ingredient_id', $value) + ->groupBy('cocktails.id') + ->havingRaw('COUNT(DISTINCT cocktail_ingredients.ingredient_id) >= ?', [count($value)]); + }); + }), AllowedFilter::callback('ignore_ingredients', function ($query, $value) use ($barMembership) { if (!is_array($value)) { $value = [$value]; From 1580151b75ea6325fc84b30ff0ee0ad8f0a9882e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karlo=20Miku=C5=A1?= Date: Thu, 4 Apr 2024 20:59:55 +0200 Subject: [PATCH 09/11] Add export throttling --- CHANGELOG.md | 10 ++++++++++ app/Http/Resources/CocktailResource.php | 6 +++--- app/Providers/RouteServiceProvider.php | 4 ++++ routes/api.php | 2 +- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20afcc85..568b5815 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# v3.12.0 +## New +- Added `/exports` endpoints + - With this endpoint you can now manage recipe exporting for specific bars +- Added `specific_ingredients` cocktails filter + - This will show recipes that always contain specific ingredients + +## Fixes +- Fixed "CocktailParty" scraper + # v3.11.0 ## New - Added POST `/cocktails/{id}/copy` endpoint diff --git a/app/Http/Resources/CocktailResource.php b/app/Http/Resources/CocktailResource.php index fc71b21f..c8f8f2e6 100644 --- a/app/Http/Resources/CocktailResource.php +++ b/app/Http/Resources/CocktailResource.php @@ -51,9 +51,9 @@ public function toArray($request) 'updated_at' => $this->updated_at?->toJson(), 'method' => new CocktailMethodResource($this->whenLoaded('method')), 'abv' => $this->abv, - 'volume' => $this->getVolume(), - 'alcohol_units' => $this->getAlcoholUnits(), - 'calories' => $this->getCalories(), + // 'volume' => $this->getVolume(), + // 'alcohol_units' => $this->getAlcoholUnits(), + // 'calories' => $this->getCalories(), 'created_user' => new UserBasicResource($this->whenLoaded('createdUser')), 'updated_user' => new UserBasicResource($this->whenLoaded('updatedUser')), 'access' => [ diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index e49ac3ee..11061acb 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -54,5 +54,9 @@ protected function configureRateLimiting() ? Limit::none() : Limit::perMinute(2)->by($request->user()->id); }); + + RateLimiter::for('exports', function (Request $request) { + return Limit::perMinute(1)->by($request->user()?->id ?: $request->ip()); + }); } } diff --git a/routes/api.php b/routes/api.php index b0c919a0..8010dcf1 100644 --- a/routes/api.php +++ b/routes/api.php @@ -235,7 +235,7 @@ Route::prefix('exports')->group(function() { Route::get('/', [ExportController::class, 'index']); - Route::post('/', [ExportController::class, 'store']); + Route::post('/', [ExportController::class, 'store'])->middleware(['throttle:exports']); Route::delete('/{id}', [ExportController::class, 'delete']); Route::get('/{id}/download', [ExportController::class, 'download']); })->middleware(['ability:*']); From e42ee496ae87f96622bf55ddee380f3ce2ae411d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karlo=20Miku=C5=A1?= Date: Sat, 6 Apr 2024 14:23:34 +0200 Subject: [PATCH 10/11] Add public id to public menu cocktail --- app/Http/Resources/MenuPublicResource.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Http/Resources/MenuPublicResource.php b/app/Http/Resources/MenuPublicResource.php index 19efac08..5cb96a5e 100644 --- a/app/Http/Resources/MenuPublicResource.php +++ b/app/Http/Resources/MenuPublicResource.php @@ -36,6 +36,8 @@ public function toArray($request) 'full' => $menuCocktail->price, 'formatted' => number_format($menuCocktail->price / 100, 2), ], + 'public_id' => $menuCocktail->cocktail->public_id, + 'slug' => $menuCocktail->cocktail->slug, 'currency' => $menuCocktail->currency, 'name' => $menuCocktail->cocktail->name, 'short_ingredients' => $menuCocktail->cocktail->getShortIngredients(), From e2bb2542c95054227037043939b07502e064d64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karlo=20Miku=C5=A1?= Date: Sun, 7 Apr 2024 10:13:08 +0200 Subject: [PATCH 11/11] Update spec --- CHANGELOG.md | 1 + docs/open-api-spec.yml | 121 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 568b5815..fc222eda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - With this endpoint you can now manage recipe exporting for specific bars - Added `specific_ingredients` cocktails filter - This will show recipes that always contain specific ingredients +- Added `public_id` and `slug` to public menu cocktail response ## Fixes - Fixed "CocktailParty" scraper diff --git a/docs/open-api-spec.yml b/docs/open-api-spec.yml index d9034766..baa1b925 100644 --- a/docs/open-api-spec.yml +++ b/docs/open-api-spec.yml @@ -82,6 +82,8 @@ tags: description: Operations related to bar menu - name: Tokens description: Operations related to PAT + - name: Exports + description: Operations related to export files security: - user_token: [] paths: @@ -2794,6 +2796,91 @@ paths: $ref: '#/components/responses/NotAuthorized' '404': $ref: '#/components/responses/NotFound' + /exports: + get: + tags: + - Exports + summary: Show exports + operationId: showExports + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/ExportResponse' + post: + tags: + - Exports + summary: Create new export + operationId: createExport + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ExportRequest' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + data: + $ref: '#/components/schemas/ExportResponse' + '403': + $ref: '#/components/responses/NotAuthorized' + /exports/{id}: + parameters: + - in: path + name: id + description: 'Database id of export' + schema: + type: integer + required: true + delete: + tags: + - Exports + summary: Delete export + operationId: deleteExport + responses: + '204': + description: Successful response + '403': + $ref: '#/components/responses/NotAuthorized' + '404': + $ref: '#/components/responses/NotFound' + /exports/{id}/download: + parameters: + - in: path + name: id + description: 'Database id of export' + schema: + type: integer + required: true + get: + tags: + - Exports + summary: Download export + operationId: downloadExport + responses: + '200': + description: Successful response + content: + application/zip: + schema: + type: string + format: binary + '403': + $ref: '#/components/responses/NotAuthorized' + '404': + $ref: '#/components/responses/NotFound' components: securitySchemes: user_token: @@ -4402,6 +4489,8 @@ components: - price - currency - name + - slug + - public_id - short_ingredients - image properties: @@ -4421,6 +4510,11 @@ components: type: string name: type: string + slug: + type: string + public_id: + nullable: true + type: integer short_ingredients: type: array items: @@ -4489,4 +4583,31 @@ components: - status properties: status: + type: boolean + ExportRequest: + type: object + required: + - bar_id + properties: + bar_id: + type: integer + ExportResponse: + type: object + required: + - id + - filename + - created_at + - bar_name + - is_done + properties: + id: + type: integer + filename: + type: string + created_at: + format: datetime + type: string + bar_name: + type: string + is_done: type: boolean \ No newline at end of file