Skip to content

Commit

Permalink
Merge pull request #374 from karlomikus/develop
Browse files Browse the repository at this point in the history
Skip ENV for meilisearch setup
  • Loading branch information
karlomikus authored Dec 6, 2024
2 parents 83d8526 + b70080c commit 0bbd43c
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 116 deletions.
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# v4.2.2
## Fixed
- Login endpoint now requires confirmation if `mail_require_confirmation` is enabled
- Meilisearch tokens are now updated on docker restart only if the key has changed
- Ingredient and Bar `images` now have size validation
- Removed wrong token ability check for export download

# v4.2.1
## Fixed
- Fixed issues with search tokens not clearing correctly
- Improved search indexing on larger datasets
- Meilisearch tokens are now updated on docker restart only if the key has changed
- Fixed personal access token middleware handling
- Price calculations should be more accurate now
- Price calculations for price per pour should be more accurate now

# v4.2.0
## New
Expand Down
101 changes: 18 additions & 83 deletions app/Console/Commands/SetupMeilisearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

namespace Kami\Cocktail\Console\Commands;

use Throwable;
use Kami\Cocktail\Models\Bar;
use Illuminate\Console\Command;
use Laravel\Scout\EngineManager;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
use Kami\Cocktail\Services\MeilisearchService;

class SetupMeilisearch extends Command
{
Expand All @@ -16,7 +16,7 @@ class SetupMeilisearch extends Command
*
* @var string
*/
protected $signature = 'bar:setup-meilisearch';
protected $signature = 'bar:setup-meilisearch {--f|force : Force search token update for all bars}';

/**
* The console command description.
Expand All @@ -36,102 +36,37 @@ public function handle(): int
return Command::INVALID;
}

$forceTokenChange = (bool) $this->option('force');
$isNewMeilisearchKey = true;

try {
$this->line('Setting up Meilisearch client keys...');
/** @var \Meilisearch\Client */
$meilisearch = resolve(EngineManager::class)->engine();

$searchApiKey = null;
$keys = $meilisearch->getKeys();
/** @var \Meilisearch\Endpoints\Keys */
foreach ($keys->getResults() as $key) {
if ($key->getName() === 'bar-assistant') {
$isNewMeilisearchKey = false;
$searchApiKey = $key;
}
}

if (!$searchApiKey) {
$searchApiKey = $meilisearch->createKey([
'actions' => ['search'],
'indexes' => ['cocktails', 'ingredients'],
'expiresAt' => null,
'name' => 'bar-assistant',
'description' => 'Client key generated by Bar Assistant Server. Key for accessing cocktails and ingredients indexes.'
]);
}
} catch (\Throwable $e) {
$this->line('Setting up bar search tokens...');
/** @var MeilisearchService */
$meilisearch = resolve(MeilisearchService::class);
$searchApiKey = $meilisearch->getSearchAPIKey();
$isNewMeilisearchKey = $meilisearch->isNewMeilisearchKey();
} catch (Throwable $e) {
$this->error($e->getMessage());
$this->error('Unable to request Meilisearch keys. Skipping.');
$this->error('Unable to request Meilisearch keys. This could mean your Meilisearch instance is not running correctly. Skipping...');

return Command::INVALID;
}

// TODO: Uncomment this next minor release
// if (!$isNewMeilisearchKey) {
// $this->line('Skipping Meilisearch setup. Key did not change.');

// return Command::SUCCESS;
// }

$this->line('Clearing existing search tokens from bars...');
DB::transaction(function () {
DB::table('bars')->update(['search_token' => null]);
});

Cache::flush();

$this->line('Updating ENV...');

// $this->line('MS Api Key: ' . $searchApiKey->getKey());
// $this->line('MS Api Key UID: ' . $searchApiKey->getUid());

