├── .editorconfig ├── .env.example ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── laravel_phpunit.yaml ├── .gitignore ├── .styleci.yml ├── LICENSE ├── README.md ├── TODO.md ├── app ├── Actions │ └── Fortify │ │ ├── CreateNewUser.php │ │ ├── PasswordValidationRules.php │ │ ├── ResetUserPassword.php │ │ ├── UpdateUserPassword.php │ │ └── UpdateUserProfileInformation.php ├── Console │ └── Kernel.php ├── Events │ ├── UserApproved.php │ └── UserVerified.php ├── Exceptions │ └── Handler.php ├── Http │ ├── Controllers │ │ ├── AllergenController.php │ │ ├── Controller.php │ │ ├── IngredientController.php │ │ ├── MenuController.php │ │ ├── PagesController.php │ │ ├── PermissionController.php │ │ ├── ProfileController.php │ │ ├── RecipeController.php │ │ ├── RoleController.php │ │ ├── SearchController.php │ │ ├── UploadController.php │ │ ├── UserController.php │ │ └── WebhookController.php │ ├── Kernel.php │ ├── Livewire │ │ ├── Admin │ │ │ └── Users │ │ │ │ └── Approval.php │ │ ├── Comments.php │ │ ├── Ingredients │ │ │ └── Index.php │ │ ├── Menus │ │ │ └── Create.php │ │ ├── Permissions │ │ │ └── Index.php │ │ ├── Recipes │ │ │ ├── Create.php │ │ │ ├── Index.php │ │ │ └── Rate.php │ │ └── Roles │ │ │ └── Index.php │ ├── Middleware │ │ ├── Authenticate.php │ │ ├── EncryptCookies.php │ │ ├── PreventRequestsDuringMaintenance.php │ │ ├── RedirectIfAuthenticated.php │ │ ├── TrimStrings.php │ │ ├── TrustHosts.php │ │ ├── TrustProxies.php │ │ ├── UserIsApproved.php │ │ ├── ValidateHttpsSignature.php │ │ └── VerifyCsrfToken.php │ └── Requests │ │ ├── AuthLoginRequest.php │ │ ├── ChangePasswordRequest.php │ │ ├── StoreAllergenRequest.php │ │ ├── StoreIngredientRequest.php │ │ ├── StorePermissionRequest.php │ │ ├── StoreRecipeRequest.php │ │ ├── StoreRegisterRequest.php │ │ ├── StoreRoleRequest.php │ │ ├── StoreServingRequest.php │ │ ├── StoreTimingRequest.php │ │ ├── StoreUserRequest.php │ │ ├── UpdateAccountRequest.php │ │ ├── UpdateAllergenRequest.php │ │ ├── UpdateIngredientRequest.php │ │ ├── UpdatePermissionRequest.php │ │ ├── UpdateRecipeRequest.php │ │ ├── UpdateRoleRequest.php │ │ ├── UpdateServingRequest.php │ │ ├── UpdateTimingRequest.php │ │ └── UpdateUserRequest.php ├── Listeners │ ├── SendNewUserNotification.php │ └── SendUserApprovedNotification.php ├── Models │ ├── Allergen.php │ ├── Category.php │ ├── Comment.php │ ├── Ingredient.php │ ├── Menu.php │ ├── Rating.php │ ├── Recipe.php │ ├── TempFile.php │ └── User.php ├── Notifications │ ├── ApproveUser.php │ └── ApprovedUser.php ├── Providers │ ├── AppServiceProvider.php │ ├── AuthServiceProvider.php │ ├── BroadcastServiceProvider.php │ ├── EventServiceProvider.php │ ├── FortifyServiceProvider.php │ └── RouteServiceProvider.php └── Rules │ ├── CurrentPasswordRule.php │ └── RecaptchaRule.php ├── artisan ├── bootstrap ├── app.php └── cache │ └── .gitignore ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── broadcasting.php ├── cache.php ├── cors.php ├── database.php ├── filesystems.php ├── fortify.php ├── hashing.php ├── logging.php ├── mail.php ├── media-library.php ├── permission.php ├── queue.php ├── recaptcha.php ├── services.php ├── session.php └── view.php ├── database ├── .gitignore ├── factories │ ├── AllergenFactory.php │ ├── IngredientFactory.php │ ├── PermissionFactory.php │ ├── RecipeFactory.php │ ├── RoleFactory.php │ └── UserFactory.php ├── migrations │ ├── 2014_10_12_000000_create_users_table.php │ ├── 2014_10_12_100000_create_password_resets_table.php │ ├── 2014_10_12_200000_add_two_factor_columns_to_users_table.php │ ├── 2020_10_25_222033_create_servings_table.php │ ├── 2020_10_25_222141_create_ingredients_table.php │ ├── 2020_10_25_222225_create_meals_table.php │ ├── 2020_11_01_212244_create_roles_table.php │ ├── 2020_11_01_214314_create_role_user_pivot_table.php │ ├── 2020_11_04_081508_create_permissions_table.php │ ├── 2020_11_05_082345_add_description_to_roles_table.php │ ├── 2020_11_05_155823_create_permission_role_pivot_table.php │ ├── 2020_11_09_005625_add_softdelete_to_users_table.php │ ├── 2020_11_17_231250_add_new_user_to_users_table.php │ ├── 2020_11_24_220553_create_ingredient_meal_pivot_table.php │ ├── 2020_11_27_232411_add_user_id_to_ingredients_table.php │ ├── 2020_11_29_220043_add_user_id_to_meals_table.php │ ├── 2020_11_30_201655_add_slug_to_ingredients_table.php │ ├── 2020_11_30_230801_add_slug_to_meals_table.php │ ├── 2020_12_01_232336_create_ratings_table.php │ ├── 2020_12_03_144041_add_instructions_to_meals_table.php │ ├── 2020_12_03_150502_add_quantity_to_ingredient_meal_pivot_table.php │ ├── 2020_12_04_143323_create_comments_table.php │ ├── 2021_02_23_195758_create_allergens_table.php │ ├── 2021_02_25_084615_create_allergen_meal_pivot_table.php │ ├── 2021_03_04_193042_remove_id_from_allergen_meal_table.php │ ├── 2021_03_06_185744_remove_old_role_permissions.php │ ├── 2021_03_06_190030_create_permission_tables.php │ ├── 2021_03_06_200435_seed_permissions_and_roles.php │ ├── 2021_03_09_135510_create_patches_table.php │ ├── 2021_03_19_191353_seed_admin_permissions.php │ ├── 2021_03_23_191703_create_media_table.php │ ├── 2021_03_24_193950_add_approved_to_users_table.php │ ├── 2021_05_21_184605_add_menu_permissions.php │ ├── 2021_05_22_141649_create_menus_table.php │ ├── 2021_05_22_142218_create_meal_menu_pivot_table.php │ ├── 2021_05_31_142157_add_admin_to_super_admins.php │ ├── 2021_06_09_191108_create_temp_files_table.php │ ├── 2021_06_13_174348_change_name_to_unique_on_meals_table.php │ ├── 2021_06_23_223742_create_likes_table.php │ ├── 2021_06_27_190328_create_categories_table.php │ ├── 2021_06_27_190602_seed_category_seeder.php │ ├── 2021_06_28_181444_rename_meals_table_to_recipe_table.php │ ├── 2021_06_28_234507_fix_model_type_in_media_table.php │ └── 2021_07_01_191325_add_category_id_to_recipes_table.php └── seeders │ ├── AdminPermissionSeeder.php │ ├── AdminUserSeeder.php │ ├── AllergenSeeder.php │ ├── CategorySeeder.php │ ├── DatabaseSeeder.php │ ├── MenuPermissionSeeder.php │ └── PermissionSeeder.php ├── deploy.sh ├── package-lock.json ├── package.json ├── phpunit.xml ├── public ├── .htaccess ├── css │ ├── app.css │ └── icons.css ├── favicon.ico ├── fonts │ ├── erudus.eot │ ├── erudus.svg │ ├── erudus.ttf │ └── erudus.woff ├── index.php ├── js │ └── app.js ├── mix-manifest.json ├── robots.txt └── web.config ├── resources ├── css │ ├── app.css │ └── vendor │ │ └── erudus │ │ ├── icons.css │ │ └── icons.scss ├── fonts │ └── vendor │ │ └── erudus │ │ ├── erudus.eot │ │ ├── erudus.svg │ │ ├── erudus.ttf │ │ └── erudus.woff ├── js │ ├── app.js │ └── bootstrap.js ├── lang │ └── en │ │ ├── auth.php │ │ ├── pagination.php │ │ ├── passwords.php │ │ └── validation.php └── views │ ├── admin │ ├── allergens │ │ ├── create.blade.php │ │ ├── edit.blade.php │ │ └── index.blade.php │ ├── dashboard.blade.php │ ├── ingredients │ │ ├── create.blade.php │ │ ├── edit.blade.php │ │ ├── index.blade.php │ │ └── show.blade.php │ ├── layout.blade.php │ ├── permissions │ │ ├── index.blade.php │ │ └── show.blade.php │ ├── roles │ │ ├── create.blade.php │ │ ├── edit.blade.php │ │ ├── index.blade.php │ │ └── show.blade.php │ └── users │ │ ├── create.blade.php │ │ ├── edit.blade.php │ │ └── index.blade.php │ ├── auth │ ├── forgot-password.blade.php │ ├── layout.blade.php │ ├── login.blade.php │ ├── register.blade.php │ ├── reset-password.blade.php │ ├── two-factor-challenge.blade.php │ └── verify-email.blade.php │ ├── cookie.blade.php │ ├── homepage.blade.php │ ├── layouts │ ├── app.blade.php │ └── header.blade.php │ ├── livewire │ ├── admin │ │ └── users │ │ │ └── approval.blade.php │ ├── comments.blade.php │ ├── ingredients │ │ └── index.blade.php │ ├── menus │ │ └── create.blade.php │ ├── permissions │ │ └── index.blade.php │ ├── recipes │ │ ├── create.blade.php │ │ ├── index.blade.php │ │ └── rate.blade.php │ └── roles │ │ └── index.blade.php │ ├── menus │ ├── create.blade.php │ └── index.blade.php │ ├── recipes │ ├── create.blade.php │ ├── edit.blade.php │ ├── index.blade.php │ ├── liked.blade.php │ └── show.blade.php │ ├── search.blade.php │ ├── user │ ├── confirm-password.blade.php │ └── profile │ │ ├── account.blade.php │ │ ├── index.blade.php │ │ ├── layout.blade.php │ │ └── security.blade.php │ └── vendor │ └── pagination │ ├── bootstrap-4.blade.php │ ├── default.blade.php │ ├── semantic-ui.blade.php │ ├── simple-bootstrap-4.blade.php │ ├── simple-default.blade.php │ ├── simple-tailwind.blade.php │ └── tailwind.blade.php ├── routes ├── api.php ├── channels.php ├── console.php └── web.php ├── server.php ├── storage ├── app │ ├── .gitignore │ └── public │ │ └── .gitignore ├── debugbar │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ ├── .gitignore │ │ └── data │ │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ ├── testing │ │ └── .gitignore │ └── views │ │ └── .gitignore ├── logs │ └── .gitignore └── media-library │ └── .gitignore ├── tailwind.config.js ├── tests ├── CreatesApplication.php ├── Feature │ ├── AllergenTest.php │ ├── AuthTest.php │ ├── IngredientTest.php │ ├── MenuTest.php │ ├── PageTest.php │ ├── PermissionTest.php │ ├── ProfileTest.php │ ├── RecipeTest.php │ ├── RoleTest.php │ ├── SearchTest.php │ └── UserTest.php ├── TestCase.php └── Unit │ └── ExampleTest.php └── webpack.mix.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Laravel 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_URL=http://localhost 6 | PROXY_SCHEME= 7 | 8 | RECAPTCHA_SITE_KEY= 9 | RECAPTCHA_SECRET_KEY= 10 | GOOGLE_ANALYTICS= 11 | 12 | WEBHOOK_SECRET= 13 | 14 | LOG_CHANNEL=stack 15 | LOG_LEVEL=debug 16 | 17 | DB_CONNECTION=mysql 18 | DB_HOST=127.0.0.1 19 | DB_PORT=3306 20 | DB_DATABASE=laravel 21 | DB_USERNAME=root 22 | DB_PASSWORD= 23 | 24 | BROADCAST_DRIVER=log 25 | CACHE_DRIVER=file 26 | QUEUE_CONNECTION=sync 27 | SESSION_DRIVER=file 28 | SESSION_LIFETIME=120 29 | 30 | REDIS_HOST=127.0.0.1 31 | REDIS_PASSWORD=null 32 | REDIS_PORT=6379 33 | 34 | MAIL_MAILER=smtp 35 | MAIL_HOST=smtp.mailtrap.io 36 | MAIL_PORT=2525 37 | MAIL_USERNAME=null 38 | MAIL_PASSWORD=null 39 | MAIL_ENCRYPTION=null 40 | MAIL_FROM_ADDRESS=null 41 | MAIL_FROM_NAME="${APP_NAME}" 42 | 43 | AWS_ACCESS_KEY_ID= 44 | AWS_SECRET_ACCESS_KEY= 45 | AWS_DEFAULT_REGION=us-east-1 46 | AWS_BUCKET= 47 | 48 | PUSHER_APP_ID= 49 | PUSHER_APP_KEY= 50 | PUSHER_APP_SECRET= 51 | PUSHER_APP_CLUSTER=mt1 52 | 53 | MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" 54 | MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" 55 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.scss linguist-vendored 4 | *.js linguist-vendored 5 | CHANGELOG.md export-ignore 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: JustinByrne 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /public/hot 3 | /public/storage 4 | /storage/*.key 5 | /vendor 6 | .env 7 | .env.backup 8 | .phpunit.result.cache 9 | Homestead.json 10 | Homestead.yaml 11 | npm-debug.log 12 | yarn-error.log 13 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | php: 2 | preset: laravel 3 | disabled: 4 | - no_unused_imports 5 | finder: 6 | not-name: 7 | - index.php 8 | - server.php 9 | js: 10 | finder: 11 | not-name: 12 | - webpack.mix.js 13 | css: true 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Justin Byrne 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mealing 2 | 3 | ![laravel tests workflow](https://github.com/JustinByrne/Mealing/actions/workflows/laravel_phpunit.yaml/badge.svg) ![GitHub](https://img.shields.io/github/license/JustinByrne/Mealing) 4 | 5 | A simple Laravel application to allow users to add recipes, and create generated menus for a week. With the menu this can also produce a list of all ingredients needed for the week of recipes that can be used as a shopping list. 6 | 7 | ## Installation 8 | 9 | 1. Download the latest release 10 | - `git clone https://github.com/JustinByrne/Mealing.git` 11 | 2. Within the new directory run the following 12 | 1. `composer install` 13 | 2. `cp .env.example .env` 14 | 3. `php artisan key:generate` 15 | 4. `php artisan storage:link` 16 | 5. `php artisan migrate` 17 | 18 | During the installation process an admin account is created, this account has all permissions by default and any new ones as they are created. 19 | 20 | email: `admin@example.com`
21 | password: `password` 22 | 23 | > It is advised that these details are changed straight after installation. 24 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # To do list 2 | 3 | - Create a good README.md file 4 | - Add ability to restrict menus by allergens 5 | - Add filters to recipe index and search pages 6 | - 7 | -------------------------------------------------------------------------------- /app/Actions/Fortify/CreateNewUser.php: -------------------------------------------------------------------------------- 1 | ['required', 'string', 'max:255'], 26 | 'email' => [ 27 | 'required', 28 | 'string', 29 | 'email', 30 | 'max:255', 31 | Rule::unique(User::class), 32 | ], 33 | 'password' => $this->passwordRules(), 34 | 'recaptcha_token' => ['required', new RecaptchaRule($input['recaptcha_token']) ], 35 | ])->validate(); 36 | 37 | $user = User::create([ 38 | 'name' => $input['name'], 39 | 'email' => $input['email'], 40 | 'password' => Hash::make($input['password']), 41 | ]); 42 | 43 | $user->assignRole('User'); 44 | 45 | return $user; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/Actions/Fortify/PasswordValidationRules.php: -------------------------------------------------------------------------------- 1 | $this->passwordRules(), 24 | ])->validate(); 25 | 26 | $user->forceFill([ 27 | 'password' => Hash::make($input['password']), 28 | ])->save(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Actions/Fortify/UpdateUserPassword.php: -------------------------------------------------------------------------------- 1 | ['required', 'string'], 24 | 'password' => $this->passwordRules(), 25 | ])->after(function ($validator) use ($user, $input) { 26 | if (! Hash::check($input['current_password'], $user->password)) { 27 | $validator->errors()->add('current_password', __('The provided password does not match your current password.')); 28 | } 29 | })->validateWithBag('updatePassword'); 30 | 31 | $user->forceFill([ 32 | 'password' => Hash::make($input['password']), 33 | ])->save(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/Actions/Fortify/UpdateUserProfileInformation.php: -------------------------------------------------------------------------------- 1 | ['required', 'string', 'max:255'], 23 | 24 | 'email' => [ 25 | 'required', 26 | 'string', 27 | 'email', 28 | 'max:255', 29 | Rule::unique('users')->ignore($user->id), 30 | ], 31 | ])->validateWithBag('updateProfileInformation'); 32 | 33 | if ($input['email'] !== $user->email && 34 | $user instanceof MustVerifyEmail) { 35 | $this->updateVerifiedUser($user, $input); 36 | } else { 37 | $user->forceFill([ 38 | 'name' => $input['name'], 39 | 'email' => $input['email'], 40 | ])->save(); 41 | } 42 | } 43 | 44 | /** 45 | * Update the given verified user's profile information. 46 | * 47 | * @param mixed $user 48 | * @param array $input 49 | * @return void 50 | */ 51 | protected function updateVerifiedUser($user, array $input) 52 | { 53 | $user->forceFill([ 54 | 'name' => $input['name'], 55 | 'email' => $input['email'], 56 | 'email_verified_at' => null, 57 | ])->save(); 58 | 59 | $user->sendEmailVerificationNotification(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('inspire')->hourly(); 28 | } 29 | 30 | /** 31 | * Register the commands for the application. 32 | * 33 | * @return void 34 | */ 35 | protected function commands() 36 | { 37 | $this->load(__DIR__.'/Commands'); 38 | 39 | require base_path('routes/console.php'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/Events/UserApproved.php: -------------------------------------------------------------------------------- 1 | user = $user; 28 | } 29 | 30 | /** 31 | * Get the channels the event should broadcast on. 32 | * 33 | * @return \Illuminate\Broadcasting\Channel|array 34 | */ 35 | public function broadcastOn() 36 | { 37 | return new PrivateChannel('channel-name'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/Events/UserVerified.php: -------------------------------------------------------------------------------- 1 | user = $user; 28 | } 29 | 30 | /** 31 | * Get the channels the event should broadcast on. 32 | * 33 | * @return \Illuminate\Broadcasting\Channel|array 34 | */ 35 | public function broadcastOn() 36 | { 37 | return new PrivateChannel('channel-name'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | validated()); 34 | 35 | return redirect()->route('admin.allergens.index'); 36 | } 37 | 38 | public function edit(Allergen $allergen): View 39 | { 40 | abort_if(Gate::denies('allergen_edit'), 403); 41 | 42 | return view('admin.allergens.edit', compact('allergen')); 43 | } 44 | 45 | public function update(UpdateAllergenRequest $request, Allergen $allergen): RedirectResponse 46 | { 47 | $allergen->update($request->validated()); 48 | 49 | return redirect()->route('admin.allergens.index'); 50 | } 51 | 52 | public function destroy(Allergen $allergen): RedirectResponse 53 | { 54 | abort_if(Gate::denies('allergen_delete'), 403); 55 | 56 | $allergen->delete(); 57 | 58 | return redirect()->route('admin.allergens.index'); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | withCount(['ratings as average_rating' => function($query) { 18 | $query->select(DB::raw('coalesce(avg(score),0)')); 19 | }])->orderByDesc('average_rating')->take(6)->get(); 20 | 21 | return view('homepage', compact('topRecipes')); 22 | } 23 | 24 | public function adminDashboard(): View 25 | { 26 | abort_if(Gate::denies('admin_access'), 403); 27 | 28 | $users = User::count(); 29 | $recipes = Recipe::count(); 30 | $ingredients = Ingredient::count(); 31 | 32 | return view('admin.dashboard', compact('users', 'recipes', 'ingredients')); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/Http/Controllers/PermissionController.php: -------------------------------------------------------------------------------- 1 | load('roles'); 26 | 27 | return view('admin.permissions.show', compact('permission')); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Http/Controllers/ProfileController.php: -------------------------------------------------------------------------------- 1 | email != $request['email']) { 35 | $user->update([ 36 | 'email_verified_at' => null, 37 | ]); 38 | } 39 | 40 | $user->update($request->only('name', 'email')); 41 | 42 | return redirect(route('profile.settings.account'))->with('profileStatus', 'Account Successfully Updated'); 43 | } 44 | 45 | public function password(ChangePasswordRequest $request): RedirectResponse 46 | { 47 | $user = Auth::User(); 48 | 49 | $user->update([ 50 | 'password' => Hash::make($request['password']) 51 | ]); 52 | 53 | return redirect(route('profile.settings.account'))->with('passwordStatus', 'Password Changed Successfully'); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/Http/Controllers/RoleController.php: -------------------------------------------------------------------------------- 1 | validated()); 32 | 33 | return redirect()->route('admin.roles.show', [$role]); 34 | } 35 | 36 | public function show(Role $role): View 37 | { 38 | abort_if(Gate::denies('role_show'), 403); 39 | 40 | $role->load('permissions'); 41 | 42 | return view('admin.roles.show', compact('role')); 43 | } 44 | 45 | public function edit(Role $role): View 46 | { 47 | abort_if(Gate::denies('role_edit'), 403); 48 | 49 | return view('admin.roles.edit', compact('role')); 50 | } 51 | 52 | public function update(UpdateRoleRequest $request, Role $role): RedirectResponse 53 | { 54 | $role->update($request->validated()); 55 | 56 | return redirect()->route('admin.roles.show', [$role]); 57 | } 58 | 59 | public function destroy(Role $role): void 60 | { 61 | abort_if(Gate::denies('role_delete'), 403); 62 | 63 | $role->delete(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/Http/Controllers/SearchController.php: -------------------------------------------------------------------------------- 1 | has('s')) { 19 | abort(404); 20 | } 21 | 22 | $s = $request->s; 23 | 24 | if (Recipe::where('name', $s)->count() == 1) { 25 | return redirect()->route('recipes.show', Recipe::where('name', $s)->first()); 26 | } 27 | 28 | $recipes = Recipe::with('ratings', 'media') 29 | ->withCount(['ratings as rating' => function($query) { 30 | $query->select(DB::raw('coalesce(avg(score),0)')); 31 | }]) 32 | ->where('name', 'like', '%' . $s . '%') 33 | ->orWhereHas('ingredients', function (Builder $query) use ($s) { 34 | $query->where('name', 'like', '%' . $s . '%'); 35 | }) 36 | ->filter()->paginate(15); 37 | 38 | return view('search', compact('recipes', 's')); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Http/Controllers/UploadController.php: -------------------------------------------------------------------------------- 1 | hasFile('image') && in_array(File::mimeType($request->file('image')), $mimeTypes)) { 18 | $file = $request->file('image'); 19 | $filename = Str::slug(str_replace("'", '', $file->getClientOriginalName())); 20 | $folder = 'tmp/' . uniqid() . '-' . now()->timestamp; 21 | Storage::putFileAs($folder, $file, $filename); 22 | 23 | TempFile::create([ 24 | 'folder' => $folder, 25 | 'filename' => $filename, 26 | ]); 27 | 28 | return $folder; 29 | } 30 | 31 | return response('Failed upload', 500); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Http/Controllers/UserController.php: -------------------------------------------------------------------------------- 1 | validated()); 36 | 37 | return redirect()->route('admin.users.index'); 38 | } 39 | 40 | public function edit(User $user): View 41 | { 42 | abort_if(Gate::denies('user_edit'), Response::HTTP_FORBIDDEN, '403 Forbidden'); 43 | 44 | return view('admin.users.edit', compact('user')); 45 | } 46 | 47 | public function update(UpdateUserRequest $request, User $user): RedirectResponse 48 | { 49 | $user->update($request->validated()); 50 | 51 | return redirect()->route('admin.users.index'); 52 | } 53 | 54 | public function destroy(User $user): RedirectResponse 55 | { 56 | abort_if(Gate::denies('user_delete'), Response::HTTP_FORBIDDEN, '403 Forbidden'); 57 | 58 | $user->delete(); 59 | 60 | return redirect()->route('admin.users.index'); 61 | } 62 | 63 | public function approve($email): RedirectResponse 64 | { 65 | $email = Crypt::decrypt($email); 66 | 67 | $user = User::where('email', $email)->first(); 68 | $user->update([ 69 | 'approved' => 1, 70 | ]); 71 | 72 | return redirect()->route('admin.users.index'); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/Http/Controllers/WebhookController.php: -------------------------------------------------------------------------------- 1 | getContent(); 21 | $githubHash = $request->header('X-Hub-Signature'); 22 | 23 | $localToken = config('app.webhook_secret'); 24 | $localHash = 'sha1=' . hash_hmac('sha1', $githubPayload, $localToken, false); 25 | 26 | if (hash_equals($githubHash, $localHash)) { 27 | $root_path = base_path(); 28 | $process = new Process([$root_path . '/deploy.sh']); 29 | 30 | try { 31 | $process->run(); 32 | 33 | Log::info($process->getOutput()); 34 | } catch (ProcessFailedException $exception) { 35 | Log::error($exception->getMessage()); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/Http/Livewire/Admin/Users/Approval.php: -------------------------------------------------------------------------------- 1 | user->update([ 20 | 'approved' => 1, 21 | ]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/Http/Livewire/Comments.php: -------------------------------------------------------------------------------- 1 | 'required' 16 | ]; 17 | 18 | public function render() 19 | { 20 | $this->recipe->load('comments.user'); 21 | 22 | return view('livewire.comments', [ 23 | 'recipe' => $this->recipe, 24 | ]); 25 | } 26 | 27 | public function addComment() 28 | { 29 | $this->validate(); 30 | 31 | $this->recipe->comments()->create([ 32 | 'comment' => $this->comment, 33 | 'user_id' => \Auth::Id(), 34 | ]); 35 | 36 | $this->comment = ''; 37 | } 38 | 39 | public function deleteComment($id) 40 | { 41 | Comment::find($id)->delete(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/Http/Livewire/Ingredients/Index.php: -------------------------------------------------------------------------------- 1 | ['except' => '']]; 13 | 14 | public function render() 15 | { 16 | $ingredients = Ingredient::with('recipes', 'user')->where('name', 'like', '%' . $this->search . '%')->orderBy('name')->paginate(25); 17 | 18 | return view('livewire.ingredients.index', compact('ingredients')); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Livewire/Menus/Create.php: -------------------------------------------------------------------------------- 1 | days = [ 17 | 'monday', 18 | 'tuesday', 19 | 'wednesday', 20 | 'thursday', 21 | 'friday', 22 | 'saturday', 23 | 'sunday' 24 | ]; 25 | } 26 | 27 | public function render() 28 | { 29 | return view('livewire.menus.create'); 30 | } 31 | 32 | public function randomizeAll() 33 | { 34 | $this->recipeIds = array(); 35 | $this->recipes = array(); 36 | 37 | $ids = Recipe::inRandomOrder() 38 | ->where('category_id', Category::where('name', 'dinner')->first()->id) 39 | ->limit(7) 40 | ->pluck('id') 41 | ->toArray(); 42 | 43 | for ($i = 0; $i < 7; $i++) { 44 | array_push($this->recipeIds, $ids[$i]); 45 | array_push($this->recipes, Recipe::find($this->recipeIds[$i])); 46 | } 47 | } 48 | 49 | public function randomize($day) 50 | { 51 | $recipe = Recipe::inRandomOrder() 52 | ->whereNotIn('id', $this->recipeIds) 53 | ->where('category_id', Category::where('name', 'dinner')->first()->id) 54 | ->first(); 55 | 56 | for ($i = 0; $i < 7; $i++) { 57 | if ( $i == $day ) { 58 | continue; 59 | } 60 | 61 | $this->recipes[$i] = Recipe::find($this->recipes[$i]); 62 | } 63 | 64 | $this->recipes[$day] = $recipe; 65 | $this->recipeIds[$day] = $recipe->id; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/Http/Livewire/Permissions/Index.php: -------------------------------------------------------------------------------- 1 | where('name', 'like', '%' . $this->search . '%')->orderBy('name')->paginate(25); 23 | 24 | return view('livewire.permissions.index', compact('permissions')); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Http/Livewire/Recipes/Index.php: -------------------------------------------------------------------------------- 1 | withCount(['ratings as rating' => function($query) { 17 | $query->select(DB::raw('coalesce(avg(score),0)')); 18 | }])->filter()->paginate(15); 19 | 20 | return view('livewire.recipes.index', compact('recipes')); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Http/Livewire/Recipes/Rate.php: -------------------------------------------------------------------------------- 1 | where('recipe_id', $this->recipeId); 18 | 19 | if ($rating->count()) { 20 | $this->score = $rating->first()->score; 21 | } 22 | 23 | return view('livewire.recipes.rate'); 24 | } 25 | 26 | public function rate($rating) 27 | { 28 | Rating::updateOrCreate( 29 | ['user_id' => Auth::id(), 'recipe_id' => $this->recipeId], 30 | ['score' => $rating] 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/Http/Livewire/Roles/Index.php: -------------------------------------------------------------------------------- 1 | with('users')->where('name', 'like', '%' . $this->search . '%')->paginate(25); 23 | 24 | return view('livewire.roles.index', compact('roles')); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | expectsJson()) { 18 | return route('login'); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | check()) { 26 | return redirect(RouteServiceProvider::HOME); 27 | } 28 | } 29 | 30 | return $next($request); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrimStrings.php: -------------------------------------------------------------------------------- 1 | allSubdomainsOfApplicationUrl(), 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrustProxies.php: -------------------------------------------------------------------------------- 1 | approved) { 21 | 22 | Auth::logout(); 23 | $request->session()->flash('message', 'Account is currently awaiting approval.'); 24 | 25 | return redirect()->route('login'); 26 | } 27 | 28 | return $next($request); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Http/Middleware/ValidateHttpsSignature.php: -------------------------------------------------------------------------------- 1 | keyResolver = function () { 19 | return App::make('config')->get('app.key'); 20 | }; 21 | } 22 | 23 | /** 24 | * Handle an incoming request. 25 | * 26 | * @param \Illuminate\Http\Request $request 27 | * @param \Closure $next 28 | * @return mixed 29 | */ 30 | public function handle(Request $request, Closure $next, $relative = null) 31 | { 32 | if (! is_null(config('app.proxy_scheme')) && config('app.proxy_scheme') == 'https') { 33 | If ($this->hasValidSignature($request)) { 34 | return $next($request); 35 | } 36 | } else { 37 | if ($request->hasValidSignature($relative !== 'relative')) { 38 | return $next($request); 39 | } 40 | } 41 | 42 | throw new InvalidSignatureException; 43 | } 44 | 45 | public function hasValidSignature(Request $request, $absolute = true) 46 | { 47 | $url = $absolute ? $request->url() : '/' . $request->path(); 48 | $url = str_replace('http://', 'https://', $url); 49 | 50 | $original = rtrim($url . '?' . Arr::query( 51 | Arr::except($request->query(), 'signature') 52 | ), '?'); 53 | 54 | $expires = $request->query('expires'); 55 | 56 | $signature = hash_hmac('sha256', $original, call_user_func($this->keyResolver)); 57 | 58 | return hash_equals($signature, (string) $request->query('signature', '')) && ! ($expires && Carbon::now()->getTimestamp() > $expires); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/Http/Middleware/VerifyCsrfToken.php: -------------------------------------------------------------------------------- 1 | 'required', 29 | 'password' => 'required', 30 | 'recaptcha_token' => ['required', new RecaptchaRule($this->recaptcha_token) ], 31 | ]; 32 | } 33 | 34 | /** 35 | * Get the error messages for the defined validation rules. 36 | * 37 | * @return array 38 | */ 39 | public function messages() 40 | { 41 | return [ 42 | 'email.required' => 'An email is required', 43 | 'password.required' => 'A password is required', 44 | ]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/Http/Requests/ChangePasswordRequest.php: -------------------------------------------------------------------------------- 1 | ['required', new CurrentPasswordRule ], 29 | 'password' => ['required', 'confirmed'] 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Http/Requests/StoreAllergenRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | 'name' => 'required', 32 | ]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/Http/Requests/StoreIngredientRequest.php: -------------------------------------------------------------------------------- 1 | 'required|unique:App\Models\Ingredient,name', 31 | ]; 32 | } 33 | 34 | /** 35 | * Get the error messages for the defined validation rules. 36 | * 37 | * @return array 38 | */ 39 | public function messages() 40 | { 41 | return [ 42 | 'name.required' => 'A name is required', 43 | 'name.unique' => 'This Ingredient already exists', 44 | ]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/Http/Requests/StorePermissionRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | ]; 32 | } 33 | 34 | /** 35 | * Get the error messages for the defined validation rules. 36 | * 37 | * @return array 38 | */ 39 | public function messages() 40 | { 41 | return [ 42 | 'name.required' => 'A title is required', 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Http/Requests/StoreRecipeRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | 'servings' => 'required', 32 | 'adults' => 'nullable', 33 | 'kids' => 'nullable', 34 | 'timing' => 'required', 35 | 'instruction' => 'required', 36 | 'category_id' => 'exists:App\Models\Category,id', 37 | 'ingredients' => 'required|array', 38 | ]; 39 | } 40 | 41 | /** 42 | * Get the error messages for the defined validation rules. 43 | * 44 | * @return array 45 | */ 46 | public function messages() 47 | { 48 | return [ 49 | 'name.required' => 'A name is required', 50 | 'servings.required' => 'A Serving quantity is required', 51 | 'adults.required' => 'Please select the recipe is for an adult or not', 52 | 'kids.required' => 'Please select the recipe is for an kid or not', 53 | 'timing.required' => 'A time frame is required', 54 | 'ingredient.required' => 'An ingredient is required', 55 | 'ingredient.array' => 'An ingredient is required', 56 | ]; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/Http/Requests/StoreRegisterRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 29 | 'email' => 'required|unique:App\Models\User,email', 30 | 'password' => 'required|confirmed', 31 | 'recaptcha_token' => ['required', new RecaptchaRule($this->recaptcha_token) ], 32 | ]; 33 | } 34 | 35 | /** 36 | * Get the error messages for the defined validation rules. 37 | * 38 | * @return array 39 | */ 40 | public function messages() 41 | { 42 | return [ 43 | 'name.required' => 'A name is required', 44 | 'email.required' => 'An email is required', 45 | 'email.unique' => 'This email has already been used', 46 | 'password.required' => 'A password is required', 47 | 'password.confirmed' => 'The passwords do not match', 48 | ]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/Http/Requests/StoreRoleRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | ]; 32 | } 33 | 34 | /** 35 | * Get the error messages for the defined validation rules. 36 | * 37 | * @return array 38 | */ 39 | public function messages() 40 | { 41 | return [ 42 | 'name.required' => 'A name is required', 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Http/Requests/StoreServingRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | ]; 32 | } 33 | 34 | /** 35 | * Get the error messages for the defined validation rules. 36 | * 37 | * @return array 38 | */ 39 | public function messages() 40 | { 41 | return [ 42 | 'quantity.required' => 'A quantity is required', 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Http/Requests/StoreTimingRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | ]; 32 | } 33 | 34 | /** 35 | * Get the error messages for the defined validation rules. 36 | * 37 | * @return array 38 | */ 39 | public function messages() 40 | { 41 | return [ 42 | 'timeFrame.required' => 'A time frame is required', 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Http/Requests/StoreUserRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | 'email' => 'required|unique:App\Models\User,email', 32 | 'password' => 'required|confirmed' 33 | ]; 34 | } 35 | 36 | /** 37 | * Get the error messages for the defined validation rules. 38 | * 39 | * @return array 40 | */ 41 | public function messages() 42 | { 43 | return [ 44 | 'name.required' => 'A name is required', 45 | 'email.required' => 'An email is required', 46 | 'email.unique' => 'This email has already been used', 47 | 'password.required' => 'A password is required', 48 | 'password.confirmed' => 'The passwords do not match', 49 | ]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/Http/Requests/UpdateAccountRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 28 | 'email' => 'email:rfc,dns' 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Http/Requests/UpdateAllergenRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | 'name' => 'required', 32 | ]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/Http/Requests/UpdateIngredientRequest.php: -------------------------------------------------------------------------------- 1 | 'required|unique:App\Models\Ingredient,name', 31 | ]; 32 | } 33 | 34 | /** 35 | * Get the error messages for the defined validation rules. 36 | * 37 | * @return array 38 | */ 39 | public function messages() 40 | { 41 | return [ 42 | 'name.required' => 'A name is required', 43 | 'name.unique' => 'This Ingredient already exists', 44 | ]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/Http/Requests/UpdatePermissionRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | ]; 32 | } 33 | 34 | /** 35 | * Get the error messages for the defined validation rules. 36 | * 37 | * @return array 38 | */ 39 | public function messages() 40 | { 41 | return [ 42 | 'name.required' => 'A title is required', 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Http/Requests/UpdateRecipeRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | 'servings' => 'required', 32 | 'adults' => 'nullable', 33 | 'kids' => 'nullable', 34 | 'timing' => 'required', 35 | 'category_id' => 'exists:App\Models\Category,id', 36 | 'instruction' => 'required' 37 | ]; 38 | } 39 | 40 | /** 41 | * Get the error messages for the defined validation rules. 42 | * 43 | * @return array 44 | */ 45 | public function messages() 46 | { 47 | return [ 48 | 'name.required' => 'A name is required', 49 | 'servings.required' => 'A Serving quantity is required', 50 | 'timing.required' => 'A time frame is required', 51 | 'instruction.required' => 'Instructions are required', 52 | ]; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/Http/Requests/UpdateRoleRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | ]; 32 | } 33 | 34 | /** 35 | * Get the error messages for the defined validation rules. 36 | * 37 | * @return array 38 | */ 39 | public function messages() 40 | { 41 | return [ 42 | 'name.required' => 'A name is required', 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Http/Requests/UpdateServingRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | ]; 32 | } 33 | 34 | /** 35 | * Get the error messages for the defined validation rules. 36 | * 37 | * @return array 38 | */ 39 | public function messages() 40 | { 41 | return [ 42 | 'quantity.required' => 'A quantity is required', 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Http/Requests/UpdateTimingRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | ]; 32 | } 33 | 34 | /** 35 | * Get the error messages for the defined validation rules. 36 | * 37 | * @return array 38 | */ 39 | public function messages() 40 | { 41 | return [ 42 | 'timeFrame.required' => 'A time frame is required', 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Http/Requests/UpdateUserRequest.php: -------------------------------------------------------------------------------- 1 | 'required', 31 | 'email' => 'required|unique:App\Models\User,email', 32 | 'password' => 'nullable|confirmed' 33 | ]; 34 | } 35 | 36 | /** 37 | * Get the error messages for the defined validation rules. 38 | * 39 | * @return array 40 | */ 41 | public function messages() 42 | { 43 | return [ 44 | 'name.required' => 'A name is required', 45 | 'email.required' => 'An email is required', 46 | 'email.unique' => 'This email has already been used', 47 | 'password.confirmed' => 'The passwords do not match', 48 | ]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/Listeners/SendNewUserNotification.php: -------------------------------------------------------------------------------- 1 | users; 33 | 34 | Notification::send($admins, new ApproveUser($event->user)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/Listeners/SendUserApprovedNotification.php: -------------------------------------------------------------------------------- 1 | user->notify(new ApprovedUser($event->user)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Models/Allergen.php: -------------------------------------------------------------------------------- 1 | belongsToMany(Recipe::class); 20 | } 21 | 22 | public static function getTableName(): string 23 | { 24 | return (new self())->getTable(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Models/Category.php: -------------------------------------------------------------------------------- 1 | hasMany(Recipe::class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Models/Comment.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class); 21 | } 22 | 23 | public function getCreatedAtAttribute($value): string 24 | { 25 | return \Carbon\Carbon::parse($value)->format('j<\s\up>S F Y'); 26 | } 27 | 28 | public static function getTableName(): string 29 | { 30 | return (new self())->getTable(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Models/Ingredient.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class); 21 | } 22 | 23 | public function recipes() 24 | { 25 | return $this->belongsToMany(Recipe::class); 26 | } 27 | 28 | public function path(): string 29 | { 30 | return route('admin.ingredients.show', [$this->slug]); 31 | } 32 | 33 | public function getRouteKeyName(): string 34 | { 35 | return 'slug'; 36 | } 37 | 38 | public function setNameAttribute($value): void 39 | { 40 | if ($value) { 41 | $this->attributes['name'] = ucwords($value); 42 | $this->attributes['slug'] = Str::slug($value, '-'); 43 | } 44 | } 45 | 46 | public static function getTableName(): string 47 | { 48 | return (new self())->getTable(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/Models/Menu.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class); 16 | } 17 | 18 | public function recipes() 19 | { 20 | return $this->belongsToMany(Recipe::class)->withPivot('date'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Models/Rating.php: -------------------------------------------------------------------------------- 1 | belongsTo(Recipe::class); 21 | } 22 | 23 | public static function getTableName(): string 24 | { 25 | return (new self())->getTable(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Models/TempFile.php: -------------------------------------------------------------------------------- 1 | email = Crypt::encrypt($user->email); 26 | } 27 | 28 | /** 29 | * Get the notification's delivery channels. 30 | * 31 | * @param mixed $notifiable 32 | * @return array 33 | */ 34 | public function via($notifiable) 35 | { 36 | return ['mail']; 37 | } 38 | 39 | /** 40 | * Get the mail representation of the notification. 41 | * 42 | * @param mixed $notifiable 43 | * @return \Illuminate\Notifications\Messages\MailMessage 44 | */ 45 | public function toMail($notifiable) 46 | { 47 | return (new MailMessage) 48 | ->subject('New User awaiting approval') 49 | ->greeting('Hello Admin!') 50 | ->line('A new user has registered, and is awaiting approval.') 51 | ->action('Approve User', url(route('admin.users.approve', $this->email))); 52 | } 53 | 54 | /** 55 | * Get the array representation of the notification. 56 | * 57 | * @param mixed $notifiable 58 | * @return array 59 | */ 60 | public function toArray($notifiable) 61 | { 62 | return [ 63 | // 64 | ]; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/Notifications/ApprovedUser.php: -------------------------------------------------------------------------------- 1 | user = $user; 25 | } 26 | 27 | /** 28 | * Get the notification's delivery channels. 29 | * 30 | * @param mixed $notifiable 31 | * @return array 32 | */ 33 | public function via($notifiable) 34 | { 35 | return ['mail']; 36 | } 37 | 38 | /** 39 | * Get the mail representation of the notification. 40 | * 41 | * @param mixed $notifiable 42 | * @return \Illuminate\Notifications\Messages\MailMessage 43 | */ 44 | public function toMail($notifiable) 45 | { 46 | return (new MailMessage) 47 | ->subject('Your account has been approved') 48 | ->greeting('Hello ' . $this->user->name . ',') 49 | ->line('Your account has been approved, you can now access Mealing') 50 | ->action('Login Now', url('/')); 51 | } 52 | 53 | /** 54 | * Get the array representation of the notification. 55 | * 56 | * @param mixed $notifiable 57 | * @return array 58 | */ 59 | public function toArray($notifiable) 60 | { 61 | return [ 62 | // 63 | ]; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | 'App\Policies\ModelPolicy', 17 | ]; 18 | 19 | /** 20 | * Register any authentication / authorization services. 21 | * 22 | * @return void 23 | */ 24 | public function boot() 25 | { 26 | $this->registerPolicies(); 27 | 28 | // Implicitly grant "Super Admin" role all permissions 29 | // This works in the app by using gate-related functions like auth()->user->can() and @can() 30 | Gate::before(function ($user, $ability) { 31 | return $user->hasRole('Super Admin') ? true : null; 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 23 | SendEmailVerificationNotification::class, 24 | ], 25 | UserVerified::class => [ 26 | SendNewUserNotification::class, 27 | ], 28 | UserApproved::class => [ 29 | SendUserApprovedNotification::class, 30 | ], 31 | ]; 32 | 33 | /** 34 | * Register any events for your application. 35 | * 36 | * @return void 37 | */ 38 | public function boot() 39 | { 40 | // 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/Providers/FortifyServiceProvider.php: -------------------------------------------------------------------------------- 1 | view('auth.login')); 32 | Fortify::registerView(fn () => view('auth.register')); 33 | Fortify::requestPasswordResetLinkView(fn () => view('auth.forgot-password')); 34 | Fortify::resetPasswordView(function ($request) { 35 | return view('auth.reset-password', ['request' => $request]); 36 | }); 37 | Fortify::verifyEmailView(fn () => view('auth.verify-email')); 38 | Fortify::confirmPasswordView(fn () => view('user.confirm-password')); 39 | Fortify::twoFactorChallengeView(fn () => view('auth.two-factor-challenge')); 40 | 41 | Fortify::createUsersUsing(CreateNewUser::class); 42 | Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class); 43 | Fortify::updateUserPasswordsUsing(UpdateUserPassword::class); 44 | Fortify::resetUserPasswordsUsing(ResetUserPassword::class); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | configureRateLimiting(); 39 | 40 | $this->routes(function () { 41 | Route::prefix('api') 42 | ->middleware('api') 43 | ->namespace($this->namespace) 44 | ->group(base_path('routes/api.php')); 45 | 46 | Route::middleware('web') 47 | ->namespace($this->namespace) 48 | ->group(base_path('routes/web.php')); 49 | }); 50 | } 51 | 52 | /** 53 | * Configure the rate limiters for the application. 54 | * 55 | * @return void 56 | */ 57 | protected function configureRateLimiting() 58 | { 59 | RateLimiter::for('api', function (Request $request) { 60 | return Limit::perMinute(60); 61 | }); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/Rules/CurrentPasswordRule.php: -------------------------------------------------------------------------------- 1 | password); 31 | } 32 | 33 | /** 34 | * Get the validation error message. 35 | * 36 | * @return string 37 | */ 38 | public function message() 39 | { 40 | return 'Users details don\'t match our records.'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/Rules/RecaptchaRule.php: -------------------------------------------------------------------------------- 1 | errors = ':atrribute field is required'; 39 | 40 | return false; 41 | } 42 | 43 | $recaptcha = new Recaptcha(config('recaptcha.secret_key')); 44 | 45 | $resp = $recaptcha->setExpectedHostname($_SERVER['HTTP_HOST']) 46 | ->setScoreThreshold($rScore) 47 | ->verify($value, $_SERVER['REMOTE_ADDR']); 48 | 49 | if (!$resp->isSuccess()) { 50 | $this->errors = $resp->getErrorCodes(); 51 | 52 | return false; 53 | } 54 | 55 | if ($resp->getScore() < $rScore) { 56 | $this->errors = 'Failed to valildate ReCaptcha'; 57 | 58 | return false; 59 | } 60 | 61 | return true; 62 | } 63 | 64 | /** 65 | * Get the validation error message. 66 | * 67 | * @return string 68 | */ 69 | public function message() 70 | { 71 | return $this->errors; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | make(Illuminate\Contracts\Console\Kernel::class); 34 | 35 | $status = $kernel->handle( 36 | $input = new Symfony\Component\Console\Input\ArgvInput, 37 | new Symfony\Component\Console\Output\ConsoleOutput 38 | ); 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Shutdown The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once Artisan has finished running, we will fire off the shutdown events 46 | | so that any final work may be done by the application before we shut 47 | | down the process. This is the last thing to happen to the request. 48 | | 49 | */ 50 | 51 | $kernel->terminate($input, $status); 52 | 53 | exit($status); 54 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | singleton( 30 | Illuminate\Contracts\Http\Kernel::class, 31 | App\Http\Kernel::class 32 | ); 33 | 34 | $app->singleton( 35 | Illuminate\Contracts\Console\Kernel::class, 36 | App\Console\Kernel::class 37 | ); 38 | 39 | $app->singleton( 40 | Illuminate\Contracts\Debug\ExceptionHandler::class, 41 | App\Exceptions\Handler::class 42 | ); 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Return The Application 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This script returns the application instance. The instance is given to 50 | | the calling script so we can separate the building of the instances 51 | | from the actual running of the application and sending responses. 52 | | 53 | */ 54 | 55 | return $app; 56 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /config/broadcasting.php: -------------------------------------------------------------------------------- 1 | env('BROADCAST_DRIVER', 'null'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Broadcast Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the broadcast connections that will be used 26 | | to broadcast events to other systems or over websockets. Samples of 27 | | each available type of connection are provided inside this array. 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'pusher' => [ 34 | 'driver' => 'pusher', 35 | 'key' => env('PUSHER_APP_KEY'), 36 | 'secret' => env('PUSHER_APP_SECRET'), 37 | 'app_id' => env('PUSHER_APP_ID'), 38 | 'options' => [ 39 | 'cluster' => env('PUSHER_APP_CLUSTER'), 40 | 'useTLS' => true, 41 | ], 42 | ], 43 | 44 | 'redis' => [ 45 | 'driver' => 'redis', 46 | 'connection' => 'default', 47 | ], 48 | 49 | 'log' => [ 50 | 'driver' => 'log', 51 | ], 52 | 53 | 'null' => [ 54 | 'driver' => 'null', 55 | ], 56 | 57 | ], 58 | 59 | ]; 60 | -------------------------------------------------------------------------------- /config/cors.php: -------------------------------------------------------------------------------- 1 | ['api/*'], 19 | 20 | 'allowed_methods' => ['*'], 21 | 22 | 'allowed_origins' => ['*'], 23 | 24 | 'allowed_origins_patterns' => [], 25 | 26 | 'allowed_headers' => ['*'], 27 | 28 | 'exposed_headers' => [], 29 | 30 | 'max_age' => 0, 31 | 32 | 'supports_credentials' => false, 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /config/hashing.php: -------------------------------------------------------------------------------- 1 | 'bcrypt', 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Bcrypt Options 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may specify the configuration options that should be used when 26 | | passwords are hashed using the Bcrypt algorithm. This will allow you 27 | | to control the amount of time it takes to hash the given password. 28 | | 29 | */ 30 | 31 | 'bcrypt' => [ 32 | 'rounds' => env('BCRYPT_ROUNDS', 10), 33 | ], 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Argon Options 38 | |-------------------------------------------------------------------------- 39 | | 40 | | Here you may specify the configuration options that should be used when 41 | | passwords are hashed using the Argon algorithm. These will allow you 42 | | to control the amount of time it takes to hash the given password. 43 | | 44 | */ 45 | 46 | 'argon' => [ 47 | 'memory' => 1024, 48 | 'threads' => 2, 49 | 'time' => 2, 50 | ], 51 | 52 | ]; 53 | -------------------------------------------------------------------------------- /config/recaptcha.php: -------------------------------------------------------------------------------- 1 | env('RECAPTCHA_SITE_KEY', ''), 15 | 16 | /* 17 | |-------------------------------------------------------------------------- 18 | | Secret Key 19 | |-------------------------------------------------------------------------- 20 | | 21 | | The secret key authorizes communication between your application backend 22 | | and the reCAPTCHA server. 23 | | 24 | */ 25 | 26 | 'secret_key' => env('RECAPTCHA_SECRET_KEY', ''), 27 | 28 | /* 29 | |-------------------------------------------------------------------------- 30 | | PHPUnit testing 31 | |-------------------------------------------------------------------------- 32 | | 33 | | During testing recaptcha cannot be used, setting this to true will skip 34 | | the recaptcha rule and return true. 35 | | 36 | */ 37 | 38 | 'testing' => env('RECAPTCHA_PHPUNIT_TESTS', false), 39 | ]; 40 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), 21 | ], 22 | 23 | 'postmark' => [ 24 | 'token' => env('POSTMARK_TOKEN'), 25 | ], 26 | 27 | 'ses' => [ 28 | 'key' => env('AWS_ACCESS_KEY_ID'), 29 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 30 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 31 | ], 32 | 33 | ]; 34 | -------------------------------------------------------------------------------- /config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | resource_path('views'), 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled View Path 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This option determines where all the compiled Blade templates will be 26 | | stored for your application. Typically, this is within the storage 27 | | directory. However, as usual, you are free to change this value. 28 | | 29 | */ 30 | 31 | 'compiled' => env( 32 | 'VIEW_COMPILED_PATH', 33 | realpath(storage_path('framework/views')) 34 | ), 35 | 36 | ]; 37 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | *.sqlite-journal 3 | -------------------------------------------------------------------------------- /database/factories/AllergenFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->word, 26 | 'name' => $this->faker->word, 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /database/factories/IngredientFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->name, 26 | 'user_id' => \App\Models\User::factory()->create()->id, 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /database/factories/PermissionFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->lexify('???'), 26 | ]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /database/factories/RecipeFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->name, 26 | 'servings' => $this->faker->randomDigit, 27 | 'adults' => $this->faker->boolean, 28 | 'kids' => $this->faker->boolean, 29 | 'timing' => $this->faker->randomDigit, 30 | 'user_id' => \App\Models\User::factory()->create()->id, 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/factories/RoleFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->word, 26 | ]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->name, 27 | 'email' => $this->faker->unique()->freeEmail, 28 | 'email_verified_at' => now(), 29 | 'approved' => 1, 30 | 'password' => 'password', 31 | 'remember_token' => Str::random(10), 32 | ]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('name'); 19 | $table->string('email')->unique(); 20 | $table->timestamp('email_verified_at')->nullable(); 21 | $table->string('password'); 22 | $table->rememberToken(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('users'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_100000_create_password_resets_table.php: -------------------------------------------------------------------------------- 1 | string('email')->index(); 18 | $table->string('token'); 19 | $table->timestamp('created_at')->nullable(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::dropIfExists('password_resets'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_200000_add_two_factor_columns_to_users_table.php: -------------------------------------------------------------------------------- 1 | text('two_factor_secret') 18 | ->after('password') 19 | ->nullable(); 20 | 21 | $table->text('two_factor_recovery_codes') 22 | ->after('two_factor_secret') 23 | ->nullable(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::table('users', function (Blueprint $table) { 35 | $table->dropColumn('two_factor_secret', 'two_factor_recovery_codes'); 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /database/migrations/2020_10_25_222033_create_servings_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('quantity'); 19 | $table->timestamps(); 20 | $table->softDeletes(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('servings'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2020_10_25_222141_create_ingredients_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('name'); 19 | $table->timestamps(); 20 | $table->softDeletes(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('ingredients'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2020_10_25_222225_create_meals_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('name'); 19 | $table->string('servings'); 20 | $table->boolean('adults'); 21 | $table->boolean('kids'); 22 | $table->string('timing'); 23 | $table->timestamps(); 24 | $table->softDeletes(); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('meals'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/2020_11_01_212244_create_roles_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('title'); 19 | $table->timestamps(); 20 | $table->softDeletes(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('roles'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2020_11_01_214314_create_role_user_pivot_table.php: -------------------------------------------------------------------------------- 1 | foreignId('role_id')->constrained()->onDelete('cascade'); 18 | $table->foreignId('user_id')->constrained()->onDelete('cascade'); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::dropIfExists('role_user'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2020_11_04_081508_create_permissions_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('title'); 19 | $table->timestamps(); 20 | $table->softDeletes(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('permissions'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2020_11_05_082345_add_description_to_roles_table.php: -------------------------------------------------------------------------------- 1 | string('description')->after('title')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('roles', function (Blueprint $table) { 29 | // 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2020_11_05_155823_create_permission_role_pivot_table.php: -------------------------------------------------------------------------------- 1 | foreignId('permission_id')->constrained()->onDelete('cascade');; 18 | $table->foreignId('role_id')->constrained()->onDelete('cascade');; 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::dropIfExists('permission_role'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2020_11_09_005625_add_softdelete_to_users_table.php: -------------------------------------------------------------------------------- 1 | softDeletes(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('users', function (Blueprint $table) { 29 | // 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2020_11_17_231250_add_new_user_to_users_table.php: -------------------------------------------------------------------------------- 1 | run(); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | // 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /database/migrations/2020_11_24_220553_create_ingredient_meal_pivot_table.php: -------------------------------------------------------------------------------- 1 | foreignId('ingredient_id')->constrained()->onDelete('cascade'); 18 | $table->foreignId('meal_id')->constrained()->onDelete('cascade'); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::dropIfExists('ingredient_meal'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2020_11_27_232411_add_user_id_to_ingredients_table.php: -------------------------------------------------------------------------------- 1 | foreignId('user_id') 18 | ->nullable() 19 | ->after('name') 20 | ->constrained() 21 | ->onDelete('cascade'); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::table('ingredients', function (Blueprint $table) { 33 | $table->dropForeign(['user_id']); 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/migrations/2020_11_29_220043_add_user_id_to_meals_table.php: -------------------------------------------------------------------------------- 1 | foreignId('user_id') 18 | ->nullable() 19 | ->after('timing') 20 | ->constrained() 21 | ->onDelete('cascade'); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | if (Schema::hasTable('meals')) { 33 | Schema::table('meals', function (Blueprint $table) { 34 | $table->dropColumn('user_id'); 35 | }); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /database/migrations/2020_11_30_201655_add_slug_to_ingredients_table.php: -------------------------------------------------------------------------------- 1 | string('slug') 18 | ->nullable() 19 | ->after('name'); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::table('ingredients', function (Blueprint $table) { 31 | $table->dropColumn('slug'); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2020_11_30_230801_add_slug_to_meals_table.php: -------------------------------------------------------------------------------- 1 | string('slug') 18 | ->nullable() 19 | ->after('name'); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | if (Schema::hasTable('meals')) { 31 | Schema::table('meals', function (Blueprint $table) { 32 | $table->dropColumn('slug'); 33 | }); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/migrations/2020_12_01_232336_create_ratings_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('user_id')->constrained()->onDelete('cascade'); 19 | $table->foreignId('meal_id')->constrained()->onDelete('cascade'); 20 | $table->integer('score'); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists('ratings'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2020_12_03_144041_add_instructions_to_meals_table.php: -------------------------------------------------------------------------------- 1 | text('instruction') 18 | ->nullable() 19 | ->after('timing'); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::table('meals', function (Blueprint $table) { 31 | // 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2020_12_03_150502_add_quantity_to_ingredient_meal_pivot_table.php: -------------------------------------------------------------------------------- 1 | string('quantity')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('ingredient_meal', function (Blueprint $table) { 29 | // 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2020_12_04_143323_create_comments_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('user_id')->constrained()->onDelete('cascade'); 19 | $table->foreignId('meal_id')->constrained()->onDelete('cascade'); 20 | $table->text('comment'); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists('comments'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2021_02_23_195758_create_allergens_table.php: -------------------------------------------------------------------------------- 1 | id(); 19 | $table->string('name'); 20 | $table->string('icon'); 21 | $table->timestamps(); 22 | }); 23 | 24 | $seeder = new AllergenSeeder; 25 | $seeder->run(); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('allergens'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/2021_02_25_084615_create_allergen_meal_pivot_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('allergen_id')->constrained()->onDelete('cascade'); 19 | $table->foreignId('meal_id')->constrained()->onDelete('cascade'); 20 | $table->string('level'); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('allergen_meal_pivot'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2021_03_04_193042_remove_id_from_allergen_meal_table.php: -------------------------------------------------------------------------------- 1 | dropColumn('id'); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('allergen_meal', function (Blueprint $table) { 29 | // 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2021_03_06_185744_remove_old_role_permissions.php: -------------------------------------------------------------------------------- 1 | run(); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | // 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /database/migrations/2021_03_09_135510_create_patches_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('patch'); 19 | $table->integer('batch'); 20 | $table->json('log')->nullable(); 21 | $table->timestamp('ran_on'); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists('patches'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2021_03_19_191353_seed_admin_permissions.php: -------------------------------------------------------------------------------- 1 | run(); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | // 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /database/migrations/2021_03_23_191703_create_media_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 13 | 14 | $table->morphs('model'); 15 | $table->uuid('uuid')->nullable()->unique(); 16 | $table->string('collection_name'); 17 | $table->string('name'); 18 | $table->string('file_name'); 19 | $table->string('mime_type')->nullable(); 20 | $table->string('disk'); 21 | $table->string('conversions_disk')->nullable(); 22 | $table->unsignedBigInteger('size'); 23 | $table->json('manipulations'); 24 | $table->json('custom_properties'); 25 | $table->json('generated_conversions'); 26 | $table->json('responsive_images'); 27 | $table->unsignedInteger('order_column')->nullable(); 28 | 29 | $table->nullableTimestamps(); 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2021_03_24_193950_add_approved_to_users_table.php: -------------------------------------------------------------------------------- 1 | boolean('approved')->default(0)->after('email_verified_at'); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('users', function (Blueprint $table) { 29 | // 30 | }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2021_05_21_184605_add_menu_permissions.php: -------------------------------------------------------------------------------- 1 | run(); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | // 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /database/migrations/2021_05_22_141649_create_menus_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('user_id')->constrained(); 19 | $table->timestamps(); 20 | $table->softDeletes(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('menus'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2021_05_22_142218_create_meal_menu_pivot_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('menu_id')->constrained(); 19 | $table->foreignId('meal_id')->constrained(); 20 | $table->date('date'); 21 | $table->timestamps(); 22 | $table->softDeletes(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('meal_menu'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/migrations/2021_05_31_142157_add_admin_to_super_admins.php: -------------------------------------------------------------------------------- 1 | first(); 18 | $admin->assignRole('Super Admin'); 19 | 20 | DB::table('users') 21 | ->where('id', 1) 22 | ->update(['approved' => 1]); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | // 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2021_06_09_191108_create_temp_files_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('folder'); 19 | $table->string('filename'); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('temp_files'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2021_06_13_174348_change_name_to_unique_on_meals_table.php: -------------------------------------------------------------------------------- 1 | string('name')->unique()->change(); 18 | $table->string('slug')->unique()->change(); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::table('meals', function (Blueprint $table) { 30 | // 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2021_06_23_223742_create_likes_table.php: -------------------------------------------------------------------------------- 1 | foreignId('meal_id'); 18 | $table->foreignId('user_id'); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::dropIfExists('likes'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2021_06_27_190328_create_categories_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('name'); 19 | $table->timestamps(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::dropIfExists('categories'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2021_06_27_190602_seed_category_seeder.php: -------------------------------------------------------------------------------- 1 | run(); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | // 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /database/migrations/2021_06_28_181444_rename_meals_table_to_recipe_table.php: -------------------------------------------------------------------------------- 1 | renameColumn('meal_id', 'recipe_id'); 24 | }); 25 | 26 | Schema::table('allergen_recipe', function (Blueprint $table) { 27 | $table->renameColumn('meal_id', 'recipe_id'); 28 | }); 29 | 30 | Schema::table('ingredient_recipe', function (Blueprint $table) { 31 | $table->renameColumn('meal_id', 'recipe_id'); 32 | }); 33 | 34 | Schema::table('likes', function (Blueprint $table) { 35 | $table->renameColumn('meal_id', 'recipe_id'); 36 | }); 37 | 38 | Schema::table('comments', function (Blueprint $table) { 39 | $table->renameColumn('meal_id', 'recipe_id'); 40 | }); 41 | 42 | Schema::table('ratings', function (Blueprint $table) { 43 | $table->renameColumn('meal_id', 'recipe_id'); 44 | }); 45 | } 46 | 47 | /** 48 | * Reverse the migrations. 49 | * 50 | * @return void 51 | */ 52 | public function down() 53 | { 54 | DB::statement('SET FOREIGN_KEY_CHECKS = 0'); 55 | Schema::dropIfExists('recipes'); 56 | Schema::dropIfExists('allergen_recipe'); 57 | Schema::dropIfExists('ingredient_recipe'); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /database/migrations/2021_06_28_234507_fix_model_type_in_media_table.php: -------------------------------------------------------------------------------- 1 | where('model_type', 'App\Models\Meal') 19 | ->update(['model_type' => 'App\Models\Recipe' ]); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | // 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /database/migrations/2021_07_01_191325_add_category_id_to_recipes_table.php: -------------------------------------------------------------------------------- 1 | foreignId('category_id') 18 | ->nullable() 19 | ->after('user_id') 20 | ->nullable() 21 | ->constrained(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::table('recipes', function (Blueprint $table) { 33 | // 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/seeders/AdminPermissionSeeder.php: -------------------------------------------------------------------------------- 1 | 'admin_access']); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /database/seeders/AdminUserSeeder.php: -------------------------------------------------------------------------------- 1 | 'Admin User', 20 | 'email' => 'admin@example.com', 21 | 'password' => 'password', 22 | 'email_verified_at' => Carbon::now(), 23 | ]); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /database/seeders/CategorySeeder.php: -------------------------------------------------------------------------------- 1 | $category, 27 | ]); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | call([ 17 | // // 18 | // ]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /database/seeders/MenuPermissionSeeder.php: -------------------------------------------------------------------------------- 1 | $permission 31 | ]); 32 | 33 | $user->givePermissionTo($permission); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd ../ 4 | 5 | php artisan down 6 | 7 | git reset HEAD^ 8 | git reset --hard 9 | git pull 10 | 11 | export COMPOSER_HOME='/tmp/composer' 12 | composer install --no-interaction --no-dev --prefer-dist 13 | 14 | php artisan cache:clear 15 | php artisan config:clear 16 | php artisan config:cache 17 | php artisan -v queue:restart 18 | php artisan migrate --force 19 | php artisan up 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 6 | "watch": "npm run development -- --watch", 7 | "watch-poll": "npm run watch -- --watch-poll", 8 | "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --disable-host-check --config=node_modules/laravel-mix/setup/webpack.config.js", 9 | "prod": "npm run production", 10 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 11 | }, 12 | "devDependencies": { 13 | "axios": "^0.19", 14 | "cross-env": "^7.0.3", 15 | "laravel-mix": "^5.0.1", 16 | "lodash": "^4.17.21", 17 | "resolve-url-loader": "^3.1.3", 18 | "sass": "^1.34.0", 19 | "sass-loader": "^8.0.2", 20 | "vue-template-compiler": "^2.6.13" 21 | }, 22 | "dependencies": { 23 | "@tailwindcss/forms": "^0.3.2", 24 | "@tailwindcss/typography": "^0.4.1", 25 | "alpinejs": "^3.2.1", 26 | "autoprefixer": "^9.8.6", 27 | "filepond": "^4.28.1", 28 | "postcss": "^7.0.35", 29 | "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.1.3" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | ./tests/Unit 10 | 11 | 12 | ./tests/Feature 13 | 14 | 15 | 16 | 17 | ./app 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinByrne/Mealing/79d82af817d7b58e235811a2ddb53420cc452c7a/public/favicon.ico -------------------------------------------------------------------------------- /public/fonts/erudus.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinByrne/Mealing/79d82af817d7b58e235811a2ddb53420cc452c7a/public/fonts/erudus.eot -------------------------------------------------------------------------------- /public/fonts/erudus.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinByrne/Mealing/79d82af817d7b58e235811a2ddb53420cc452c7a/public/fonts/erudus.ttf -------------------------------------------------------------------------------- /public/fonts/erudus.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinByrne/Mealing/79d82af817d7b58e235811a2ddb53420cc452c7a/public/fonts/erudus.woff -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class); 50 | 51 | $response = tap($kernel->handle( 52 | $request = Request::capture() 53 | ))->send(); 54 | 55 | $kernel->terminate($request, $response); 56 | -------------------------------------------------------------------------------- /public/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/js/app.js": "/js/app.js", 3 | "/css/icons.css": "/css/icons.css", 4 | "/css/app.css": "/css/app.css" 5 | } 6 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /public/web.config: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /resources/css/app.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss/base"; 2 | @import "tailwindcss/components"; 3 | @import "tailwindcss/utilities"; 4 | @import "~filepond/dist/filepond.min.css"; 5 | 6 | [x-cloak] { 7 | display: none !important; 8 | } 9 | .rating { 10 | unicode-bidi: bidi-override; 11 | direction: rtl; 12 | } 13 | .rating > i:hover:before, 14 | .rating > i:hover ~ i:before{ 15 | @apply font-black; 16 | } -------------------------------------------------------------------------------- /resources/fonts/vendor/erudus/erudus.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinByrne/Mealing/79d82af817d7b58e235811a2ddb53420cc452c7a/resources/fonts/vendor/erudus/erudus.eot -------------------------------------------------------------------------------- /resources/fonts/vendor/erudus/erudus.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinByrne/Mealing/79d82af817d7b58e235811a2ddb53420cc452c7a/resources/fonts/vendor/erudus/erudus.ttf -------------------------------------------------------------------------------- /resources/fonts/vendor/erudus/erudus.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinByrne/Mealing/79d82af817d7b58e235811a2ddb53420cc452c7a/resources/fonts/vendor/erudus/erudus.woff -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- 1 | import * as FilePond from 'filepond'; 2 | import Alpine from 'alpinejs'; 3 | 4 | window.Alpine = Alpine; 5 | window.FilePond = FilePond; 6 | 7 | Alpine.start(); -------------------------------------------------------------------------------- /resources/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | window._ = require('lodash'); 2 | 3 | /** 4 | * We'll load the axios HTTP library which allows us to easily issue requests 5 | * to our Laravel back-end. This library automatically handles sending the 6 | * CSRF token as a header based on the value of the "XSRF" token cookie. 7 | */ 8 | 9 | window.axios = require('axios'); 10 | 11 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 12 | 13 | /** 14 | * Echo exposes an expressive API for subscribing to channels and listening 15 | * for events that are broadcast by Laravel. Echo and event broadcasting 16 | * allows your team to easily build robust real-time web applications. 17 | */ 18 | 19 | // import Echo from 'laravel-echo'; 20 | 21 | // window.Pusher = require('pusher-js'); 22 | 23 | // window.Echo = new Echo({ 24 | // broadcaster: 'pusher', 25 | // key: process.env.MIX_PUSHER_APP_KEY, 26 | // cluster: process.env.MIX_PUSHER_APP_CLUSTER, 27 | // forceTLS: true 28 | // }); 29 | -------------------------------------------------------------------------------- /resources/lang/en/auth.php: -------------------------------------------------------------------------------- 1 | 'These credentials do not match our records.', 17 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /resources/lang/en/pagination.php: -------------------------------------------------------------------------------- 1 | '« Previous', 17 | 'next' => 'Next »', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /resources/lang/en/passwords.php: -------------------------------------------------------------------------------- 1 | 'Your password has been reset!', 17 | 'sent' => 'We have emailed your password reset link!', 18 | 'throttled' => 'Please wait before retrying.', 19 | 'token' => 'This password reset token is invalid.', 20 | 'user' => "We can't find a user with that email address.", 21 | 22 | ]; 23 | -------------------------------------------------------------------------------- /resources/views/admin/allergens/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layout') 2 | 3 | @section('title', 'Create New Allergen') 4 | 5 | @section('admin.content') 6 |
7 |

