├── .editorconfig ├── .env.example ├── .gitattributes ├── .gitignore ├── .styleci.yml ├── README.md ├── app ├── Console │ └── Kernel.php ├── Events │ └── PostPublishEvent.php ├── Exceptions │ └── Handler.php ├── Http │ ├── Controllers │ │ ├── AccountController.php │ │ ├── Auth │ │ │ ├── ConfirmPasswordController.php │ │ │ ├── ForgotPasswordController.php │ │ │ ├── LoginController.php │ │ │ ├── RegisterController.php │ │ │ ├── ResetPasswordController.php │ │ │ └── VerificationController.php │ │ ├── Controller.php │ │ ├── DashboardController.php │ │ ├── MediaController.php │ │ ├── NotificationController.php │ │ ├── PostController.php │ │ ├── ProfileController.php │ │ └── SocialAuthController.php │ ├── Kernel.php │ ├── Middleware │ │ ├── Authenticate.php │ │ ├── EncryptCookies.php │ │ ├── PreventRequestsDuringMaintenance.php │ │ ├── RedirectIfAuthenticated.php │ │ ├── TrimStrings.php │ │ ├── TrustHosts.php │ │ ├── TrustProxies.php │ │ └── VerifyCsrfToken.php │ └── Requests │ │ ├── MediaRequest.php │ │ ├── PostRequest.php │ │ ├── ProfileInfoRequest.php │ │ └── ProfilePasswordRequest.php ├── Jobs │ └── PublishPost.php ├── Listeners │ ├── PostPublishListener.php │ └── SuccessfulLoginListener.php ├── Models │ ├── Account.php │ ├── Casts │ │ └── DiffForHumans.php │ ├── Media.php │ ├── Notification.php │ ├── Post.php │ ├── Session.php │ ├── SocialAuthUser.php │ └── User.php ├── Notifications │ ├── LoginNotification.php │ └── PostNotification.php ├── Policies │ ├── AccountPolicy.php │ ├── MediaPolicy.php │ ├── NotificationPolicy.php │ ├── PostPolicy.php │ └── SessionPolicy.php ├── PostaBot │ ├── Contracts │ │ └── Tokenizable.php │ ├── Exceptions │ │ └── TokenizerException.php │ ├── Facades │ │ └── Publisher.php │ ├── PublisherManager.php │ ├── PublisherProviders │ │ ├── Facebook.php │ │ └── Twitter.php │ └── TokenizerProviders │ │ ├── Facebook.php │ │ └── Twitter.php ├── Providers │ ├── AppServiceProvider.php │ ├── AuthServiceProvider.php │ ├── BroadcastServiceProvider.php │ ├── EventServiceProvider.php │ ├── PostaBotServiceProvider.php │ └── RouteServiceProvider.php ├── Repositories │ └── UserRepository.php ├── Rules │ ├── CheckOldPass.php │ ├── PostAccountsCheck.php │ └── PostMediaCheck.php └── Services │ ├── MediaService.php │ ├── PostService.php │ ├── ProfileService.php │ └── SocialAuthService.php ├── artisan ├── bootstrap ├── app.php └── cache │ └── .gitignore ├── composer.json ├── config ├── app.php ├── auth.php ├── broadcasting.php ├── cache.php ├── cors.php ├── database.php ├── filesystems.php ├── hashing.php ├── logging.php ├── mail.php ├── queue.php ├── services.php ├── session.php ├── view.php └── websockets.php ├── database ├── .gitignore ├── factories │ └── UserFactory.php ├── migrations │ ├── 0000_00_00_000000_create_websockets_statistics_entries_table.php │ ├── 2014_10_12_000000_create_users_table.php │ ├── 2014_10_12_100000_create_password_resets_table.php │ ├── 2019_08_19_000000_create_failed_jobs_table.php │ ├── 2020_10_27_110614_create_media_table.php │ ├── 2020_10_30_110207_create_social_auth_users_table.php │ ├── 2020_10_31_133916_create_sessions_table.php │ ├── 2020_10_31_133920_create_sessions_pub_id_column.php │ ├── 2020_11_04_123546_create_accounts_table.php │ ├── 2020_11_12_031714_create_posts_table.php │ ├── 2021_02_17_140216_create_media_post_table.php │ ├── 2021_02_20_081434_create_jobs_table.php │ ├── 2021_02_24_190312_create_account_post_table.php │ └── 2021_02_28_020647_create_notifications_table.php └── seeders │ └── DatabaseSeeder.php ├── package.json ├── phpunit.xml ├── public ├── .htaccess ├── css │ ├── adminlte.css │ ├── app.css │ ├── calendar.css │ ├── fontawesome.css │ ├── icheck.css │ └── tempusdominus.css ├── favicon.ico ├── images │ ├── avatar.png │ └── logo.png ├── index.php ├── js │ ├── adminlte.js │ ├── app.js │ ├── bootstrap.js │ ├── calendar.js │ └── jquery.js ├── mix-manifest.json ├── resources │ └── js │ │ └── test1.js ├── robots.txt ├── storage ├── web.config └── webfonts │ ├── fa-brands-400.eot │ ├── fa-brands-400.svg │ ├── fa-brands-400.ttf │ ├── fa-brands-400.woff │ ├── fa-brands-400.woff2 │ ├── fa-regular-400.eot │ ├── fa-regular-400.svg │ ├── fa-regular-400.ttf │ ├── fa-regular-400.woff │ ├── fa-regular-400.woff2 │ ├── fa-solid-900.eot │ ├── fa-solid-900.svg │ ├── fa-solid-900.ttf │ ├── fa-solid-900.woff │ └── fa-solid-900.woff2 ├── resources ├── css │ └── app.css ├── js │ ├── app.js │ ├── bootstrap.js │ └── components │ │ ├── EventBus.js │ │ ├── MediaUploader.vue │ │ ├── Notifications.vue │ │ ├── PostCreator.vue │ │ └── PostEditor.vue ├── lang │ └── en │ │ ├── auth.php │ │ ├── pagination.php │ │ ├── passwords.php │ │ └── validation.php ├── sass │ ├── _variables.scss │ └── app.scss └── views │ ├── auth │ ├── login.blade.php │ ├── passwords │ │ ├── confirm.blade.php │ │ ├── email.blade.php │ │ └── reset.blade.php │ ├── register.blade.php │ └── verify.blade.php │ ├── layouts │ ├── app.blade.php │ ├── auth.blade.php │ └── pagination.blade.php │ └── main │ ├── accounts.blade.php │ ├── calendar.blade.php │ ├── dashboard.blade.php │ ├── media.blade.php │ ├── notifications.blade.php │ ├── posts │ ├── create.blade.php │ ├── edit.blade.php │ ├── index.blade.php │ └── show.blade.php │ └── profile.blade.php ├── routes ├── api.php ├── channels.php ├── console.php └── web.php ├── screenshots ├── 1.png ├── 10.png ├── 11.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png ├── 8.png ├── 9.png └── logo.png ├── server.php ├── storage ├── app │ └── .gitignore ├── debugbar │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ ├── .gitignore │ │ └── data │ │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ ├── testing │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore ├── tests ├── CreatesApplication.php ├── Feature │ └── AuthTest.php └── TestCase.php └── webpack.mix.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Laravel 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_URL=http://localhost 6 | 7 | LOG_CHANNEL=stack 8 | LOG_LEVEL=debug 9 | 10 | DB_CONNECTION=mysql 11 | DB_HOST=127.0.0.1 12 | DB_PORT=3306 13 | DB_DATABASE=laravel 14 | DB_USERNAME=root 15 | DB_PASSWORD= 16 | 17 | BROADCAST_DRIVER=pusher 18 | CACHE_DRIVER=file 19 | QUEUE_CONNECTION=database 20 | SESSION_DRIVER=database 21 | SESSION_LIFETIME=35791394 22 | 23 | 24 | 25 | MAIL_MAILER=smtp 26 | MAIL_HOST=smtp.mailtrap.io 27 | MAIL_PORT=2525 28 | MAIL_USERNAME=null 29 | MAIL_PASSWORD=null 30 | MAIL_ENCRYPTION=null 31 | MAIL_FROM_ADDRESS=null 32 | MAIL_FROM_NAME="${APP_NAME}" 33 | 34 | 35 | 36 | PUSHER_APP_ID= 37 | PUSHER_APP_KEY= 38 | PUSHER_APP_SECRET= 39 | PUSHER_APP_CLUSTER=mt1 40 | PUSHER_APP_SCHEME=https 41 | 42 | MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" 43 | MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" 44 | 45 | LARAVEL_WEBSOCKETS_SSL_LOCAL_CERT= 46 | LARAVEL_WEBSOCKETS_SSL_LOCAL_PK= 47 | 48 | FACEBOOK_CLIENT_ID= 49 | FACEBOOK_CLIENT_SECRET= 50 | FACEBOOK_REDIRECT= 51 | 52 | 53 | GOOGLE_CLIENT_ID= 54 | GOOGLE_CLIENT_SECRET= 55 | GOOGLE_REDIRECT= 56 | 57 | 58 | 59 | TWITTER_CLIENT_ID= 60 | TWITTER_CLIENT_SECRET= 61 | TWITTER_REDIRECT= 62 | 63 | 64 | 65 | INSTAGRAM_CLIENT_ID= 66 | INSTAGRAM_CLIENT_SECRET= 67 | INSTAGRAM_REDIRECT= 68 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.scss linguist-vendored 4 | *.js linguist-vendored 5 | CHANGELOG.md export-ignore 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /public/hot 3 | /storage/*.key 4 | /vendor 5 | .env 6 | .env.backup 7 | .phpunit.result.cache 8 | Homestead.json 9 | Homestead.yaml 10 | npm-debug.log 11 | yarn-error.log 12 | /.idea 13 | /app/PostaBot/TokenizerProviders/Instagram.php 14 | package-lock.json 15 | composer.lock -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | php: 2 | preset: laravel 3 | disabled: 4 | - no_unused_imports 5 | finder: 6 | not-name: 7 | - index.php 8 | - server.php 9 | js: 10 | finder: 11 | not-name: 12 | - webpack.mix.js 13 | css: true 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Logo 3 | 4 |

Laravel LaPosta

5 | 6 |

7 | social media scheduler 8 |

9 |

10 | 11 | ## About The Project 12 | demo project built with laravel to schedule social media posts for later publish .. project focuses on using laravel framework components , architecture, patterns and tests. 13 | 14 | 15 | ## prerequisite 16 | try to make twitter or facebook application with publish permissions 17 | and update tokens in .env file 18 | 19 | ## Usage 20 | 21 | 1. Clone the repo 22 | 23 | ```sh 24 | git clone https://github.com/civilcoder55/laravel-laposta.git 25 | ``` 26 | 27 | 2. update env file 28 | 29 | ```sh 30 | cp .env.example .env 31 | ``` 32 | 33 | 3. start workers 34 | 35 | ```sh 36 | php artisan queue:work 37 | ``` 38 | 39 | ```sh 40 | php artisan schedule:work 41 | ``` 42 | 43 | ```sh 44 | php artisan websockets:serve 45 | ``` 46 | 47 | 5. start server 48 | 49 | ```sh 50 | php artisan serve 51 | ``` 52 | 53 | 6. access website at 54 | 55 | ```sh 56 | http://127.0.0.1:8000 57 | ``` 58 | 59 | ## Screenshots 60 | 61 |

62 |

63 |

64 |

65 |

66 |

67 |

68 |

69 |

70 |

71 |

