Skip to content

Commit

Permalink
Merge pull request #28 from mrefferdk/phpcs
Browse files Browse the repository at this point in the history
Multi user support
  • Loading branch information
mrefferdk authored Oct 24, 2022
2 parents ff5c1d7 + dd6fe0b commit 9600dec
Show file tree
Hide file tree
Showing 28 changed files with 297 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: shivammathur/setup-php@15c43e89cdef867065b0213be354c2841860869e
- uses: shivammathur/setup-php@e04e1d97f0c0481c6e1ba40f8a538454fe5d7709
with:
php-version: '8.1'
- uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: shivammathur/setup-php@15c43e89cdef867065b0213be354c2841860869e
- uses: shivammathur/setup-php@e04e1d97f0c0481c6e1ba40f8a538454fe5d7709
with:
php-version: '8.1'
- uses: actions/checkout@v3
Expand Down
4 changes: 4 additions & 0 deletions app/Http/Controllers/Api/RecipeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Http\Controllers\Api;

use App\Http\Services\AccessService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Models\Recipe;
Expand All @@ -27,7 +28,10 @@ public function index()
*/
public function show($id)
{
//dd(auth()->user());
/** @var Recipe $recipe */
$recipe = Recipe::with('ingredients')->find($id);
//AccessService::hasReadAccessOrThrowException($recipe);
return response()->json($recipe);
}
}
11 changes: 8 additions & 3 deletions app/Http/Controllers/Api/ScrapeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
namespace App\Http\Controllers\Api;

use App\Http\Services\ScrapeService;
use App\Http\Services\UserHashService;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

class ScrapeController extends Controller
Expand All @@ -15,19 +17,22 @@ class ScrapeController extends Controller
*
* @return JsonResponse
*/
public function index(Request $request, ScrapeService $scrapeService)
public function store(Request $request, ScrapeService $scrapeService)
{
// TODO we use this home brew solution, because there's no Auth()->user() when calling API routes. Find out how to do this properly with Sanctum
$userId = $request->get('userId');

$url = $request->get('url');
// TODO create middleware for this
if ($scrapeService->isAlreadyScraped($url)) {
return response()->json(['error' => 'This URL is already scraped'], 400);
}

try {
$recipe = $scrapeService->scrapeAndSave($url);
$recipe = $scrapeService->scrapeAndSave($url, $userId);
return response()->json(['recipe' => $recipe->toArray(), 'url' => '/recipes/' . $recipe->id]);
} catch (Exception $e) {
Log::error('Exception found in '. __METHOD__ . ' with url: ' . $url);
Log::error('Exception found in '. __METHOD__ . ' with url: ' . $url, ['e' => $e->getMessage(), 'line' => $e->getLine(), 'file' => $e->getFile()]);
return response()->json(['error' => 'No scraper found for provided domain or some other error has occured'], 501);
}
}
Expand Down
14 changes: 10 additions & 4 deletions app/Http/Controllers/RecipeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@

namespace App\Http\Controllers;

use App\Http\Services\AccessService;
use App\Http\Services\RecipeService;
use App\Models\Ingredient;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use App\Models\Recipe;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\View\View;

class RecipeController extends Controller
Expand All @@ -20,7 +18,7 @@ class RecipeController extends Controller
*/
public function index(): View
{
$recipes = DB::table('recipes')->orderBy('title')->get();
$recipes = Recipe::ForUser()->orderBy('title')->get();
return view('recipes.list', ['recipes' => $recipes]);
}

Expand Down Expand Up @@ -65,12 +63,14 @@ public function store(Request $request): RedirectResponse
public function show(Request $request, int $id): View
{
$recipe = Recipe::find($id);
AccessService::hasReadAccessOrThrowException($recipe);

$totalTime = (int) $recipe->work_time + (int) $recipe->cooking_time;
return view('recipes.show', [
'recipe' => $recipe,
'showEditOptions' => $request->exists('showEditOptions'),
'totalTime' => $totalTime,
'canEdit' => AccessService::hasWriteAccess($recipe),
]);
}

Expand All @@ -81,6 +81,8 @@ public function edit(int $id): View
{
/** @var Recipe $recipe */
$recipe = Recipe::find($id);
AccessService::hasWriteAccessOrThrowException($recipe);

$numberOfIngredientFields = count($recipe->ingredients) + 10;
return view('recipes.edit', [
'method' => 'PUT',
Expand All @@ -96,6 +98,10 @@ public function edit(int $id): View
*/
public function update(Request $request, int $id): RedirectResponse
{
/** @var Recipe $recipe */
$recipe = Recipe::find($id);
AccessService::hasReadAccessOrThrowException($recipe);

/** @var RecipeService $recipeService */
$recipeService = app(RecipeService::class);

Expand Down
8 changes: 5 additions & 3 deletions app/Http/Controllers/ScrapeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Ingredient;
use App\Http\Services\UserHashService;

class ScrapeController extends Controller
{
public function index()
{
return view('scrape.dashboard');
return view('scrape.dashboard', [
'userId' => Auth()->user()->id,
'userIdHash' => UserHashService::getUserHash(),
]);
}
}
2 changes: 2 additions & 0 deletions app/Http/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use App\Http\Middleware\TrimStrings;
use App\Http\Middleware\TrustProxies;
use App\Http\Middleware\ValidateRecipeInput;
use App\Http\Middleware\ValidateUserHash;
use App\Http\Middleware\VerifyCsrfToken;
use Fruitcake\Cors\HandleCors;
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
Expand Down Expand Up @@ -85,5 +86,6 @@ class Kernel extends HttpKernel
'throttle' => ThrottleRequests::class,
'verified' => EnsureEmailIsVerified::class,
'recipe.form.validate' => ValidateRecipeInput::class,
'validate.user.hash' => ValidateUserHash::class,
];
}
26 changes: 26 additions & 0 deletions app/Http/Middleware/ValidateUserHash.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App\Http\Middleware;

use App\Http\Services\UserHashService;
use Closure;
use Illuminate\Http\Request;

class ValidateUserHash
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$userId = $request->get('userId');
if ($request->get('userIdHash') !== UserHashService::getUserHashById($userId)) {
return response()->json(['error' => 'Not authorized'], 401);
}
return $next($request);
}
}
49 changes: 49 additions & 0 deletions app/Http/Services/AccessService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace App\Http\Services;