$envFile = App::environmentFilePath();
$envContents = file_get_contents($envFile);
if (!$envContents) {
$this->error('Unable to read ENV file. Aborting.');
if (!$isNewMeilisearchKey && !$forceTokenChange) {
$this->line('Skipping Meilisearch setup. Key did not change.');

return Command::INVALID;
}

// Update existing variables
if (str_contains($envContents, 'MEILISEARCH_API_KEY=')) {
file_put_contents($envFile, str_replace(
'MEILISEARCH_API_KEY=' . config('scout.meilisearch.api_key'),
'MEILISEARCH_API_KEY=' . $searchApiKey->getKey(),
$envContents
));
} else {
file_put_contents($envFile, $envContents . PHP_EOL . 'MEILISEARCH_API_KEY=' . $searchApiKey->getKey());
return Command::SUCCESS;
}

$envContents = file_get_contents($envFile);
if (!$envContents) {
$this->error('Unable to read ENV file. Aborting.');

return Command::INVALID;
}

if (str_contains($envContents, 'MEILISEARCH_API_KEY_UID=')) {
file_put_contents($envFile, str_replace(
'MEILISEARCH_API_KEY_UID=' . config('scout.meilisearch.api_key_uid'),
'MEILISEARCH_API_KEY_UID=' . $searchApiKey->getUid(),
$envContents
));
} else {
file_put_contents($envFile, $envContents . PHP_EOL . 'MEILISEARCH_API_KEY_UID=' . $searchApiKey->getUid());
}

$this->line('Updating search tokens for bars...');
DB::transaction(function () {
DB::transaction(function () use ($searchApiKey) {
$bars = Bar::all();
foreach ($bars as $bar) {
$bar->updateSearchToken();
$bar->updateSearchToken($searchApiKey->getUid(), $searchApiKey->getKey());
}
});

Cache::flush();

$this->info('Meilisearch setup done!');

return Command::SUCCESS;
Expand Down
6 changes: 5 additions & 1 deletion app/Http/Controllers/AuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ public function authenticate(Request $request): JsonResource
'password' => ['required'],
]);

$user = User::where('email', $request->email)->first();
if (config('bar-assistant.mail_require_confirmation') === true) {
$user = User::where('email', $request->email)->where('email_verified_at', '<>', null)->first();
} else {
$user = User::where('email', $request->email)->first();
}

if (!$user || !Hash::check($request->password, $user->password)) {
if (config('bar-assistant.mail_require_confirmation') === true) {
Expand Down
2 changes: 0 additions & 2 deletions app/Http/Controllers/BarController.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,6 @@ public function store(BarRequest $request): JsonResponse

$bar->load('createdUser', 'updatedUser', 'images');

$bar->updateSearchToken();

$request->user()->joinBarAs($bar, UserRoleEnum::Admin);

SetupBar::dispatch($bar, $request->user(), $barOptions);
Expand Down
1 change: 1 addition & 0 deletions app/Http/Requests/BarRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public function rules()
Rule::enum(BarStatusEnum::class),
'nullable',
],
'images' => 'array|max:1',
];
}
}
2 changes: 1 addition & 1 deletion app/Http/Requests/CocktailRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function rules()
'name' => 'required',
'instructions' => 'required',
'ingredients' => 'array',
'images' => 'array',
'images' => 'array|max:10',
'images.*' => 'integer',
'ingredients.*.ingredient_id' => 'required|integer',
'ingredients.*.units' => 'required_with:ingredients.*.amount',
Expand Down
1 change: 1 addition & 0 deletions app/Http/Requests/IngredientRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public function rules()
'prices.*.amount' => 'required|numeric|gte:0',
'prices.*.units' => 'required',
'prices.*.price_category_id' => 'int|required',
'images' => 'array|max:1',
];
}
}
19 changes: 12 additions & 7 deletions app/Models/Bar.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
use Spatie\Sluggable\HasSlug;
use Laravel\Scout\EngineManager;
use Spatie\Sluggable\SlugOptions;
use Illuminate\Support\Facades\Log;
use Illuminate\Database\Eloquent\Model;
use Kami\Cocktail\Models\Concerns\HasImages;
use Kami\Cocktail\Models\Concerns\HasAuthors;
use Kami\Cocktail\Models\Enums\BarStatusEnum;
use Kami\Cocktail\Services\Image\ImageService;
use Kami\Cocktail\Services\MeilisearchService;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
Expand All @@ -38,19 +40,22 @@ protected static function booted(): void
static::retrieved(function (Bar $bar) {
if (
$bar->search_token ||
config('scout.driver') === null ||
config('scout.meilisearch.api_key_uid') === null ||
config('scout.meilisearch.api_key') === null
config('scout.driver') === null
) {
return;
}

$bar->updateSearchToken();
$meilisearch = resolve(MeilisearchService::class);
$searchApiKey = $meilisearch->getSearchAPIKey();

$bar->updateSearchToken($searchApiKey->getUid(), $searchApiKey->getKey());
});
}

