diff --git a/.env.development.example b/.env.development.example index 43d3a10fd..c956daafd 100644 --- a/.env.development.example +++ b/.env.development.example @@ -6,6 +6,7 @@ USERID= GROUPID= ############################################################################################################ +APP_NAME=Coolify-localhost APP_ID=development APP_ENV=local APP_KEY= diff --git a/app/Console/Commands/WaitlistInvite.php b/app/Console/Commands/WaitlistInvite.php index 7374b1f2f..a3b47089a 100644 --- a/app/Console/Commands/WaitlistInvite.php +++ b/app/Console/Commands/WaitlistInvite.php @@ -6,20 +6,20 @@ use App\Models\Waitlist; use Illuminate\Console\Command; use Illuminate\Notifications\Messages\MailMessage; +use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; class WaitlistInvite extends Command { - public Waitlist|null $next_patient = null; - public User|null $new_user = null; + public Waitlist|User|null $next_patient = null; public string|null $password = null; /** * The name and signature of the console command. * * @var string */ - protected $signature = 'waitlist:invite {email?}'; + protected $signature = 'waitlist:invite {email?} {--only-email}'; /** * The console command description. @@ -34,7 +34,16 @@ class WaitlistInvite extends Command public function handle() { if ($this->argument('email')) { - $this->next_patient = Waitlist::where('email', $this->argument('email'))->first(); + if ($this->option('only-email')) { + $this->next_patient = User::whereEmail($this->argument('email'))->first(); + $this->password = Str::password(); + $this->next_patient->update([ + 'password' => Hash::make($this->password), + 'force_password_reset' => true, + ]); + } else { + $this->next_patient = Waitlist::where('email', $this->argument('email'))->first(); + } if (!$this->next_patient) { $this->error("{$this->argument('email')} not found in the waitlist."); return; @@ -43,6 +52,10 @@ public function handle() $this->next_patient = Waitlist::orderBy('created_at', 'asc')->where('verified', true)->first(); } if ($this->next_patient) { + if ($this->option('only-email')) { + $this->send_email(); + return; + } $this->register_user(); $this->remove_from_waitlist(); $this->send_email(); @@ -55,7 +68,7 @@ private function register_user() $already_registered = User::whereEmail($this->next_patient->email)->first(); if (!$already_registered) { $this->password = Str::password(); - $this->new_user = User::create([ + User::create([ 'name' => Str::of($this->next_patient->email)->before('@'), 'email' => $this->next_patient->email, 'password' => Hash::make($this->password), @@ -73,10 +86,14 @@ private function remove_from_waitlist() } private function send_email() { + ray($this->next_patient->email, $this->password); + $token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password"); + $loginLink = route('auth.link', ['token' => $token]); $mail = new MailMessage(); $mail->view('emails.waitlist-invitation', [ 'email' => $this->next_patient->email, 'password' => $this->password, + 'loginLink' => $loginLink, ]); $mail->subject('Congratulations! You are invited to join Coolify Cloud.'); send_user_an_email($mail, $this->next_patient->email); diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 39b0881e3..29d705faf 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -8,15 +8,41 @@ use App\Models\StandalonePostgresql; use App\Models\TeamInvitation; use App\Models\User; +use Auth; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; +use Illuminate\Support\Facades\Crypt; +use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Http; use Throwable; +use Str; + class Controller extends BaseController { use AuthorizesRequests, ValidatesRequests; + public function link() + { + $token = request()->get('token'); + if ($token) { + $decrypted = Crypt::decryptString($token); + $email = Str::of($decrypted)->before('@@@'); + $password = Str::of($decrypted)->after('@@@'); + $user = User::whereEmail($email)->first(); + if (!$user) { + return redirect()->route('login'); + } + if (Hash::check($password, $user->password)) { + Auth::login($user); + $team = $user->teams()->first(); + session(['currentTeam' => $team]); + return redirect()->route('dashboard'); + } + } + return redirect()->route('login')->with('error', 'Invalid credentials.'); + } public function subscription() { if (!isCloud()) { @@ -37,10 +63,12 @@ public function license() ]); } - public function force_passoword_reset() { + public function force_passoword_reset() + { return view('auth.force-password-reset'); } - public function boarding() { + public function boarding() + { if (currentTeam()->boarding || isDev()) { return view('boarding'); } else { diff --git a/app/Http/Livewire/ForcePasswordReset.php b/app/Http/Livewire/ForcePasswordReset.php index 96a273e80..df2b37691 100644 --- a/app/Http/Livewire/ForcePasswordReset.php +++ b/app/Http/Livewire/ForcePasswordReset.php @@ -18,22 +18,26 @@ class ForcePasswordReset extends Component 'password' => 'required|min:8', 'password_confirmation' => 'required|same:password', ]; - public function mount() { + public function mount() + { $this->email = auth()->user()->email; } - public function submit() { + public function submit() + { try { $this->rateLimit(10); $this->validate(); + $firstLogin = auth()->user()->created_at == auth()->user()->updated_at; auth()->user()->forceFill([ 'password' => Hash::make($this->password), 'force_password_reset' => false, ])->save(); - auth()->logout(); - return redirect()->route('login')->with('status', 'Your initial password has been set.'); - } catch(\Exception $e) { - return general_error_handler(err:$e, that:$this); + if ($firstLogin) { + send_internal_notification('First login for ' . auth()->user()->email); + } + return redirect()->route('dashboard'); + } catch (\Exception $e) { + return general_error_handler(err: $e, that: $this); } } - } diff --git a/app/Http/Middleware/CheckForcePasswordReset.php b/app/Http/Middleware/CheckForcePasswordReset.php index 8d1670de5..e8129deda 100644 --- a/app/Http/Middleware/CheckForcePasswordReset.php +++ b/app/Http/Middleware/CheckForcePasswordReset.php @@ -16,6 +16,12 @@ class CheckForcePasswordReset public function handle(Request $request, Closure $next): Response { if (auth()->user()) { + if ($request->path() === 'auth/link') { + auth()->logout(); + request()->session()->invalidate(); + request()->session()->regenerateToken(); + return $next($request); + } $force_password_reset = auth()->user()->force_password_reset; if ($force_password_reset) { if ($request->routeIs('auth.force-password-reset') || $request->path() === 'livewire/message/force-password-reset') { diff --git a/app/Notifications/Internal/GeneralNotification.php b/app/Notifications/Internal/GeneralNotification.php index ee13a6cc2..3fee2acab 100644 --- a/app/Notifications/Internal/GeneralNotification.php +++ b/app/Notifications/Internal/GeneralNotification.php @@ -12,12 +12,12 @@ class GeneralNotification extends Notification implements ShouldQueue use Queueable; public function __construct(public string $message) - {} + { + } public function via(object $notifiable): array { - $channels[] = DiscordChannel::class; - return $channels; + return [DiscordChannel::class]; } public function toDiscord(): string diff --git a/app/Notifications/TransactionalEmails/ResetPassword.php b/app/Notifications/TransactionalEmails/ResetPassword.php index 6844aa705..2a11051b3 100644 --- a/app/Notifications/TransactionalEmails/ResetPassword.php +++ b/app/Notifications/TransactionalEmails/ResetPassword.php @@ -50,10 +50,6 @@ public function toMail($notifiable) protected function buildMailMessage($url) { $mail = new MailMessage(); - $mail->from( - data_get($this->settings, 'smtp_from_address'), - data_get($this->settings, 'smtp_from_name'), - ); $mail->subject('Reset Password'); $mail->view('emails.reset-password', ['url' => $url, 'count' => config('auth.passwords.' . config('auth.defaults.passwords') . '.expire')]); return $mail; diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php index a5fce4858..66d85bf13 100644 --- a/app/Providers/FortifyServiceProvider.php +++ b/app/Providers/FortifyServiceProvider.php @@ -57,6 +57,7 @@ public function boot(): void }); Fortify::loginView(function () { + abort(503,'Login is disabled'); $settings = InstanceSettings::get(); $users = User::count(); if ($users == 0) { diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index e45dfa08a..1d3dba13d 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -242,7 +242,8 @@ function validate_cron_expression($expression_to_validate): bool function send_internal_notification(string $message): void { try { - $baseUrl = base_url(false); + ray('Sending internal notification... 📬 ' . $message); + $baseUrl = config('app.name'); $team = Team::find(0); $team->notify(new GeneralNotification("👀 Internal notifications from {$baseUrl}: " . $message)); } catch (\Throwable $th) { diff --git a/database/migrations/2023_08_22_071054_add_stripe_reasons.php b/database/migrations/2023_08_22_071054_add_stripe_reasons.php new file mode 100644 index 000000000..98f85c921 --- /dev/null +++ b/database/migrations/2023_08_22_071054_add_stripe_reasons.php @@ -0,0 +1,32 @@ +string('stripe_feedback')->nullable()->after('stripe_cancel_at_period_end'); + $table->string('stripe_comment')->nullable()->after('stripe_feedback'); + + + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('subscriptions', function (Blueprint $table) { + $table->dropColumn('stripe_feedback'); + $table->dropColumn('stripe_comment'); + }); + } +}; diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 838bc5a11..2ed0564bb 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -51,6 +51,11 @@ class="text-xs normal-case hover:no-underline btn btn-sm bg-coollabs-gradient"> {{ session('status') }} @endif + @if (session('error')) +
401
+You don't have permission to access this page. +
+ +403
+You don't have permission to access this page. +
+ +404
-Sorry, we couldn’t find the page you’re looking - for. -
- +@extends('layouts.base') +404
+Sorry, we couldn’t find the page you’re looking + for. +
+419
-Sorry, we couldn’t find the page you’re looking - for. -
- +@extends('layouts.base') +419
+Sorry, we couldn’t find the page you’re looking + for. +
+429
+You're making too many requests. Please wait a few seconds before trying again. +
+ +500
+There has been an error, we are working on it. +
+ @if ($exception->getMessage() !== '') +Error: {{ $exception->getMessage() }} +
+ @endif + +503
+Service Unavailable. Be right back. Thanks for your + patience. +
+ +