72 |
73 | 74 | ## Video Preview 75 | 76 | - [Youtube Video](https://www.youtube.com/watch?v=38HfwgmgL-8) 77 | 78 | ## Built With 79 | 80 | - [Laravel](https://laravel.com) 81 | 82 | 83 | -------------------------------------------------------------------------------- /app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | call(function () { 21 | $now = Carbon::now()->timestamp; 22 | $posts = Post::with(['accounts', 'media'])->where(['draft' => 0, 'locked' => 0]) 23 | ->whereBetween('schedule_date', [$now + 1, $now + 60])->get(); 24 | foreach ($posts as $post) { 25 | foreach ($post->accounts as $account) { 26 | PublishPost::dispatch($post, $account)->delay($post->schedule_date); 27 | } 28 | } 29 | })->everyMinute(); 30 | 31 | } 32 | 33 | protected function commands() 34 | { 35 | $this->load(__DIR__ . '/Commands'); 36 | 37 | require base_path('routes/console.php'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/Events/PostPublishEvent.php: -------------------------------------------------------------------------------- 1 | post = $post; 19 | $this->account = $account; 20 | $this->error = $error; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | redirect(); 21 | } 22 | 23 | // handling redirection callback form platform and add account to database 24 | public function callback($platform, Tokenizable $Tokenizer) 25 | { 26 | $Tokenizer->getAndSaveData(); 27 | return redirect()->route('accounts.index')->with('status', "{$platform} account added successfully"); 28 | } 29 | 30 | public function destroy(Account $account) 31 | { 32 | $this->authorize('delete', $account); 33 | $account->delete(); 34 | return redirect()->route('accounts.index')->with('status', "{$account->platform} account deleted successfully"); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ConfirmPasswordController.php: -------------------------------------------------------------------------------- 1 | middleware('auth'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ForgotPasswordController.php: -------------------------------------------------------------------------------- 1 | middleware('guest')->except('logout'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/RegisterController.php: -------------------------------------------------------------------------------- 1 | middleware('guest'); 42 | } 43 | 44 | /** 45 | * Get a validator for an incoming registration request. 46 | * 47 | * @param array $data 48 | * @return \Illuminate\Contracts\Validation\Validator 49 | */ 50 | protected function validator(array $data) 51 | { 52 | return Validator::make($data, [ 53 | 'name' => ['required', 'string', 'max:255'], 54 | 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 55 | 'password' => ['required', 'string', 'min:8', 'confirmed'], 56 | ]); 57 | } 58 | 59 | /** 60 | * Create a new user instance after a valid registration. 61 | * 62 | * @param array $data 63 | * @return User 64 | */ 65 | protected function create(array $data) 66 | { 67 | return User::create([ 68 | 'name' => $data['name'], 69 | 'email' => $data['email'], 70 | 'password' => Hash::make($data['password']), 71 | ]); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ResetPasswordController.php: -------------------------------------------------------------------------------- 1 | middleware('auth'); 39 | $this->middleware('signed')->only('verify'); 40 | $this->middleware('throttle:6,1')->only('verify', 'resend'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | json(['success' => true, 'media' => $media]); 24 | } 25 | 26 | public function showOriginal($mediaName) 27 | { 28 | $userId = auth()->id(); 29 | if (preg_match('/^[a-f0-9]{32}.(jpg|png|jpeg)$/i', $mediaName) && Storage::disk('local')->exists("media/{$userId}/original/{$mediaName}")) { 30 | return response()->file(storage_path("app/media/{$userId}/original/{$mediaName}")); 31 | } 32 | abort('404'); 33 | } 34 | 35 | public function showThumb($mediaName) 36 | { 37 | $userId = auth()->id(); 38 | if (preg_match('/^[a-f0-9]{32}.(jpg|png|jpeg)$/i', $mediaName) && Storage::disk('local')->exists("media/{$userId}/thumb/{$mediaName}")) { 39 | return response()->file(storage_path("app/media/{$userId}/thumb/{$mediaName}")); 40 | } 41 | abort('404'); 42 | } 43 | 44 | public function destroy(Media $media) 45 | { 46 | $this->authorize('delete', $media); 47 | $response = MediaService::delete($media); 48 | return response()->json($response); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/Http/Controllers/NotificationController.php: -------------------------------------------------------------------------------- 1 | authorize('read', $notification); 21 | $notification->markAsRead(); 22 | Cache::forget('notifications.' . auth()->user()->id); 23 | return response()->json(['success' => true]); 24 | } 25 | 26 | public function destroy(Notification $notification) 27 | { 28 | $this->authorize('delete', $notification); 29 | $notification->delete(); 30 | Cache::forget('notifications.' . auth()->user()->id); 31 | return redirect()->route('notifications.index')->with('status', "Notification deleted successfully"); 32 | } 33 | 34 | public function destroyAll() 35 | { 36 | auth()->user()->notifications()->delete(); 37 | Cache::forget('notifications.' . auth()->user()->id); 38 | return redirect()->route('notifications.index')->with('status', "All notifications deleted successfully"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Http/Controllers/PostController.php: -------------------------------------------------------------------------------- 1 | query('type') == 'drafted') { 16 | $posts = UserRepository::getDraftedPosts(); 17 | } else { 18 | $posts = UserRepository::getQueuedPosts(); 19 | } 20 | return view('main.posts.index', compact(['posts'])); 21 | } 22 | 23 | public function create() 24 | { 25 | $userMedia = UserRepository::getMedia(['id', 'name']); 26 | $userAccounts = UserRepository::getAccounts(['id', 'type', 'name']); 27 | return view('main.posts.create', compact(['userMedia', 'userAccounts'])); 28 | } 29 | 30 | public function store(PostRequest $request) 31 | { 32 | PostService::store($request); 33 | return redirect()->route('posts.create')->with('status', 'your post saved successfuly'); 34 | } 35 | 36 | public function show(Post $post) 37 | { 38 | $this->authorize('show', $post); 39 | $post->load(['media:id,name']); 40 | return view('main.posts.show', compact(['post'])); 41 | } 42 | public function edit(Post $post) 43 | { 44 | $this->authorize('edit', $post); 45 | $post->load(['accounts:id,name,type', 'media:id,name']); 46 | $userAccounts = UserRepository::getAccounts(['name', 'type', 'id']); 47 | $userMedia = UserRepository::getMedia(); 48 | return view('main.posts.edit', compact(['userAccounts', 'userMedia', 'post'])); 49 | } 50 | 51 | public function update(PostRequest $request, Post $post) 52 | { 53 | $this->authorize('edit', $post); 54 | PostService::update($request, $post); 55 | return redirect()->route('posts.edit', $post->id)->with('status', 'your post updated successfully'); 56 | } 57 | 58 | public function destroy(Post $post) 59 | { 60 | $this->authorize('delete', $post); 61 | $post->delete(); 62 | return redirect()->route('posts.index', ($post->draft ? ['type' => 'drafted'] : ''))->with('status', 'your post deleted successfully'); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/Http/Controllers/ProfileController.php: -------------------------------------------------------------------------------- 1 | _avatar) { 25 | ProfileService::storeAvatar($request); 26 | } 27 | auth()->user()->update($request->validated()); 28 | return redirect()->route('profile.index')->with('status', 'Your information updated successfully'); 29 | } 30 | 31 | public function updatePassword(ProfilePasswordRequest $request) 32 | { 33 | auth()->user()->update(['password' => Hash::make($request->password)]); 34 | return redirect()->route('profile.index')->with('status', 'Your password updated successfully'); 35 | } 36 | 37 | public function destroySession($id) 38 | { 39 | $session = auth()->user()->session()->where(['pub_id' => $id])->firstOrFail(); 40 | $this->authorize('delete', $session); 41 | Session::getHandler()->destroy($session->id); 42 | return redirect()->route('profile.index')->with('status', 'Session destroyed successfully'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/Http/Controllers/SocialAuthController.php: -------------------------------------------------------------------------------- 1 | redirect(); 15 | } 16 | 17 | public function callback($provider) 18 | { 19 | $data = Socialite::driver($provider)->user(); 20 | return SocialAuthService::handleUser($data, $provider); 21 | } 22 | 23 | public function disconnectFacebook() 24 | { 25 | $socialUser = auth()->user()->socialAuthUser()->where(['provider' => 'facebook'])->firstOrFail(); 26 | $socialUser->delete(); 27 | SocialAuthService::revokeFacebookToken($socialUser->token); 28 | return redirect()->route('profile.index'); 29 | } 30 | 31 | public function disconnectGoogle() 32 | { 33 | $socialUser = auth()->user()->socialAuthUser()->where(['provider' => 'google'])->firstOrFail(); 34 | $socialUser->delete(); 35 | SocialAuthService::revokeGoogleToken($socialUser->token); 36 | return redirect()->route('profile.index'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/Http/Kernel.php: -------------------------------------------------------------------------------- 1 | [ 54 | EncryptCookies::class, 55 | AddQueuedCookiesToResponse::class, 56 | StartSession::class, 57 | // \Illuminate\Session\Middleware\AuthenticateSession::class, 58 | ShareErrorsFromSession::class, 59 | VerifyCsrfToken::class, 60 | SubstituteBindings::class, 61 | ], 62 | 63 | 'api' => [ 64 | 'throttle:api', 65 | SubstituteBindings::class, 66 | ], 67 | ]; 68 | 69 | /** 70 | * The application's route middleware. 71 | * 72 | * These middleware may be assigned to groups or used individually. 73 | * 74 | * @var array 75 | */ 76 | protected $routeMiddleware = [ 77 | 'auth' => Authenticate::class, 78 | 'auth.basic' => AuthenticateWithBasicAuth::class, 79 | 'cache.headers' => SetCacheHeaders::class, 80 | 'can' => Authorize::class, 81 | 'guest' => RedirectIfAuthenticated::class, 82 | 'password.confirm' => RequirePassword::class, 83 | 'signed' => ValidateSignature::class, 84 | 'throttle' => ThrottleRequests::class, 85 | 'verified' => EnsureEmailIsVerified::class, 86 | ]; 87 | } 88 | -------------------------------------------------------------------------------- /app/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | expectsJson()) { 19 | return route('login'); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | check()) { 26 | return redirect(RouteServiceProvider::HOME); 27 | } 28 | } 29 | 30 | return $next($request); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrimStrings.php: -------------------------------------------------------------------------------- 1 | allSubdomainsOfApplicationUrl(), 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrustProxies.php: -------------------------------------------------------------------------------- 1 | ['required', 'array', 'min:1'], 18 | 'media.*' => ['required', 'image', 'mimes:jpeg,png,jpg'], 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Http/Requests/PostRequest.php: -------------------------------------------------------------------------------- 1 | ['required', 'boolean'], 22 | 'accounts' => ['required_if:draft,==,0', 'array', 'min:1', new PostAccountsCheck], 23 | 'accounts.*' => ['distinct', 'integer'], 24 | 'message' => ['required_if:draft,==,0', 'string', 'nullable'], 25 | 'media' => ['array', 'min:1', new PostMediaCheck], 26 | 'media.*' => ['distinct', 'integer'], 27 | 'schedule_date' => ['required_if:draft,==,0', 'date_format:d/m/Y h:i A', 'after:now', 'nullable'], 28 | ]; 29 | } 30 | 31 | public function withValidator($validator) 32 | { 33 | if (!$validator->fails()) { 34 | $validator->after(function ($validator) { 35 | // convert date to timestamp 36 | if ($this->schedule_date) { 37 | $this->merge(['schedule_date' => Carbon::createFromFormat('d/m/Y h:i A', $this->schedule_date)->timestamp]); 38 | } 39 | // convert non media to empty array 40 | if (!$this->media) { 41 | $this->merge(['media' => []]); 42 | } 43 | // convert non accounts to empty array 44 | if (!$this->accounts) { 45 | $this->merge(['accounts' => []]); 46 | } 47 | 48 | }); 49 | } 50 | 51 | } 52 | 53 | public function messages() 54 | { 55 | return [ 56 | 'accounts.required_if' => 'Please select at least one account', 57 | 'message.required_if' => 'Please add text message', 58 | 'schedule_date.required_if' => 'Please select publish date and time', 59 | ]; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/Http/Requests/ProfileInfoRequest.php: -------------------------------------------------------------------------------- 1 | ['unique:users,email,' . auth()->user()->id, 'required', 'email'], 19 | 'name' => ['required', 'string'], 20 | '_avatar' => ['image'], 21 | ]; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/Http/Requests/ProfilePasswordRequest.php: -------------------------------------------------------------------------------- 1 | ['required', new CheckOldPass], 19 | 'password' => ['required', 'min:6', 'confirmed'], 20 | ]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Jobs/PublishPost.php: -------------------------------------------------------------------------------- 1 | post = $post; 24 | $this->account = $account; 25 | } 26 | 27 | public function handle() 28 | { 29 | Publisher::driver($this->account->platform)->publishPost($this->post, $this->account); 30 | event(new PostPublishEvent($this->post, $this->account)); 31 | } 32 | 33 | public function failed(Exception $exception) 34 | { 35 | $error = $exception->getMessage(); 36 | event(new PostPublishEvent($this->post, $this->account, $error)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/Listeners/PostPublishListener.php: -------------------------------------------------------------------------------- 1 | createLog($event); 14 | User::where(['id' => $event->post->user_id])->first()->notify(new PostNotification($event->post)); 15 | } 16 | 17 | private function createLog($event) 18 | { 19 | if ($event->error) { 20 | $log = [[ 21 | 'status' => 'danger', 22 | 'message' => "Post can't be published with {$event->account->name} {$event->account->platform} {$event->account->type} reason: $event->error", 23 | ]]; 24 | $status = (($event->post->status == 'failed' || $event->post->status == 'pending') ? 'failed' : 'critical'); 25 | $event->post->update(['status' => $status, 'locked' => 1, 'logs' => $log]); 26 | } else { 27 | $log = [[ 28 | 'status' => 'success', 29 | 'message' => "Post published successfully at {$event->post->schedule_date} to {$event->account->name} {$event->account->platform} {$event->account->type}", 30 | ]]; 31 | $status = (($event->post->status == 'succeeded' || $event->post->status == 'pending') ? 'succeeded' : 'critical'); 32 | $event->post->update(['status' => $status, 'locked' => 1, 'logs' => $log]); 33 | } 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/Listeners/SuccessfulLoginListener.php: -------------------------------------------------------------------------------- 1 | request = $request; 17 | } 18 | 19 | public function handle(Login $event) 20 | { 21 | //if new registered user , don't send notification 22 | if ($event->user->created_at->timestamp + 1 >= Carbon::now()->timestamp) { 23 | return; 24 | } 25 | $agent = new Parser($this->request->server('HTTP_USER_AGENT')); 26 | $browser = $agent->browser->toString(); 27 | $os = $agent->os->toString(); 28 | $event->user->notify(new LoginNotification($browser, $os)); // fire LoginNotification which store notification in database and brodcast it 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Models/Account.php: -------------------------------------------------------------------------------- 1 | belongsTo('App\User'); 23 | } 24 | 25 | public function posts() 26 | { 27 | return $this->belongsToMany(Post::class); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/Models/Casts/DiffForHumans.php: -------------------------------------------------------------------------------- 1 | diffForHumans(); 14 | } 15 | 16 | public function set($model, string $key, $value, array $attributes) 17 | { 18 | 19 | return $value; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Models/Media.php: -------------------------------------------------------------------------------- 1 | delete($media->original_path); 22 | Storage::disk('local')->delete($media->thumb_path); 23 | })); 24 | 25 | } 26 | 27 | public function user() 28 | { 29 | return $this->belongsTo('App\User'); 30 | } 31 | 32 | public function posts() 33 | { 34 | return $this->belongsToMany(Post::class); 35 | } 36 | 37 | public function getSrcAttribute() 38 | { 39 | return route('media.show.thumb', $this->name); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/Models/Notification.php: -------------------------------------------------------------------------------- 1 | 'array', 15 | 'read_at' => 'datetime', 16 | 'created_at' => DiffForHumans::class, 17 | ]; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/Models/Post.php: -------------------------------------------------------------------------------- 1 | 'array', 16 | ]; 17 | 18 | public function user() 19 | { 20 | return $this->belongsTo('App\User'); 21 | } 22 | 23 | public function accounts() 24 | { 25 | return $this->belongsToMany(Account::class); 26 | } 27 | 28 | public function media() 29 | { 30 | return $this->belongsToMany(Media::class); 31 | } 32 | 33 | public function getAccountsIdsAttribute() 34 | { 35 | return array_map(function ($item) { 36 | return $item['id']; 37 | }, $this->accounts->toArray()); 38 | } 39 | 40 | public function getMediaIdsAttribute() 41 | { 42 | // return array_map(function ($item) { 43 | // return $item['id']; 44 | // }, $this->media->toArray()); 45 | return $this->media->toArray(); 46 | } 47 | public function getScheduleDateAttribute($value) 48 | { 49 | 50 | return $value ? Carbon::createFromTimestamp($value) : null; 51 | } 52 | 53 | public function setLogsAttribute($value) 54 | { 55 | $old = isset($this->attributes['logs']) ? json_decode($this->attributes['logs']) : []; 56 | $new = array_merge($value, $old); 57 | $this->attributes['logs'] = json_encode($new); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/Models/Session.php: -------------------------------------------------------------------------------- 1 | 'string', 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /app/Models/SocialAuthUser.php: -------------------------------------------------------------------------------- 1 | belongsTo(User::class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/Models/User.php: -------------------------------------------------------------------------------- 1 | 'datetime']; 18 | 19 | public function media() 20 | { 21 | return $this->hasMany(Media::class); 22 | } 23 | 24 | public function socialAuthUser() 25 | { 26 | return $this->hasMany(SocialAuthUser::class); 27 | } 28 | 29 | public function session() 30 | { 31 | return $this->hasMany(Session::class); 32 | } 33 | 34 | public function accounts() 35 | { 36 | return $this->hasMany(Account::class); 37 | } 38 | 39 | public function posts() 40 | { 41 | return $this->hasMany(Post::class); 42 | } 43 | 44 | public function notifications() 45 | { 46 | return $this->morphMany(Notification::class, 'notifiable'); 47 | } 48 | 49 | public function receivesBroadcastNotificationsOn() 50 | { 51 | return 'users.' . $this->id; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/Notifications/LoginNotification.php: -------------------------------------------------------------------------------- 1 | message = "New Login From $browser On $os"; 19 | $this->link = route('profile.index') . "#sessionsTable"; 20 | } 21 | 22 | public function via($notifiable) 23 | { 24 | return ['broadcast', 'database']; 25 | } 26 | 27 | public function toDatabase($notifiable): array 28 | { 29 | Cache::forget('notifications.' . $notifiable->id); 30 | return ['status' => 'warning', 'type' => 'login', 'message' => $this->message, 'link' => $this->link]; 31 | } 32 | 33 | public function toBroadcast($notifiable) 34 | { 35 | return (new BroadcastMessage([ 36 | 'id' => $this->id, 37 | 'data' => [ 38 | 'status' => 'warning', 39 | 'type' => 'login', 40 | 'message' => $this->message, 41 | 'link' => $this->link, 42 | ], 43 | 'created_at' => 'just now', 44 | ])); 45 | } 46 | 47 | public function toArray($notifiable) 48 | { 49 | return [ 50 | // 51 | ]; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /app/Notifications/PostNotification.php: -------------------------------------------------------------------------------- 1 | status = $post->status == 'succeeded' ? 'success' : 'error'; 21 | $this->message = "Post #$post->id status changed to $post->status "; 22 | $this->link = route('posts.show', $post->id); 23 | } 24 | 25 | public function via($notifiable) 26 | { 27 | return ['database', 'broadcast']; 28 | } 29 | 30 | public function toDatabase($notifiable): array 31 | { 32 | Cache::forget('notifications.' . $notifiable->id); 33 | return ['status' => $this->status, 'type' => 'post', 'message' => $this->message, 'link' => $this->link]; 34 | } 35 | 36 | public function toBroadcast($notifiable) 37 | { 38 | return (new BroadcastMessage([ 39 | 'id' => $this->id, 40 | 'data' => [ 41 | 'status' => $this->status, // for front-end 42 | 'type' => 'post', 43 | 'message' => $this->message, 44 | 'link' => $this->link, 45 | ], 46 | 'created_at' => 'just now', 47 | ])); 48 | } 49 | 50 | public function toArray($notifiable) 51 | { 52 | return [ 53 | // 54 | ]; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/Policies/AccountPolicy.php: -------------------------------------------------------------------------------- 1 | id == $account->user_id; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/Policies/MediaPolicy.php: -------------------------------------------------------------------------------- 1 | id == $media->user_id; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/Policies/NotificationPolicy.php: -------------------------------------------------------------------------------- 1 | id == $notification->notifiable_id; 16 | } 17 | 18 | public function delete(User $user, Notification $notification) 19 | { 20 | return $user->id == $notification->notifiable_id; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/Policies/PostPolicy.php: -------------------------------------------------------------------------------- 1 | id == $post->user_id; 16 | } 17 | 18 | public function edit(User $user, Post $post) 19 | { 20 | return $user->id == $post->user_id && $post->locked == 0; 21 | } 22 | 23 | public function delete(User $user, Post $post) 24 | { 25 | return $user->id == $post->user_id; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/Policies/SessionPolicy.php: -------------------------------------------------------------------------------- 1 | id == $session->user_id; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/PostaBot/Contracts/Tokenizable.php: -------------------------------------------------------------------------------- 1 | error = $error; 13 | } 14 | public function render() 15 | { 16 | return redirect()->route('accounts.index')->with('error', $this->error); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/PostaBot/Facades/Publisher.php: -------------------------------------------------------------------------------- 1 | uid; 14 | $message = $post->message; 15 | $token = $account->token; 16 | 17 | if ($post->media->count() > 0) { 18 | $url = "https://graph.facebook.com/$uid/feed?message=$message&access_token=$token"; 19 | 20 | foreach ($post->media as $index => $media) { 21 | $id = $this->uploadSinglePhoto($uid, $token, $media->original_path); 22 | $url .= "&attached_media%5B$index%5D=%7B%22media_fbid%22%3A%22$id%22%7D"; 23 | } 24 | $response = Http::post($url); 25 | 26 | } else { 27 | $response = Http::post("https://graph.facebook.com/$uid/feed?message=$message&access_token=$token"); 28 | } 29 | } catch (\Throwable $th) { 30 | throw new Exception("internal error happened"); 31 | } 32 | 33 | if (!$response->successful()) { 34 | throw new Exception($response->json()['error']['message']); 35 | } 36 | 37 | } 38 | 39 | private function uploadSinglePhoto($uid, $token, $path) 40 | { 41 | $response = Http::attach('attachment', file_get_contents(storage_path("app/{$path}")), 'photo.jpg')->post("https://graph.facebook.com/$uid/photos", ['access_token' => $token, 'published' => false]); 42 | return $response->json()['id']; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/PostaBot/PublisherProviders/Twitter.php: -------------------------------------------------------------------------------- 1 | token; 16 | $secret = $account->secret; 17 | 18 | $this->connection = new TwitterOAuth($consumer_key, $consumer_secret, $token, $secret); 19 | $this->connection->setTimeouts(30, 60); 20 | // split messages every 280 chars as twitter max chars is 280 21 | $messages_array = str_split($post->message, 280); 22 | 23 | // upload media 24 | $media_ids = ""; 25 | foreach ($post->media as $index => $media) { 26 | $id = $this->uploadSinglePhoto($media->original_path); 27 | $media_ids .= "$id,"; 28 | } 29 | 30 | //split every 4 media as twitter max media per post is 4 31 | $media_ids_array = array_map(function ($arr) { 32 | return implode(",", $arr); 33 | }, array_chunk(explode(",", $media_ids), 4)); 34 | 35 | $tweets = array_map(function ($message, $media_ids) { 36 | $result = []; 37 | if ($message) {$result['status'] = $message;} 38 | if ($media_ids) {$result['media_ids'] = $media_ids;} 39 | return $result; 40 | }, $messages_array, $media_ids_array); 41 | 42 | $parent = ""; 43 | foreach ($tweets as $tweet) { 44 | if ($parent) { 45 | $tweet = array_merge($tweet, ['in_reply_to_status_id' => $parent]); 46 | } 47 | 48 | $result = $this->connection->post('statuses/update', $tweet); 49 | 50 | if ($this->connection->getLastHttpCode() != 200) { 51 | throw new \Exception($this->connection->getLastBody()->errors[0]->message); 52 | } 53 | 54 | $parent = $result->id; 55 | } 56 | 57 | } catch (\Exception $ex) { 58 | throw new \Exception($ex->getMessage()); 59 | } 60 | 61 | } 62 | 63 | private function uploadSinglePhoto($path) 64 | { 65 | $media = $this->connection->upload('media/upload', ['media' => storage_path("app/{$path}")]); 66 | return $media->media_id_string; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /app/PostaBot/TokenizerProviders/Twitter.php: -------------------------------------------------------------------------------- 1 | redirect(); 13 | } 14 | 15 | public function getAndSaveData() 16 | { 17 | $user = Socialite::driver('twitter')->user(); 18 | auth()->user()->accounts()->updateOrCreate( 19 | ['platform' => 'Twitter', 'uid' => $user->id], 20 | ['name' => $user->nickname, 'type' => 'Account', 'token' => $user->token, 'secret' => $user->tokenSecret] 21 | ); 22 | } 23 | 24 | public function revoke($accessToken) 25 | { 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | rememberForever('notifications.' . auth()->user()->id, function () { 30 | return auth() 31 | ->user() 32 | ->notifications() 33 | ->orderBy('created_at', 'desc') 34 | ->limit(10) 35 | ->get(); 36 | }); 37 | 38 | $view->with('notifications', $notifications); 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/Providers/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | registerPolicies(); 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'App\Listeners\SuccessfulLoginListener', 19 | ], 20 | 21 | PostPublishEvent::class => [ 22 | PostPublishListener::class 23 | ], 24 | 25 | ]; 26 | 27 | /** 28 | * Register any events for your application. 29 | * 30 | * @return void 31 | */ 32 | public function boot() 33 | { 34 | // 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/Providers/PostaBotServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton(Tokenizable::class, function ($app) { 20 | $platforms = ['facebook' => 'App\PostaBot\TokenizerProviders\Facebook', 'twitter' => 'App\PostaBot\TokenizerProviders\Twitter', 'instagram' => 'App\PostaBot\TokenizerProviders\Instagram']; 21 | $platform = Route::current()->parameter('platform'); 22 | return new $platforms[$platform]; 23 | 24 | }); 25 | 26 | $this->app->singleton(PublisherManager::class, function ($app) { 27 | return new PublisherManager($app); 28 | }); 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | configureRateLimiting(); 39 | 40 | $this->routes(function () { 41 | Route::prefix('api') 42 | ->middleware('api') 43 | ->namespace($this->namespace) 44 | ->group(base_path('routes/api.php')); 45 | 46 | Route::middleware('web') 47 | ->namespace($this->namespace) 48 | ->group(base_path('routes/web.php')); 49 | }); 50 | } 51 | 52 | /** 53 | * Configure the rate limiters for the application. 54 | * 55 | * @return void 56 | */ 57 | protected function configureRateLimiting() 58 | { 59 | RateLimiter::for('api', function (Request $request) { 60 | return Limit::perMinute(60); 61 | }); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/Repositories/UserRepository.php: -------------------------------------------------------------------------------- 1 | user()->posts()->selectRaw("count(*) AS total, 12 | SUM(IF(draft = 1 , 1, 0)) AS drafts, 13 | SUM(IF(draft = 0 AND status='pending' , 1, 0)) AS queued, 14 | SUM(IF(draft = 0 AND status='succeeded', 1, 0)) AS succeeded, 15 | SUM(IF(draft = 1 AND status='failed', 1, 0)) AS failed" 16 | )->first(); 17 | $accounts = auth()->user()->accounts()->count(); 18 | 19 | return [ 20 | 'posts' => $posts->total, 21 | 'drafts' => $posts->drafts ?? 0, 22 | 'queued' => $posts->queued ?? 0, 23 | 'succeeded' => $posts->succeeded ?? 0, 24 | 'failed' => $posts->failed ?? 0, 25 | 'accounts' => $accounts, 26 | ]; 27 | } 28 | 29 | public static function getAccounts($fields = "*") 30 | { 31 | return auth()->user()->accounts()->orderBy('created_at', 'desc')->get($fields); 32 | } 33 | 34 | public static function getScheduledPosts() 35 | { 36 | return auth()->user()->posts()->where(['draft' => 0, 'locked' => 0])->get(); 37 | } 38 | 39 | public static function getMedia($fields = "*") 40 | { 41 | return auth()->user()->media()->get($fields); 42 | } 43 | 44 | public static function getDraftedPosts() 45 | { 46 | return auth()->user()->posts()->where(['draft' => 1])->withCount(['accounts', 'media'])->orderBy('updated_at', 'desc')->paginate(16); 47 | } 48 | 49 | public static function getQueuedPosts() 50 | { 51 | return auth()->user()->posts()->where(['draft' => 0])->withCount(['accounts', 'media'])->orderBy('updated_at', 'desc')->paginate(16); 52 | } 53 | 54 | public static function getNotifications() 55 | { 56 | return auth()->user()->notifications()->orderBy('created_at', 'desc')->paginate(15); 57 | } 58 | 59 | public static function getSessions() 60 | { 61 | return auth()->user()->session()->orderBy('last_activity', 'desc')->get()->toArray(); 62 | } 63 | 64 | public static function getSocialStatus() 65 | { 66 | $facebook = auth()->user()->socialAuthUser()->where(['provider' => 'facebook'])->first(); 67 | $google = auth()->user()->socialAuthUser()->where(['provider' => 'google'])->first(); 68 | 69 | return [ 70 | "facebook" => $facebook, 71 | "google" => $google, 72 | ]; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /app/Rules/CheckOldPass.php: -------------------------------------------------------------------------------- 1 | user()->password); 15 | } 16 | 17 | public function message() 18 | { 19 | return 'You entered wrong password '; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/Rules/PostAccountsCheck.php: -------------------------------------------------------------------------------- 1 | get(); 16 | if (!request()->draft && $accounts->count() == 0) { 17 | return false; 18 | } 19 | foreach ($accounts as $a) { 20 | if ($a->user_id != auth()->user()->id) { 21 | return false; 22 | } 23 | } 24 | return true; 25 | } 26 | 27 | 28 | public function message() 29 | { 30 | return 'Malformed Request , Please try again .. '; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Rules/PostMediaCheck.php: -------------------------------------------------------------------------------- 1 | get(); 15 | foreach ($media as $m) { 16 | if ($m->user_id != auth()->user()->id) { 17 | return false; 18 | } 19 | } 20 | return true; 21 | } 22 | 23 | 24 | public function message() 25 | { 26 | return 'Malformed Request , Please try again .. '; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/Services/MediaService.php: -------------------------------------------------------------------------------- 1 | user()->id . "/original"; 14 | $thumbPath = "media/" . auth()->user()->id . "/thumb"; 15 | 16 | foreach ($request->file('media') as $file) { 17 | $mediaName = md5(Str::random(40) . microtime()) . "." . $file->getClientOriginalExtension(); 18 | $mediaOriginalPath = $file->storeAs($originalPath, $mediaName); // save original at storage/app/media/{id}/original/{name} 19 | $mediaThumbPath = $file->storeAs($thumbPath, $mediaName); // save thumbnail at storage/app/media/{id}/thumb/{name} 20 | Image::make(storage_path("app/{$mediaThumbPath}"))->fit(120, 120)->save(); // resize image thumbnail 21 | $m = auth()->user()->media()->create([ 22 | 'name' => $mediaName, 23 | 'original_path' => $mediaOriginalPath, 24 | 'thumb_path' => $mediaThumbPath, 25 | ]); 26 | $media[] = ['id' => $m->id, 'src' => route('media.show.thumb', $mediaName)]; 27 | } 28 | 29 | return $media; 30 | } 31 | 32 | public static function delete($media) 33 | { 34 | if ($media->posts()->where(['draft' => 0, 'locked' => 0])->count() != 0) { 35 | return ['success' => false, 'message' => "Media #$media->id attached to some queued posts , please delete posts first"]; 36 | } 37 | $media->delete(); 38 | return ['success' => true]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/Services/PostService.php: -------------------------------------------------------------------------------- 1 | user()->posts()->create(['draft' => (bool) $request->draft, 'message' => $request->message, 'schedule_date' => $request->schedule_date, 'logs' => $log]); 14 | $post->media()->attach($request->media); 15 | $post->accounts()->attach($request->accounts); 16 | } 17 | public static function update($request, $post) 18 | { 19 | $post->draft = (bool) $request->draft ? 1 : 0; 20 | $post->message = $request->message; 21 | $post->schedule_date = $request->schedule_date; 22 | $mediaSync = $post->media()->sync($request->media); 23 | $accountsSync = $post->accounts()->sync($request->accounts); 24 | if ($post->isDirty() || ($mediaSync['attached'] || $mediaSync['detached']) || ($accountsSync['attached'] || $accountsSync['detached'])) { 25 | $log = static::updateLog($request); 26 | $post->logs = $log; 27 | $post->save(); 28 | } 29 | } 30 | private static function storeLog($request) 31 | { 32 | $now = Carbon::now()->toDateTimeString(); 33 | $then = Carbon::createFromTimestamp($request->schedule_date)->toDateTimeString(); 34 | if ($request->draft) { 35 | $message = "Post saved as draft at {$now}"; 36 | } else { 37 | $message = "Post saved and scheduled at {$now} to be published at {$then}"; 38 | } 39 | return [['status' => 'info', 'message' => $message]]; 40 | } 41 | 42 | private static function updateLog($request) 43 | { 44 | $now = Carbon::now()->toDateTimeString(); 45 | $then = Carbon::createFromTimestamp($request->schedule_date)->toDateTimeString(); 46 | if ($request->draft) { 47 | $message = "Post updated and saved as draft at {$now}"; 48 | } else { 49 | $message = "Post updated and scheduled at {$now} to be published at {$then}"; 50 | } 51 | return [['status' => 'info', 'message' => $message]]; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/Services/ProfileService.php: -------------------------------------------------------------------------------- 1 | _avatar->store('profile', 'public'); 17 | Image::make(public_path("storage/{$avatarPath}"))->fit(128, 128)->save(); 18 | auth()->user()->update(['avatar' => $avatarPath]); 19 | } 20 | 21 | private static function deleteOldAvatar() 22 | { 23 | $oldAvatarPath = auth()->user()->avatar; 24 | Storage::disk('local')->delete("public/{$oldAvatarPath}"); 25 | } 26 | 27 | public static function formatSessions($s) 28 | { 29 | return array_map(function ($session) { 30 | $agent = new Parser($session['user_agent']); 31 | $session['browser'] = $agent->browser->toString(); 32 | $session['os'] = $agent->os->toString(); 33 | $session['device'] = $agent->device->type; 34 | $session['status'] = (Session::getId() == $session['id']) ? 'This session' : ''; 35 | $session['last_activity'] = ($session['status'] == 'This session') ? 'Active Now' : Carbon::createFromTimestamp($session['last_activity'])->diffForHumans(); 36 | return $session; 37 | }, $s); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/Services/SocialAuthService.php: -------------------------------------------------------------------------------- 1 | id]])->first(); 18 | $loggedIn = Auth::check(); 19 | // login user with his linked social account 20 | if ($socialUser && !$loggedIn) { 21 | Auth::login($socialUser->user); 22 | return redirect()->route('dashboard'); 23 | } 24 | 25 | // redirect if user already logged in 26 | if ($socialUser && $loggedIn) { 27 | return redirect()->route('profile.index'); 28 | } 29 | 30 | // link existing user with social account 31 | if (!$socialUser && $loggedIn) { 32 | $alreadyExist = User::where(['email' => $data->email])->first(); 33 | if ($alreadyExist) { 34 | return redirect()->route('profile.index')->with('status', 'already linked to another laposta account'); 35 | } 36 | auth()->user()->socialAuthUser()->create(['provider' => $provider, 'uid' => $data->id, 'token' => $data->token]); 37 | return redirect()->route('profile.index'); 38 | } 39 | 40 | //register with social account 41 | 42 | if (!$socialUser && !$loggedIn) { 43 | $alreadyExist = User::where(['email' => $data->email])->first(); 44 | $user = $alreadyExist ? $alreadyExist : User::create(['email' => $data->email, 'name' => $data->name, 'password' => Hash::make(Str::random(24))]); 45 | $user->socialAuthUser()->create(['provider' => $provider, 'uid' => $data->id, 'token' => $data->token]); 46 | Auth::login($user); 47 | return redirect()->route('dashboard'); 48 | } 49 | 50 | return redirect()->route('login')->with('status', 'Error happend '); 51 | } 52 | 53 | public static function revokeFacebookToken($token) 54 | { 55 | $response = Http::delete("https://graph.facebook.com/v2.4/me/permissions?access_token={$token}"); 56 | } 57 | 58 | public static function revokeGoogleToken($token) 59 | { 60 | $response = Http::withHeaders([ 61 | 'Content-type' => 'application/x-www-form-urlencoded', 62 | ])->post("https://oauth2.googleapis.com/revoke?token={$token}"); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | make(Illuminate\Contracts\Console\Kernel::class); 34 | 35 | $status = $kernel->handle( 36 | $input = new Symfony\Component\Console\Input\ArgvInput, 37 | new Symfony\Component\Console\Output\ConsoleOutput 38 | ); 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Shutdown The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once Artisan has finished running, we will fire off the shutdown events 46 | | so that any final work may be done by the application before we shut 47 | | down the process. This is the last thing to happen to the request. 48 | | 49 | */ 50 | 51 | $kernel->terminate($input, $status); 52 | 53 | exit($status); 54 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | singleton( 30 | Illuminate\Contracts\Http\Kernel::class, 31 | App\Http\Kernel::class 32 | ); 33 | 34 | $app->singleton( 35 | Illuminate\Contracts\Console\Kernel::class, 36 | App\Console\Kernel::class 37 | ); 38 | 39 | $app->singleton( 40 | Illuminate\Contracts\Debug\ExceptionHandler::class, 41 | App\Exceptions\Handler::class 42 | ); 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Return The Application 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This script returns the application instance. The instance is given to 50 | | the calling script so we can separate the building of the instances 51 | | from the actual running of the application and sending responses. 52 | | 53 | */ 54 | 55 | return $app; 56 | -------------------------------------------------------------------------------- /bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel/laravel", 3 | "type": "project", 4 | "description": "The Laravel Framework.", 5 | "keywords": [ 6 | "framework", 7 | "laravel" 8 | ], 9 | "license": "MIT", 10 | "require": { 11 | "php": "^7.4.1", 12 | "abraham/twitteroauth": "^2.0", 13 | "beyondcode/laravel-websockets": "^1.11", 14 | "doctrine/dbal": "^2.12", 15 | "fideloper/proxy": "^4.2", 16 | "fruitcake/laravel-cors": "^2.0", 17 | "guzzlehttp/guzzle": "^7.2", 18 | "intervention/image": "^2.5", 19 | "laravel/framework": "^8.0", 20 | "laravel/socialite": "^5.0", 21 | "laravel/tinker": "^2.0", 22 | "laravel/ui": "^3.0", 23 | "predis/predis": "^1.1", 24 | "pusher/pusher-php-server": "~3.0", 25 | "whichbrowser/parser": "^2.1" 26 | }, 27 | "require-dev": { 28 | "barryvdh/laravel-debugbar": "^3.5", 29 | "facade/ignition": "^2.3.6", 30 | "fzaninotto/faker": "^1.9.1", 31 | "mockery/mockery": "^1.3.1", 32 | "nunomaduro/collision": "^5.0", 33 | "phpunit/phpunit": "^9.3" 34 | }, 35 | "config": { 36 | "optimize-autoloader": true, 37 | "preferred-install": "dist", 38 | "sort-packages": true 39 | }, 40 | "extra": { 41 | "laravel": { 42 | "dont-discover": [] 43 | } 44 | }, 45 | "autoload": { 46 | "psr-4": { 47 | "App\\": "app/", 48 | "Database\\Factories\\": "database/factories/", 49 | "Database\\Seeders\\": "database/seeders/" 50 | } 51 | }, 52 | "autoload-dev": { 53 | "psr-4": { 54 | "Tests\\": "tests/" 55 | } 56 | }, 57 | "minimum-stability": "dev", 58 | "prefer-stable": true, 59 | "scripts": { 60 | "post-autoload-dump": [ 61 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", 62 | "@php artisan package:discover --ansi" 63 | ], 64 | "post-root-package-install": [ 65 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" 66 | ], 67 | "post-create-project-cmd": [ 68 | "@php artisan key:generate --ansi" 69 | ] 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /config/auth.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'guard' => 'web', 18 | 'passwords' => '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 | | here which uses session storage and the Eloquent user provider. 29 | | 30 | | All authentication drivers have a user provider. This defines how the 31 | | users are actually retrieved out of your database or other storage 32 | | mechanisms used by this application to persist your user's data. 33 | | 34 | | Supported: "session", "token" 35 | | 36 | */ 37 | 38 | 'guards' => [ 39 | 'web' => [ 40 | 'driver' => 'session', 41 | 'provider' => 'users', 42 | ], 43 | 44 | 'api' => [ 45 | 'driver' => 'token', 46 | 'provider' => 'users', 47 | 'hash' => false, 48 | ], 49 | ], 50 | 51 | /* 52 | |-------------------------------------------------------------------------- 53 | | User Providers 54 | |-------------------------------------------------------------------------- 55 | | 56 | | All authentication drivers have a user provider. This defines how the 57 | | users are actually retrieved out of your database or other storage 58 | | mechanisms used by this application to persist your user's data. 59 | | 60 | | If you have multiple user tables or models you may configure multiple 61 | | sources which represent each model / table. These sources may then 62 | | be assigned to any extra authentication guards you have defined. 63 | | 64 | | Supported: "database", "eloquent" 65 | | 66 | */ 67 | 68 | 'providers' => [ 69 | 'users' => [ 70 | 'driver' => 'eloquent', 71 | 'model' => App\Models\User::class, 72 | ], 73 | 74 | // 'users' => [ 75 | // 'driver' => 'database', 76 | // 'table' => 'users', 77 | // ], 78 | ], 79 | 80 | /* 81 | |-------------------------------------------------------------------------- 82 | | Resetting Passwords 83 | |-------------------------------------------------------------------------- 84 | | 85 | | You may specify multiple password reset configurations if you have more 86 | | than one user table or model in the application and you want to have 87 | | separate password reset settings based on the specific user types. 88 | | 89 | | The expire time is the number of minutes that the reset token should be 90 | | considered valid. This security feature keeps tokens short-lived so 91 | | they have less time to be guessed. You may change this as needed. 92 | | 93 | */ 94 | 95 | 'passwords' => [ 96 | 'users' => [ 97 | 'provider' => 'users', 98 | 'table' => 'password_resets', 99 | 'expire' => 60, 100 | 'throttle' => 60, 101 | ], 102 | ], 103 | 104 | /* 105 | |-------------------------------------------------------------------------- 106 | | Password Confirmation Timeout 107 | |-------------------------------------------------------------------------- 108 | | 109 | | Here you may define the amount of seconds before a password confirmation 110 | | times out and the user is prompted to re-enter their password via the 111 | | confirmation screen. By default, the timeout lasts for three hours. 112 | | 113 | */ 114 | 115 | 'password_timeout' => 10800, 116 | 117 | ]; 118 | -------------------------------------------------------------------------------- /config/broadcasting.php: -------------------------------------------------------------------------------- 1 | env('BROADCAST_DRIVER', 'null'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Broadcast Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the broadcast connections that will be used 26 | | to broadcast events to other systems or over websockets. Samples of 27 | | each available type of connection are provided inside this array. 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'pusher' => [ 34 | 'driver' => 'pusher', 35 | 'key' => env('PUSHER_APP_KEY'), 36 | 'secret' => env('PUSHER_APP_SECRET'), 37 | 'app_id' => env('PUSHER_APP_ID'), 38 | 'options' => [ 39 | 'cluster' => env('PUSHER_APP_CLUSTER'), 40 | 'host' => env('APP_URL', 'localhost'), 41 | 'port' => 6001, 42 | 'scheme' => env('PUSHER_APP_SCHEME', 'http'), 43 | 'encrypted' => true, 44 | //'useTLS' => true, 45 | 'curl_options' => [ 46 | CURLOPT_SSL_VERIFYHOST => 0, 47 | CURLOPT_SSL_VERIFYPEER => 0, 48 | ], 49 | 50 | ], 51 | ], 52 | 53 | 'redis' => [ 54 | 'driver' => 'redis', 55 | 'connection' => 'default', 56 | ], 57 | 58 | 'log' => [ 59 | 'driver' => 'log', 60 | ], 61 | 62 | 'null' => [ 63 | 'driver' => 'null', 64 | ], 65 | 66 | ], 67 | 68 | ]; 69 | -------------------------------------------------------------------------------- /config/cache.php: -------------------------------------------------------------------------------- 1 | env('CACHE_DRIVER', 'file'), 22 | 23 | /* 24 | |-------------------------------------------------------------------------- 25 | | Cache Stores 26 | |-------------------------------------------------------------------------- 27 | | 28 | | Here you may define all of the cache "stores" for your application as 29 | | well as their drivers. You may even define multiple stores for the 30 | | same cache driver to group types of items stored in your caches. 31 | | 32 | */ 33 | 34 | 'stores' => [ 35 | 36 | 'apc' => [ 37 | 'driver' => 'apc', 38 | ], 39 | 40 | 'array' => [ 41 | 'driver' => 'array', 42 | 'serialize' => false, 43 | ], 44 | 45 | 'database' => [ 46 | 'driver' => 'database', 47 | 'table' => 'cache', 48 | 'connection' => null, 49 | ], 50 | 51 | 'file' => [ 52 | 'driver' => 'file', 53 | 'path' => storage_path('framework/cache/data'), 54 | ], 55 | 56 | 'memcached' => [ 57 | 'driver' => 'memcached', 58 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), 59 | 'sasl' => [ 60 | env('MEMCACHED_USERNAME'), 61 | env('MEMCACHED_PASSWORD'), 62 | ], 63 | 'options' => [ 64 | // Memcached::OPT_CONNECT_TIMEOUT => 2000, 65 | ], 66 | 'servers' => [ 67 | [ 68 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 69 | 'port' => env('MEMCACHED_PORT', 11211), 70 | 'weight' => 100, 71 | ], 72 | ], 73 | ], 74 | 75 | 'redis' => [ 76 | 'driver' => 'redis', 77 | 'connection' => 'cache', 78 | ], 79 | 80 | 'dynamodb' => [ 81 | 'driver' => 'dynamodb', 82 | 'key' => env('AWS_ACCESS_KEY_ID'), 83 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 84 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 85 | 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), 86 | 'endpoint' => env('DYNAMODB_ENDPOINT'), 87 | ], 88 | 89 | ], 90 | 91 | /* 92 | |-------------------------------------------------------------------------- 93 | | Cache Key Prefix 94 | |-------------------------------------------------------------------------- 95 | | 96 | | When utilizing a RAM based store such as APC or Memcached, there might 97 | | be other applications utilizing the same cache. So, we'll specify a 98 | | value to get prefixed to all our keys so we can avoid collisions. 99 | | 100 | */ 101 | 102 | 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_cache'), 103 | 104 | ]; 105 | -------------------------------------------------------------------------------- /config/cors.php: -------------------------------------------------------------------------------- 1 | ['api/*'], 19 | 20 | 'allowed_methods' => ['*'], 21 | 22 | 'allowed_origins' => ['*'], 23 | 24 | 'allowed_origins_patterns' => [], 25 | 26 | 'allowed_headers' => ['*'], 27 | 28 | 'exposed_headers' => [], 29 | 30 | 'max_age' => 0, 31 | 32 | 'supports_credentials' => false, 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | env('FILESYSTEM_DRIVER', 'local'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Default Cloud Filesystem Disk 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Many applications store files both locally and in the cloud. For this 24 | | reason, you may specify a default "cloud" driver here. This driver 25 | | will be bound as the Cloud disk implementation in the container. 26 | | 27 | */ 28 | 29 | 'cloud' => env('FILESYSTEM_CLOUD', 's3'), 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Filesystem Disks 34 | |-------------------------------------------------------------------------- 35 | | 36 | | Here you may configure as many filesystem "disks" as you wish, and you 37 | | may even configure multiple disks of the same driver. Defaults have 38 | | been setup for each driver as an example of the required options. 39 | | 40 | | Supported Drivers: "local", "ftp", "sftp", "s3" 41 | | 42 | */ 43 | 44 | 'disks' => [ 45 | 46 | 'local' => [ 47 | 'driver' => 'local', 48 | 'root' => storage_path('app'), 49 | ], 50 | 51 | 'public' => [ 52 | 'driver' => 'local', 53 | 'root' => storage_path('app/public'), 54 | 'url' => env('APP_URL') . '/storage', 55 | 'visibility' => 'public', 56 | ], 57 | 58 | 's3' => [ 59 | 'driver' => 's3', 60 | 'key' => env('AWS_ACCESS_KEY_ID'), 61 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 62 | 'region' => env('AWS_DEFAULT_REGION'), 63 | 'bucket' => env('AWS_BUCKET'), 64 | 'url' => env('AWS_URL'), 65 | 'endpoint' => env('AWS_ENDPOINT'), 66 | ], 67 | 68 | ], 69 | 70 | /* 71 | |-------------------------------------------------------------------------- 72 | | Symbolic Links 73 | |-------------------------------------------------------------------------- 74 | | 75 | | Here you may configure the symbolic links that will be created when the 76 | | `storage:link` Artisan command is executed. The array keys should be 77 | | the locations of the links and the values should be their targets. 78 | | 79 | */ 80 | 81 | 'links' => [ 82 | public_path('storage') => storage_path('app/public'), 83 | ], 84 | 85 | ]; 86 | -------------------------------------------------------------------------------- /config/hashing.php: -------------------------------------------------------------------------------- 1 | 'bcrypt', 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Bcrypt Options 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may specify the configuration options that should be used when 26 | | passwords are hashed using the Bcrypt algorithm. This will allow you 27 | | to control the amount of time it takes to hash the given password. 28 | | 29 | */ 30 | 31 | 'bcrypt' => [ 32 | 'rounds' => env('BCRYPT_ROUNDS', 10), 33 | ], 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Argon Options 38 | |-------------------------------------------------------------------------- 39 | | 40 | | Here you may specify the configuration options that should be used when 41 | | passwords are hashed using the Argon algorithm. These will allow you 42 | | to control the amount of time it takes to hash the given password. 43 | | 44 | */ 45 | 46 | 'argon' => [ 47 | 'memory' => 1024, 48 | 'threads' => 2, 49 | 'time' => 2, 50 | ], 51 | 52 | ]; 53 | -------------------------------------------------------------------------------- /config/logging.php: -------------------------------------------------------------------------------- 1 | env('LOG_CHANNEL', 'stack'), 21 | 22 | /* 23 | |-------------------------------------------------------------------------- 24 | | Log Channels 25 | |-------------------------------------------------------------------------- 26 | | 27 | | Here you may configure the log channels for your application. Out of 28 | | the box, Laravel uses the Monolog PHP logging library. This gives 29 | | you a variety of powerful log handlers / formatters to utilize. 30 | | 31 | | Available Drivers: "single", "daily", "slack", "syslog", 32 | | "errorlog", "monolog", 33 | | "custom", "stack" 34 | | 35 | */ 36 | 37 | 'channels' => [ 38 | 'stack' => [ 39 | 'driver' => 'stack', 40 | 'channels' => ['single'], 41 | 'ignore_exceptions' => false, 42 | ], 43 | 44 | 'single' => [ 45 | 'driver' => 'single', 46 | 'path' => storage_path('logs/laravel.log'), 47 | 'level' => env('LOG_LEVEL', 'debug'), 48 | ], 49 | 50 | 'daily' => [ 51 | 'driver' => 'daily', 52 | 'path' => storage_path('logs/laravel.log'), 53 | 'level' => env('LOG_LEVEL', 'debug'), 54 | 'days' => 14, 55 | ], 56 | 57 | 'slack' => [ 58 | 'driver' => 'slack', 59 | 'url' => env('LOG_SLACK_WEBHOOK_URL'), 60 | 'username' => 'Laravel Log', 61 | 'emoji' => ':boom:', 62 | 'level' => env('LOG_LEVEL', 'critical'), 63 | ], 64 | 65 | 'papertrail' => [ 66 | 'driver' => 'monolog', 67 | 'level' => env('LOG_LEVEL', 'debug'), 68 | 'handler' => SyslogUdpHandler::class, 69 | 'handler_with' => [ 70 | 'host' => env('PAPERTRAIL_URL'), 71 | 'port' => env('PAPERTRAIL_PORT'), 72 | ], 73 | ], 74 | 75 | 'stderr' => [ 76 | 'driver' => 'monolog', 77 | 'handler' => StreamHandler::class, 78 | 'formatter' => env('LOG_STDERR_FORMATTER'), 79 | 'with' => [ 80 | 'stream' => 'php://stderr', 81 | ], 82 | ], 83 | 84 | 'syslog' => [ 85 | 'driver' => 'syslog', 86 | 'level' => env('LOG_LEVEL', 'debug'), 87 | ], 88 | 89 | 'errorlog' => [ 90 | 'driver' => 'errorlog', 91 | 'level' => env('LOG_LEVEL', 'debug'), 92 | ], 93 | 94 | 'null' => [ 95 | 'driver' => 'monolog', 96 | 'handler' => NullHandler::class, 97 | ], 98 | 99 | 'emergency' => [ 100 | 'path' => storage_path('logs/laravel.log'), 101 | ], 102 | ], 103 | 104 | ]; 105 | -------------------------------------------------------------------------------- /config/mail.php: -------------------------------------------------------------------------------- 1 | env('MAIL_MAILER', 'smtp'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Mailer Configurations 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may configure all of the mailers used by your application plus 24 | | their respective settings. Several examples have been configured for 25 | | you and you are free to add your own as your application requires. 26 | | 27 | | Laravel supports a variety of mail "transport" drivers to be used while 28 | | sending an e-mail. You will specify which one you are using for your 29 | | mailers below. You are free to add additional mailers as required. 30 | | 31 | | Supported: "smtp", "sendmail", "mailgun", "ses", 32 | | "postmark", "log", "array" 33 | | 34 | */ 35 | 36 | 'mailers' => [ 37 | 'smtp' => [ 38 | 'transport' => 'smtp', 39 | 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), 40 | 'port' => env('MAIL_PORT', 587), 41 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 42 | 'username' => env('MAIL_USERNAME'), 43 | 'password' => env('MAIL_PASSWORD'), 44 | 'timeout' => null, 45 | 'auth_mode' => null, 46 | ], 47 | 48 | 'ses' => [ 49 | 'transport' => 'ses', 50 | ], 51 | 52 | 'mailgun' => [ 53 | 'transport' => 'mailgun', 54 | ], 55 | 56 | 'postmark' => [ 57 | 'transport' => 'postmark', 58 | ], 59 | 60 | 'sendmail' => [ 61 | 'transport' => 'sendmail', 62 | 'path' => '/usr/sbin/sendmail -bs', 63 | ], 64 | 65 | 'log' => [ 66 | 'transport' => 'log', 67 | 'channel' => env('MAIL_LOG_CHANNEL'), 68 | ], 69 | 70 | 'array' => [ 71 | 'transport' => 'array', 72 | ], 73 | ], 74 | 75 | /* 76 | |-------------------------------------------------------------------------- 77 | | Global "From" Address 78 | |-------------------------------------------------------------------------- 79 | | 80 | | You may wish for all e-mails sent by your application to be sent from 81 | | the same address. Here, you may specify a name and address that is 82 | | used globally for all e-mails that are sent by your application. 83 | | 84 | */ 85 | 86 | 'from' => [ 87 | 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), 88 | 'name' => env('MAIL_FROM_NAME', 'Example'), 89 | ], 90 | 91 | /* 92 | |-------------------------------------------------------------------------- 93 | | Markdown Mail Settings 94 | |-------------------------------------------------------------------------- 95 | | 96 | | If you are using Markdown based email rendering, you may configure your 97 | | theme and component paths here, allowing you to customize the design 98 | | of the emails. Or, you may simply stick with the Laravel defaults! 99 | | 100 | */ 101 | 102 | 'markdown' => [ 103 | 'theme' => 'default', 104 | 105 | 'paths' => [ 106 | resource_path('views/vendor/mail'), 107 | ], 108 | ], 109 | 110 | ]; 111 | -------------------------------------------------------------------------------- /config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_CONNECTION', 'sync'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Queue Connections 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here you may configure the connection information for each server that 24 | | is used by your application. A default configuration has been added 25 | | for each back-end shipped with Laravel. You are 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 | 'table' => 'jobs', 40 | 'queue' => 'default', 41 | 'retry_after' => 90, 42 | ], 43 | 44 | 'beanstalkd' => [ 45 | 'driver' => 'beanstalkd', 46 | 'host' => 'localhost', 47 | 'queue' => 'default', 48 | 'retry_after' => 90, 49 | 'block_for' => 0, 50 | ], 51 | 52 | 'sqs' => [ 53 | 'driver' => 'sqs', 54 | 'key' => env('AWS_ACCESS_KEY_ID'), 55 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 56 | 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), 57 | 'queue' => env('SQS_QUEUE', 'your-queue-name'), 58 | 'suffix' => env('SQS_SUFFIX'), 59 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 60 | ], 61 | 62 | 'redis' => [ 63 | 'driver' => 'redis', 64 | 'connection' => 'default', 65 | 'queue' => env('REDIS_QUEUE', 'default'), 66 | 'retry_after' => 90, 67 | 'block_for' => null, 68 | ], 69 | 70 | ], 71 | 72 | /* 73 | |-------------------------------------------------------------------------- 74 | | Failed Queue Jobs 75 | |-------------------------------------------------------------------------- 76 | | 77 | | These options configure the behavior of failed queue job logging so you 78 | | can control which database and table are used to store the jobs that 79 | | have failed. You may change them to any database / table you wish. 80 | | 81 | */ 82 | 83 | 'failed' => [ 84 | 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), 85 | 'database' => env('DB_CONNECTION', 'mysql'), 86 | 'table' => 'failed_jobs', 87 | ], 88 | 89 | ]; 90 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), 21 | ], 22 | 23 | 'postmark' => [ 24 | 'token' => env('POSTMARK_TOKEN'), 25 | ], 26 | 27 | 'ses' => [ 28 | 'key' => env('AWS_ACCESS_KEY_ID'), 29 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 30 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 31 | ], 32 | 33 | 'facebook' => [ 34 | 'client_id' => env('FACEBOOK_CLIENT_ID'), 35 | 'client_secret' => env('FACEBOOK_CLIENT_SECRET'), 36 | 'redirect' => env('FACEBOOK_REDIRECT'), 37 | ], 38 | 39 | 'google' => [ 40 | 'client_id' => env('GOOGLE_CLIENT_ID'), 41 | 'client_secret' => env('GOOGLE_CLIENT_SECRET'), 42 | 'redirect' => env('GOOGLE_REDIRECT'), 43 | ], 44 | 45 | 'twitter' => [ 46 | 'client_id' => env('TWITTER_CLIENT_ID'), 47 | 'client_secret' => env('TWITTER_CLIENT_SECRET'), 48 | 'redirect' => env('TWITTER_REDIRECT'), 49 | ], 50 | 'instagram' => [ 51 | 'client_id' => env('INSTAGRAM_CLIENT_ID'), 52 | 'client_secret' => env('INSTAGRAM_CLIENT_SECRET'), 53 | 'redirect' => env('INSTAGRAM_REDIRECT'), 54 | ], 55 | 56 | ]; 57 | -------------------------------------------------------------------------------- /config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | resource_path('views'), 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled View Path 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This option determines where all the compiled Blade templates will be 26 | | stored for your application. Typically, this is within the storage 27 | | directory. However, as usual, you are free to change this value. 28 | | 29 | */ 30 | 31 | 'compiled' => env( 32 | 'VIEW_COMPILED_PATH', 33 | realpath(storage_path('framework/views')) 34 | ), 35 | 36 | ]; 37 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | *.sqlite-journal 3 | -------------------------------------------------------------------------------- /database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->name, 27 | 'email' => $this->faker->unique()->safeEmail, 28 | 'email_verified_at' => now(), 29 | 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 30 | 'remember_token' => Str::random(10), 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/0000_00_00_000000_create_websockets_statistics_entries_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('app_id'); 19 | $table->integer('peak_connection_count'); 20 | $table->integer('websocket_message_count'); 21 | $table->integer('api_message_count'); 22 | $table->nullableTimestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('websockets_statistics_entries'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('name'); 19 | $table->string('email')->unique(); 20 | $table->timestamp('email_verified_at')->nullable(); 21 | $table->string('password'); 22 | $table->string('avatar')->nullable(); 23 | $table->rememberToken(); 24 | $table->timestamps(); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('users'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_100000_create_password_resets_table.php: -------------------------------------------------------------------------------- 1 | string('email')->index(); 18 | $table->string('token'); 19 | $table->timestamp('created_at')->nullable(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::dropIfExists('password_resets'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2019_08_19_000000_create_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->string('uuid')->unique(); 19 | $table->text('connection'); 20 | $table->text('queue'); 21 | $table->longText('payload'); 22 | $table->longText('exception'); 23 | $table->timestamp('failed_at')->useCurrent(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('failed_jobs'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/migrations/2020_10_27_110614_create_media_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('user_id')->constrained()->onDelete('cascade'); 19 | $table->string('name'); 20 | $table->string('original_path'); 21 | $table->string('thumb_path'); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('media'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/migrations/2020_10_30_110207_create_social_auth_users_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('user_id')->constrained()->onDelete('cascade'); 19 | $table->enum('provider', array('facebook', 'google')); 20 | $table->string('uid')->unique(); 21 | $table->string('token'); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('social_auth_users'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/migrations/2020_10_31_133916_create_sessions_table.php: -------------------------------------------------------------------------------- 1 | integer('pub_id')->unique()->index(); 20 | $table->string('id')->primary(); 21 | $table->foreignId('user_id')->nullable()->index(); 22 | $table->string('ip_address', 45)->nullable(); 23 | $table->text('user_agent')->nullable(); 24 | $table->text('payload'); 25 | $table->integer('last_activity')->index(); 26 | 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::dropIfExists('sessions'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /database/migrations/2020_10_31_133920_create_sessions_pub_id_column.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('user_id')->constrained()->onDelete('cascade'); 19 | $table->enum('platform', array('facebook', 'twitter', 'instagram')); 20 | $table->string('uid'); 21 | $table->string('name'); 22 | $table->string('type'); 23 | $table->string('token'); 24 | $table->string('secret')->nullable(); 25 | $table->timestamps(); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | * 32 | * @return void 33 | */ 34 | public function down() 35 | { 36 | Schema::dropIfExists('accounts'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /database/migrations/2020_11_12_031714_create_posts_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->foreignId('user_id')->constrained()->onDelete('cascade'); 19 | $table->text('message')->nullable(); 20 | $table->boolean('draft')->default(1)->index(); 21 | $table->enum('status', array('pending', 'succeeded', 'critical', 'failed'))->default('pending'); 22 | $table->json('logs')->nullable(); 23 | $table->bigInteger('schedule_date')->nullable()->index(); 24 | $table->boolean('locked')->default(0)->index(); 25 | $table->timestamps(); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | * 32 | * @return void 33 | */ 34 | public function down() 35 | { 36 | Schema::dropIfExists('posts'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /database/migrations/2021_02_17_140216_create_media_post_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | 19 | $table->bigInteger('post_id')->unsigned(); 20 | $table->bigInteger('media_id')->unsigned(); 21 | 22 | $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade'); 23 | $table->foreign('media_id')->references('id')->on('media')->onDelete('cascade'); 24 | 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('media_post'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/2021_02_20_081434_create_jobs_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->string('queue')->index(); 19 | $table->longText('payload'); 20 | $table->unsignedTinyInteger('attempts'); 21 | $table->unsignedInteger('reserved_at')->nullable(); 22 | $table->unsignedInteger('available_at'); 23 | $table->unsignedInteger('created_at'); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('jobs'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/migrations/2021_02_24_190312_create_account_post_table.php: -------------------------------------------------------------------------------- 1 | id(); 18 | $table->bigInteger('post_id')->unsigned(); 19 | $table->bigInteger('account_id')->unsigned(); 20 | 21 | $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade'); 22 | $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('account_post'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/migrations/2021_02_28_020647_create_notifications_table.php: -------------------------------------------------------------------------------- 1 | uuid('id')->primary(); 18 | $table->string('type'); 19 | $table->morphs('notifiable'); 20 | $table->text('data'); 21 | $table->timestamp('read_at')->nullable(); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('notifications'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --config=node_modules/laravel-mix/setup/webpack.config.js", 6 | "watch": "npm run development -- --watch", 7 | "watch-poll": "npm run watch -- --watch-poll", 8 | "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --disable-host-check --config=node_modules/laravel-mix/setup/webpack.config.js", 9 | "prod": "npm run production", 10 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --config=node_modules/laravel-mix/setup/webpack.config.js" 11 | }, 12 | "devDependencies": { 13 | "axios": "^0.21.1", 14 | "bootstrap": "^4.6.0", 15 | "cross-env": "^7.0", 16 | "jquery": "^3.6", 17 | "laravel-mix": "^6.0.0-beta.17", 18 | "lodash": "^4.17.19", 19 | "popper.js": "^1.16.1", 20 | "resolve-url-loader": "^3.1.2", 21 | "sass": "^1.32.11", 22 | "sass-loader": "^11.0.1", 23 | "vue": "^2.6.12", 24 | "vue-loader": "^15.9.5", 25 | "vue-template-compiler": "^2.6.12" 26 | }, 27 | "dependencies": { 28 | "-": "0.0.1", 29 | "laravel-echo": "^1.10.0", 30 | "moment": "^2.29.1", 31 | "pusher-js": "4.3.0", 32 | "save": "^2.4.0", 33 | "vue-observe-visibility": "^1.0.0", 34 | "vue-select-image": "^1.9.0", 35 | "vue-toast-notification": "^0.6.2", 36 | "vue2-datepicker": "^3.9.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | ./tests/Unit 10 | 11 | 12 | ./tests/Feature 13 | 14 | 15 | 16 | 17 | ./app 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/favicon.ico -------------------------------------------------------------------------------- /public/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/images/avatar.png -------------------------------------------------------------------------------- /public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/images/logo.png -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class); 50 | 51 | $response = tap($kernel->handle( 52 | $request = Request::capture() 53 | ))->send(); 54 | 55 | $kernel->terminate($request, $response); 56 | -------------------------------------------------------------------------------- /public/mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/js/app.js": "/js/app.js", 3 | "/css/app.css": "/css/app.css" 4 | } 5 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /public/storage: -------------------------------------------------------------------------------- 1 | /media/veracrypt1/Dev/finished-projects/laravel-laposta/storage/app/public -------------------------------------------------------------------------------- /public/web.config: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/public/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /resources/css/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/resources/css/app.css -------------------------------------------------------------------------------- /resources/js/app.js: -------------------------------------------------------------------------------- 1 | require("./bootstrap"); 2 | 3 | import Vue from "vue"; 4 | import VueObserveVisibility from "vue-observe-visibility"; 5 | import VueToast from "vue-toast-notification"; 6 | import "vue-toast-notification/dist/theme-sugar.css"; 7 | 8 | Vue.component( 9 | "MediaUploader", 10 | require("./components/MediaUploader.vue").default 11 | ); 12 | 13 | Vue.component("PostCreator", require("./components/PostCreator.vue").default); 14 | Vue.component("PostEditor", require("./components/PostEditor.vue").default); 15 | Vue.component( 16 | "Notifications", 17 | require("./components/Notifications.vue").default 18 | ); 19 | 20 | Vue.use(VueObserveVisibility); 21 | Vue.use(VueToast); 22 | const app = new Vue({ 23 | el: "#app" 24 | }); 25 | -------------------------------------------------------------------------------- /resources/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | window._ = require("lodash"); 2 | 3 | /** 4 | * We'll load jQuery and the Bootstrap jQuery plugin which provides support 5 | * for JavaScript based Bootstrap features such as modals and tabs. This 6 | * code may be modified to fit the specific needs of your application. 7 | */ 8 | 9 | try { 10 | window.Popper = require("popper.js").default; 11 | window.$ = window.jQuery = require("jquery"); 12 | 13 | require("bootstrap"); 14 | } catch (e) {} 15 | 16 | /** 17 | * We'll load the axios HTTP library which allows us to easily issue requests 18 | * to our Laravel back-end. This library automatically handles sending the 19 | * CSRF token as a header based on the value of the "XSRF" token cookie. 20 | */ 21 | 22 | window.axios = require("axios"); 23 | 24 | window.axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest"; 25 | 26 | /** 27 | * Echo exposes an expressive API for subscribing to channels and listening 28 | * for events that are broadcast by Laravel. Echo and event broadcasting 29 | * allows your team to easily build robust real-time web applications. 30 | */ 31 | 32 | import Echo from "laravel-echo"; 33 | 34 | window.Pusher = require("pusher-js"); 35 | 36 | window.Echo = new Echo({ 37 | broadcaster: "pusher", 38 | key: process.env.MIX_PUSHER_APP_KEY, 39 | cluster: process.env.MIX_PUSHER_APP_CLUSTER, 40 | wsHost: window.location.hostname, 41 | disableStats: true, 42 | wsPort: 6001, 43 | wssPort: 6001, 44 | encrypted: true, 45 | forceTLS: true, 46 | enabledTransports: ["ws", "wss"] 47 | }); 48 | -------------------------------------------------------------------------------- /resources/js/components/EventBus.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | 3 | const Bus = new Vue({}); 4 | 5 | export default Bus; 6 | -------------------------------------------------------------------------------- /resources/js/components/MediaUploader.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 107 | -------------------------------------------------------------------------------- /resources/js/components/Notifications.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 108 | -------------------------------------------------------------------------------- /resources/js/components/PostCreator.vue: -------------------------------------------------------------------------------- 1 | 105 | 106 | 144 | -------------------------------------------------------------------------------- /resources/lang/en/auth.php: -------------------------------------------------------------------------------- 1 | 'These credentials do not match our records.', 17 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /resources/lang/en/pagination.php: -------------------------------------------------------------------------------- 1 | '« Previous', 17 | 'next' => 'Next »', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /resources/lang/en/passwords.php: -------------------------------------------------------------------------------- 1 | 'Your password has been reset!', 17 | 'sent' => 'We have emailed your password reset link!', 18 | 'throttled' => 'Please wait before retrying.', 19 | 'token' => 'This password reset token is invalid.', 20 | 'user' => "We can't find a user with that email address.", 21 | 22 | ]; 23 | -------------------------------------------------------------------------------- /resources/sass/_variables.scss: -------------------------------------------------------------------------------- 1 | // Body 2 | $body-bg: #f8fafc; 3 | 4 | // Typography 5 | $font-family-sans-serif: 'Nunito', sans-serif; 6 | $font-size-base: 0.9rem; 7 | $line-height-base: 1.6; 8 | 9 | // Colors 10 | $blue: #3490dc; 11 | $indigo: #6574cd; 12 | $purple: #9561e2; 13 | $pink: #f66d9b; 14 | $red: #e3342f; 15 | $orange: #f6993f; 16 | $yellow: #ffed4a; 17 | $green: #38c172; 18 | $teal: #4dc0b5; 19 | $cyan: #6cb2eb; 20 | -------------------------------------------------------------------------------- /resources/sass/app.scss: -------------------------------------------------------------------------------- 1 | // Fonts 2 | @import url('https://fonts.googleapis.com/css?family=Nunito'); 3 | 4 | // Variables 5 | @import 'variables'; 6 | 7 | // Bootstrap 8 | @import '~bootstrap/scss/bootstrap'; 9 | -------------------------------------------------------------------------------- /resources/views/auth/login.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.auth') 2 | @section('title', 'Login') 3 | 4 | @section('content') 5 |
6 |
7 | 8 | @if (session('status')) 9 | 12 | @endif 13 |
14 | @csrf 15 |
16 | 20 |
21 |
22 | 23 |
24 |
25 | @error('email') 26 | 27 | {{ $message }} 28 | 29 | @enderror 30 |
31 |
32 | 35 |
36 |
37 | 38 |
39 |
40 | @error('password') 41 | 42 | {{ $message }} 43 | 44 | @enderror 45 |
46 |
47 |
48 |
49 | 50 | 53 |
54 |
55 | 56 |
57 | 58 |
59 | 60 |
61 |
62 | 63 | 73 | 74 | 75 |