8 | Create new Allergen 9 |

10 |
11 |
12 | @csrf 13 |
14 | 17 | 18 |
19 |
20 | 23 | 24 |
25 |
26 | 29 |
30 |
31 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/allergens/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layout') 2 | 3 | @section('title', 'Edit - ' . $allergen->name) 4 | 5 | @section('admin.content') 6 |
7 |

8 | Edit Allergen - {{ $allergen->name }} 9 |

10 |
11 |
12 | @csrf 13 | @method('PATCH') 14 |
15 | 18 | 19 |
20 |
21 | 24 | 25 |
26 |
27 | 30 |
31 |
32 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/allergens/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layout') 2 | 3 | @section('title', 'Allergens') 4 | 5 | @section('admin.content') 6 |
7 |

8 | All Allergens 9 |

10 |
11 |
12 | 13 | 14 | 15 | 18 | 21 | 22 | 23 | 24 | @foreach ($allergens as $allergen) 25 | @if ($loop->odd) 26 | 27 | @else 28 | 29 | @endif 30 | 35 | 46 | 47 | @endforeach 48 | 49 |
31 | 32 | {{ $allergen->name }} 33 | 34 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
50 |
51 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/dashboard.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layout') 2 | 3 | @section('title', 'Admin Dashboard') 4 | 5 | @section('admin.content') 6 |
7 |
8 |