use App\Models\Recipe;

class AccessService
{
public static function hasReadAccessOrThrowException(Recipe $recipe): bool
{
if (!self::hasReadAccess($recipe)) {
throw new \Exception('You shall not pass', 403);
}

return true;
}

public static function hasReadAccess(Recipe $recipe): bool
{
if ($recipe->user_id != null && $recipe->user_id !== auth()->user()->id) {
return false;
}

return true;
}

public static function isAdmin(): bool
{
return auth()->user()->admin;
}

public static function hasWriteAccessOrThrowException(Recipe $recipe): bool
{
if (!self::hasWriteAccess($recipe)) {
throw new \Exception('You shall not pass', 403);
}

return true;
}

public static function hasWriteAccess(Recipe $recipe): bool
{
if ($recipe->user_id !== auth()->user()->id && !self::isAdmin()) {
return false;
}

return true;
}
}
3 changes: 2 additions & 1 deletion app/Http/Services/RecipeService.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use App\Models\Recipe;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class RecipeService
{
Expand Down Expand Up @@ -65,8 +66,8 @@ private function saveRecipe(Request $request, Recipe $recipe): void
$recipe->number = $request->get('number');
$recipe->cooking_time = (int) trim($request->get('cooking_time'));
$recipe->work_time = (int) trim($request->get('work_time'));
$recipe->image_path = '$fileName';
$recipe->active = $request->get('active') ?? false;
$recipe->user_id = Auth()->user()->id;
$recipe->save();
}
}
5 changes: 4 additions & 1 deletion app/Http/Services/ScrapeService.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@ public function isAlreadyScraped(string $url)
return $found !== 0;
}

public function scrapeAndSave(string $url): Recipe
public function scrapeAndSave(string $url, int $userId): Recipe
{
$scraper = $this->getScraper($url);
$content = $scraper->scrapeAndGetContent();
/** @var RecipeAdapterService $adapterService */
$adapterService = app(RecipeAdapterService::class);
$adapter = $adapterService->getAdapter($url);

// TODO modify and use RecipeService to save the adapted content
$recipe = $adapter::adapt($content);
$recipe->user_id = $userId;
$recipe->save();

if ($content['imageSrc'] && $image = file_get_contents($content['imageSrc'])) {
Expand Down
25 changes: 25 additions & 0 deletions app/Http/Services/UserHashService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace App\Http\Services;

class UserHashService
{
/**
* This method only has a User in Web routes NOT api routes.
* @return string
*/
public static function getUserHash(): string
{
return self::getUserHashById(Auth()->user()->id);
}

/**
* Use this in API routes where you can provide a user id from the request
* @param int $userId
* @return string
*/
public static function getUserHashById(int $userId): string
{
return sha1(config('app.key') . $userId);
}
}
13 changes: 13 additions & 0 deletions app/Models/Recipe.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Database\Eloquent\Model;
use App\Models\Ingredient;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;

class Recipe extends Model
{
Expand All @@ -24,6 +25,7 @@ class Recipe extends Model
'cooking_time',
'work_time',
'ingredients',
'user_id',
];

/**
Expand All @@ -33,4 +35,15 @@ public function ingredients(): HasMany
{
return $this->hasMany(Ingredient::class, 'recipe_id');
}

public function user(): HasOne
{
return $this->hasOne(User::class, 'id');
}

public function scopeForUser($query)
{
// TODO add column "for_all" to make globally visible recipes
return $query->where('user_id', auth()->user()?->id)->orWhere('user_id', '=', null);
}
}
2 changes: 1 addition & 1 deletion app/Providers/RouteServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class RouteServiceProvider extends ServiceProvider
*
* @var string
*/
public const HOME = '/dashboard';
public const HOME = '/';

/**
* The controller namespace for the application.
Expand Down
12 changes: 7 additions & 5 deletions database/factories/RecipeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Database\Factories;

use App\Models\Recipe;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

class RecipeFactory extends Factory
Expand All @@ -24,11 +25,12 @@ public function definition()
return [
'title' => $this->faker->sentence(3),
'body' => $this->faker->paragraphs(10, true),
'number' => $this->faker->numberBetween(1,10),
'cooking_time' => $this->faker->numberBetween(1,120),
'work_time' => $this->faker->numberBetween(1,120),
'active' => $this->faker->numberBetween(0,1),
'image_path' => $this->faker->numberBetween(0,2) . '.jpg',
'number' => $this->faker->numberBetween(1, 10),
'cooking_time' => $this->faker->numberBetween(1, 120),
'work_time' => $this->faker->numberBetween(1, 120),
'active' => $this->faker->numberBetween(0, 1),
'image_path' => $this->faker->numberBetween(0, 2) . '.jpg',
'user_id' => 1,
];
}
}
4 changes: 2 additions & 2 deletions database/factories/UserFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ class UserFactory extends Factory
public function definition()
{
return [
'name' => $this->faker->name,
'name' => 'effer',
'email' => '[email protected]',
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
'remember_token' => '123234',
];
}

Expand Down
Loading

0 comments on commit 9600dec

Please sign in to comment.