├── .DS_Store ├── README.md ├── backend ├── .editorconfig ├── .env.example ├── .gitattributes ├── .gitignore ├── README.md ├── app │ ├── Events │ │ ├── CommentIncrement.php │ │ ├── PostBroadCastEvent.php │ │ └── TestEvent.php │ ├── Http │ │ ├── Controllers │ │ │ ├── API │ │ │ │ ├── AuthController.php │ │ │ │ ├── CommentController.php │ │ │ │ ├── PostController.php │ │ │ │ └── UserController.php │ │ │ └── Controller.php │ │ └── Requests │ │ │ └── PostRequest.php │ ├── Models │ │ ├── Comment.php │ │ ├── Post.php │ │ └── User.php │ └── Providers │ │ └── AppServiceProvider.php ├── artisan ├── bootstrap │ ├── app.php │ ├── cache │ │ └── .gitignore │ └── providers.php ├── composer.json ├── composer.lock ├── config │ ├── app.php │ ├── auth.php │ ├── broadcasting.php │ ├── cache.php │ ├── cors.php │ ├── database.php │ ├── filesystems.php │ ├── logging.php │ ├── mail.php │ ├── queue.php │ ├── reverb.php │ ├── sanctum.php │ ├── services.php │ └── session.php ├── database │ ├── .gitignore │ ├── factories │ │ └── UserFactory.php │ ├── migrations │ │ ├── 0001_01_01_000000_create_users_table.php │ │ ├── 0001_01_01_000001_create_cache_table.php │ │ ├── 0001_01_01_000002_create_jobs_table.php │ │ ├── 2024_03_15_123846_create_personal_access_tokens_table.php │ │ ├── 2024_03_17_105321_create_posts_table.php │ │ └── 2024_03_20_163112_create_comments_table.php │ └── seeders │ │ └── DatabaseSeeder.php ├── package.json ├── phpunit.xml ├── public │ ├── .htaccess │ ├── favicon.ico │ ├── index.php │ └── robots.txt ├── resources │ ├── css │ │ └── app.css │ ├── js │ │ ├── app.js │ │ ├── bootstrap.js │ │ └── echo.js │ └── views │ │ └── welcome.blade.php ├── routes │ ├── api.php │ ├── channels.php │ ├── console.php │ └── web.php ├── storage │ ├── app │ │ ├── .gitignore │ │ └── public │ │ │ └── .gitignore │ ├── framework │ │ ├── .gitignore │ │ ├── cache │ │ │ ├── .gitignore │ │ │ └── data │ │ │ │ └── .gitignore │ │ ├── sessions │ │ │ └── .gitignore │ │ ├── testing │ │ │ └── .gitignore │ │ └── views │ │ │ └── .gitignore │ └── logs │ │ └── .gitignore ├── tests │ ├── Feature │ │ └── ExampleTest.php │ ├── Pest.php │ ├── TestCase.php │ └── Unit │ │ └── ExampleTest.php └── vite.config.js └── frontend ├── .env.example ├── .gitignore ├── README.md ├── components.json ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── 404.svg ├── auth_img.svg ├── avatar.png ├── favicon.ico └── logo.svg ├── src ├── app │ ├── (daily_dev) │ │ ├── layout.tsx │ │ └── page.tsx │ ├── api │ │ ├── auth │ │ │ └── [...nextauth] │ │ │ │ ├── authOptions.ts │ │ │ │ └── route.ts │ │ └── image-preview │ │ │ └── route.ts │ ├── globals.css │ ├── layout.tsx │ ├── login │ │ └── page.tsx │ └── not-found.tsx ├── components │ ├── auth │ │ ├── Login.tsx │ │ └── Register.tsx │ ├── base │ │ ├── MobileSidebar.tsx │ │ ├── Navbar.tsx │ │ ├── ProfileMenu.tsx │ │ ├── SearchInput.tsx │ │ ├── Sidebar.tsx │ │ └── SidebarLinks.tsx │ ├── comment │ │ ├── AddComment.tsx │ │ └── CommentCard.tsx │ ├── common │ │ └── UserAvatar.tsx │ ├── post │ │ ├── AddPost.tsx │ │ ├── PostCard.tsx │ │ ├── Posts.tsx │ │ └── ShowPost.tsx │ └── ui │ │ ├── button.tsx │ │ ├── card.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu.tsx │ │ ├── input.tsx │ │ ├── label.tsx │ │ ├── sheet.tsx │ │ ├── tabs.tsx │ │ └── textarea.tsx ├── dataFetch │ └── postFetch.ts ├── lib │ ├── apiEndPoints.ts │ ├── axios.config.ts │ ├── echo.config.ts │ ├── env.ts │ └── utils.ts ├── middleware.ts └── providers │ └── AuthProvider.tsx ├── tailwind.config.ts ├── tsconfig.json └── types.ts /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TusharVashishth/daily-dev-clone/39c9f0a69bf072015771f4edf73f14dd3eba169b/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Daily Dev Clone 2 | 3 | Let's Build Daily Dev Clone using the future by using future-proof technology Laravel 11 as the backend and Next js 14 with typescript as the Front end. 4 | 5 | In this video, we will also explore Reverb for real-time updates. 6 | 7 | If you use this code then forget to give this star ⭐️⭐️ 8 | -------------------------------------------------------------------------------- /backend/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /backend/.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Laravel 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_TIMEZONE=UTC 6 | APP_URL=http://localhost 7 | 8 | APP_LOCALE=en 9 | APP_FALLBACK_LOCALE=en 10 | APP_FAKER_LOCALE=en_US 11 | 12 | APP_MAINTENANCE_DRIVER=file 13 | APP_MAINTENANCE_STORE=database 14 | 15 | BCRYPT_ROUNDS=12 16 | 17 | LOG_CHANNEL=stack 18 | LOG_STACK=single 19 | LOG_DEPRECATIONS_CHANNEL=null 20 | LOG_LEVEL=debug 21 | 22 | DB_CONNECTION=pgsql 23 | DB_HOST=127.0.0.1 24 | DB_PORT=5432 25 | DB_DATABASE=backend 26 | DB_USERNAME=root 27 | DB_PASSWORD= 28 | 29 | SESSION_DRIVER=database 30 | SESSION_LIFETIME=120 31 | SESSION_ENCRYPT=false 32 | SESSION_PATH=/ 33 | SESSION_DOMAIN=null 34 | 35 | BROADCAST_CONNECTION=log 36 | FILESYSTEM_DISK=local 37 | QUEUE_CONNECTION=database 38 | 39 | CACHE_STORE=database 40 | CACHE_PREFIX= 41 | 42 | MEMCACHED_HOST=127.0.0.1 43 | 44 | REDIS_CLIENT=phpredis 45 | REDIS_HOST=127.0.0.1 46 | REDIS_PASSWORD=null 47 | REDIS_PORT=6379 48 | 49 | MAIL_MAILER=log 50 | MAIL_HOST=127.0.0.1 51 | MAIL_PORT=2525 52 | MAIL_USERNAME=null 53 | MAIL_PASSWORD=null 54 | MAIL_ENCRYPTION=null 55 | MAIL_FROM_ADDRESS="hello@example.com" 56 | MAIL_FROM_NAME="${APP_NAME}" 57 | 58 | AWS_ACCESS_KEY_ID= 59 | AWS_SECRET_ACCESS_KEY= 60 | AWS_DEFAULT_REGION=us-east-1 61 | AWS_BUCKET= 62 | AWS_USE_PATH_STYLE_ENDPOINT=false 63 | 64 | VITE_APP_NAME="${APP_NAME}" 65 | -------------------------------------------------------------------------------- /backend/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.blade.php diff=html 4 | *.css diff=css 5 | *.html diff=html 6 | *.md diff=markdown 7 | *.php diff=php 8 | 9 | /.github export-ignore 10 | CHANGELOG.md export-ignore 11 | .styleci.yml export-ignore 12 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | /.phpunit.cache 2 | /node_modules 3 | /public/build 4 | /public/hot 5 | /public/storage 6 | /storage/*.key 7 | /vendor 8 | .env 9 | .env.backup 10 | .env.production 11 | .phpunit.result.cache 12 | Homestead.json 13 | Homestead.yaml 14 | auth.json 15 | npm-debug.log 16 | yarn-error.log 17 | /.fleet 18 | /.idea 19 | /.vscode 20 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 |

Laravel Logo

2 | 3 |

4 | Build Status 5 | Total Downloads 6 | Latest Stable Version 7 | License 8 |