9 | {{ $users }} 10 |

11 |

12 | Users 13 |

14 |
15 |
16 |

17 | {{ $recipes }} 18 |

19 |

20 | Recipes 21 |

22 |
23 |
24 |

25 | {{ $ingredients }} 26 |

27 |

28 | Ingredients 29 |

30 |
31 |
32 |

33 | 0 34 |

35 |

36 | TBC 37 |

38 |
39 |
40 | @endsection 41 | -------------------------------------------------------------------------------- /resources/views/admin/ingredients/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layout') 2 | 3 | @section('title', 'Create New Ingrident') 4 | 5 | @section('admin.content') 6 |
7 |

8 | Create new Ingredient 9 |

10 |
11 |
12 | @csrf 13 |
14 | 17 | 18 |
19 |
20 | 23 |
24 |
25 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/ingredients/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layout') 2 | 3 | @section('title', 'Edit - ' . $ingredient->name) 4 | 5 | @section('admin.content') 6 |
7 |

8 | Edit Ingredient - {{ $ingredient->name }} 9 |

10 |
11 |
12 | @csrf 13 | @method('PATCH') 14 |
15 | 18 | 19 |
20 |
21 | 24 |
25 |
26 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/ingredients/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layout') 2 | 3 | @section('title', 'Ingredients') 4 | 5 | @section('admin.content') 6 | @livewire('ingredients.index') 7 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/permissions/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layout') 2 | 3 | @section('title', 'Permissions') 4 | 5 | @section('admin.content') 6 | @livewire('permissions.index') 7 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/permissions/show.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layout') 2 | 3 | @section('title', $permission->name) 4 | 5 | @section('admin.content') 6 |
7 |