76 | @if (Route::has('password.request')) 77 | I forgot my password 78 | @endif 79 | 80 |

81 |

82 | Register a new membership 83 |

84 |
85 | 86 |
87 | 88 | @endsection 89 | -------------------------------------------------------------------------------- /resources/views/auth/passwords/confirm.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.auth') 2 | @section('title','Confirm Your Password') 3 | @section('content') 4 |
5 |
6 | 7 | @if (session('status')) 8 | 11 | @endif 12 |
13 | @csrf 14 |
15 | 18 |
19 |
20 | 21 |
22 |
23 | 24 | @error('password') 25 | 26 | {{ $message }} 27 | 28 | @enderror 29 |
30 |
31 |
32 | 33 |
34 | 35 |
36 |
37 | @if (Route::has('password.request')) 38 |

39 | I forgot my password 40 |

41 | @endif 42 |
43 | 44 |
45 | 46 | @endsection 47 | -------------------------------------------------------------------------------- /resources/views/auth/passwords/email.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.auth') 2 | @section('title', 'Forgot Password') 3 | @section('content') 4 |
5 |
6 | 7 | @if (session('status')) 8 | 11 | @endif 12 |
13 | @csrf 14 |
15 | 19 |
20 |
21 | 22 |
23 |
24 | 25 | @error('email') 26 | 27 | {{ $message }} 28 | 29 | @enderror 30 |
31 |
32 |
33 | 34 |
35 | 36 |
37 |
38 | 39 |

