Skip to content

Commit de08eb7

Browse files
authored
Merge pull request #517 from karlomikus/develop
minor release
2 parents deef052 + ff76d29 commit de08eb7

File tree

80 files changed

+19690
-465
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+19690
-465
lines changed

.dockerignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ tests
1616
.editorconfig
1717
pint.json
1818
.spectral.yml
19+
.todo
1920

2021
Dockerfile
2122
Dockerfile.dev
@@ -29,4 +30,4 @@ README.md
2930
ecs.php
3031

3132
SECURITY.md
32-
CONTRIBUTING.md
33+
CONTRIBUTING.md

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ notes.sql
1919
composer.phar
2020
.phpunit.cache
2121
/resources/data
22-
litestream.yml
22+
litestream.yml
23+
.todo

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
# v5.7.0
2+
## New
3+
- Import parent cocktails via datapack
4+
- Added `is_public` property to `Bar` schema
5+
- If set to `true`, bar will expose public endpoints `/public/{barId}/*`
6+
- New public endpoints are still being documented
7+
- Added optional `html_content` to scrape request schema
8+
- This allows you to send raw HTML content to scrape endpoint, instead of server making a request to the URL
9+
- Scraping now respects `robots.txt` rules
10+
- Scraping is now done with identifiable user agent
11+
12+
# v5.6.1
13+
## Fixes
14+
- Fixed `parent_cocktail_id` type error when importing data via datapack
15+
116
# v5.6.0
217
## New
318
- Added cocktail recipe parent ID tracking

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ This repository only contains the API server, if you are looking for easy to use
2424
</p>
2525