9 | 10 | ## About Laravel 11 | 12 | Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: 13 | 14 | - [Simple, fast routing engine](https://laravel.com/docs/routing). 15 | - [Powerful dependency injection container](https://laravel.com/docs/container). 16 | - Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. 17 | - Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). 18 | - Database agnostic [schema migrations](https://laravel.com/docs/migrations). 19 | - [Robust background job processing](https://laravel.com/docs/queues). 20 | - [Real-time event broadcasting](https://laravel.com/docs/broadcasting). 21 | 22 | Laravel is accessible, powerful, and provides tools required for large, robust applications. 23 | 24 | ## Learning Laravel 25 | 26 | Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. 27 | 28 | You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch. 29 | 30 | If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. 31 | 32 | ## Laravel Sponsors 33 | 34 | We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com). 35 | 36 | ### Premium Partners 37 | 38 | - **[Vehikl](https://vehikl.com/)** 39 | - **[Tighten Co.](https://tighten.co)** 40 | - **[WebReinvent](https://webreinvent.com/)** 41 | - **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)** 42 | - **[64 Robots](https://64robots.com)** 43 | - **[Curotec](https://www.curotec.com/services/technologies/laravel/)** 44 | - **[Cyber-Duck](https://cyber-duck.co.uk)** 45 | - **[DevSquad](https://devsquad.com/hire-laravel-developers)** 46 | - **[Jump24](https://jump24.co.uk)** 47 | - **[Redberry](https://redberry.international/laravel/)** 48 | - **[Active Logic](https://activelogic.com)** 49 | - **[byte5](https://byte5.de)** 50 | - **[OP.GG](https://op.gg)** 51 | 52 | ## Contributing 53 | 54 | Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). 55 | 56 | ## Code of Conduct 57 | 58 | In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). 59 | 60 | ## Security Vulnerabilities 61 | 62 | If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. 63 | 64 | ## License 65 | 66 | The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). 67 | -------------------------------------------------------------------------------- /backend/app/Events/CommentIncrement.php: -------------------------------------------------------------------------------- 1 | post_id = $post_id; 25 | } 26 | 27 | /** 28 | * Get the channels the event should broadcast on. 29 | * 30 | * @return array 31 | */ 32 | public function broadcastOn(): array 33 | { 34 | return [ 35 | new Channel('post-broadcast'), 36 | ]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /backend/app/Events/PostBroadCastEvent.php: -------------------------------------------------------------------------------- 1 | post = $post; 26 | } 27 | 28 | /** 29 | * Get the channels the event should broadcast on. 30 | * 31 | * @return array 32 | */ 33 | public function broadcastOn(): array 34 | { 35 | return [ 36 | new Channel('post-broadcast'), 37 | ]; 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /backend/app/Events/TestEvent.php: -------------------------------------------------------------------------------- 1 | data = $payload; 25 | } 26 | 27 | /** 28 | * Get the channels the event should broadcast on. 29 | * 30 | * @return array 31 | */ 32 | public function broadcastOn(): array 33 | { 34 | return 35 | [ 36 | new Channel('test-event'), 37 | ]; 38 | // return new Channel('test-channel'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /backend/app/Http/Controllers/API/AuthController.php: -------------------------------------------------------------------------------- 1 | validate([ 16 | "name" => "required|min:2|max:50", 17 | "email" => "required|email|unique:users,email", 18 | "username" => "required|alpha_num:ascii|min:4|max:50|unique:users,username", 19 | "password" => "required|min:6|max:50|confirmed" 20 | ]); 21 | 22 | try { 23 | $payload["password"] = Hash::make($payload["password"]); 24 | User::create($payload); 25 | return response()->json(["status" => 200, "message" => "Account created successfully!"]); 26 | } catch (\Exception $err) { 27 | Log::info("user_register_err =>" . $err->getMessage()); 28 | return response()->json(["status" => 500, "message" => "Something went wrong!"], 500); 29 | } 30 | } 31 | 32 | // * Login user 33 | public function login(Request $request) 34 | { 35 | $payload = $request->validate([ 36 | "email" => "required|email", 37 | "password" => "required" 38 | ]); 39 | 40 | try { 41 | $user = User::where("email", $payload["email"])->first(); 42 | if ($user) { 43 | // * Check password 44 | if (!Hash::check($payload["password"], $user->password)) { 45 | return response()->json(["status" => 401, "message" => "Invalid credentials."]); 46 | } 47 | 48 | $token = $user->createToken("web")->plainTextToken; 49 | $authRes = array_merge($user->toArray(), ["token" => $token]); 50 | return ["status" => 200, "user" => $authRes, "message" => "Loggedin succssfully!"]; 51 | } 52 | return response()->json(["status" => 401, "message" => "No account found with these credentials."]); 53 | } catch (\Exception $err) { 54 | Log::info("user_register_err =>" . $err->getMessage()); 55 | return response()->json(["status" => 500, "message" => "Something went wrong!"], 500); 56 | } 57 | } 58 | 59 | // * check credentials 60 | public function checkCredentias(Request $request) 61 | { 62 | $payload = $request->validate([ 63 | "email" => "required|email", 64 | "password" => "required" 65 | ]); 66 | 67 | try { 68 | $user = User::where("email", $payload["email"])->first(); 69 | if ($user) { 70 | // * Check password 71 | if (!Hash::check($payload["password"], $user->password)) { 72 | return response()->json(["status" => 401, "message" => "Invalid credentials."]); 73 | } 74 | return ["status" => 200, "message" => "Loggedin succssfully!"]; 75 | } 76 | return response()->json(["status" => 401, "message" => "No account found with these credentials."]); 77 | } catch (\Exception $err) { 78 | Log::info("user_register_err =>" . $err->getMessage()); 79 | return response()->json(["status" => 500, "message" => "Something went wrong!"], 500); 80 | } 81 | } 82 | 83 | // * Logout 84 | public function logout(Request $request) 85 | { 86 | try { 87 | $request->user()->currentAccessToken()->delete(); 88 | return ["status" => 200, "message" => "logged out successfully!"]; 89 | } catch (\Exception $err) { 90 | Log::info("user_logout_err =>" . $err->getMessage()); 91 | return response()->json(["status" => 500, "message" => "Something went wrong!"], 500); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /backend/app/Http/Controllers/API/CommentController.php: -------------------------------------------------------------------------------- 1 | get("post_id"); 20 | $comments = Comment::select("id", "user_id", "post_id", "comment", "created_at")->where("post_id", $postId)->with('user')->orderByDesc("id")->cursorPaginate(15); 21 | return response()->json($comments); 22 | } 23 | 24 | /** 25 | * Store a newly created resource in storage. 26 | */ 27 | public function store(Request $request) 28 | { 29 | $payload = $request->validate([ 30 | "comment" => "required|min:2|max:2000", 31 | "post_id" => "required" 32 | ]); 33 | try { 34 | $user = $request->user(); 35 | $payload["user_id"] = $user->id; 36 | Post::where("id", $payload["post_id"])->increment("comment_count", 1); 37 | $comment = Comment::create($payload)->with('user')->orderByDesc("id")->first(); 38 | CommentIncrement::dispatch($payload["post_id"]); 39 | return response()->json(["comment" => $comment, "message" => "Comment added succcesfully!"]); 40 | } catch (\Exception $err) { 41 | Log::info("post-error => " . $err->getMessage()); 42 | return response()->json(["message" => "something went wrong.please try again!"], 500); 43 | } 44 | 45 | } 46 | 47 | /** 48 | * Display the specified resource. 49 | */ 50 | public function show(string $id) 51 | { 52 | // 53 | } 54 | 55 | /** 56 | * Update the specified resource in storage. 57 | */ 58 | public function update(Request $request, string $id) 59 | { 60 | // 61 | } 62 | 63 | /** 64 | * Remove the specified resource from storage. 65 | */ 66 | public function destroy(string $id) 67 | { 68 | // 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /backend/app/Http/Controllers/API/PostController.php: -------------------------------------------------------------------------------- 1 | with('user')->orderByDesc("id")->cursorPaginate(20); 21 | return response()->json($posts); 22 | } 23 | 24 | /** 25 | * Store a newly created resource in storage. 26 | */ 27 | public function store(PostRequest $request) 28 | { 29 | $payload = $request->validated(); 30 | try { 31 | $user = $request->user(); 32 | $payload["user_id"] = $user->id; 33 | $post = Post::create($payload)->with("user")->orderByDesc("id")->first(); 34 | PostBroadCastEvent::dispatch($post); 35 | return response()->json(["message" => "Post created successfully!", "post" => $post]); 36 | } catch (\Exception $err) { 37 | Log::info("post-error => " . $err->getMessage()); 38 | return response()->json(["message" => "something went wrong.please try again!"], 500); 39 | } 40 | 41 | } 42 | 43 | /** 44 | * Display the specified resource. 45 | */ 46 | public function show(string $id) 47 | { 48 | // 49 | } 50 | 51 | /** 52 | * Update the specified resource in storage. 53 | */ 54 | public function update(Request $request, string $id) 55 | { 56 | // 57 | } 58 | 59 | /** 60 | * Remove the specified resource from storage. 61 | */ 62 | public function destroy(string $id) 63 | { 64 | // 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /backend/app/Http/Controllers/API/UserController.php: -------------------------------------------------------------------------------- 1 | validate([ 15 | "profile_image" => "required|image|mimes:jpg,png,svg,webp,jpeg,gif|max:2048" 16 | ]); 17 | try { 18 | $user = $request->user(); 19 | $filename = $payload["profile_image"]->store("images_" . $user->id); 20 | User::where("id", $user->id)->update(["profile_image" => $filename]); 21 | return response()->json(["image" => $filename]); 22 | } catch (\Exception $err) { 23 | Log::info("Profile image error =>" . $err->getMessage()); 24 | return response()->json(["message" => "Something went wrong!"], 500); 25 | } 26 | 27 | 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backend/app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | |string> 21 | */ 22 | public function rules(): array 23 | { 24 | return [ 25 | "title" => "required|min:2|max:190", 26 | "url" => "required|url:https", 27 | "image_url" => "required|url:https,http", 28 | "description" => "nullable|min:10|max:20000" 29 | ]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /backend/app/Models/Comment.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class, "user_id", "id")->select("id", "profile_image", "username", "name"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /backend/app/Models/Post.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class, "user_id", "id")->select("id", "profile_image"); 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /backend/app/Models/User.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | protected $fillable = [ 21 | 'name', 22 | 'email', 23 | 'password', 24 | 'username', 25 | 'profile_image' 26 | ]; 27 | 28 | /** 29 | * The attributes that should be hidden for serialization. 30 | * 31 | * @var array 32 | */ 33 | protected $hidden = [ 34 | 'password', 35 | 'remember_token', 36 | ]; 37 | 38 | /** 39 | * Get the attributes that should be cast. 40 | * 41 | * @return array 42 | */ 43 | protected function casts(): array 44 | { 45 | return [ 46 | 'email_verified_at' => 'datetime', 47 | 'password' => 'hashed', 48 | ]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /backend/app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | handleCommand(new ArgvInput); 14 | 15 | exit($status); 16 | -------------------------------------------------------------------------------- /backend/bootstrap/app.php: -------------------------------------------------------------------------------- 1 | withRouting( 9 | web: __DIR__.'/../routes/web.php', 10 | api: __DIR__.'/../routes/api.php', 11 | commands: __DIR__.'/../routes/console.php', 12 | channels: __DIR__.'/../routes/channels.php', 13 | health: '/up', 14 | ) 15 | ->withMiddleware(function (Middleware $middleware) { 16 | // 17 | }) 18 | ->withExceptions(function (Exceptions $exceptions) { 19 | // 20 | })->create(); 21 | -------------------------------------------------------------------------------- /backend/bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /backend/bootstrap/providers.php: -------------------------------------------------------------------------------- 1 | env('APP_NAME', 'Laravel'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Application Environment 21 | |-------------------------------------------------------------------------- 22 | | 23 | | This value determines the "environment" your application is currently 24 | | running in. This may determine how you prefer to configure various 25 | | services the application utilizes. Set this in your ".env" file. 26 | | 27 | */ 28 | 29 | 'env' => env('APP_ENV', 'production'), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Application Debug Mode 34 | |-------------------------------------------------------------------------- 35 | | 36 | | When your application is in debug mode, detailed error messages with 37 | | stack traces will be shown on every error that occurs within your 38 | | application. If disabled, a simple generic error page is shown. 39 | | 40 | */ 41 | 42 | 'debug' => (bool) env('APP_DEBUG', false), 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Application URL 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This URL is used by the console to properly generate URLs when using 50 | | the Artisan command line tool. You should set this to the root of 51 | | the application so that it's available within Artisan commands. 52 | | 53 | */ 54 | 55 | 'url' => env('APP_URL', 'http://localhost'), 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | Application Timezone 60 | |-------------------------------------------------------------------------- 61 | | 62 | | Here you may specify the default timezone for your application, which 63 | | will be used by the PHP date and date-time functions. The timezone 64 | | is set to "UTC" by default as it is suitable for most use cases. 65 | | 66 | */ 67 | 68 | 'timezone' => env('APP_TIMEZONE', 'UTC'), 69 | 70 | /* 71 | |-------------------------------------------------------------------------- 72 | | Application Locale Configuration 73 | |-------------------------------------------------------------------------- 74 | | 75 | | The application locale determines the default locale that will be used 76 | | by Laravel's translation / localization methods. This option can be 77 | | set to any locale for which you plan to have translation strings. 78 | | 79 | */ 80 | 81 | 'locale' => env('APP_LOCALE', 'en'), 82 | 83 | 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), 84 | 85 | 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), 86 | 87 | /* 88 | |-------------------------------------------------------------------------- 89 | | Encryption Key 90 | |-------------------------------------------------------------------------- 91 | | 92 | | This key is utilized by Laravel's encryption services and should be set 93 | | to a random, 32 character string to ensure that all encrypted values 94 | | are secure. You should do this prior to deploying the application. 95 | | 96 | */ 97 | 98 | 'cipher' => 'AES-256-CBC', 99 | 100 | 'key' => env('APP_KEY'), 101 | 102 | 'previous_keys' => [ 103 | ...array_filter( 104 | explode(',', env('APP_PREVIOUS_KEYS', '')) 105 | ), 106 | ], 107 | 108 | /* 109 | |-------------------------------------------------------------------------- 110 | | Maintenance Mode Driver 111 | |-------------------------------------------------------------------------- 112 | | 113 | | These configuration options determine the driver used to determine and 114 | | manage Laravel's "maintenance mode" status. The "cache" driver will 115 | | allow maintenance mode to be controlled across multiple machines. 116 | | 117 | | Supported drivers: "file", "cache" 118 | | 119 | */ 120 | 121 | 'maintenance' => [ 122 | 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), 123 | 'store' => env('APP_MAINTENANCE_STORE', 'database'), 124 | ], 125 | 126 | ]; 127 | -------------------------------------------------------------------------------- /backend/config/auth.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'guard' => env('AUTH_GUARD', 'web'), 18 | 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), 19 | ], 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Authentication Guards 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Next, you may define every authentication guard for your application. 27 | | Of course, a great default configuration has been defined for you 28 | | which utilizes session storage plus the Eloquent user provider. 29 | | 30 | | All authentication guards have a user provider, which defines how the 31 | | users are actually retrieved out of your database or other storage 32 | | system used by the application. Typically, Eloquent is utilized. 33 | | 34 | | Supported: "session" 35 | | 36 | */ 37 | 38 | 'guards' => [ 39 | 'web' => [ 40 | 'driver' => 'session', 41 | 'provider' => 'users', 42 | ], 43 | ], 44 | 45 | /* 46 | |-------------------------------------------------------------------------- 47 | | User Providers 48 | |-------------------------------------------------------------------------- 49 | | 50 | | All authentication guards have a user provider, which defines how the 51 | | users are actually retrieved out of your database or other storage 52 | | system used by the application. Typically, Eloquent is utilized. 53 | | 54 | | If you have multiple user tables or models you may configure multiple 55 | | providers to represent the model / table. These providers may then 56 | | be assigned to any extra authentication guards you have defined. 57 | | 58 | | Supported: "database", "eloquent" 59 | | 60 | */ 61 | 62 | 'providers' => [ 63 | 'users' => [ 64 | 'driver' => 'eloquent', 65 | 'model' => env('AUTH_MODEL', App\Models\User::class), 66 | ], 67 | 68 | // 'users' => [ 69 | // 'driver' => 'database', 70 | // 'table' => 'users', 71 | // ], 72 | ], 73 | 74 | /* 75 | |-------------------------------------------------------------------------- 76 | | Resetting Passwords 77 | |-------------------------------------------------------------------------- 78 | | 79 | | These configuration options specify the behavior of Laravel's password 80 | | reset functionality, including the table utilized for token storage 81 | | and the user provider that is invoked to actually retrieve users. 82 | | 83 | | The expiry time is the number of minutes that each reset token will be 84 | | considered valid. This security feature keeps tokens short-lived so 85 | | they have less time to be guessed. You may change this as needed. 86 | | 87 | | The throttle setting is the number of seconds a user must wait before 88 | | generating more password reset tokens. This prevents the user from 89 | | quickly generating a very large amount of password reset tokens. 90 | | 91 | */ 92 | 93 | 'passwords' => [ 94 | 'users' => [ 95 | 'provider' => 'users', 96 | 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), 97 | 'expire' => 60, 98 | 'throttle' => 60, 99 | ], 100 | ], 101 | 102 | /* 103 | |-------------------------------------------------------------------------- 104 | | Password Confirmation Timeout 105 | |-------------------------------------------------------------------------- 106 | | 107 | | Here you may define the amount of seconds before a password confirmation 108 | | window expires and users are asked to re-enter their password via the 109 | | confirmation screen. By default, the timeout lasts for three hours. 110 | | 111 | */ 112 | 113 | 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), 114 | 115 | ]; 116 | -------------------------------------------------------------------------------- /backend/config/broadcasting.php: -------------------------------------------------------------------------------- 1 | env('BROADCAST_CONNECTION', '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 | 'reverb' => [ 34 | 'driver' => 'reverb', 35 | 'key' => env('REVERB_APP_KEY'), 36 | 'secret' => env('REVERB_APP_SECRET'), 37 | 'app_id' => env('REVERB_APP_ID'), 38 | 'options' => [ 39 | 'host' => env('REVERB_HOST'), 40 | 'port' => env('REVERB_PORT', 443), 41 | 'scheme' => env('REVERB_SCHEME', 'https'), 42 | 'useTLS' => env('REVERB_SCHEME', 'https') === 'https', 43 | ], 44 | 'client_options' => [ 45 | // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html 46 | ], 47 | ], 48 | 49 | 'pusher' => [ 50 | 'driver' => 'pusher', 51 | 'key' => env('PUSHER_APP_KEY'), 52 | 'secret' => env('PUSHER_APP_SECRET'), 53 | 'app_id' => env('PUSHER_APP_ID'), 54 | 'options' => [ 55 | 'cluster' => env('PUSHER_APP_CLUSTER'), 56 | 'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com', 57 | 'port' => env('PUSHER_PORT', 443), 58 | 'scheme' => env('PUSHER_SCHEME', 'https'), 59 | 'encrypted' => true, 60 | 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https', 61 | ], 62 | 'client_options' => [ 63 | // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html 64 | ], 65 | ], 66 | 67 | 'ably' => [ 68 | 'driver' => 'ably', 69 | 'key' => env('ABLY_KEY'), 70 | ], 71 | 72 | 'log' => [ 73 | 'driver' => 'log', 74 | ], 75 | 76 | 'null' => [ 77 | 'driver' => 'null', 78 | ], 79 | 80 | ], 81 | 82 | ]; 83 | -------------------------------------------------------------------------------- /backend/config/cache.php: -------------------------------------------------------------------------------- 1 | env('CACHE_STORE', 'database'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Cache Stores 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the cache "stores" for your application as 26 | | well as their drivers. You may even define multiple stores for the 27 | | same cache driver to group types of items stored in your caches. 28 | | 29 | | Supported drivers: "apc", "array", "database", "file", "memcached", 30 | | "redis", "dynamodb", "octane", "null" 31 | | 32 | */ 33 | 34 | 'stores' => [ 35 | 36 | 'array' => [ 37 | 'driver' => 'array', 38 | 'serialize' => false, 39 | ], 40 | 41 | 'database' => [ 42 | 'driver' => 'database', 43 | 'table' => env('DB_CACHE_TABLE', 'cache'), 44 | 'connection' => env('DB_CACHE_CONNECTION', null), 45 | 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION', null), 46 | ], 47 | 48 | 'file' => [ 49 | 'driver' => 'file', 50 | 'path' => storage_path('framework/cache/data'), 51 | 'lock_path' => storage_path('framework/cache/data'), 52 | ], 53 | 54 | 'memcached' => [ 55 | 'driver' => 'memcached', 56 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), 57 | 'sasl' => [ 58 | env('MEMCACHED_USERNAME'), 59 | env('MEMCACHED_PASSWORD'), 60 | ], 61 | 'options' => [ 62 | // Memcached::OPT_CONNECT_TIMEOUT => 2000, 63 | ], 64 | 'servers' => [ 65 | [ 66 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 67 | 'port' => env('MEMCACHED_PORT', 11211), 68 | 'weight' => 100, 69 | ], 70 | ], 71 | ], 72 | 73 | 'redis' => [ 74 | 'driver' => 'redis', 75 | 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), 76 | 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), 77 | ], 78 | 79 | 'dynamodb' => [ 80 | 'driver' => 'dynamodb', 81 | 'key' => env('AWS_ACCESS_KEY_ID'), 82 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 83 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 84 | 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), 85 | 'endpoint' => env('DYNAMODB_ENDPOINT'), 86 | ], 87 | 88 | 'octane' => [ 89 | 'driver' => 'octane', 90 | ], 91 | 92 | ], 93 | 94 | /* 95 | |-------------------------------------------------------------------------- 96 | | Cache Key Prefix 97 | |-------------------------------------------------------------------------- 98 | | 99 | | When utilizing the APC, database, memcached, Redis, and DynamoDB cache 100 | | stores, there might be other applications using the same cache. For 101 | | that reason, you may prefix every cache key to avoid collisions. 102 | | 103 | */ 104 | 105 | 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), 106 | 107 | ]; 108 | -------------------------------------------------------------------------------- /backend/config/cors.php: -------------------------------------------------------------------------------- 1 | ['api/*', 'sanctum/csrf-cookie', "/broadcasting/auth"], 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 | -------------------------------------------------------------------------------- /backend/config/database.php: -------------------------------------------------------------------------------- 1 | env('DB_CONNECTION', 'sqlite'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Database Connections 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Below are all of the database connections defined for your application. 27 | | An example configuration is provided for each database system which 28 | | is supported by Laravel. You're free to add / remove connections. 29 | | 30 | */ 31 | 32 | 'connections' => [ 33 | 34 | 'sqlite' => [ 35 | 'driver' => 'sqlite', 36 | 'url' => env('DB_URL'), 37 | 'database' => env('DB_DATABASE', database_path('database.sqlite')), 38 | 'prefix' => '', 39 | 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), 40 | ], 41 | 42 | 'mysql' => [ 43 | 'driver' => 'mysql', 44 | 'url' => env('DB_URL'), 45 | 'host' => env('DB_HOST', '127.0.0.1'), 46 | 'port' => env('DB_PORT', '3306'), 47 | 'database' => env('DB_DATABASE', 'laravel'), 48 | 'username' => env('DB_USERNAME', 'root'), 49 | 'password' => env('DB_PASSWORD', ''), 50 | 'unix_socket' => env('DB_SOCKET', ''), 51 | 'charset' => env('DB_CHARSET', 'utf8mb4'), 52 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 53 | 'prefix' => '', 54 | 'prefix_indexes' => true, 55 | 'strict' => true, 56 | 'engine' => null, 57 | 'options' => extension_loaded('pdo_mysql') ? array_filter([ 58 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), 59 | ]) : [], 60 | ], 61 | 62 | 'mariadb' => [ 63 | 'driver' => 'mariadb', 64 | 'url' => env('DB_URL'), 65 | 'host' => env('DB_HOST', '127.0.0.1'), 66 | 'port' => env('DB_PORT', '3306'), 67 | 'database' => env('DB_DATABASE', 'laravel'), 68 | 'username' => env('DB_USERNAME', 'root'), 69 | 'password' => env('DB_PASSWORD', ''), 70 | 'unix_socket' => env('DB_SOCKET', ''), 71 | 'charset' => env('DB_CHARSET', 'utf8mb4'), 72 | 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), 73 | 'prefix' => '', 74 | 'prefix_indexes' => true, 75 | 'strict' => true, 76 | 'engine' => null, 77 | 'options' => extension_loaded('pdo_mysql') ? array_filter([ 78 | PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), 79 | ]) : [], 80 | ], 81 | 82 | 'pgsql' => [ 83 | 'driver' => 'pgsql', 84 | 'url' => env('DB_URL'), 85 | 'host' => env('DB_HOST', '127.0.0.1'), 86 | 'port' => env('DB_PORT', '5432'), 87 | 'database' => env('DB_DATABASE', 'laravel'), 88 | 'username' => env('DB_USERNAME', 'root'), 89 | 'password' => env('DB_PASSWORD', ''), 90 | 'charset' => env('DB_CHARSET', 'utf8'), 91 | 'prefix' => '', 92 | 'prefix_indexes' => true, 93 | 'search_path' => 'public', 94 | 'sslmode' => 'prefer', 95 | ], 96 | 97 | 'sqlsrv' => [ 98 | 'driver' => 'sqlsrv', 99 | 'url' => env('DB_URL'), 100 | 'host' => env('DB_HOST', 'localhost'), 101 | 'port' => env('DB_PORT', '1433'), 102 | 'database' => env('DB_DATABASE', 'laravel'), 103 | 'username' => env('DB_USERNAME', 'root'), 104 | 'password' => env('DB_PASSWORD', ''), 105 | 'charset' => env('DB_CHARSET', 'utf8'), 106 | 'prefix' => '', 107 | 'prefix_indexes' => true, 108 | // 'encrypt' => env('DB_ENCRYPT', 'yes'), 109 | // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), 110 | ], 111 | 112 | ], 113 | 114 | /* 115 | |-------------------------------------------------------------------------- 116 | | Migration Repository Table 117 | |-------------------------------------------------------------------------- 118 | | 119 | | This table keeps track of all the migrations that have already run for 120 | | your application. Using this information, we can determine which of 121 | | the migrations on disk haven't actually been run on the database. 122 | | 123 | */ 124 | 125 | 'migrations' => [ 126 | 'table' => 'migrations', 127 | 'update_date_on_publish' => true, 128 | ], 129 | 130 | /* 131 | |-------------------------------------------------------------------------- 132 | | Redis Databases 133 | |-------------------------------------------------------------------------- 134 | | 135 | | Redis is an open source, fast, and advanced key-value store that also 136 | | provides a richer body of commands than a typical key-value system 137 | | such as Memcached. You may define your connection settings here. 138 | | 139 | */ 140 | 141 | 'redis' => [ 142 | 143 | 'client' => env('REDIS_CLIENT', 'phpredis'), 144 | 145 | 'options' => [ 146 | 'cluster' => env('REDIS_CLUSTER', 'redis'), 147 | 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), 148 | ], 149 | 150 | 'default' => [ 151 | 'url' => env('REDIS_URL'), 152 | 'host' => env('REDIS_HOST', '127.0.0.1'), 153 | 'username' => env('REDIS_USERNAME'), 154 | 'password' => env('REDIS_PASSWORD'), 155 | 'port' => env('REDIS_PORT', '6379'), 156 | 'database' => env('REDIS_DB', '0'), 157 | ], 158 | 159 | 'cache' => [ 160 | 'url' => env('REDIS_URL'), 161 | 'host' => env('REDIS_HOST', '127.0.0.1'), 162 | 'username' => env('REDIS_USERNAME'), 163 | 'password' => env('REDIS_PASSWORD'), 164 | 'port' => env('REDIS_PORT', '6379'), 165 | 'database' => env('REDIS_CACHE_DB', '1'), 166 | ], 167 | 168 | ], 169 | 170 | ]; 171 | -------------------------------------------------------------------------------- /backend/config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DISK', 'local'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Filesystem Disks 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Below you may configure as many filesystem disks as necessary, and you 24 | | may even configure multiple disks for the same driver. Examples for 25 | | most supported storage drivers are configured here for reference. 26 | | 27 | | Supported Drivers: "local", "ftp", "sftp", "s3" 28 | | 29 | */ 30 | 31 | 'disks' => [ 32 | 33 | 'local' => [ 34 | 'driver' => 'local', 35 | 'root' => storage_path('app'), 36 | 'throw' => false, 37 | ], 38 | 39 | 'public' => [ 40 | 'driver' => 'local', 41 | 'root' => storage_path('app/public'), 42 | 'url' => env('APP_URL').'/storage', 43 | 'visibility' => 'public', 44 | 'throw' => false, 45 | ], 46 | 47 | 's3' => [ 48 | 'driver' => 's3', 49 | 'key' => env('AWS_ACCESS_KEY_ID'), 50 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 51 | 'region' => env('AWS_DEFAULT_REGION'), 52 | 'bucket' => env('AWS_BUCKET'), 53 | 'url' => env('AWS_URL'), 54 | 'endpoint' => env('AWS_ENDPOINT'), 55 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), 56 | 'throw' => false, 57 | ], 58 | 59 | ], 60 | 61 | /* 62 | |-------------------------------------------------------------------------- 63 | | Symbolic Links 64 | |-------------------------------------------------------------------------- 65 | | 66 | | Here you may configure the symbolic links that will be created when the 67 | | `storage:link` Artisan command is executed. The array keys should be 68 | | the locations of the links and the values should be their targets. 69 | | 70 | */ 71 | 72 | 'links' => [ 73 | public_path('storage') => storage_path('app/public'), 74 | ], 75 | 76 | ]; 77 | -------------------------------------------------------------------------------- /backend/config/logging.php: -------------------------------------------------------------------------------- 1 | env('LOG_CHANNEL', 'stack'), 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Deprecations Log Channel 26 | |-------------------------------------------------------------------------- 27 | | 28 | | This option controls the log channel that should be used to log warnings 29 | | regarding deprecated PHP and library features. This allows you to get 30 | | your application ready for upcoming major versions of dependencies. 31 | | 32 | */ 33 | 34 | 'deprecations' => [ 35 | 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), 36 | 'trace' => env('LOG_DEPRECATIONS_TRACE', false), 37 | ], 38 | 39 | /* 40 | |-------------------------------------------------------------------------- 41 | | Log Channels 42 | |-------------------------------------------------------------------------- 43 | | 44 | | Here you may configure the log channels for your application. Laravel 45 | | utilizes the Monolog PHP logging library, which includes a variety 46 | | of powerful log handlers and formatters that you're free to use. 47 | | 48 | | Available Drivers: "single", "daily", "slack", "syslog", 49 | | "errorlog", "monolog", "custom", "stack" 50 | | 51 | */ 52 | 53 | 'channels' => [ 54 | 55 | 'stack' => [ 56 | 'driver' => 'stack', 57 | 'channels' => explode(',', env('LOG_STACK', 'single')), 58 | 'ignore_exceptions' => false, 59 | ], 60 | 61 | 'single' => [ 62 | 'driver' => 'single', 63 | 'path' => storage_path('logs/laravel.log'), 64 | 'level' => env('LOG_LEVEL', 'debug'), 65 | 'replace_placeholders' => true, 66 | ], 67 | 68 | 'daily' => [ 69 | 'driver' => 'daily', 70 | 'path' => storage_path('logs/laravel.log'), 71 | 'level' => env('LOG_LEVEL', 'debug'), 72 | 'days' => env('LOG_DAILY_DAYS', 14), 73 | 'replace_placeholders' => true, 74 | ], 75 | 76 | 'slack' => [ 77 | 'driver' => 'slack', 78 | 'url' => env('LOG_SLACK_WEBHOOK_URL'), 79 | 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), 80 | 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), 81 | 'level' => env('LOG_LEVEL', 'critical'), 82 | 'replace_placeholders' => true, 83 | ], 84 | 85 | 'papertrail' => [ 86 | 'driver' => 'monolog', 87 | 'level' => env('LOG_LEVEL', 'debug'), 88 | 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), 89 | 'handler_with' => [ 90 | 'host' => env('PAPERTRAIL_URL'), 91 | 'port' => env('PAPERTRAIL_PORT'), 92 | 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), 93 | ], 94 | 'processors' => [PsrLogMessageProcessor::class], 95 | ], 96 | 97 | 'stderr' => [ 98 | 'driver' => 'monolog', 99 | 'level' => env('LOG_LEVEL', 'debug'), 100 | 'handler' => StreamHandler::class, 101 | 'formatter' => env('LOG_STDERR_FORMATTER'), 102 | 'with' => [ 103 | 'stream' => 'php://stderr', 104 | ], 105 | 'processors' => [PsrLogMessageProcessor::class], 106 | ], 107 | 108 | 'syslog' => [ 109 | 'driver' => 'syslog', 110 | 'level' => env('LOG_LEVEL', 'debug'), 111 | 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), 112 | 'replace_placeholders' => true, 113 | ], 114 | 115 | 'errorlog' => [ 116 | 'driver' => 'errorlog', 117 | 'level' => env('LOG_LEVEL', 'debug'), 118 | 'replace_placeholders' => true, 119 | ], 120 | 121 | 'null' => [ 122 | 'driver' => 'monolog', 123 | 'handler' => NullHandler::class, 124 | ], 125 | 126 | 'emergency' => [ 127 | 'path' => storage_path('logs/laravel.log'), 128 | ], 129 | 130 | ], 131 | 132 | ]; 133 | -------------------------------------------------------------------------------- /backend/config/mail.php: -------------------------------------------------------------------------------- 1 | env('MAIL_MAILER', 'log'), 18 | 19 | /* 20 | |-------------------------------------------------------------------------- 21 | | Mailer Configurations 22 | |-------------------------------------------------------------------------- 23 | | 24 | | Here you may configure all of the mailers used by your application plus 25 | | their respective settings. Several examples have been configured for 26 | | you and you are free to add your own as your application requires. 27 | | 28 | | Laravel supports a variety of mail "transport" drivers that can be used 29 | | when delivering an email. You may specify which one you're using for 30 | | your mailers below. You may also add additional mailers if needed. 31 | | 32 | | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", 33 | | "postmark", "log", "array", "failover", "roundrobin" 34 | | 35 | */ 36 | 37 | 'mailers' => [ 38 | 39 | 'smtp' => [ 40 | 'transport' => 'smtp', 41 | 'url' => env('MAIL_URL'), 42 | 'host' => env('MAIL_HOST', '127.0.0.1'), 43 | 'port' => env('MAIL_PORT', 2525), 44 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 45 | 'username' => env('MAIL_USERNAME'), 46 | 'password' => env('MAIL_PASSWORD'), 47 | 'timeout' => null, 48 | 'local_domain' => env('MAIL_EHLO_DOMAIN'), 49 | ], 50 | 51 | 'ses' => [ 52 | 'transport' => 'ses', 53 | ], 54 | 55 | 'postmark' => [ 56 | 'transport' => 'postmark', 57 | // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), 58 | // 'client' => [ 59 | // 'timeout' => 5, 60 | // ], 61 | ], 62 | 63 | 'sendmail' => [ 64 | 'transport' => 'sendmail', 65 | 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), 66 | ], 67 | 68 | 'log' => [ 69 | 'transport' => 'log', 70 | 'channel' => env('MAIL_LOG_CHANNEL'), 71 | ], 72 | 73 | 'array' => [ 74 | 'transport' => 'array', 75 | ], 76 | 77 | 'failover' => [ 78 | 'transport' => 'failover', 79 | 'mailers' => [ 80 | 'smtp', 81 | 'log', 82 | ], 83 | ], 84 | 85 | ], 86 | 87 | /* 88 | |-------------------------------------------------------------------------- 89 | | Global "From" Address 90 | |-------------------------------------------------------------------------- 91 | | 92 | | You may wish for all emails sent by your application to be sent from 93 | | the same address. Here you may specify a name and address that is 94 | | used globally for all emails that are sent by your application. 95 | | 96 | */ 97 | 98 | 'from' => [ 99 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), 100 | 'name' => env('MAIL_FROM_NAME', 'Example'), 101 | ], 102 | 103 | ]; 104 | -------------------------------------------------------------------------------- /backend/config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_CONNECTION', 'database'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Queue Connections 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may configure the connection options for every queue backend 24 | | used by your application. An example configuration is provided for 25 | | each backend supported by Laravel. You're also free to add more. 26 | | 27 | | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'sync' => [ 34 | 'driver' => 'sync', 35 | ], 36 | 37 | 'database' => [ 38 | 'driver' => 'database', 39 | 'connection' => env('DB_QUEUE_CONNECTION', null), 40 | 'table' => env('DB_QUEUE_TABLE', 'jobs'), 41 | 'queue' => env('DB_QUEUE', 'default'), 42 | 'retry_after' => env('DB_QUEUE_RETRY_AFTER', 90), 43 | 'after_commit' => false, 44 | ], 45 | 46 | 'beanstalkd' => [ 47 | 'driver' => 'beanstalkd', 48 | 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), 49 | 'queue' => env('BEANSTALKD_QUEUE', 'default'), 50 | 'retry_after' => env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), 51 | 'block_for' => 0, 52 | 'after_commit' => false, 53 | ], 54 | 55 | 'sqs' => [ 56 | 'driver' => 'sqs', 57 | 'key' => env('AWS_ACCESS_KEY_ID'), 58 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 59 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 60 | 'queue' => env('SQS_QUEUE', 'default'), 61 | 'suffix' => env('SQS_SUFFIX'), 62 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 63 | 'after_commit' => false, 64 | ], 65 | 66 | 'redis' => [ 67 | 'driver' => 'redis', 68 | 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), 69 | 'queue' => env('REDIS_QUEUE', 'default'), 70 | 'retry_after' => env('REDIS_QUEUE_RETRY_AFTER', 90), 71 | 'block_for' => null, 72 | 'after_commit' => false, 73 | ], 74 | 75 | ], 76 | 77 | /* 78 | |-------------------------------------------------------------------------- 79 | | Job Batching 80 | |-------------------------------------------------------------------------- 81 | | 82 | | The following options configure the database and table that store job 83 | | batching information. These options can be updated to any database 84 | | connection and table which has been defined by your application. 85 | | 86 | */ 87 | 88 | 'batching' => [ 89 | 'database' => env('DB_CONNECTION', 'sqlite'), 90 | 'table' => 'job_batches', 91 | ], 92 | 93 | /* 94 | |-------------------------------------------------------------------------- 95 | | Failed Queue Jobs 96 | |-------------------------------------------------------------------------- 97 | | 98 | | These options configure the behavior of failed queue job logging so you 99 | | can control how and where failed jobs are stored. Laravel ships with 100 | | support for storing failed jobs in a simple file or in a database. 101 | | 102 | | Supported drivers: "database-uuids", "dynamodb", "file", "null" 103 | | 104 | */ 105 | 106 | 'failed' => [ 107 | 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), 108 | 'database' => env('DB_CONNECTION', 'sqlite'), 109 | 'table' => 'failed_jobs', 110 | ], 111 | 112 | ]; 113 | -------------------------------------------------------------------------------- /backend/config/reverb.php: -------------------------------------------------------------------------------- 1 | env('REVERB_SERVER', 'reverb'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Reverb Servers 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may define details for each of the supported Reverb servers. 24 | | Each server has its own configuration options that are defined in 25 | | the array below. You should ensure all the options are present. 26 | | 27 | */ 28 | 29 | 'servers' => [ 30 | 31 | 'reverb' => [ 32 | 'host' => env('REVERB_SERVER_HOST', '0.0.0.0'), 33 | 'port' => env('REVERB_SERVER_PORT', 8080), 34 | 'hostname' => env('REVERB_HOST'), 35 | 'options' => [ 36 | 'tls' => [], 37 | ], 38 | 'scaling' => [ 39 | 'enabled' => env('REVERB_SCALING_ENABLED', false), 40 | 'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'), 41 | ], 42 | 'pulse_ingest_interval' => env('REVERB_PULSE_INGEST_INTERVAL', 15), 43 | ], 44 | 45 | ], 46 | 47 | /* 48 | |-------------------------------------------------------------------------- 49 | | Reverb Applications 50 | |-------------------------------------------------------------------------- 51 | | 52 | | Here you may define how Reverb applications are managed. If you choose 53 | | to use the "config" provider, you may define an array of apps which 54 | | your server will support, including their connection credentials. 55 | | 56 | */ 57 | 58 | 'apps' => [ 59 | 60 | 'provider' => 'config', 61 | 62 | 'apps' => [ 63 | [ 64 | 'key' => env('REVERB_APP_KEY'), 65 | 'secret' => env('REVERB_APP_SECRET'), 66 | 'app_id' => env('REVERB_APP_ID'), 67 | 'options' => [ 68 | 'host' => env('REVERB_HOST'), 69 | 'port' => env('REVERB_PORT', 443), 70 | 'scheme' => env('REVERB_SCHEME', 'https'), 71 | 'useTLS' => env('REVERB_SCHEME', 'https') === 'https', 72 | ], 73 | 'allowed_origins' => ['*'], 74 | 'ping_interval' => env('REVERB_APP_PING_INTERVAL', 60), 75 | 'max_message_size' => env('REVERB_APP_MAX_MESSAGE_SIZE', 10_000), 76 | ], 77 | ], 78 | 79 | ], 80 | 81 | ]; 82 | -------------------------------------------------------------------------------- /backend/config/sanctum.php: -------------------------------------------------------------------------------- 1 | explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( 19 | '%s%s', 20 | 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', 21 | Sanctum::currentApplicationUrlWithPort() 22 | ))), 23 | 24 | /* 25 | |-------------------------------------------------------------------------- 26 | | Sanctum Guards 27 | |-------------------------------------------------------------------------- 28 | | 29 | | This array contains the authentication guards that will be checked when 30 | | Sanctum is trying to authenticate a request. If none of these guards 31 | | are able to authenticate the request, Sanctum will use the bearer 32 | | token that's present on an incoming request for authentication. 33 | | 34 | */ 35 | 36 | 'guard' => ['web'], 37 | 38 | /* 39 | |-------------------------------------------------------------------------- 40 | | Expiration Minutes 41 | |-------------------------------------------------------------------------- 42 | | 43 | | This value controls the number of minutes until an issued token will be 44 | | considered expired. This will override any values set in the token's 45 | | "expires_at" attribute, but first-party sessions are not affected. 46 | | 47 | */ 48 | 49 | 'expiration' => null, 50 | 51 | /* 52 | |-------------------------------------------------------------------------- 53 | | Token Prefix 54 | |-------------------------------------------------------------------------- 55 | | 56 | | Sanctum can prefix new tokens in order to take advantage of numerous 57 | | security scanning initiatives maintained by open source platforms 58 | | that notify developers if they commit tokens into repositories. 59 | | 60 | | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning 61 | | 62 | */ 63 | 64 | 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), 65 | 66 | /* 67 | |-------------------------------------------------------------------------- 68 | | Sanctum Middleware 69 | |-------------------------------------------------------------------------- 70 | | 71 | | When authenticating your first-party SPA with Sanctum you may need to 72 | | customize some of the middleware Sanctum uses while processing the 73 | | request. You may change the middleware listed below as required. 74 | | 75 | */ 76 | 77 | 'middleware' => [ 78 | 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, 79 | 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, 80 | 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, 81 | ], 82 | 83 | ]; 84 | -------------------------------------------------------------------------------- /backend/config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'token' => env('POSTMARK_TOKEN'), 19 | ], 20 | 21 | 'ses' => [ 22 | 'key' => env('AWS_ACCESS_KEY_ID'), 23 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 24 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 25 | ], 26 | 27 | 'slack' => [ 28 | 'notifications' => [ 29 | 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), 30 | 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), 31 | ], 32 | ], 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /backend/config/session.php: -------------------------------------------------------------------------------- 1 | env('SESSION_DRIVER', 'database'), 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Session Lifetime 26 | |-------------------------------------------------------------------------- 27 | | 28 | | Here you may specify the number of minutes that you wish the session 29 | | to be allowed to remain idle before it expires. If you want them 30 | | to expire immediately when the browser is closed then you may 31 | | indicate that via the expire_on_close configuration option. 32 | | 33 | */ 34 | 35 | 'lifetime' => env('SESSION_LIFETIME', 120), 36 | 37 | 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), 38 | 39 | /* 40 | |-------------------------------------------------------------------------- 41 | | Session Encryption 42 | |-------------------------------------------------------------------------- 43 | | 44 | | This option allows you to easily specify that all of your session data 45 | | should be encrypted before it's stored. All encryption is performed 46 | | automatically by Laravel and you may use the session like normal. 47 | | 48 | */ 49 | 50 | 'encrypt' => env('SESSION_ENCRYPT', false), 51 | 52 | /* 53 | |-------------------------------------------------------------------------- 54 | | Session File Location 55 | |-------------------------------------------------------------------------- 56 | | 57 | | When utilizing the "file" session driver, the session files are placed 58 | | on disk. The default storage location is defined here; however, you 59 | | are free to provide another location where they should be stored. 60 | | 61 | */ 62 | 63 | 'files' => storage_path('framework/sessions'), 64 | 65 | /* 66 | |-------------------------------------------------------------------------- 67 | | Session Database Connection 68 | |-------------------------------------------------------------------------- 69 | | 70 | | When using the "database" or "redis" session drivers, you may specify a 71 | | connection that should be used to manage these sessions. This should 72 | | correspond to a connection in your database configuration options. 73 | | 74 | */ 75 | 76 | 'connection' => env('SESSION_CONNECTION'), 77 | 78 | /* 79 | |-------------------------------------------------------------------------- 80 | | Session Database Table 81 | |-------------------------------------------------------------------------- 82 | | 83 | | When using the "database" session driver, you may specify the table to 84 | | be used to store sessions. Of course, a sensible default is defined 85 | | for you; however, you're welcome to change this to another table. 86 | | 87 | */ 88 | 89 | 'table' => env('SESSION_TABLE', 'sessions'), 90 | 91 | /* 92 | |-------------------------------------------------------------------------- 93 | | Session Cache Store 94 | |-------------------------------------------------------------------------- 95 | | 96 | | When using one of the framework's cache driven session backends, you may 97 | | define the cache store which should be used to store the session data 98 | | between requests. This must match one of your defined cache stores. 99 | | 100 | | Affects: "apc", "dynamodb", "memcached", "redis" 101 | | 102 | */ 103 | 104 | 'store' => env('SESSION_STORE'), 105 | 106 | /* 107 | |-------------------------------------------------------------------------- 108 | | Session Sweeping Lottery 109 | |-------------------------------------------------------------------------- 110 | | 111 | | Some session drivers must manually sweep their storage location to get 112 | | rid of old sessions from storage. Here are the chances that it will 113 | | happen on a given request. By default, the odds are 2 out of 100. 114 | | 115 | */ 116 | 117 | 'lottery' => [2, 100], 118 | 119 | /* 120 | |-------------------------------------------------------------------------- 121 | | Session Cookie Name 122 | |-------------------------------------------------------------------------- 123 | | 124 | | Here you may change the name of the session cookie that is created by 125 | | the framework. Typically, you should not need to change this value 126 | | since doing so does not grant a meaningful security improvement. 127 | | 128 | | 129 | */ 130 | 131 | 'cookie' => env( 132 | 'SESSION_COOKIE', 133 | Str::slug(env('APP_NAME', 'laravel'), '_').'_session' 134 | ), 135 | 136 | /* 137 | |-------------------------------------------------------------------------- 138 | | Session Cookie Path 139 | |-------------------------------------------------------------------------- 140 | | 141 | | The session cookie path determines the path for which the cookie will 142 | | be regarded as available. Typically, this will be the root path of 143 | | your application, but you're free to change this when necessary. 144 | | 145 | */ 146 | 147 | 'path' => env('SESSION_PATH', '/'), 148 | 149 | /* 150 | |-------------------------------------------------------------------------- 151 | | Session Cookie Domain 152 | |-------------------------------------------------------------------------- 153 | | 154 | | This value determines the domain and subdomains the session cookie is 155 | | available to. By default, the cookie will be available to the root 156 | | domain and all subdomains. Typically, this shouldn't be changed. 157 | | 158 | */ 159 | 160 | 'domain' => env('SESSION_DOMAIN'), 161 | 162 | /* 163 | |-------------------------------------------------------------------------- 164 | | HTTPS Only Cookies 165 | |-------------------------------------------------------------------------- 166 | | 167 | | By setting this option to true, session cookies will only be sent back 168 | | to the server if the browser has a HTTPS connection. This will keep 169 | | the cookie from being sent to you when it can't be done securely. 170 | | 171 | */ 172 | 173 | 'secure' => env('SESSION_SECURE_COOKIE'), 174 | 175 | /* 176 | |-------------------------------------------------------------------------- 177 | | HTTP Access Only 178 | |-------------------------------------------------------------------------- 179 | | 180 | | Setting this value to true will prevent JavaScript from accessing the 181 | | value of the cookie and the cookie will only be accessible through 182 | | the HTTP protocol. It's unlikely you should disable this option. 183 | | 184 | */ 185 | 186 | 'http_only' => env('SESSION_HTTP_ONLY', true), 187 | 188 | /* 189 | |-------------------------------------------------------------------------- 190 | | Same-Site Cookies 191 | |-------------------------------------------------------------------------- 192 | | 193 | | This option determines how your cookies behave when cross-site requests 194 | | take place, and can be used to mitigate CSRF attacks. By default, we 195 | | will set this value to "lax" to permit secure cross-site requests. 196 | | 197 | | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value 198 | | 199 | | Supported: "lax", "strict", "none", null 200 | | 201 | */ 202 | 203 | 'same_site' => env('SESSION_SAME_SITE', 'lax'), 204 | 205 | /* 206 | |-------------------------------------------------------------------------- 207 | | Partitioned Cookies 208 | |-------------------------------------------------------------------------- 209 | | 210 | | Setting this value to true will tie the cookie to the top-level site for 211 | | a cross-site context. Partitioned cookies are accepted by the browser 212 | | when flagged "secure" and the Same-Site attribute is set to "none". 213 | | 214 | */ 215 | 216 | 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), 217 | 218 | ]; 219 | -------------------------------------------------------------------------------- /backend/database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /backend/database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class UserFactory extends Factory 13 | { 14 | /** 15 | * The current password being used by the factory. 16 | */ 17 | protected static ?string $password; 18 | 19 | /** 20 | * Define the model's default state. 21 | * 22 | * @return array 23 | */ 24 | public function definition(): array 25 | { 26 | return [ 27 | 'name' => fake()->name(), 28 | 'email' => fake()->unique()->safeEmail(), 29 | 'email_verified_at' => now(), 30 | 'password' => static::$password ??= Hash::make('password'), 31 | 'remember_token' => Str::random(10), 32 | ]; 33 | } 34 | 35 | /** 36 | * Indicate that the model's email address should be unverified. 37 | */ 38 | public function unverified(): static 39 | { 40 | return $this->state(fn (array $attributes) => [ 41 | 'email_verified_at' => null, 42 | ]); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /backend/database/migrations/0001_01_01_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | id(); 15 | $table->string('name'); 16 | $table->string('email')->unique(); 17 | $table->string('username')->unique(); 18 | $table->string("profile_image")->nullable(); 19 | $table->timestamp('email_verified_at')->nullable(); 20 | $table->string('password'); 21 | $table->rememberToken(); 22 | $table->timestamps(); 23 | }); 24 | 25 | Schema::create('password_reset_tokens', function (Blueprint $table) { 26 | $table->string('email')->primary(); 27 | $table->string('token'); 28 | $table->timestamp('created_at')->nullable(); 29 | }); 30 | 31 | Schema::create('sessions', function (Blueprint $table) { 32 | $table->string('id')->primary(); 33 | $table->foreignId('user_id')->nullable()->index(); 34 | $table->string('ip_address', 45)->nullable(); 35 | $table->text('user_agent')->nullable(); 36 | $table->longText('payload'); 37 | $table->integer('last_activity')->index(); 38 | }); 39 | } 40 | 41 | /** 42 | * Reverse the migrations. 43 | */ 44 | public function down(): void 45 | { 46 | Schema::dropIfExists('users'); 47 | Schema::dropIfExists('password_reset_tokens'); 48 | Schema::dropIfExists('sessions'); 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /backend/database/migrations/0001_01_01_000001_create_cache_table.php: -------------------------------------------------------------------------------- 1 | string('key')->primary(); 16 | $table->mediumText('value'); 17 | $table->integer('expiration'); 18 | }); 19 | 20 | Schema::create('cache_locks', function (Blueprint $table) { 21 | $table->string('key')->primary(); 22 | $table->string('owner'); 23 | $table->integer('expiration'); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | */ 30 | public function down(): void 31 | { 32 | Schema::dropIfExists('cache'); 33 | Schema::dropIfExists('cache_locks'); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /backend/database/migrations/0001_01_01_000002_create_jobs_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('queue')->index(); 17 | $table->longText('payload'); 18 | $table->unsignedTinyInteger('attempts'); 19 | $table->unsignedInteger('reserved_at')->nullable(); 20 | $table->unsignedInteger('available_at'); 21 | $table->unsignedInteger('created_at'); 22 | }); 23 | 24 | Schema::create('job_batches', function (Blueprint $table) { 25 | $table->string('id')->primary(); 26 | $table->string('name'); 27 | $table->integer('total_jobs'); 28 | $table->integer('pending_jobs'); 29 | $table->integer('failed_jobs'); 30 | $table->longText('failed_job_ids'); 31 | $table->mediumText('options')->nullable(); 32 | $table->integer('cancelled_at')->nullable(); 33 | $table->integer('created_at'); 34 | $table->integer('finished_at')->nullable(); 35 | }); 36 | 37 | Schema::create('failed_jobs', function (Blueprint $table) { 38 | $table->id(); 39 | $table->string('uuid')->unique(); 40 | $table->text('connection'); 41 | $table->text('queue'); 42 | $table->longText('payload'); 43 | $table->longText('exception'); 44 | $table->timestamp('failed_at')->useCurrent(); 45 | }); 46 | } 47 | 48 | /** 49 | * Reverse the migrations. 50 | */ 51 | public function down(): void 52 | { 53 | Schema::dropIfExists('jobs'); 54 | Schema::dropIfExists('job_batches'); 55 | Schema::dropIfExists('failed_jobs'); 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /backend/database/migrations/2024_03_15_123846_create_personal_access_tokens_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->morphs('tokenable'); 17 | $table->string('name'); 18 | $table->string('token', 64)->unique(); 19 | $table->text('abilities')->nullable(); 20 | $table->timestamp('last_used_at')->nullable(); 21 | $table->timestamp('expires_at')->nullable(); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | */ 29 | public function down(): void 30 | { 31 | Schema::dropIfExists('personal_access_tokens'); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /backend/database/migrations/2024_03_17_105321_create_posts_table.php: -------------------------------------------------------------------------------- 1 | id(); 15 | $table->foreignId('user_id')->constrained()->onDelete("CASCADE"); 16 | $table->text("url"); 17 | $table->text("image_url"); 18 | $table->string("title")->index(); 19 | $table->text("description")->nullable(); 20 | $table->unsignedInteger("vote")->default(0); 21 | $table->unsignedInteger("comment_count")->default(0); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | */ 29 | public function down(): void 30 | { 31 | Schema::dropIfExists('posts'); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /backend/database/migrations/2024_03_20_163112_create_comments_table.php: -------------------------------------------------------------------------------- 1 | id(); 15 | $table->foreignId("user_id")->constrained()->onDelete("CASCADE"); 16 | $table->foreignId("post_id")->constrained()->onDelete("CASCADE"); 17 | $table->text("comment")->nullable(); 18 | $table->timestamps(); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | */ 25 | public function down(): void 26 | { 27 | Schema::dropIfExists('comments'); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /backend/database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 17 | 18 | User::factory()->create([ 19 | 'name' => 'Test User', 20 | 'email' => 'test@example.com', 21 | ]); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build" 7 | }, 8 | "devDependencies": { 9 | "axios": "^1.6.4", 10 | "laravel-vite-plugin": "^1.0", 11 | "vite": "^5.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/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 | -------------------------------------------------------------------------------- /backend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TusharVashishth/daily-dev-clone/39c9f0a69bf072015771f4edf73f14dd3eba169b/backend/public/favicon.ico -------------------------------------------------------------------------------- /backend/public/index.php: -------------------------------------------------------------------------------- 1 | handleRequest(Request::capture()); 18 | -------------------------------------------------------------------------------- /backend/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /backend/resources/css/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TusharVashishth/daily-dev-clone/39c9f0a69bf072015771f4edf73f14dd3eba169b/backend/resources/css/app.css -------------------------------------------------------------------------------- /backend/resources/js/app.js: -------------------------------------------------------------------------------- 1 | import './bootstrap'; 2 | -------------------------------------------------------------------------------- /backend/resources/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | window.axios = axios; 3 | 4 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 5 | 6 | /** 7 | * Echo exposes an expressive API for subscribing to channels and listening 8 | * for events that are broadcast by Laravel. Echo and event broadcasting 9 | * allow your team to quickly build robust real-time web applications. 10 | */ 11 | 12 | import './echo'; 13 | -------------------------------------------------------------------------------- /backend/resources/js/echo.js: -------------------------------------------------------------------------------- 1 | import Echo from 'laravel-echo'; 2 | 3 | import Pusher from 'pusher-js'; 4 | window.Pusher = Pusher; 5 | 6 | window.Echo = new Echo({ 7 | broadcaster: 'reverb', 8 | key: import.meta.env.VITE_REVERB_APP_KEY, 9 | wsHost: import.meta.env.VITE_REVERB_HOST, 10 | wsPort: import.meta.env.VITE_REVERB_PORT ?? 80, 11 | wssPort: import.meta.env.VITE_REVERB_PORT ?? 443, 12 | forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https', 13 | enabledTransports: ['ws', 'wss'], 14 | }); 15 | -------------------------------------------------------------------------------- /backend/routes/api.php: -------------------------------------------------------------------------------- 1 | user(); 16 | })->middleware('auth:sanctum'); 17 | 18 | Route::middleware('auth:sanctum')->group(function () { 19 | Route::apiResources([ 20 | "post" => PostController::class, 21 | "comment" => CommentController::class, 22 | ]); 23 | 24 | Route::post("/update/profile", [UserController::class, 'updateProfileImage']); 25 | }); 26 | 27 | 28 | Route::post("/test/channel", function (Request $request) { 29 | // $post = Post::where("id", "2")->with('user')->first(); 30 | // PostBroadCastEvent::dispatch($post); 31 | CommentIncrement::dispatch(2); 32 | // TestEvent::dispatch($request->all()); 33 | return response()->json(["message" => "data sent successfully!"]); 34 | }); 35 | 36 | Route::post("/auth/login", [AuthController::class, 'login']); 37 | Route::post("/auth/register", [AuthController::class, 'register']); 38 | Route::post("/auth/checkCredentials", [AuthController::class, 'checkCredentias']); 39 | 40 | Broadcast::routes(['middleware' => ['auth:sanctum']]); -------------------------------------------------------------------------------- /backend/routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 9 | // return true; 10 | }); 11 | -------------------------------------------------------------------------------- /backend/routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 8 | })->purpose('Display an inspiring quote')->hourly(); 9 | -------------------------------------------------------------------------------- /backend/routes/web.php: -------------------------------------------------------------------------------- 1 | get('/'); 5 | 6 | $response->assertStatus(200); 7 | }); 8 | -------------------------------------------------------------------------------- /backend/tests/Pest.php: -------------------------------------------------------------------------------- 1 | in('Feature'); 18 | 19 | /* 20 | |-------------------------------------------------------------------------- 21 | | Expectations 22 | |-------------------------------------------------------------------------- 23 | | 24 | | When you're writing tests, you often need to check that values meet certain conditions. The 25 | | "expect()" function gives you access to a set of "expectations" methods that you can use 26 | | to assert different things. Of course, you may extend the Expectation API at any time. 27 | | 28 | */ 29 | 30 | expect()->extend('toBeOne', function () { 31 | return $this->toBe(1); 32 | }); 33 | 34 | /* 35 | |-------------------------------------------------------------------------- 36 | | Functions 37 | |-------------------------------------------------------------------------- 38 | | 39 | | While Pest is very powerful out-of-the-box, you may have some testing code specific to your 40 | | project that you don't want to repeat in every file. Here you can also expose helpers as 41 | | global functions to help you to reduce the number of lines of code in your test files. 42 | | 43 | */ 44 | 45 | function something() 46 | { 47 | // .. 48 | } 49 | -------------------------------------------------------------------------------- /backend/tests/TestCase.php: -------------------------------------------------------------------------------- 1 | toBeTrue(); 5 | }); 6 | -------------------------------------------------------------------------------- /backend/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import laravel from 'laravel-vite-plugin'; 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | laravel({ 7 | input: ['resources/css/app.css', 'resources/js/app.js'], 8 | refresh: true, 9 | }), 10 | ], 11 | }); 12 | -------------------------------------------------------------------------------- /frontend/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_NEXTAUTH_URL=http://localhost:3000 2 | NEXTAUTH_SECRET="YOUR_KEY" 3 | NEXT_PUBLIC_API_URL=http://localhost:8000 4 | 5 | NEXT_PUBLIC_REVERB_APP_ID=266721 6 | NEXT_PUBLIC_REVERB_APP_KEY=YOUR_KEY 7 | NEXT_PUBLIC_REVERB_APP_SECRET=YOUR_KEY 8 | NEXT_PUBLIC_REVERB_HOST="localhost" 9 | NEXT_PUBLIC_REVERB_PORT=8080 10 | NEXT_PUBLIC_REVERB_SCHEME=http -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 37 | -------------------------------------------------------------------------------- /frontend/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /frontend/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | remotePatterns: [ 5 | { 6 | hostname: "*", 7 | protocol: "https", 8 | }, 9 | { 10 | hostname: "localhost", 11 | protocol: "http", 12 | }, 13 | ], 14 | }, 15 | reactStrictMode: false, 16 | }; 17 | 18 | export default nextConfig; 19 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@radix-ui/react-dialog": "^1.0.5", 13 | "@radix-ui/react-dropdown-menu": "^2.0.6", 14 | "@radix-ui/react-icons": "^1.3.0", 15 | "@radix-ui/react-label": "^2.0.2", 16 | "@radix-ui/react-slot": "^1.0.2", 17 | "@radix-ui/react-tabs": "^1.0.4", 18 | "axios": "^1.6.8", 19 | "class-variance-authority": "^0.7.0", 20 | "clsx": "^2.1.0", 21 | "link-preview-js": "^3.0.5", 22 | "lucide-react": "^0.358.0", 23 | "moment": "^2.30.1", 24 | "next": "14.1.3", 25 | "next-auth": "^4.24.7", 26 | "react": "^18", 27 | "react-dom": "^18", 28 | "react-toastify": "^10.0.5", 29 | "tailwind-merge": "^2.2.1", 30 | "tailwindcss-animate": "^1.0.7", 31 | "use-immer": "^0.9.0" 32 | }, 33 | "devDependencies": { 34 | "@types/node": "^20", 35 | "@types/react": "^18", 36 | "@types/react-dom": "^18", 37 | "autoprefixer": "^10.0.1", 38 | "laravel-echo": "^1.16.0", 39 | "postcss": "^8", 40 | "pusher-js": "^8.4.0-rc2", 41 | "tailwindcss": "^3.3.0", 42 | "typescript": "^5" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /frontend/public/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TusharVashishth/daily-dev-clone/39c9f0a69bf072015771f4edf73f14dd3eba169b/frontend/public/avatar.png -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TusharVashishth/daily-dev-clone/39c9f0a69bf072015771f4edf73f14dd3eba169b/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Full Logo- Dark/W Logo 4 | 16 | -------------------------------------------------------------------------------- /frontend/src/app/(daily_dev)/layout.tsx: -------------------------------------------------------------------------------- 1 | import Navbar from "@/components/base/Navbar"; 2 | import Sidebar from "@/components/base/Sidebar"; 3 | import { getServerSession } from "next-auth"; 4 | import { 5 | CustomSession, 6 | authOptions, 7 | } from "../api/auth/[...nextauth]/authOptions"; 8 | 9 | export default async function DailyDevLayout({ 10 | children, 11 | }: Readonly<{ 12 | children: React.ReactNode; 13 | }>) { 14 | const session = (await getServerSession(authOptions)) as CustomSession; 15 | return ( 16 |
17 | 18 |
19 | 20 |
21 | {children} 22 |
23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/app/(daily_dev)/page.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { getServerSession } from "next-auth"; 3 | import { 4 | CustomSession, 5 | authOptions, 6 | } from "../api/auth/[...nextauth]/authOptions"; 7 | import { getPosts } from "@/dataFetch/postFetch"; 8 | import Posts from "@/components/post/Posts"; 9 | 10 | export default async function App() { 11 | const session = (await getServerSession(authOptions)) as CustomSession; 12 | const posts: APIResponseType = await getPosts( 13 | session.user?.token! 14 | ); 15 | return ( 16 |
17 | 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/app/api/auth/[...nextauth]/authOptions.ts: -------------------------------------------------------------------------------- 1 | import { LOGIN_URL } from "@/lib/apiEndPoints"; 2 | import myAxios from "@/lib/axios.config"; 3 | import axios, { AxiosResponse } from "axios"; 4 | import { AuthOptions, ISODateString, User } from "next-auth"; 5 | import { JWT } from "next-auth/jwt"; 6 | import CredentialsProvider from "next-auth/providers/credentials"; 7 | 8 | export interface CustomSession { 9 | user?: CustomUser; 10 | expires: ISODateString; 11 | } 12 | export interface CustomUser { 13 | id?: string | null; 14 | name?: string | null; 15 | email?: string | null; 16 | profile_image?: string | null; 17 | token?: string | null; 18 | created_at?: string | null; 19 | updated_at?: string | null; 20 | } 21 | export const authOptions: AuthOptions = { 22 | pages: { 23 | signIn: "/login", 24 | }, 25 | callbacks: { 26 | async jwt({ token, user, trigger, session }) { 27 | // * When we update the session 28 | if (trigger === "update" && session?.profile_image) { 29 | const user: CustomUser = token.user as CustomUser; 30 | user.profile_image = session?.profile_image; 31 | console.log("The token is", token); 32 | } 33 | if (user) { 34 | token.user = user; 35 | } 36 | return token; 37 | }, 38 | 39 | async session({ 40 | session, 41 | token, 42 | user, 43 | }: { 44 | session: CustomSession; 45 | token: JWT; 46 | user: User; 47 | }) { 48 | session.user = token.user as CustomUser; 49 | return session; 50 | }, 51 | }, 52 | 53 | providers: [ 54 | CredentialsProvider({ 55 | name: "Credentials", 56 | credentials: { 57 | email: {}, 58 | password: {}, 59 | }, 60 | async authorize(credentials, req) { 61 | const res = await myAxios.post(LOGIN_URL, credentials); 62 | const response = res.data; 63 | const user = response?.user; 64 | if (user) { 65 | return user; 66 | } else { 67 | return null; 68 | } 69 | }, 70 | }), 71 | ], 72 | }; 73 | -------------------------------------------------------------------------------- /frontend/src/app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | import NextAuth from "next-auth/next"; 2 | import { authOptions } from "./authOptions"; 3 | 4 | const nextAuth = NextAuth(authOptions); 5 | 6 | export { nextAuth as GET, nextAuth as POST }; 7 | -------------------------------------------------------------------------------- /frontend/src/app/api/image-preview/route.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from "next/server"; 2 | import { getLinkPreview } from "link-preview-js"; 3 | import { unstable_noStore } from "next/cache"; 4 | 5 | export async function POST(request: NextRequest) { 6 | unstable_noStore(); 7 | try { 8 | const body = await request.json(); 9 | const data: ImagePreviewResType = (await getLinkPreview(body.url, { 10 | imagesPropertyType: "og", 11 | followRedirects: "follow", 12 | })) as ImagePreviewResType; 13 | return NextResponse.json({ status: 200, data }); 14 | } catch (error) { 15 | return NextResponse.json({ status: 404, message: "Not found" }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 222.2 84% 4.9%; 9 | 10 | --card: 0 0% 100%; 11 | --card-foreground: 222.2 84% 4.9%; 12 | 13 | --popover: 0 0% 100%; 14 | --popover-foreground: 222.2 84% 4.9%; 15 | 16 | --primary: 222.2 47.4% 11.2%; 17 | --primary-foreground: 210 40% 98%; 18 | 19 | --secondary: 210 40% 96.1%; 20 | --secondary-foreground: 222.2 47.4% 11.2%; 21 | 22 | --muted: 210 40% 96.1%; 23 | --muted-foreground: 215.4 16.3% 46.9%; 24 | 25 | --accent: 210 40% 96.1%; 26 | --accent-foreground: 222.2 47.4% 11.2%; 27 | 28 | --destructive: 0 84.2% 60.2%; 29 | --destructive-foreground: 210 40% 98%; 30 | 31 | --border: 214.3 31.8% 91.4%; 32 | --input: 214.3 31.8% 91.4%; 33 | --ring: 222.2 84% 4.9%; 34 | 35 | --radius: 0.5rem; 36 | } 37 | 38 | .dark { 39 | 40 | --background: 220, 23%, 8%; 41 | --foreground: 210 40% 98%; 42 | 43 | --card: 220, 23%, 8%; 44 | --card-foreground: 210 40% 98%; 45 | 46 | --popover: 220, 23%, 8%; 47 | --popover-foreground: 210 40% 98%; 48 | 49 | --primary: 210 40% 98%; 50 | --primary-foreground: 222.2 47.4% 11.2%; 51 | 52 | --secondary: 217.2 32.6% 17.5%; 53 | --secondary-foreground: 210 40% 98%; 54 | 55 | --muted: 225, 12%, 13%; 56 | --muted-foreground: 215 20.2% 65.1%; 57 | 58 | --accent: 217.2 32.6% 17.5%; 59 | --accent-foreground: 210 40% 98%; 60 | 61 | --destructive: 0 62.8% 30.6%; 62 | --destructive-foreground: 210 40% 98%; 63 | 64 | --border: 217.2 32.6% 17.5%; 65 | --input: 217.2 32.6% 17.5%; 66 | --ring: 212.7 26.8% 83.9%; 67 | 68 | --cabbage: #ce3df3 69 | } 70 | } 71 | 72 | @layer base { 73 | * { 74 | @apply border-border; 75 | } 76 | 77 | body { 78 | @apply bg-background text-foreground; 79 | } 80 | } -------------------------------------------------------------------------------- /frontend/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Nunito } from "next/font/google"; 3 | import "./globals.css"; 4 | import { ToastContainer } from "react-toastify"; 5 | import "react-toastify/dist/ReactToastify.css"; 6 | import AuthProvider from "@/providers/AuthProvider"; 7 | 8 | const inter = Nunito({ subsets: ["latin"] }); 9 | 10 | export const metadata: Metadata = { 11 | title: "daily.dev | Where developers suffere together", 12 | description: "daily.dev | Where developers suffere together", 13 | }; 14 | 15 | export default function RootLayout({ 16 | children, 17 | }: Readonly<{ 18 | children: React.ReactNode; 19 | }>) { 20 | return ( 21 | 22 | 23 | 24 | {children} 25 | 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/app/login/page.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import React from "react"; 3 | import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; 4 | import Login from "@/components/auth/Login"; 5 | import Register from "@/components/auth/Register"; 6 | 7 | export default function login() { 8 | return ( 9 |
10 |
11 | auth_img 18 |
19 |
20 |
21 | logo 22 |

23 | Where developers suffer together 24 |

25 |
26 | 27 | 28 | Login 29 | Register 30 | 31 | 32 | 33 | 34 |
35 |
36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /frontend/src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@/components/ui/button"; 2 | import Image from "next/image"; 3 | import Link from "next/link"; 4 | 5 | export default function NotFound() { 6 | return ( 7 |
8 | 404 15 | 16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/components/auth/Login.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useState } from "react"; 3 | import { Input } from "@/components/ui/input"; 4 | import { Label } from "@/components/ui/label"; 5 | import { TabsContent } from "@/components/ui/tabs"; 6 | import { Button } from "@/components/ui/button"; 7 | import { 8 | Card, 9 | CardContent, 10 | CardDescription, 11 | CardHeader, 12 | CardTitle, 13 | } from "@/components/ui/card"; 14 | import { CHECK_CREDENTIALS } from "@/lib/apiEndPoints"; 15 | import { signIn } from "next-auth/react"; 16 | import { toast } from "react-toastify"; 17 | import myAxios from "@/lib/axios.config"; 18 | 19 | export default function Login() { 20 | const [authState, setAuthState] = useState({ 21 | email: "", 22 | password: "", 23 | }); 24 | const [errors, setErrors] = useState({ 25 | email: [], 26 | password: [], 27 | }); 28 | const [loading, setLoading] = useState(false); 29 | 30 | const handleSubmit = (event: React.FormEvent) => { 31 | event.preventDefault(); 32 | setLoading(true); 33 | myAxios 34 | .post(CHECK_CREDENTIALS, authState) 35 | .then((res) => { 36 | const response = res.data; 37 | setLoading(false); 38 | if (response?.status == 200) { 39 | signIn("credentials", { 40 | email: authState.email, 41 | password: authState.password, 42 | redirect: true, 43 | callbackUrl: "/", 44 | }); 45 | } else if (response?.status == 401) { 46 | toast.error(response?.message); 47 | } 48 | }) 49 | .catch((err) => { 50 | setLoading(false); 51 | if (err.response?.status == 422) { 52 | setErrors(err.response?.data?.errors); 53 | } else { 54 | toast.error("Something went wrong.please try again!"); 55 | } 56 | }); 57 | }; 58 | return ( 59 | 60 | 61 | 62 | Login 63 | Welcome back to Daily Dev. 64 | 65 | 66 |
67 |
68 | 69 | 76 | setAuthState({ ...authState, email: e.target.value }) 77 | } 78 | /> 79 | {errors?.email?.[0]} 80 |
81 |
82 | 83 | 90 | setAuthState({ ...authState, password: e.target.value }) 91 | } 92 | /> 93 | {errors?.password?.[0]} 94 |
95 |
96 | 99 |
100 |
101 |
102 |
103 |
104 | ); 105 | } 106 | -------------------------------------------------------------------------------- /frontend/src/components/auth/Register.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useState } from "react"; 3 | import { Input } from "@/components/ui/input"; 4 | import { Label } from "@/components/ui/label"; 5 | import { TabsContent } from "@/components/ui/tabs"; 6 | import { 7 | Card, 8 | CardContent, 9 | CardDescription, 10 | CardHeader, 11 | CardTitle, 12 | } from "@/components/ui/card"; 13 | import { REGISTER_URL } from "@/lib/apiEndPoints"; 14 | import { toast } from "react-toastify"; 15 | import { Button } from "../ui/button"; 16 | import { signIn } from "next-auth/react"; 17 | import myAxios from "@/lib/axios.config"; 18 | 19 | export default function Register() { 20 | const [loading, setLoading] = useState(false); 21 | const [authState, setAuthState] = useState({ 22 | name: "", 23 | email: "", 24 | username: "", 25 | password: "", 26 | password_confirmation: "", 27 | }); 28 | const [errors, setErrors] = useState({ 29 | name: [], 30 | email: [], 31 | username: [], 32 | password: [], 33 | }); 34 | 35 | const handleSubmit = async (event: React.FormEvent) => { 36 | event.preventDefault(); 37 | setLoading(true); 38 | await myAxios 39 | .post(REGISTER_URL, authState) 40 | .then((res) => { 41 | const response = res.data; 42 | setLoading(false); 43 | toast.success("Account created successfully!."); 44 | if (response?.status == 200) { 45 | signIn("credentials", { 46 | email: authState.email, 47 | password: authState.password, 48 | redirect: true, 49 | callbackUrl: "/", 50 | }); 51 | } 52 | setLoading(false); 53 | setAuthState({}); 54 | }) 55 | .catch((err) => { 56 | setLoading(false); 57 | if (err.response?.status == 422) { 58 | setErrors(err.response?.data?.errors); 59 | } else { 60 | toast.error("Something went wrong.please try again!"); 61 | } 62 | }); 63 | }; 64 | return ( 65 | 66 | 67 | 68 | Register 69 | Welcome to Daily Dev. 70 | 71 | 72 |
73 |
74 | 75 | 82 | setAuthState({ ...authState, name: e.target.value }) 83 | } 84 | /> 85 | {errors?.name?.[0]} 86 |
87 |
88 | 89 | 95 | setAuthState({ ...authState, username: e.target.value }) 96 | } 97 | /> 98 | {errors?.username?.[0]} 99 |
100 |
101 | 102 | 108 | setAuthState({ ...authState, email: e.target.value }) 109 | } 110 | /> 111 | {errors?.email?.[0]} 112 |
113 |
114 | 115 | 122 | setAuthState({ ...authState, password: e.target.value }) 123 | } 124 | /> 125 | {errors?.password?.[0]} 126 |
127 |
128 | 129 | 136 | setAuthState({ 137 | ...authState, 138 | password_confirmation: e.target.value, 139 | }) 140 | } 141 | /> 142 |
143 |
144 | 147 |
148 |
149 |
150 |
151 |
152 | ); 153 | } 154 | -------------------------------------------------------------------------------- /frontend/src/components/base/MobileSidebar.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useState } from "react"; 3 | import { 4 | Sheet, 5 | SheetContent, 6 | SheetDescription, 7 | SheetHeader, 8 | SheetTitle, 9 | SheetTrigger, 10 | } from "@/components/ui/sheet"; 11 | import { Menu } from "lucide-react"; 12 | import SidebarLinks from "./SidebarLinks"; 13 | 14 | export default function MobileSidebar() { 15 | const [open, setOpen] = useState(false); 16 | return ( 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/components/base/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import React from "react"; 3 | import SearchInput from "./SearchInput"; 4 | import { BellIcon } from "@radix-ui/react-icons"; 5 | import { Button } from "../ui/button"; 6 | import { Menu } from "lucide-react"; 7 | import MobileSidebar from "./MobileSidebar"; 8 | import ProfileMenu from "./ProfileMenu"; 9 | import { CustomUser } from "@/app/api/auth/[...nextauth]/authOptions"; 10 | 11 | export default function Navbar({ user }: { user: CustomUser }) { 12 | return ( 13 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/components/base/ProfileMenu.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React, { useState } from "react"; 3 | import { 4 | DropdownMenu, 5 | DropdownMenuContent, 6 | DropdownMenuItem, 7 | DropdownMenuLabel, 8 | DropdownMenuSeparator, 9 | DropdownMenuTrigger, 10 | } from "@/components/ui/dropdown-menu"; 11 | import { 12 | Dialog, 13 | DialogContent, 14 | DialogDescription, 15 | DialogHeader, 16 | DialogTitle, 17 | } from "@/components/ui/dialog"; 18 | import Image from "next/image"; 19 | import { Button } from "../ui/button"; 20 | import { DialogClose } from "@radix-ui/react-dialog"; 21 | import { signOut, useSession } from "next-auth/react"; 22 | import { Label } from "../ui/label"; 23 | import { Input } from "../ui/input"; 24 | import myAxios from "@/lib/axios.config"; 25 | import { LOGOUT_URL, UPDATE_PROFILE } from "@/lib/apiEndPoints"; 26 | import { CustomUser } from "@/app/api/auth/[...nextauth]/authOptions"; 27 | import { toast } from "react-toastify"; 28 | import { getImageUrl } from "@/lib/utils"; 29 | 30 | export default function ProfileMenu({ user }: { user: CustomUser }) { 31 | const [logoutOpen, setLogOutOpen] = useState(false); 32 | const [profileOpen, setProfileOpen] = useState(false); 33 | const [image, setImage] = useState(null); 34 | const [errors, setErrors] = useState({ 35 | profile_image: [], 36 | }); 37 | const [loading, setLoading] = useState(false); 38 | const { update } = useSession(); 39 | 40 | const handleImageChange = (event: React.ChangeEvent) => { 41 | const file = event.target.files?.[0]; 42 | if (file) { 43 | setImage(file); 44 | } 45 | }; 46 | const logoutUser = async () => { 47 | myAxios 48 | .post( 49 | LOGOUT_URL, 50 | {}, 51 | { 52 | headers: { 53 | Authorization: `Bearer ${user.token}`, 54 | }, 55 | } 56 | ) 57 | .then((res) => { 58 | signOut({ 59 | callbackUrl: "/login", 60 | redirect: true, 61 | }); 62 | }) 63 | .catch((err) => { 64 | toast.error("Something went wrong.Please try again!"); 65 | }); 66 | }; 67 | 68 | const updateProfile = (event: React.FormEvent) => { 69 | event.preventDefault(); 70 | setLoading(true); 71 | const formData = new FormData(); 72 | formData.append("profile_image", image ?? ""); 73 | myAxios 74 | .post(UPDATE_PROFILE, formData, { 75 | headers: { 76 | Authorization: "Bearer " + user.token, 77 | }, 78 | }) 79 | .then((res) => { 80 | const response = res.data; 81 | setLoading(false); 82 | update({ profile_image: response.image }); 83 | toast.success("Profile image updated successfully!"); 84 | setProfileOpen(false); 85 | }) 86 | .catch((err) => { 87 | setLoading(false); 88 | if (err.response?.status == 422) { 89 | setErrors(err.response?.data?.errors); 90 | } else { 91 | toast.error("Something went wrong.please try again!"); 92 | } 93 | }); 94 | }; 95 | return ( 96 | <> 97 | {/* Logout dialog */} 98 | 99 | 100 | 101 | Are you absolutely sure? 102 | 103 | This action expire your session and you have to login back to 104 | access your dashboard. 105 | 106 | 107 |
108 | 111 | 112 | 113 | 114 |
115 |
116 |
117 | 118 | {/* Profile Image update */} 119 | 120 | e.preventDefault()}> 121 | 122 | Change profile 123 | 124 |
125 |
126 | 127 | 133 | {errors.profile_image?.[0]} 134 |
135 |
136 | 140 |
141 |
142 |
143 |
144 | 145 | 146 | 147 | {user.profile_image ? ( 148 | logo 154 | ) : ( 155 | logo 162 | )} 163 | 164 | 165 | My Account 166 | 167 | setProfileOpen(true)}> 168 | Profile 169 | 170 | setLogOutOpen(true)}> 171 | Logout 172 | 173 | 174 | 175 | 176 | ); 177 | } 178 | -------------------------------------------------------------------------------- /frontend/src/components/base/SearchInput.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { MagnifyingGlassIcon } from "@radix-ui/react-icons"; 3 | export default function SearchInput() { 4 | return ( 5 |
6 | 10 | 11 | 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/components/base/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | 5 | import SidebarLinks from "./SidebarLinks"; 6 | 7 | export default function Sidebar() { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/components/base/SidebarLinks.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import React from "react"; 3 | import Image from "next/image"; 4 | import { Search, Flame, ArrowBigUp, Link as LinkIcon } from "lucide-react"; 5 | import AddPost from "../post/AddPost"; 6 | 7 | export default function SidebarLinks() { 8 | return ( 9 |
10 | 11 | person 12 |

My feed

13 | 14 | 15 |

Discover

16 |
    17 |
  • 18 | 19 | 20 |

    Popular

    21 | 22 |
  • 23 | 24 |
  • 25 | 26 | 27 |

    Search

    28 | 29 |
  • 30 |
  • 31 | 32 | 33 |

    Most Voted

    34 | 35 |
  • 36 |
37 | 38 |

Contribute

39 |
    40 |
  • 41 | 42 |
  • 43 |
44 |
45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /frontend/src/components/comment/AddComment.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { Button } from "../ui/button"; 3 | import UserAvatar from "../common/UserAvatar"; 4 | import { Textarea } from "../ui/textarea"; 5 | import { useSession } from "next-auth/react"; 6 | import { CustomUser } from "@/app/api/auth/[...nextauth]/authOptions"; 7 | import myAxios from "@/lib/axios.config"; 8 | import { COMMENT_URL } from "@/lib/apiEndPoints"; 9 | import { toast } from "react-toastify"; 10 | import CommentCard from "./CommentCard"; 11 | 12 | export default function AddComment({ post }: { post: PostApiType }) { 13 | const [showBox, setShowBox] = useState(true); 14 | const { data } = useSession(); 15 | const user: CustomUser = data?.user as CustomUser; 16 | const [comment, setComment] = useState(""); 17 | const [errors, setErrors] = useState({ 18 | post_id: [], 19 | comment: [], 20 | }); 21 | const [loading, setLoading] = useState(false); 22 | const [comments, setComments] = useState>(); 23 | 24 | useEffect(() => { 25 | fetchComments(); 26 | }, []); 27 | 28 | const addComment = (event: React.FormEvent) => { 29 | event.preventDefault(); 30 | setLoading(true); 31 | myAxios 32 | .post( 33 | COMMENT_URL, 34 | { 35 | comment, 36 | post_id: post.id, 37 | }, 38 | { 39 | headers: { 40 | Authorization: `Bearer ${user.token}`, 41 | }, 42 | } 43 | ) 44 | .then((res) => { 45 | const response = res.data; 46 | setLoading(false); 47 | setComments((prevState) => { 48 | if (prevState) { 49 | if (prevState?.data.length === 0) { 50 | return { 51 | ...prevState, 52 | data: [response.comment], 53 | }; 54 | } else { 55 | return { 56 | ...prevState, 57 | data: [response.comment, ...prevState.data], 58 | }; 59 | } 60 | } 61 | }); 62 | 63 | setComment(""); 64 | toast.success("Comment added successfully!"); 65 | }) 66 | .catch((err) => { 67 | setLoading(false); 68 | if (err.response?.status === 422) { 69 | setErrors(err.response?.data.errors); 70 | } else { 71 | toast.error("Something went wrong.Please try again!"); 72 | } 73 | }); 74 | }; 75 | 76 | const fetchComments = () => { 77 | myAxios 78 | .get(`${COMMENT_URL}?post_id=${post.id}`, { 79 | headers: { 80 | Authorization: `Bearer ${user.token}`, 81 | }, 82 | }) 83 | .then((res) => { 84 | setComments(res.data); 85 | console.log("The response is", res.data); 86 | }) 87 | .catch((err) => { 88 | toast.error("Someting went wrong while fetching comments!"); 89 | }); 90 | }; 91 | 92 | return ( 93 |
94 | {showBox ? ( 95 |
setShowBox(false)} 98 | > 99 |
100 | 101 |

Share your thoughts

102 |
103 | 104 |
105 | ) : ( 106 |
107 |
108 |
109 |