40 | Login 41 |

42 |

43 | Register a new membership 44 |

45 |
46 | 47 |
48 | 49 | @endsection 50 | -------------------------------------------------------------------------------- /resources/views/auth/passwords/reset.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.auth') 2 | @section('title','Recover Password') 3 | @section('content') 4 | 5 |
6 |
7 | 8 | 9 |
10 | @csrf 11 | 12 | 13 |
14 | 17 |
18 |
19 | 20 |
21 |
22 | @error('email') 23 | 24 | {{ $message }} 25 | 26 | @enderror 27 |
28 | 29 |
30 | 33 |
34 |
35 | 36 |
37 |
38 | @error('password') 39 | 40 | {{ $message }} 41 | 42 | @enderror 43 |
44 |
45 | 47 |
48 |
49 | 50 |
51 |
52 |
53 |
54 |
55 | 56 |
57 | 58 |
59 |
60 | 61 |

62 | Login 63 |

64 |
65 | 66 |
67 | 68 | 69 | 70 | @endsection 71 | -------------------------------------------------------------------------------- /resources/views/auth/register.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.auth') 2 | @section('title', 'Registers') 3 | @section('content') 4 |
5 |
6 | 7 | 8 |
9 | @csrf 10 | 11 |
12 | 15 |
16 |
17 | 18 |
19 |
20 | @error('name') 21 | 22 | {{ $message }} 23 | 24 | @enderror 25 |
26 |
27 | 31 |
32 |
33 | 34 |
35 |
36 | @error('email') 37 | 38 | {{ $message }} 39 | 40 | @enderror 41 |
42 |
43 | 46 |
47 |
48 | 49 |
50 |
51 | @error('password') 52 | 53 | {{ $message }} 54 | 55 | @enderror 56 |
57 |
58 | 60 |
61 |
62 | 63 |
64 |
65 |
66 |
67 | 68 | 69 |
70 | 71 |
72 | 73 |
74 |
75 | 76 | 87 | 88 | I already have a membership 89 |
90 | 91 |
92 | 93 | 94 | 95 | 96 | 97 | @endsection 98 | -------------------------------------------------------------------------------- /resources/views/auth/verify.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('content') 4 |
5 |
6 |
7 |
8 |
{{ __('Verify Your Email Address') }}
9 | 10 |
11 | @if (session('resent')) 12 | 15 | @endif 16 | 17 | {{ __('Before proceeding, please check your email for a verification link.') }} 18 | {{ __('If you did not receive the email') }}, 19 |
20 | @csrf 21 | 23 | . 24 |
25 |
26 |
27 |
28 |
29 |
30 | @endsection 31 | -------------------------------------------------------------------------------- /resources/views/layouts/auth.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | La Posta | @yield('title') 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 28 | 29 | @yield('content') 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /resources/views/layouts/pagination.blade.php: -------------------------------------------------------------------------------- 1 | @if ($paginator->hasPages()) 2 | 33 | @endif 34 | -------------------------------------------------------------------------------- /resources/views/main/calendar.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('title', 'Calendar') 4 | @section('stylesheet') 5 | 6 | @endsection 7 | @section('content') 8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | @endsection 17 | 18 | @section('script') 19 | 20 | 39 | @endsection -------------------------------------------------------------------------------- /resources/views/main/dashboard.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | @section('title', 'Dashboard') 3 | @section('content') 4 |
5 |
6 |
7 |
8 |
9 |