8 | {{ $permission->name }} Permission 9 |

10 |
11 |
12 |

13 | Roles 14 |

15 | @if ($permission->roles()->count() != 0) 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | @foreach ($permission->roles as $role) 26 | @if ($loop->odd) 27 | 28 | @else 29 | 30 | @endif 31 | 36 | 37 | @endforeach 38 | 39 |
32 | 33 | {{ $role->name }} 34 | 35 |
40 | @else 41 |

42 | This permission is not connected to any Roles 43 |

44 | @endif 45 |
46 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/roles/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layout') 2 | 3 | @section('title', 'Create New Role') 4 | 5 | @section('admin.content') 6 |
7 |

8 | Create new Role 9 |

10 |
11 |
12 | @csrf 13 |
14 | 17 | 18 |
19 |
20 | 23 |
24 |
25 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/roles/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layout') 2 | 3 | @section('title', 'Edit - ' . $role->name) 4 | 5 | @section('admin.content') 6 |
7 |

8 | Edit {{ $role->name }} Role 9 |

10 |
11 |
12 | @csrf 13 |
14 | 17 | 18 |
19 |
20 | 23 |
24 |
25 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/roles/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layout') 2 | 3 | @section('title', 'Roles') 4 | 5 | @section('admin.content') 6 | @livewire('roles.index') 7 | @endsection -------------------------------------------------------------------------------- /resources/views/admin/roles/show.blade.php: -------------------------------------------------------------------------------- 1 | @extends('admin.layout') 2 | 3 | @section('title', $role->name) 4 | 5 | @section('admin.content') 6 |
7 |