public function updateSearchToken(): void
public function updateSearchToken(string $apiKeyUid, string $apiKey): void
{
Log::debug('Updating search token for bar ' . $this->id);

/** @var \Meilisearch\Client */
$meilisearch = resolve(EngineManager::class)->engine();

Expand All @@ -61,9 +66,9 @@ public function updateSearchToken(): void
];

$tenantToken = $meilisearch->generateTenantToken(
config('scout.meilisearch.api_key_uid'),
$apiKeyUid,
$rules,
['apiKey' => config('scout.meilisearch.api_key')]
['apiKey' => $apiKey]
);

$this->search_token = $tenantToken;
Expand Down
49 changes: 49 additions & 0 deletions app/Services/MeilisearchService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace Kami\Cocktail\Services;

use Meilisearch\Client;
use Meilisearch\Endpoints\Keys;

final class MeilisearchService
{
private bool $isNewMeilisearchKey;

public function __construct(private readonly Client $client)
{
}

public function isNewMeilisearchKey(): bool
{
return $this->isNewMeilisearchKey;
}

public function getSearchAPIKey(): Keys
{
$searchApiKey = null;

$keys = $this->client->getKeys();
/** @var \Meilisearch\Endpoints\Keys */
foreach ($keys->getResults() as $key) {
if ($key->getName() === 'bar-assistant') {
$this->isNewMeilisearchKey = false;
$searchApiKey = $key;
}
}

if (!$searchApiKey) {
$this->isNewMeilisearchKey = true;
$searchApiKey = $this->client->createKey([
'actions' => ['search'],
'indexes' => ['cocktails', 'ingredients'],
'expiresAt' => null,
'name' => 'bar-assistant',
'description' => 'Client key generated by Bar Assistant Server. Key for accessing cocktails and ingredients indexes.'
]);
}

return $searchApiKey;
}
}
4 changes: 2 additions & 2 deletions config/scout.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@
'meilisearch' => [
'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
'key' => env('MEILISEARCH_KEY', null),
'api_key' => env('MEILISEARCH_API_KEY', null),
'api_key_uid' => env('MEILISEARCH_API_KEY_UID', null),
// 'api_key' => env('MEILISEARCH_API_KEY', null),
// 'api_key_uid' => env('MEILISEARCH_API_KEY_UID', null),
'index-settings' => [
\Kami\Cocktail\Models\Cocktail::class => [
'filterableAttributes' => ['tags', 'bar_id'],
Expand Down
2 changes: 1 addition & 1 deletion routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
Route::get('/menus/{barSlug}', [MenuController::class, 'show']);
});

Route::prefix('exports')->middleware(['ability:*'])->group(function () {
Route::prefix('exports')->group(function () {
Route::get('/{id}/download', [ExportController::class, 'download'])->name('exports.download');
});

Expand Down
34 changes: 17 additions & 17 deletions tests/Feature/Http/AuthControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -209,21 +209,21 @@ public function test_password_check_response(): void
);
}

// public function test_confirm_account_verify_response(): void
// {
// Config::set('bar-assistant.mail_require_confirmation', true);

// $user = User::factory()->create([
// 'email' => 'test@test.com',
// 'password' => Hash::make('my-test-password'),
// 'email_verified_at' => null,
// ]);

// $response = $this->postJson('/api/auth/login', [
// 'email' => $user->email,
// 'password' => 'my-test-password'
// ]);

// $response->dump();
// }
public function test_login_requires_confirmation(): void
{
Config::set('bar-assistant.mail_require_confirmation', true);

$user = User::factory()->create([
'email' => 'test@test.com',
'password' => Hash::make('my-test-password'),
'email_verified_at' => null,
]);

$response = $this->postJson('/api/auth/login', [
'email' => $user->email,
'password' => 'my-test-password'
]);

$response->assertStatus(400);
}
}

0 comments on commit 0bbd43c

Please sign in to comment.