Welcome to Dashboard

10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 20 |
21 | Accounts 22 | {{ $statistics['accounts'] }} 23 |
24 |
25 |
26 |
27 |
28 | 29 |
30 | Posts 31 | {{ $statistics['posts']}} 32 |
33 |
34 |
35 |
36 |
37 | 38 |
39 | Drafts 40 | {{ $statistics['drafts']}} 41 |
42 |
43 |
44 |
45 |
46 | 47 |
48 | Queued 49 | {{ $statistics['queued']}} 50 |
51 |
52 |
53 |
54 |
55 |
56 | 57 | 58 |
59 | succeeded 60 | {{ $statistics['succeeded'] }} 61 |
62 |
63 |
64 |
65 |
66 | 67 |
68 | Failed 69 | {{ $statistics['failed']}} 70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | @endsection -------------------------------------------------------------------------------- /resources/views/main/media.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | @section('title', 'Media') 3 | @section('content') 4 |
5 |
6 |
7 |
8 |
9 |
10 | 11 |
12 |
13 |
14 |
15 |
16 |
17 | @endsection -------------------------------------------------------------------------------- /resources/views/main/notifications.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | @section('title', 'Notifications') 3 | @section('content') 4 |
5 |
6 |
7 | @if (session('status')) 8 |
9 |
10 | 13 |
14 |
15 | @endif 16 | @if (session('error')) 17 |
18 |
19 | 22 |
23 |
24 | @endif 25 |
26 |
27 |
28 |
29 |

