diff --git a/app/Console/Commands/Emails.php b/app/Console/Commands/Emails.php
index 67c50675f..7324ad694 100644
--- a/app/Console/Commands/Emails.php
+++ b/app/Console/Commands/Emails.php
@@ -24,7 +24,7 @@
use Illuminate\Mail\Message;
use Illuminate\Notifications\Messages\MailMessage;
use Mail;
-use Str;
+use Illuminate\Support\Str;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\select;
@@ -62,7 +62,7 @@ public function handle()
'application-status-changed' => 'Application - Status Changed',
'backup-success' => 'Database - Backup Success',
'backup-failed' => 'Database - Backup Failed',
- 'invitation-link' => 'Invitation Link',
+ // 'invitation-link' => 'Invitation Link',
'waitlist-invitation-link' => 'Waitlist Invitation Link',
'waitlist-confirmation' => 'Waitlist Confirmation',
'realusers-before-trial' => 'REAL - Registered Users Before Trial without Subscription',
@@ -141,20 +141,20 @@ public function handle()
$this->mail = (new BackupSuccess($backup, $db))->toMail();
$this->sendEmail();
break;
- case 'invitation-link':
- $user = User::all()->first();
- $invitation = TeamInvitation::whereEmail($user->email)->first();
- if (!$invitation) {
- $invitation = TeamInvitation::create([
- 'uuid' => Str::uuid(),
- 'email' => $user->email,
- 'team_id' => 1,
- 'link' => 'http://example.com',
- ]);
- }
- $this->mail = (new InvitationLink($user))->toMail();
- $this->sendEmail();
- break;
+ // case 'invitation-link':
+ // $user = User::all()->first();
+ // $invitation = TeamInvitation::whereEmail($user->email)->first();
+ // if (!$invitation) {
+ // $invitation = TeamInvitation::create([
+ // 'uuid' => Str::uuid(),
+ // 'email' => $user->email,
+ // 'team_id' => 1,
+ // 'link' => 'http://example.com',
+ // ]);
+ // }
+ // $this->mail = (new InvitationLink($user))->toMail();
+ // $this->sendEmail();
+ // break;
case 'waitlist-invitation-link':
$this->mail = new MailMessage();
$this->mail->view('emails.waitlist-invitation', [
diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
index d15107c19..368f76f8f 100644
--- a/app/Console/Kernel.php
+++ b/app/Console/Kernel.php
@@ -21,7 +21,7 @@ protected function schedule(Schedule $schedule): void
if (isDev()) {
// $schedule->job(new ContainerStatusJob(Server::find(0)))->everyTenMinutes()->onOneServer();
// $schedule->command('horizon:snapshot')->everyMinute();
- // $schedule->job(new CleanupInstanceStuffsJob)->everyMinute();
+ $schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
// $schedule->job(new CheckResaleLicenseJob)->hourly();
// $schedule->job(new DockerCleanupJob)->everyOddHour();
// $this->instance_auto_update($schedule);
@@ -29,7 +29,7 @@ protected function schedule(Schedule $schedule): void
$this->check_resources($schedule);
} else {
$schedule->command('horizon:snapshot')->everyFiveMinutes();
- $schedule->job(new CleanupInstanceStuffsJob)->everyTenMinutes()->onOneServer();
+ $schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
$schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
$schedule->job(new DockerCleanupJob)->everyTenMinutes()->onOneServer();
$this->instance_auto_update($schedule);
diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php
index 913879c6b..0794d2507 100644
--- a/app/Http/Controllers/Controller.php
+++ b/app/Http/Controllers/Controller.php
@@ -3,21 +3,18 @@
namespace App\Http\Controllers;
use App\Models\InstanceSettings;
-use App\Models\Project;
use App\Models\S3Storage;
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\Auth;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Hash;
-use Illuminate\Support\Facades\Http;
+use Illuminate\Support\Str;
use Throwable;
-use Str;
-
class Controller extends BaseController
{
@@ -35,8 +32,15 @@ public function link()
return redirect()->route('login');
}
if (Hash::check($password, $user->password)) {
+ $invitation = TeamInvitation::whereEmail($email);
+ if ($invitation->exists()) {
+ $team = $invitation->first()->team;
+ $user->teams()->attach($team->id, ['role' => $invitation->first()->role]);
+ $invitation->delete();
+ } else {
+ $team = $user->teams()->first();
+ }
Auth::login($user);
- $team = $user->teams()->first();
session(['currentTeam' => $team]);
return redirect()->route('dashboard');
}
@@ -137,24 +141,20 @@ public function acceptInvitation()
try {
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
$user = User::whereEmail($invitation->email)->firstOrFail();
- if (is_null(auth()->user())) {
- return redirect()->route('login');
- }
if (auth()->user()->id !== $user->id) {
abort(401);
}
-
- $createdAt = $invitation->created_at;
- $diff = $createdAt->diffInMinutes(now());
- if ($diff <= config('constants.invitation.link.expiration')) {
+ $invitationValid = $invitation->isValid();
+ if ($invitationValid) {
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
+ refreshSession($invitation->team);
$invitation->delete();
return redirect()->route('team.index');
} else {
- $invitation->delete();
abort(401);
}
} catch (Throwable $e) {
+ ray($e->getMessage());
throw $e;
}
}
diff --git a/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php
index ef84dca08..d73c644ef 100644
--- a/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php
+++ b/app/Http/Livewire/Project/Shared/EnvironmentVariable/All.php
@@ -5,7 +5,7 @@
use App\Models\EnvironmentVariable;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
-use Str;
+use Illuminate\Support\Str;
class All extends Component
{
diff --git a/app/Http/Livewire/Team/InviteLink.php b/app/Http/Livewire/Team/InviteLink.php
index b554e6575..c22c54a61 100644
--- a/app/Http/Livewire/Team/InviteLink.php
+++ b/app/Http/Livewire/Team/InviteLink.php
@@ -4,9 +4,13 @@
use App\Models\TeamInvitation;
use App\Models\User;
-use App\Notifications\TransactionalEmails\InvitationLink;
+use Illuminate\Notifications\Messages\MailMessage;
+use Illuminate\Support\Facades\Artisan;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
+use Illuminate\Support\Facades\Crypt;
+use Illuminate\Support\Facades\Hash;
+use Illuminate\Support\Str;
class InviteLink extends Component
{
@@ -20,53 +24,68 @@ public function mount()
public function viaEmail()
{
- $this->generate_invite_link(isEmail: true);
+ $this->generate_invite_link(sendEmail: true);
}
- private function generate_invite_link(bool $isEmail = false)
+ public function viaLink()
+ {
+ $this->generate_invite_link(sendEmail: false);
+ }
+ private function generate_invite_link(bool $sendEmail = false)
{
try {
- $uuid = new Cuid2(32);
- $link = url('/') . config('constants.invitation.link.base_url') . $uuid;
-
- $user = User::whereEmail($this->email);
-
- if (!$user->exists()) {
- return general_error_handler(that: $this, customErrorMessage: "$this->email must be registered first (or activate transactional emails to invite via email).");
- }
-
$member_emails = currentTeam()->members()->get()->pluck('email');
if ($member_emails->contains($this->email)) {
return general_error_handler(that: $this, customErrorMessage: "$this->email is already a member of " . currentTeam()->name . ".");
}
+ $uuid = new Cuid2(32);
+ $link = url('/') . config('constants.invitation.link.base_url') . $uuid;
+ $user = User::whereEmail($this->email)->first();
- $invitation = TeamInvitation::whereEmail($this->email);
-
- if ($invitation->exists()) {
- $created_at = $invitation->first()->created_at;
- $diff = $created_at->diffInMinutes(now());
- if ($diff <= config('constants.invitation.link.expiration')) {
- return general_error_handler(that: $this, customErrorMessage: "Invitation already sent to $this->email and waiting for action.");
+ if (is_null($user)) {
+ $password = Str::password();
+ $user = User::create([
+ 'name' => Str::of($this->email)->before('@'),
+ 'email' => $this->email,
+ 'password' => Hash::make($password),
+ 'force_password_reset' => true,
+ ]);
+ $token = Crypt::encryptString("{$user->email}@@@$password");
+ $link = route('auth.link', ['token' => $token]);
+ }
+ $invitation = TeamInvitation::whereEmail($this->email)->first();
+ if (!is_null($invitation)) {
+ $invitationValid = $invitation->isValid();
+ if ($invitationValid) {
+ return general_error_handler(that: $this, customErrorMessage: "Pending invitation already exists for $this->email.");
} else {
$invitation->delete();
}
}
- TeamInvitation::firstOrCreate([
+ $invitation = TeamInvitation::firstOrCreate([
'team_id' => currentTeam()->id,
'uuid' => $uuid,
'email' => $this->email,
'role' => $this->role,
'link' => $link,
- 'via' => $isEmail ? 'email' : 'link',
+ 'via' => $sendEmail ? 'email' : 'link',
]);
- if ($isEmail) {
- $user->first()->notify(new InvitationLink);
+ if ($sendEmail) {
+ $mail = new MailMessage();
+ $mail->view('emails.invitation-link', [
+ 'team' => currentTeam()->name,
+ 'invitation_link' => $link,
+ ]);
+ $mail->subject('You have been invited to ' . currentTeam()->name . ' on ' . config('app.name') . '.');
+ send_user_an_email($mail, $this->email);
$this->emit('success', 'Invitation sent via email successfully.');
+ $this->emit('refreshInvitations');
+ return;
} else {
$this->emit('success', 'Invitation link generated.');
+ $this->emit('refreshInvitations');
}
- $this->emit('refreshInvitations');
} catch (\Throwable $e) {
$error_message = $e->getMessage();
if ($e->getCode() === '23505') {
@@ -75,9 +94,4 @@ private function generate_invite_link(bool $isEmail = false)
return general_error_handler(err: $e, that: $this, customErrorMessage: $error_message);
}
}
-
- public function viaLink()
- {
- $this->generate_invite_link();
- }
}
diff --git a/app/Http/Livewire/Team/Member.php b/app/Http/Livewire/Team/Member.php
index df8fcd7af..34b8e6bde 100644
--- a/app/Http/Livewire/Team/Member.php
+++ b/app/Http/Livewire/Team/Member.php
@@ -3,6 +3,7 @@
namespace App\Http\Livewire\Team;
use App\Models\User;
+use Illuminate\Support\Facades\Cache;
use Livewire\Component;
class Member extends Component
@@ -24,6 +25,10 @@ public function makeReadonly()
public function remove()
{
$this->member->teams()->detach(currentTeam());
+ Cache::forget("team:{$this->member->id}");
+ Cache::remember('team:' . $this->member->id, 3600, function() {
+ return $this->member->teams()->first();
+ });
$this->emit('reloadWindow');
}
}
diff --git a/app/Http/Livewire/Waitlist/Index.php b/app/Http/Livewire/Waitlist/Index.php
index d2ce6fe19..86f8d8929 100644
--- a/app/Http/Livewire/Waitlist/Index.php
+++ b/app/Http/Livewire/Waitlist/Index.php
@@ -6,7 +6,7 @@
use App\Models\User;
use App\Models\Waitlist;
use Livewire\Component;
-use Str;
+use Illuminate\Support\Str;
class Index extends Component
{
diff --git a/app/Http/Middleware/IsBoardingFlow.php b/app/Http/Middleware/IsBoardingFlow.php
index 5858fe191..6e820f483 100644
--- a/app/Http/Middleware/IsBoardingFlow.php
+++ b/app/Http/Middleware/IsBoardingFlow.php
@@ -5,6 +5,7 @@
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
+use Illuminate\Support\Str;
class IsBoardingFlow
{
@@ -17,6 +18,9 @@ public function handle(Request $request, Closure $next): Response
{
// ray()->showQueries()->color('orange');
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
+ if (Str::startsWith($request->path(), 'invitations')) {
+ return $next($request);
+ }
return redirect('boarding');
}
return $next($request);
diff --git a/app/Http/Middleware/IsSubscriptionValid.php b/app/Http/Middleware/IsSubscriptionValid.php
index a9354e982..5774e5190 100644
--- a/app/Http/Middleware/IsSubscriptionValid.php
+++ b/app/Http/Middleware/IsSubscriptionValid.php
@@ -5,6 +5,7 @@
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
+use Illuminate\Support\Str;
class IsSubscriptionValid
{
@@ -31,6 +32,9 @@ public function handle(Request $request, Closure $next): Response
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
// ray('SubscriptionValid Middleware');
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
+ if (Str::startsWith($request->path(), 'invitations')) {
+ return $next($request);
+ }
return redirect('subscription');
} else {
return $next($request);
diff --git a/app/Jobs/CleanupInstanceStuffsJob.php b/app/Jobs/CleanupInstanceStuffsJob.php
index f9d88cd4d..81a6963ea 100644
--- a/app/Jobs/CleanupInstanceStuffsJob.php
+++ b/app/Jobs/CleanupInstanceStuffsJob.php
@@ -2,6 +2,7 @@
namespace App\Jobs;
+use App\Models\TeamInvitation;
use App\Models\Waitlist;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
@@ -32,7 +33,12 @@ public function handle(): void
} catch (\Throwable $e) {
send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage());
ray($e->getMessage());
- throw $e;
+ }
+ try {
+ $this->cleanup_invitation_link();
+ } catch (\Throwable $e) {
+ send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage());
+ ray($e->getMessage());
}
}
@@ -43,4 +49,11 @@ private function cleanup_waitlist()
$item->delete();
}
}
+ private function cleanup_invitation_link()
+ {
+ $invitation = TeamInvitation::all();
+ foreach ($invitation as $item) {
+ $item->isValid();
+ }
+ }
}
diff --git a/app/Jobs/ContainerStatusJob.php b/app/Jobs/ContainerStatusJob.php
index 5fb0655b0..100c33e51 100644
--- a/app/Jobs/ContainerStatusJob.php
+++ b/app/Jobs/ContainerStatusJob.php
@@ -17,7 +17,7 @@
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
-use Str;
+use Illuminate\Support\Str;
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
{
diff --git a/app/Jobs/SendMessageToTelegramJob.php b/app/Jobs/SendMessageToTelegramJob.php
index 37f1cf381..23d49c40e 100644
--- a/app/Jobs/SendMessageToTelegramJob.php
+++ b/app/Jobs/SendMessageToTelegramJob.php
@@ -9,7 +9,7 @@
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http;
-use Str;
+use Illuminate\Support\Str;
class SendMessageToTelegramJob implements ShouldQueue, ShouldBeEncrypted
{
diff --git a/app/Models/TeamInvitation.php b/app/Models/TeamInvitation.php
index a5c382b3b..75a726f9f 100644
--- a/app/Models/TeamInvitation.php
+++ b/app/Models/TeamInvitation.php
@@ -19,4 +19,13 @@ public function team()
{
return $this->belongsTo(Team::class);
}
+ public function isValid() {
+ $createdAt = $this->created_at;
+ $diff = $createdAt->diffInMinutes(now());
+ if ($diff <= config('constants.invitation.link.expiration')) {
+ return true;
+ } else {
+ $this->delete();
+ }
+ }
}
diff --git a/app/Models/User.php b/app/Models/User.php
index 7df134408..aba05acf3 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -4,10 +4,10 @@
use App\Notifications\Channels\SendsEmail;
use App\Notifications\TransactionalEmails\ResetPassword as TransactionalEmailsResetPassword;
-use Cache;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
+use Illuminate\Support\Facades\Cache;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Sanctum\HasApiTokens;
@@ -61,7 +61,7 @@ public function sendPasswordResetNotification($token): void
public function isAdmin()
{
- return $this->pivot->role === 'admin' || $this->pivot->role === 'owner';
+ return data_get($this->pivot,'role') === 'admin' || data_get($this->pivot,'role') === 'owner';
}
public function isAdminFromSession()
@@ -78,7 +78,8 @@ public function isAdminFromSession()
if ($is_part_of_root_team && $is_admin_of_root_team) {
return true;
}
- $role = $teams->where('id', session('currentTeam')->id)->first()->pivot->role;
+ $team = $teams->where('id', session('currentTeam')->id)->first();
+ $role = data_get($team,'pivot.role');
return $role === 'admin' || $role === 'owner';
}
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 5c504b243..ffd429c58 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -11,6 +11,7 @@
use Illuminate\Database\QueryException;
use Illuminate\Mail\Message;
use Illuminate\Notifications\Messages\MailMessage;
+use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Route;
diff --git a/bootstrap/helpers/subscriptions.php b/bootstrap/helpers/subscriptions.php
index ae3506782..ac41b1a1d 100644
--- a/bootstrap/helpers/subscriptions.php
+++ b/bootstrap/helpers/subscriptions.php
@@ -56,7 +56,7 @@ function isSubscriptionActive()
}
$subscription = $team?->subscription;
- if (!$subscription) {
+ if (is_null($subscription)) {
return false;
}
if (isLemon()) {
diff --git a/database/migrations/2023_08_22_071060_change_invitation_link_length.php b/database/migrations/2023_08_22_071060_change_invitation_link_length.php
new file mode 100644
index 000000000..4efb03351
--- /dev/null
+++ b/database/migrations/2023_08_22_071060_change_invitation_link_length.php
@@ -0,0 +1,29 @@
+text('link')->change();
+
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('team_invitations', function (Blueprint $table) {
+ $table->string('link')->change();
+ });
+ }
+};
diff --git a/resources/views/emails/invitation-link.blade.php b/resources/views/emails/invitation-link.blade.php
index 05c36bb8a..ae72745ba 100644
--- a/resources/views/emails/invitation-link.blade.php
+++ b/resources/views/emails/invitation-link.blade.php
@@ -6,6 +6,5 @@
If you have any questions, please contact the team owner.
-If it was not you who requested this invitation, please ignore this email, or instantly revoke the invitation by clicking [here]({{ $invitation_link }}/revoke).
-
+If it was not you who requested this invitation, please ignore this email.
diff --git a/resources/views/subscription/index.blade.php b/resources/views/subscription/index.blade.php
index e13d856de..d36f26c81 100644
--- a/resources/views/subscription/index.blade.php
+++ b/resources/views/subscription/index.blade.php
@@ -1,7 +1,34 @@
@if ($settings->is_resale_license_active)
-
-
+ @if (auth()->user()->isAdminFromSession())
+
+
+
+
Subscription
+
+
+
+ Currently active team: {{ session('currentTeam.name') }}
+
+ @if (request()->query->get('cancelled'))
+
+
+ Something went wrong with your subscription. Please try again or contact
+ support.
+