8 | {{ $role->name }} Role 9 |

10 |
11 |
12 |

13 | Permissions 14 |

15 | @if ($role->permissions()->count() != 0) 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | @foreach ($role->permissions as $permission) 26 | @if ($loop->odd) 27 | 28 | @else 29 | 30 | @endif 31 | 36 | 37 | @endforeach 38 | 39 |
32 | 33 | {{ $permission->name }} 34 | 35 |
40 | @else 41 |

42 | This role contains no permissions 43 |

44 | @endif 45 |
46 | @endsection -------------------------------------------------------------------------------- /resources/views/auth/forgot-password.blade.php: -------------------------------------------------------------------------------- 1 | @extends('auth.layout') 2 | 3 | @section('title', 'Forgotten Password') 4 | 5 | @section('content') 6 |
7 | @if (session('status')) 8 |
9 | {{ session('status') }} 10 |
11 | @endif 12 | 13 |
14 |
15 | @csrf 16 |
17 |
18 | 21 |
22 |
23 | 24 | @error('email') 25 |

26 | {{ $message }} 27 |

28 | @enderror 29 |
30 |
31 |
32 |
33 |
34 | 37 |
38 |
39 |
40 |
41 |
42 | @endsection -------------------------------------------------------------------------------- /resources/views/auth/layout.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Mealing @yield('title') 10 | 11 | 12 | 13 |
14 |
15 |
16 | 17 |