Notifications

30 |
31 |
32 | @csrf 33 | @method('DELETE') 34 |
35 | 39 |
40 |
41 |
42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | @foreach ($allNotifications as $notification) 53 | @if ($notification->data['type'] == 'login') 54 | 55 | 56 | 58 | @elseif($notification->data["type"] == 'post') 59 | 60 | 61 | 62 | @endif 63 | 65 | 75 | 76 | @endforeach 77 | 78 |
TypeMessageTimeAction
Login 57 | {{ $notification->data['message'] }}
Post{{ $notification->data['message'] }}{{ $notification->created_at}} 64 | 66 |
68 | @csrf 69 | @method('DELETE') 70 | 73 |
74 |
79 |
80 | @if ($allNotifications->hasPages()) 81 | 84 | @endif 85 |
86 |
87 |
88 |
89 |
90 |
91 | @endsection -------------------------------------------------------------------------------- /resources/views/main/posts/create.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | @section('title', 'Create Post') 3 | @section('content') 4 |
5 |
6 |
7 | @if (session('status')) 8 |
9 |
10 | 13 |
14 |
15 | @endif 16 | @if ($errors->any()) 17 |
18 |
19 |
20 |
    21 | @foreach ($errors->all() as $error) 22 |
  • {{ $error }}
  • 23 | @endforeach 24 |
