diff --git a/app/Console/Commands/InviteFromWaitlist.php b/app/Console/Commands/InviteFromWaitlist.php new file mode 100644 index 000000000..367e7d3e7 --- /dev/null +++ b/app/Console/Commands/InviteFromWaitlist.php @@ -0,0 +1,77 @@ +next_patient = Waitlist::orderBy('created_at', 'asc')->where('verified', true)->first(); + if ($this->next_patient) { + $this->register_user(); + $this->remove_from_waitlist(); + $this->send_email(); + } else { + $this->info('No one in the waitlist who is verified. 👀'); + } + } + 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([ + 'name' => Str::of($this->next_patient->email)->before('@'), + 'email' => $this->next_patient->email, + 'password' => Hash::make($this->password), + 'force_password_reset' => true, + ]); + $this->info("User registered ({$this->next_patient->email}) successfully. 🎉"); + } else { + throw new \Exception('User already registered'); + } + } + private function remove_from_waitlist() + { + $this->next_patient->delete(); + $this->info("User removed from waitlist successfully."); + } + private function send_email() + { + $mail = new MailMessage(); + $mail->view('emails.waitlist-invitation', [ + 'email' => $this->next_patient->email, + 'password' => $this->password, + ]); + $mail->subject('Congratulations! You are invited to join Coolify Cloud.'); + send_user_an_email($mail, $this->next_patient->email); + $this->info("Email sent successfully. 📧"); + } +} diff --git a/app/Http/Livewire/Subscription/Actions.php b/app/Http/Livewire/Subscription/Actions.php new file mode 100644 index 000000000..588a0521c --- /dev/null +++ b/app/Http/Livewire/Subscription/Actions.php @@ -0,0 +1,72 @@ +user()->currentTeam()->subscription->lemon_subscription_id; + if (!$subscription_id) { + throw new \Exception('No subscription found'); + } + $response = Http::withHeaders([ + 'Accept' => 'application/vnd.api+json', + 'Content-Type' => 'application/vnd.api+json', + 'Authorization' => 'Bearer ' . config('coolify.lemon_squeezy_api_key'), + ])->delete('https://api.lemonsqueezy.com/v1/subscriptions/' . $subscription_id); + $json = $response->json(); + if ($response->failed()) { + $error = data_get($json, 'errors.0.status'); + if ($error === '404') { + throw new \Exception('Subscription not found.'); + } + throw new \Exception(data_get($json, 'errors.0.title', 'Something went wrong. Please try again later.')); + } else { + $this->emit('success', 'Subscription cancelled successfully. Reloading in 5s.'); + $this->emit('reloadWindow', 5000); + } + } catch (\Exception $e) { + return general_error_handler($e, $this); + } + } + public function resume() + { + try { + $subscription_id = auth()->user()->currentTeam()->subscription->lemon_subscription_id; + if (!$subscription_id) { + throw new \Exception('No subscription found'); + } + $response = Http::withHeaders([ + 'Accept' => 'application/vnd.api+json', + 'Content-Type' => 'application/vnd.api+json', + 'Authorization' => 'Bearer ' . config('coolify.lemon_squeezy_api_key'), + ])->patch('https://api.lemonsqueezy.com/v1/subscriptions/' . $subscription_id, [ + 'data' => [ + 'type' => 'subscriptions', + 'id' => $subscription_id, + 'attributes' => [ + 'cancelled' => false, + ], + ], + ]); + $json = $response->json(); + if ($response->failed()) { + $error = data_get($json, 'errors.0.status'); + if ($error === '404') { + throw new \Exception('Subscription not found.'); + } + throw new \Exception(data_get($json, 'errors.0.title', 'Something went wrong. Please try again later.')); + } else { + $this->emit('success', 'Subscription resumed successfully. Reloading in 5s.'); + $this->emit('reloadWindow', 5000); + } + } catch (\Exception $e) { + return general_error_handler($e, $this); + } + } +} diff --git a/app/Http/Livewire/Waitlist.php b/app/Http/Livewire/Waitlist.php index ffa1b1f5c..de633ceac 100644 --- a/app/Http/Livewire/Waitlist.php +++ b/app/Http/Livewire/Waitlist.php @@ -18,7 +18,7 @@ class Waitlist extends Component public function mount() { if (is_dev()) { - $this->email = 'test@example.com'; + $this->email = 'waitlist@example.com'; } } public function submit() @@ -27,8 +27,7 @@ public function submit() try { $already_registered = User::whereEmail($this->email)->first(); if ($already_registered) { - $this->emit('success', 'You are already registered (Thank you 💜).'); - return; + throw new \Exception('You are already on the waitlist or registered.
Please check your email to verify your email address or contact support.'); } $found = ModelsWaitlist::where('email', $this->email)->first(); if ($found) { @@ -36,7 +35,7 @@ public function submit() $this->emit('error', 'You are already on the waitlist.
Please check your email to verify your email address.'); return; } - $this->emit('error', 'You are already on the waitlist.'); + $this->emit('error', 'You are already on the waitlist.
You will be notified when your turn comes.
Thank you.'); return; } $waitlist = ModelsWaitlist::create([ diff --git a/app/Jobs/SendConfirmationForWaitlistJob.php b/app/Jobs/SendConfirmationForWaitlistJob.php index 5c1518fc6..3ff4982d9 100755 --- a/app/Jobs/SendConfirmationForWaitlistJob.php +++ b/app/Jobs/SendConfirmationForWaitlistJob.php @@ -24,10 +24,6 @@ public function __construct(public string $email, public string $uuid) public function handle() { try { - $settings = InstanceSettings::get(); - - - set_transanctional_email_settings($settings); $mail = new MailMessage(); $confirmation_url = base_url() . '/webhooks/waitlist/confirm?email=' . $this->email . '&confirmation_code=' . $this->uuid; @@ -39,18 +35,7 @@ public function handle() 'cancel_url' => $cancel_url, ]); $mail->subject('You are on the waitlist!'); - Mail::send( - [], - [], - fn(Message $message) => $message - ->from( - data_get($settings, 'smtp_from_address'), - data_get($settings, 'smtp_from_name') - ) - ->to($this->email) - ->subject($mail->subject) - ->html((string) $mail->render()) - ); + send_user_an_email($mail, $this->email); } catch (\Throwable $th) { send_internal_notification('SendConfirmationForWaitlistJob failed with error: ' . $th->getMessage()); ray($th->getMessage()); diff --git a/app/Models/User.php b/app/Models/User.php index f0a85d182..b048ef9e6 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -4,7 +4,6 @@ use App\Notifications\Channels\SendsEmail; use App\Notifications\TransactionalEmails\ResetPassword as TransactionalEmailsResetPassword; -use App\Notifications\TrnsactionalEmails\ResetPassword; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 2d315020f..4b470cb48 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -5,7 +5,10 @@ use App\Notifications\Internal\GeneralNotification; use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException; use Illuminate\Database\QueryException; +use Illuminate\Mail\Message; +use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Route; use Illuminate\Support\Str; use Nubs\RandomNameGenerator\All; @@ -205,3 +208,20 @@ function send_internal_notification(string $message): void ray($th->getMessage()); } } +function send_user_an_email(MailMessage $mail, string $email): void +{ + $settings = InstanceSettings::get(); + set_transanctional_email_settings($settings); + Mail::send( + [], + [], + fn (Message $message) => $message + ->from( + data_get($settings, 'smtp_from_address'), + data_get($settings, 'smtp_from_name') + ) + ->to($email) + ->subject($mail->subject) + ->html((string) $mail->render()) + ); +} diff --git a/config/coolify.php b/config/coolify.php index b9e092175..0276b9a78 100644 --- a/config/coolify.php +++ b/config/coolify.php @@ -4,6 +4,7 @@ 'self_hosted' => env('SELF_HOSTED', true), 'waitlist' => env('WAITLIST', false), 'license_url' => 'https://license.coolify.io', + 'lemon_squeezy_api_key' => env('LEMON_SQUEEZY_API_KEY', null), 'lemon_squeezy_webhook_secret' => env('LEMON_SQUEEZY_WEBHOOK_SECRET', null), 'lemon_squeezy_checkout_id_monthly_basic' => env('LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_BASIC', null), 'lemon_squeezy_checkout_id_monthly_pro' => env('LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_PRO', null), diff --git a/resources/js/components/MagicBar.vue b/resources/js/components/MagicBar.vue index 0626f16f1..f0203b885 100644 --- a/resources/js/components/MagicBar.vue +++ b/resources/js/components/MagicBar.vue @@ -128,6 +128,15 @@ +