18 | Mealing @yield('title') 19 |

20 |
21 | @yield('content') 22 |
23 |
24 | 25 | -------------------------------------------------------------------------------- /resources/views/auth/verify-email.blade.php: -------------------------------------------------------------------------------- 1 | @extends('auth.layout') 2 | 3 | @section('title', 'Verify Email') 4 | 5 | @section('content') 6 |
7 | @if (session('status') == 'verification-link-sent') 8 |
9 | Verification link has been sent. 10 |
11 | @endif 12 | 13 |

14 | Before proceeding, please check your email for a verification link. If you did not receive the email, use the button below. 15 |

16 | 17 |
18 |
19 | @csrf 20 | 23 |
24 |
25 |
26 | @endsection -------------------------------------------------------------------------------- /resources/views/homepage.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('title', 'Cookie Policy') 4 | 5 | @section('content') 6 |

7 | Top rated recipes 8 |

9 |
10 | @foreach ($topRecipes as $recipe) 11 | 12 |
13 |
14 | @if ($recipe->getMedia()->count() > 0) 15 | {{ $recipe->name }} 16 | @else 17 | No image available 18 | @endif 19 |
20 |
21 |

22 | {{ ucfirst($recipe->name) }} 23 |

24 |

25 | @for ($x = 0; $x < 5; $x++) 26 | @if (floor($recipe->avg_rating)-$x >= 1) 27 | 28 | @elseif ($recipe->avg_rating-$x > 0) 29 | 30 | @else 31 | 32 | @endif 33 | @endfor 34 |