25 |
26 |
27 |
28 | @endif 29 |
30 |
31 |
32 | 33 | 34 |
35 |
36 |
37 |
38 | 42 | 43 |
44 |
45 |
46 |
47 |
48 |
49 | @endsection -------------------------------------------------------------------------------- /resources/views/main/posts/edit.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | @section('title', 'Edit Post') 3 | @section('content') 4 |
5 |
6 |
7 | @if (session('status')) 8 |
9 |
10 | 13 |
14 |
15 | @endif 16 | @if ($errors->any()) 17 |
18 |
19 |
20 |
    21 | @foreach ($errors->all() as $error) 22 |
  • {{ $error }}
  • 23 | @endforeach 24 |
25 |
26 |
27 |
28 | @endif 29 |
30 |
31 |
32 | 34 | 35 |
36 |
37 |
38 |
39 | 42 | 43 |
44 |
45 |
46 |

47 | 48 | Logs 49 |

50 |
51 |
52 | @foreach ($post->logs as $log) 53 |
54 |
{{ $log['status'] }}
55 |

{{ $log['message'] }}

56 |
57 | @endforeach 58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | @endsection -------------------------------------------------------------------------------- /resources/views/main/posts/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | @section('title', 'Posts') 3 | @section('stylesheet') 4 | 22 | @endsection 23 | @section('content') 24 |
25 |
26 |
27 | @if (session('status')) 28 |
29 |
30 | 33 |
34 |
35 | @endif 36 |
37 |
38 |
39 |
40 |