2626
## Features
27-
- [Includes over 300 cocktail recipes with detailed information](https://github.com/bar-assistant/data)
28-
- Includes over 150 base ingredients with categories
27+
- [Includes over 500 cocktail recipes with detailed information](https://github.com/bar-assistant/data)
28+
- Includes over 250 base ingredients with categories
2929
- Add and manage multiple bars and bar members
3030
- Fine-grained user control with user roles
3131
- Endpoints for managing and filtering ingredients and cocktails
@@ -39,6 +39,7 @@ This repository only contains the API server, if you are looking for easy to use
3939
- Support for glass types, utensils, tags, ingredient categories and more
4040
- Cocktail recipe importing via URL, JSON, YAML or custom collections
4141
- Support for cocktail ratings
42+
- Support for cocktail variations
4243
- Create user-specific cocktail collections for easy referencing and sharing
4344
- Support for cocktail and ingredient notes
4445
- Supports sharing recipes by public links, custom recipe images and printing
@@ -48,6 +49,7 @@ This repository only contains the API server, if you are looking for easy to use
4849
- Data export support in various formats
4950
- Support for multiple ingredient prices
5051
- Automatic cocktail price calculation based on ingredients
52+
- SSO Support
5153

5254
## Container images
5355

app/Console/Commands/BarExportRecipes.php

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
use Kami\Cocktail\Models\Bar;
99
use Illuminate\Console\Command;
1010
use Kami\Cocktail\Models\Export;
11+
12+
use function Laravel\Prompts\spin;
13+
1114
use Illuminate\Support\Facades\DB;
1215

1316
use function Laravel\Prompts\search;
@@ -24,14 +27,16 @@ class BarExportRecipes extends Command
2427
*
2528
* @var string
2629
*/
27-
protected $signature = 'bar:export-recipes {barId?} {--t|type=datapack : Export type} {--u|units= : Force unit conversion when possible (none, ml, oz, cl)}';
30+
protected $signature = 'bar:export-recipes {barId?} {--t|type=datapack : Export type (datapack, schema, md, json-ld, xml, yaml)} {--u|units= : Force unit conversion when possible (none, ml, oz, cl)}';
2831

2932
/**
3033
* The console command description.
3134
*
3235
* @var string
3336
*/
34-
protected $description = 'Export data from a single bar. Available export types: datapack, schema, markdown, json-ld, xml, yaml';
37+
protected $description = 'Export data from a single bar';
38+
39+
protected $help = 'This command allows you to export data from a single bar in various formats. You can specify the bar ID as an argument, or search for it interactively if not provided.';
3540

3641
public function __construct(private readonly ToDataPack $datapackExporter, private readonly ToRecipeType $recipeExporter)
3742
{
@@ -44,6 +49,14 @@ public function __construct(private readonly ToDataPack $datapackExporter, priva
4449
public function handle(): int
4550
{
4651
$barId = (int) $this->argument('barId');
52+
$type = ExportTypeEnum::tryFrom($this->option('type') ?? 'datapack');
53+
$units = ForceUnitConvertEnum::tryFrom($this->option('units') ?? 'none');
54+
55+
$this->newLine();
56+
$this->line('Exporting data from a bar');
57+
$this->newLine();
58+
$this->line('Selected export type: ' . ($type->value ?? 'datapack'));
59+
$this->line('Selected unit conversion: ' . ($units->value ?? 'none'));
4760

4861
if ($barId === 0) {
4962
$barId = search(
@@ -56,9 +69,6 @@ public function handle(): int
5669
);
5770
}
5871

59-
$type = ExportTypeEnum::tryFrom($this->option('type') ?? 'datapack');
60-
$units = ForceUnitConvertEnum::tryFrom($this->option('units') ?? 'none');
61-
6272
try {
6373
$bar = Bar::findOrFail($barId);
6474
} catch (Throwable $e) {
@@ -67,13 +77,20 @@ public function handle(): int
6777
return Command::FAILURE;
6878
}
6979

70-
$this->line(sprintf('Starting new export (%s | %s) from bar: %s - "%s"', $type->value, $units->value, $bar->id, $bar->name));
80+
$this->line(sprintf('Starting new %s export from bar: [%s] %s', $type->value, $bar->id, $bar->name));
7181

72-
if ($type === ExportTypeEnum::Datapack) {
73-
$filename = $this->datapackExporter->process($bar->id, Export::generateFilename('datapack'), $units);
74-
} else {
75-
$filename = $this->recipeExporter->process($bar->id, Export::generateFilename($type->getFilenameContext()), $type, $units);
76-
}
82+
$filename = spin(
83+
callback: function () use ($bar, $type, $units) {
84+
if ($type === ExportTypeEnum::Datapack) {
85+
$filename = $this->datapackExporter->process($bar->id, Export::generateFilename('datapack'), $units);
86+
} else {
87+
$filename = $this->recipeExporter->process($bar->id, Export::generateFilename($type->getFilenameContext()), $type, $units);
88+
}
89+
90+
return $filename;
91+
},
92+
message: 'Generating...'
93+
);
7794

7895
$this->output->success('Data exported to file: ' . $filename);
7996

app/External/Export/ToDataPack.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ private function dumpCocktails(int $barId, ZipArchive &$zip, ?Units $toUnits = n
8888
'glass',
8989
'method',
9090
'tags',
91-
'utensils'
91+
'utensils',
92+
'parentCocktail',
9293
)->where('bar_id', $barId)->get();
9394

9495
/** @var Cocktail $cocktail */
@@ -98,7 +99,7 @@ private function dumpCocktails(int $barId, ZipArchive &$zip, ?Units $toUnits = n
9899
/** @var \Kami\Cocktail\Models\Image $img */
99100
foreach ($cocktail->images as $img) {
100101
try {
101-
$zip->addFile($img->getPath(), 'cocktails/' . $cocktail->getExternalId() . '/' . $img->getFileName());
102+
$zip->addFile($img->getPath(), 'cocktails/' . $cocktail->getExternalId() . '/' . $img->getExternalId());
102103
} catch (ImageFileNotFoundException $e) {
103104
Log::warning($e->getMessage());
104105
}
@@ -120,7 +121,7 @@ private function dumpIngredients(int $barId, ZipArchive &$zip): void
120121

121122
/** @var \Kami\Cocktail\Models\Image $img */
122123
foreach ($ingredient->images as $img) {
123-
$zip->addFile($img->getPath(), 'ingredients/' . $ingredient->getExternalId() . '/' . $img->getFileName());
124+
$zip->addFile($img->getPath(), 'ingredients/' . $ingredient->getExternalId() . '/' . $img->getExternalId());
124125
}
125126

126127
$ingredientExportData = $this->prepareDataOutput($data->toDataPackArray());

app/External/Import/FromDataPack.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ private function importCalculators(string $filepath, int $barId): void
162162

163163
private function importIngredients(Filesystem $dataDisk, Bar $bar, User $user): void
164164
{
165+
$timerStart = microtime(true);
166+
165167
$existingIngredients = DB::table('ingredients')->select('id', 'name')->where('bar_id', $bar->id)->get()->keyBy(function ($ingredient) {
166168
return Str::slug($ingredient->name);
167169
});
@@ -173,6 +175,7 @@ private function importIngredients(Filesystem $dataDisk, Bar $bar, User $user):
173175
$barImagesDir = $bar->getIngredientsDirectory();
174176
$this->uploadsDisk->makeDirectory($barImagesDir);
175177

178+
$imagesTimer = 0.0;
176179
foreach ($this->getDataFromDir('ingredients', $dataDisk) as $fromYield) {
177180
[$externalIngredient, $filePath] = $fromYield;
178181
$externalIngredient = IngredientExternal::fromDataPackArray($externalIngredient);
@@ -208,6 +211,7 @@ private function importIngredients(Filesystem $dataDisk, Bar $bar, User $user):
208211
}
209212

210213
// For performance, manually copy the files and create image references
214+
$imagesTimerStart = microtime(true);
211215
foreach ($externalIngredient->images as $image) {
212216
$baseSrcImagePath = $filePath . $image->getLocalFilePath();
213217
$fileExtension = File::extension($dataDisk->path($baseSrcImagePath));
@@ -226,8 +230,12 @@ private function importIngredients(Filesystem $dataDisk, Bar $bar, User $user):
226230
'updated_at' => null,
227231
];
228232
}
233+
$imageTimerEnd = microtime(true);
234+
$imagesTimer += ($imageTimerEnd - $imagesTimerStart);
229235
}
230236

237+
Log::debug(sprintf('Ingredient image copy completed in %d ms', $imagesTimer * 1000));
238+
231239
// Start inserting
232240
DB::beginTransaction();
233241

@@ -280,10 +288,15 @@ private function importIngredients(Filesystem $dataDisk, Bar $bar, User $user):
280288
DB::commit();
281289

282290
$this->ingredientRepository->rebuildMaterializedPath($bar->id);
291+
292+
$timerEnd = microtime(true);
293+
Log::debug(sprintf('Ingredients import completed in %d ms', ($timerEnd - $timerStart) * 1000));
283294
}
284295

285296
private function importBaseCocktails(Filesystem $dataDisk, Bar $bar, User $user): void
286297
{
298+
$timerStart = microtime(true);
299+
287300
$dbIngredients = DB::table('ingredients')->select('id', DB::raw('LOWER(name) AS name'))->where('bar_id', $bar->id)->get()->keyBy('name')->map(fn ($row) => $row->id)->toArray();
288301
$dbGlasses = DB::table('glasses')->select('id', DB::raw('LOWER(name) AS name'))->where('bar_id', $bar->id)->get()->keyBy('name')->map(fn ($row) => $row->id)->toArray();
289302
$dbMethods = DB::table('cocktail_methods')->select('id', DB::raw('LOWER(name) AS name'))->where('bar_id', $bar->id)->get()->keyBy('name')->map(fn ($row) => $row->id)->toArray();
@@ -298,9 +311,11 @@ private function importBaseCocktails(Filesystem $dataDisk, Bar $bar, User $user)
298311
$newCocktailIngredients = [];
299312
$newCocktailSubstituteIngredients = [];
300313
$cocktailUtensilsMap = [];
314+
$cocktailParentMap = [];
301315
$barImagesDir = 'cocktails/' . $bar->id . '/';
302316
$this->uploadsDisk->makeDirectory($barImagesDir);
303317

318+
$imagesTimer = 0.0;
304319
foreach ($this->getDataFromDir('cocktails', $dataDisk) as $fromYield) {
305320
[$cocktail, $filePath] = $fromYield;
306321

@@ -328,6 +343,10 @@ private function importBaseCocktails(Filesystem $dataDisk, Bar $bar, User $user)
328343
'updated_at' => $externalCocktail->updatedAt,
329344
];
330345

346+
if ($externalCocktail->parentCocktailId) {
347+
$cocktailParentMap[$slug] = $externalCocktail->parentCocktailId . '-' . $bar->id;
348+
}
349+
331350
foreach ($externalCocktail->tags as $tag) {
332351
$tag = trim($tag);
333352
if (!in_array($tag, $uniqueTags)) {
@@ -380,6 +399,7 @@ private function importBaseCocktails(Filesystem $dataDisk, Bar $bar, User $user)
380399
}
381400

382401
// For performance, manually copy the files and create image references
402+
$imagesTimerStart = microtime(true);
383403
foreach ($externalCocktail->images as $image) {
384404
$baseSrcImagePath = $filePath . $image->getLocalFilePath();
385405
$fileExtension = File::extension($dataDisk->path($baseSrcImagePath));
@@ -399,8 +419,12 @@ private function importBaseCocktails(Filesystem $dataDisk, Bar $bar, User $user)
399419
'updated_at' => null,
400420
];
401421
}
422+
$imageTimerEnd = microtime(true);
423+
$imagesTimer += ($imageTimerEnd - $imagesTimerStart);
402424
}
403425

426+
Log::debug(sprintf('Cocktail image copy completed in %d ms', $imagesTimer * 1000));
427+
404428
DB::beginTransaction();
405429

406430
$tagsToInsert = [];
@@ -453,6 +477,15 @@ private function importBaseCocktails(Filesystem $dataDisk, Bar $bar, User $user)
453477

454478
$cocktailIngredientsToInsert = array_merge($cocktailIngredientsWithAssignedCocktail, $cocktailIngredientsToInsert);
455479
}
480+
481+
// Add parent cocktail id
482+
if (isset($cocktailParentMap[$cocktail->slug])) {
483+
$parentSlug = $cocktailParentMap[$cocktail->slug];
484+
$parentCocktailId = $cocktails->firstWhere('slug', $parentSlug);
485+
if (isset($parentCocktailId->id)) {
486+
DB::table('cocktails')->where('slug', $cocktail->slug)->where('bar_id', $bar->id)->update(['parent_cocktail_id' => $parentCocktailId->id]);
487+
}
488+
}
456489
}
457490
DB::table('cocktail_ingredients')->insert($cocktailIngredientsToInsert);
458491
DB::table('cocktail_tag')->insert($cocktailTagsToInsert);
@@ -482,6 +515,9 @@ private function importBaseCocktails(Filesystem $dataDisk, Bar $bar, User $user)
482515
DB::table('images')->insert($imagesToInsert);
483516

484517
DB::commit();
518+
519+
$timerEnd = microtime(true);
520+
Log::debug(sprintf('Cocktails import completed in %d ms', ($timerEnd - $timerStart) * 1000));
485521
}
486522

487523
private function copyResourceImage(Filesystem $dataDisk, string $baseSrcImagePath, string $targetImagePath): void

app/External/Model/Cocktail.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public static function fromModel(CocktailModel $model, bool $useFileURI = false,
6969
$model->utensils->pluck('name')->toArray(),
7070
$images,
7171
$ingredients,
72-
$model->parent_cocktail_id !== null ? (string) $model->parent_cocktail_id : null,
72+
$model->parentCocktail?->getExternalId(),
7373
$model->year,
7474
);
7575
}

app/Http/Controllers/AuthController.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public function authenticate(Request $request): JsonResource
5757
if (!$user || !Hash::check($request->password, $user->password)) {
5858
Log::warning('User tried to login with invalid credentials', [
5959
'email' => $request->email,
60+
'is_verified' => $user?->hasVerifiedEmail(),
6061
]);
6162

6263
if (config('bar-assistant.mail_require_confirmation') === true) {

0 commit comments

Comments
 (0)