mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-16 20:49:28 +00:00
feat: force password reset + waitlist
This commit is contained in:
@@ -4,6 +4,7 @@ namespace App\Console;
|
||||
|
||||
use App\Jobs\CheckResaleLicenseJob;
|
||||
use App\Jobs\CheckResaleLicenseKeys;
|
||||
use App\Jobs\CleanupInstanceStuffsJob;
|
||||
use App\Jobs\DatabaseBackupJob;
|
||||
use App\Jobs\DockerCleanupJob;
|
||||
use App\Jobs\InstanceApplicationsStatusJob;
|
||||
@@ -22,12 +23,14 @@ class Kernel extends ConsoleKernel
|
||||
$schedule->command('horizon:snapshot')->everyMinute();
|
||||
$schedule->job(new InstanceApplicationsStatusJob)->everyMinute();
|
||||
$schedule->job(new ProxyCheckJob)->everyFiveMinutes();
|
||||
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute();
|
||||
|
||||
// $schedule->job(new CheckResaleLicenseJob)->hourly();
|
||||
// $schedule->job(new DockerCleanupJob)->everyOddHour();
|
||||
// $schedule->job(new InstanceAutoUpdateJob(true))->everyMinute();
|
||||
} else {
|
||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute();
|
||||
$schedule->job(new InstanceApplicationsStatusJob)->everyMinute();
|
||||
$schedule->job(new CheckResaleLicenseJob)->hourly();
|
||||
$schedule->job(new ProxyCheckJob)->everyFiveMinutes();
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Models\Server;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\TeamInvitation;
|
||||
use App\Models\User;
|
||||
use App\Models\Waitlist;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
@@ -18,6 +19,12 @@ class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, ValidatesRequests;
|
||||
|
||||
public function waitlist() {
|
||||
$waiting_in_line = Waitlist::whereVerified(true)->count();
|
||||
return view('auth.waitlist', [
|
||||
'waiting_in_line' => $waiting_in_line,
|
||||
]);
|
||||
}
|
||||
public function subscription()
|
||||
{
|
||||
if (!is_cloud()) {
|
||||
@@ -38,6 +45,9 @@ class Controller extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
public function force_passoword_reset() {
|
||||
return view('auth.force-password-reset');
|
||||
}
|
||||
public function dashboard()
|
||||
{
|
||||
$projects = Project::ownedByCurrentTeam()->get();
|
||||
|
||||
@@ -37,6 +37,7 @@ class Kernel extends HttpKernel
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
\App\Http\Middleware\CheckForcePasswordReset::class,
|
||||
\App\Http\Middleware\SubscriptionValid::class,
|
||||
|
||||
],
|
||||
|
||||
36
app/Http/Livewire/ForcePasswordReset.php
Normal file
36
app/Http/Livewire/ForcePasswordReset.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Livewire\Component;
|
||||
|
||||
class ForcePasswordReset extends Component
|
||||
{
|
||||
public string $email;
|
||||
public string $password;
|
||||
public string $password_confirmation;
|
||||
|
||||
protected $rules = [
|
||||
'email' => 'required|email',
|
||||
'password' => 'required|min:8',
|
||||
'password_confirmation' => 'required|same:password',
|
||||
];
|
||||
public function mount() {
|
||||
$this->email = auth()->user()->email;
|
||||
}
|
||||
public function submit() {
|
||||
try {
|
||||
$this->validate();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
49
app/Http/Livewire/Waitlist.php
Normal file
49
app/Http/Livewire/Waitlist.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Jobs\SendConfirmationForWaitlistJob;
|
||||
use App\Models\Waitlist as ModelsWaitlist;
|
||||
use Livewire\Component;
|
||||
|
||||
class Waitlist extends Component
|
||||
{
|
||||
public string $email;
|
||||
public int $waiting_in_line = 0;
|
||||
|
||||
protected $rules = [
|
||||
'email' => 'required|email',
|
||||
];
|
||||
public function mount()
|
||||
{
|
||||
if (is_dev()) {
|
||||
$this->email = 'test@example.com';
|
||||
}
|
||||
}
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
try {
|
||||
$found = ModelsWaitlist::where('email', $this->email)->first();
|
||||
ray($found);
|
||||
if ($found) {
|
||||
if (!$found->verified) {
|
||||
$this->emit('error', 'You are already on the waitlist. <br>Please check your email to verify your email address.');
|
||||
return;
|
||||
}
|
||||
$this->emit('error', 'You are already on the waitlist.');
|
||||
return;
|
||||
}
|
||||
$waitlist = ModelsWaitlist::create([
|
||||
'email' => $this->email,
|
||||
'type' => 'registration',
|
||||
]);
|
||||
|
||||
$this->emit('success', 'You have been added to the waitlist.');
|
||||
dispatch(new SendConfirmationForWaitlistJob($this->email, $waitlist->uuid));
|
||||
} catch (\Exception $e) {
|
||||
return general_error_handler(err: $e, that: $this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
29
app/Http/Middleware/CheckForcePasswordReset.php
Normal file
29
app/Http/Middleware/CheckForcePasswordReset.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CheckForcePasswordReset
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (auth()->user()) {
|
||||
$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') {
|
||||
return $next($request);
|
||||
}
|
||||
return redirect()->route('auth.force-password-reset');
|
||||
}
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ class RedirectIfAuthenticated
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ class SubscriptionValid
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
|
||||
if (!auth()->user() || !is_cloud()) {
|
||||
if ($request->path() === 'subscription') {
|
||||
return redirect('/');
|
||||
@@ -36,7 +35,10 @@ class SubscriptionValid
|
||||
'subscription',
|
||||
'login',
|
||||
'register',
|
||||
'waitlist',
|
||||
'force-password-reset',
|
||||
'logout',
|
||||
'livewire/message/force-password-reset',
|
||||
'livewire/message/check-license',
|
||||
'livewire/message/switch-team',
|
||||
];
|
||||
|
||||
43
app/Jobs/CleanupInstanceStuffsJob.php
Normal file
43
app/Jobs/CleanupInstanceStuffsJob.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Waitlist;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CleanupInstanceStuffsJob implements ShouldQueue, ShouldBeUnique
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// public function uniqueId(): string
|
||||
// {
|
||||
// return $this->container_name;
|
||||
// }
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
$this->cleanup_waitlist();
|
||||
} catch (\Exception $e) {
|
||||
ray($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function cleanup_waitlist()
|
||||
{
|
||||
$waitlist = Waitlist::whereVerified(false)->where('created_at', '<', now()->subMinutes(config('constants.waitlist.confirmation_valid_for_minutes')))->get();
|
||||
foreach ($waitlist as $item) {
|
||||
$item->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
59
app/Jobs/SendConfirmationForWaitlistJob.php
Executable file
59
app/Jobs/SendConfirmationForWaitlistJob.php
Executable file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Waitlist;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Mail\Message;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendConfirmationForWaitlistJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
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;
|
||||
$cancel_url = base_url() . '/webhooks/waitlist/cancel?email=' . $this->email . '&confirmation_code=' . $this->uuid;
|
||||
|
||||
$mail->view('emails.waitlist-confirmation',
|
||||
[
|
||||
'confirmation_url' => $confirmation_url,
|
||||
'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())
|
||||
);
|
||||
} catch (\Throwable $th) {
|
||||
ray($th->getMessage());
|
||||
throw $th;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Models;
|
||||
|
||||
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;
|
||||
@@ -14,18 +15,14 @@ class User extends Authenticatable implements SendsEmail
|
||||
{
|
||||
use HasApiTokens, HasFactory, Notifiable, TwoFactorAuthenticatable;
|
||||
|
||||
protected $fillable = [
|
||||
'id',
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
protected $guarded = [];
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
protected $casts = [
|
||||
'email_verified_at' => 'datetime',
|
||||
'force_password_reset' => 'boolean',
|
||||
];
|
||||
|
||||
protected static function boot()
|
||||
@@ -57,7 +54,7 @@ class User extends Authenticatable implements SendsEmail
|
||||
|
||||
public function sendPasswordResetNotification($token): void
|
||||
{
|
||||
$this->notify(new ResetPassword($token));
|
||||
$this->notify(new TransactionalEmailsResetPassword($token));
|
||||
}
|
||||
|
||||
public function isAdmin()
|
||||
|
||||
11
app/Models/Waitlist.php
Normal file
11
app/Models/Waitlist.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class Waitlist extends BaseModel
|
||||
{
|
||||
use HasFactory;
|
||||
protected $guarded = [];
|
||||
}
|
||||
@@ -14,6 +14,7 @@ use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Laravel\Fortify\Contracts\RegisterResponse;
|
||||
use Laravel\Fortify\Features;
|
||||
use Laravel\Fortify\Fortify;
|
||||
|
||||
class FortifyServiceProvider extends ServiceProvider
|
||||
@@ -41,13 +42,19 @@ class FortifyServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
|
||||
Fortify::createUsersUsing(CreateNewUser::class);
|
||||
Fortify::registerView(function () {
|
||||
ray('asd');
|
||||
$settings = InstanceSettings::get();
|
||||
if (!$settings->is_registration_enabled) {
|
||||
return redirect()->route('login');
|
||||
}
|
||||
return view('auth.register');
|
||||
if (config('coolify.waitlist')) {
|
||||
return view('auth.waitlist');
|
||||
} else {
|
||||
return view('auth.register');
|
||||
}
|
||||
});
|
||||
|
||||
Fortify::loginView(function () {
|
||||
|
||||
Reference in New Issue
Block a user