All Posts

41 |
42 |
43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | @foreach ($posts as $index => $post) 58 | 59 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 80 | 81 | @endforeach 82 | 83 |
#MessageMediaAccountsStatusCreated_AtTo_Published_AtAction
{{ $post->id }} 60 | {{ $post->message }}{{ $post->media_count }}{{ $post->accounts_count}}{{ $post->draft ? 'draft' : $post->status }}{{ $post->created_at }}{{ $post->schedule_date }} 68 |
69 | @csrf 70 | @method('DELETE') 71 | @if (!$post->locked) 72 | 74 | 75 | @endif 76 | 78 |
79 |
84 |
85 | @if ($posts->hasPages()) 86 | 89 | @endif 90 |
91 |
92 |
93 |
94 |
95 |
96 | @endsection -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | get('/user', function (Request $request) { 18 | return $request->user(); 19 | }); 20 | -------------------------------------------------------------------------------- /routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 18 | }); 19 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 19 | })->purpose('Display an inspiring quote'); 20 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | where(['provider' => '(facebook|google)'])->name('connect.social'); 8 | Route::get('/login/{provider}/callback', 'SocialAuthController@callback'); 9 | 10 | Route::group(['middleware' => 'auth'], function () { 11 | Route::redirect('/', '/dashboard'); 12 | Route::get('/disconnect/facebook', 'SocialAuthController@disconnectFacebook')->name('disconnect.facebook'); 13 | Route::get('/disconnect/google', 'SocialAuthController@disconnectGoogle')->name('disconnect.google'); 14 | Route::get('/dashboard', 'DashboardController@index')->name('dashboard'); 15 | Route::get('/calendar', 'DashboardController@calendar')->name('calendar'); 16 | 17 | Route::prefix('notifications')->group(function () { 18 | Route::get('/', 'NotificationController@index')->name('notifications.index'); 19 | Route::put('/{notification}', 'NotificationController@read')->name('notifications.read'); 20 | Route::delete('delete/all', 'NotificationController@destroyAll')->name('notifications.destroy.all'); 21 | Route::delete('delete/{notification}', 'NotificationController@destroy')->name('notifications.destroy'); 22 | }); 23 | 24 | Route::prefix('profile')->group(function () { 25 | Route::get('/', 'ProfileController@index')->name('profile.index'); 26 | Route::post('/', 'ProfileController@update')->name('profile.update'); 27 | Route::post('password', 'ProfileController@updatePassword')->name('profile.update.password'); 28 | Route::get('session/destroy/{id}', 'ProfileController@destroySession')->name('profile.session.destroy'); 29 | }); 30 | 31 | Route::prefix('media')->group(function () { 32 | Route::get('/', 'MediaController@index')->name('media.index'); 33 | Route::post('/', 'MediaController@store')->name('media.store'); 34 | Route::get('original/{mediaName}', 'MediaController@showOriginal')->name('media.show.original'); 35 | Route::get('thumb/{mediaName}', 'MediaController@showThumb')->name('media.show.thumb'); 36 | Route::delete('delete/{media}', 'MediaController@destroy')->name('media.destroy'); 37 | }); 38 | 39 | Route::prefix('accounts')->where(['platform' => '(facebook|twitter)'])->group(function () { //will add |instagram later ... 40 | Route::get('/', 'AccountController@index')->name('accounts.index'); 41 | Route::get('{platform}/connect', 'AccountController@connect')->name('accounts.connect'); 42 | Route::get('{platform}/callback', 'AccountController@callback')->name('accounts.connect.callback'); 43 | Route::delete('delete/{account}', 'AccountController@destroy')->name('accounts.destroy'); 44 | }); 45 | 46 | Route::resource('posts', 'PostController'); 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/screenshots/1.png -------------------------------------------------------------------------------- /screenshots/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/screenshots/10.png -------------------------------------------------------------------------------- /screenshots/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/screenshots/11.png -------------------------------------------------------------------------------- /screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/screenshots/2.png -------------------------------------------------------------------------------- /screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/screenshots/3.png -------------------------------------------------------------------------------- /screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/screenshots/4.png -------------------------------------------------------------------------------- /screenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/screenshots/5.png -------------------------------------------------------------------------------- /screenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/screenshots/6.png -------------------------------------------------------------------------------- /screenshots/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/screenshots/7.png -------------------------------------------------------------------------------- /screenshots/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/screenshots/8.png -------------------------------------------------------------------------------- /screenshots/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/screenshots/9.png -------------------------------------------------------------------------------- /screenshots/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/civilcoder55/laravel-laposta/6b900ad8401e2c6999791a43798979e9e1308161/screenshots/logo.png -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | $uri = urldecode( 11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) 12 | ); 13 | 14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the 15 | // built-in PHP web server. This provides a convenient way to test a Laravel 16 | // application without having installed a "real" web server software here. 17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { 18 | return false; 19 | } 20 | 21 | require_once __DIR__.'/public/index.php'; 22 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !/public/profile/default.png 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/debugbar/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 19 | 20 | return $app; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Feature/AuthTest.php: -------------------------------------------------------------------------------- 1 | get('/register')->assertSuccessful()->assertViewIs('auth.register'); 20 | } 21 | 22 | /** @test */ 23 | public function guest_can_view_login_page() 24 | { 25 | $this->get('/login')->assertSuccessful()->assertViewIs('auth.login'); 26 | } 27 | 28 | /** @test */ 29 | public function user_cant_view_register_page() 30 | { 31 | $user = User::factory()->make(); 32 | $this->actingAs($user)->get('/register')->assertRedirect('/dashboard'); 33 | } 34 | 35 | /** @test */ 36 | public function user_cant_view_login_page() 37 | { 38 | $user = User::factory()->make(); 39 | $this->actingAs($user)->get('/login')->assertRedirect('/dashboard'); 40 | } 41 | 42 | /** @test */ 43 | public function user_can_register() 44 | { 45 | $this->post('/register', [ 46 | 'name' => $this->faker->name, 47 | 'email' => $this->faker->email, 48 | 'password' => 'password', 49 | 'password_confirmation' => 'password', 50 | ])->assertRedirect('/dashboard'); 51 | $this->assertDatabaseCount('users', 1); 52 | } 53 | 54 | /** @test */ 55 | public function user_cant_register_with_same_email() 56 | { 57 | $user = User::factory()->create(); 58 | $this->from('/register')->post('/register', [ 59 | 'name' => $this->faker->name, 60 | 'email' => $user->email, 61 | 'password' => 'password', 62 | 'password_confirmation' => 'password', 63 | ])->assertRedirect('/register')->assertSessionHasErrors(['email']); 64 | $this->assertGuest()->assertDatabaseCount('users', 1); 65 | } 66 | 67 | /** @test */ 68 | public function valid_user_can_login() 69 | { 70 | Notification::fake(); 71 | $user = User::factory()->create(); 72 | $this->post('/login', [ 73 | 'email' => $user->email, 74 | 'password' => 'password', 75 | ])->assertRedirect('/dashboard'); 76 | $this->assertAuthenticatedAs($user); 77 | } 78 | 79 | /** @test */ 80 | public function invalid_user_cant_login() 81 | { 82 | $this->from('/login')->post('/login', [ 83 | 'email' => 'wrong_email@gmail.com', 84 | 'password' => 'wrongPassword', 85 | ])->assertRedirect('/login')->assertSessionHasErrors(['email']); 86 | $this->assertGuest(); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 |