35 |
36 |
37 |
38 | @endforeach 39 |
40 | @endsection -------------------------------------------------------------------------------- /resources/views/livewire/admin/users/approval.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @if ($user->approved != 1 && ! is_null($user->email_verified_at)) 3 | 6 | @elseif (is_null($user->email_verified_at)) 7 | 8 | Not Verified 9 | 10 | @endif 11 |
12 | -------------------------------------------------------------------------------- /resources/views/livewire/recipes/rate.blade.php: -------------------------------------------------------------------------------- 1 |
2 | @for ($i = 5; $i > 0; $i--) 3 | @if ($score >= $i) 4 | 5 | @else 6 | 7 | @endif 8 | @endfor 9 |
10 | -------------------------------------------------------------------------------- /resources/views/livewire/roles/index.blade.php: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | All Roles 5 |

6 |
7 |
8 | 9 | 10 | 11 | 14 | 17 | 20 | 21 | 22 | 23 | @foreach ($roles as $role) 24 | @if ($loop->odd) 25 | 26 | @else 27 | 28 | @endif 29 | 34 | 37 | 40 | 41 | @endforeach 42 | 43 |
30 | 31 | {{ $role->name }} 32 | 33 | 35 | {{ $role->users->count()}} 36 | 38 | {{ $role->permissions->count()}} 39 |
44 |
45 | {{ $roles->links() }} 46 |
47 |
48 |
-------------------------------------------------------------------------------- /resources/views/menus/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('title', 'Create New Menu') 4 | 5 | @section('content') 6 |
7 | @csrf 8 | 9 | @livewire('menus.create') 10 | 13 |
14 | @endsection -------------------------------------------------------------------------------- /resources/views/recipes/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('title', 'All Recipes') 4 | 5 | @section('content') 6 | @livewire('recipes.index') 7 | @endsection -------------------------------------------------------------------------------- /resources/views/user/confirm-password.blade.php: -------------------------------------------------------------------------------- 1 | @extends('user.profile.layout') 2 | 3 | @section('title', 'Confirm Password') 4 | 5 | @section('user.content') 6 |
7 |
8 |

9 | Confirm Password 10 |

11 |
12 |
13 | @error('password') 14 |
15 | {{ $message }} 16 |
17 | @enderror 18 |
19 | @csrf 20 |
21 | 24 | 25 |
26 |
27 | 30 |
31 |
32 |
33 |
34 | @endsection -------------------------------------------------------------------------------- /resources/views/user/profile/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('user.profile.layout') 2 | 3 | @section('title', Auth::user()->name) 4 | 5 | @section('user.content') 6 |
7 |
8 |

9 | User Profile 10 |

11 |
12 |
13 |
14 |
15 | {{ Auth::user()->getFullName() }} 16 |
17 |
18 |
19 |

20 | Name: {{ Auth::user()->getFullName() }}
21 | Email: {{ Auth::user()->email }}
22 | Subscripiton: Free 23 |

24 |
25 |
26 |
27 |
28 |
29 | @endsection -------------------------------------------------------------------------------- /resources/views/user/profile/layout.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 |
5 |
6 |
7 | @yield('user.content') 8 |
9 |
10 |
11 |
12 |
13 |

14 | User Menu 15 |

16 |
17 | 20 |
21 |
22 | 37 |
38 |
39 |
40 | @endsection -------------------------------------------------------------------------------- /resources/views/vendor/pagination/default.blade.php: -------------------------------------------------------------------------------- 1 | @if ($paginator->hasPages()) 2 | 46 | @endif 47 | -------------------------------------------------------------------------------- /resources/views/vendor/pagination/semantic-ui.blade.php: -------------------------------------------------------------------------------- 1 | @if ($paginator->hasPages()) 2 | 36 | @endif 37 | -------------------------------------------------------------------------------- /resources/views/vendor/pagination/simple-bootstrap-4.blade.php: -------------------------------------------------------------------------------- 1 | @if ($paginator->hasPages()) 2 | 27 | @endif 28 | -------------------------------------------------------------------------------- /resources/views/vendor/pagination/simple-default.blade.php: -------------------------------------------------------------------------------- 1 | @if ($paginator->hasPages()) 2 | 19 | @endif 20 | -------------------------------------------------------------------------------- /resources/views/vendor/pagination/simple-tailwind.blade.php: -------------------------------------------------------------------------------- 1 | @if ($paginator->hasPages()) 2 | 25 | @endif 26 | -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | get('/user', function (Request $request) { 18 | return $request->user(); 19 | }); 20 | -------------------------------------------------------------------------------- /routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 18 | }); 19 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 19 | })->purpose('Display an inspiring quote'); 20 | -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | $uri = urldecode( 11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) 12 | ); 13 | 14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the 15 | // built-in PHP web server. This provides a convenient way to test a Laravel 16 | // application without having installed a "real" web server software here. 17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { 18 | return false; 19 | } 20 | 21 | require_once __DIR__.'/public/index.php'; 22 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/debugbar/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/media-library/.gitignore: -------------------------------------------------------------------------------- 1 | temp/* -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | purge: [ 3 | './resources/**/*.blade.php', 4 | ], 5 | darkMode: 'media', 6 | theme: { 7 | extend: { 8 | typography(theme) { 9 | return { 10 | dark: { 11 | css: { 12 | color: theme("colors.gray.200"), 13 | '[class~="lead"]': { color: theme("colors.gray.300") }, 14 | a: { color: theme("colors.gray.100") }, 15 | strong: { color: theme("colors.gray.100") }, 16 | "ul > li::before": { backgroundColor: theme("colors.gray.400") }, 17 | hr: { borderColor: theme("colors.gray.800") }, 18 | blockquote: { 19 | color: theme("colors.gray.100"), 20 | borderLeftColor: theme("colors.gray.800"), 21 | }, 22 | h1: { color: theme("colors.gray.100") }, 23 | h2: { color: theme("colors.gray.100") }, 24 | h3: { color: theme("colors.gray.100") }, 25 | h4: { color: theme("colors.gray.100") }, 26 | code: { color: theme("colors.gray.100") }, 27 | "a code": { color: theme("colors.gray.100") }, 28 | pre: { 29 | color: theme("colors.gray.200"), 30 | backgroundColor: theme("colors.gray.800"), 31 | }, 32 | thead: { 33 | color: theme("colors.gray.100"), 34 | borderBottomColor: theme("colors.gray.700"), 35 | }, 36 | "tbody tr": { borderBottomColor: theme("colors.gray.800") }, 37 | }, 38 | }, 39 | }; 40 | }, 41 | height: theme => ({ 42 | "screen/2": "50vh", 43 | "screen/3": "calc(100vh / 3)", 44 | "screen/4": "calc(100vh / 4)", 45 | "screen/5": "calc(100vh / 5)", 46 | }) 47 | }, 48 | }, 49 | variants: { 50 | extend: { 51 | borderWidth: ['hover'], 52 | typography: ["dark"] 53 | }, 54 | }, 55 | plugins: [ 56 | require('@tailwindcss/forms'), 57 | require('@tailwindcss/typography'), 58 | ], 59 | } 60 | -------------------------------------------------------------------------------- /tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 19 | 20 | return $app; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 17 | } 18 | } -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | const mix = require('laravel-mix'); 2 | 3 | /* 4 | |-------------------------------------------------------------------------- 5 | | Mix Asset Management 6 | |-------------------------------------------------------------------------- 7 | | 8 | | Mix provides a clean, fluent API for defining some Webpack build steps 9 | | for your Laravel applications. By default, we are compiling the CSS 10 | | file for the application as well as bundling up all the JS files. 11 | | 12 | */ 13 | 14 | mix.js('resources/js/app.js', 'public/js') 15 | .sass('resources/css/vendor/erudus/icons.scss', 'public/css') 16 | .postCss('resources/css/app.css', 'public/css', [ 17 | require('tailwindcss'), 18 | ]); 19 | --------------------------------------------------------------------------------