fix: invitation
This commit is contained in:
parent
82c235d5af
commit
b07cc500e7
@ -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', [
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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';
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -56,7 +56,7 @@ function isSubscriptionActive()
|
||||
}
|
||||
$subscription = $team?->subscription;
|
||||
|
||||
if (!$subscription) {
|
||||
if (is_null($subscription)) {
|
||||
return false;
|
||||
}
|
||||
if (isLemon()) {
|
||||
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('team_invitations', function (Blueprint $table) {
|
||||
$table->text('link')->change();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('team_invitations', function (Blueprint $table) {
|
||||
$table->string('link')->change();
|
||||
});
|
||||
}
|
||||
};
|
@ -6,6 +6,5 @@
|
||||
|
||||
If you have any questions, please contact the team owner.<br><br>
|
||||
|
||||
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.
|
||||
</x-emails.layout>
|
||||
|
@ -1,7 +1,34 @@
|
||||
<x-layout-subscription>
|
||||
@if ($settings->is_resale_license_active)
|
||||
<div class="flex justify-center mx-10">
|
||||
<div x-data>
|
||||
@if (auth()->user()->isAdminFromSession())
|
||||
<div class="flex justify-center mx-10">
|
||||
<div x-data>
|
||||
<div class="flex gap-2">
|
||||
<h1>Subscription</h1>
|
||||
<livewire:switch-team />
|
||||
</div>
|
||||
<div class="flex items-center pb-8">
|
||||
<span>Currently active team: <span
|
||||
class="text-warning">{{ session('currentTeam.name') }}</span></span>
|
||||
</div>
|
||||
@if (request()->query->get('cancelled'))
|
||||
<div class="mb-6 rounded alert alert-error">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span>Something went wrong with your subscription. Please try again or contact
|
||||
support.</span>
|
||||
</div>
|
||||
@endif
|
||||
@if (config('subscription.provider') !== null)
|
||||
<livewire:subscription.pricing-plans />
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col justify-center mx-10">
|
||||
<div class="flex gap-2">
|
||||
<h1>Subscription</h1>
|
||||
<livewire:switch-team />
|
||||
@ -10,22 +37,10 @@
|
||||
<span>Currently active team: <span
|
||||
class="text-warning">{{ session('currentTeam.name') }}</span></span>
|
||||
</div>
|
||||
@if (request()->query->get('cancelled'))
|
||||
<div class="mb-6 rounded alert alert-error">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span>Something went wrong with your subscription. Please try again or contact support.</span>
|
||||
</div>
|
||||
@endif
|
||||
@if (config('subscription.provider') !== null)
|
||||
<livewire:subscription.pricing-plans />
|
||||
@endif
|
||||
<div>You are not an admin or have been removed from this team. If this does not make sense, please <span class="text-white underline cursor-pointer" wire:click="help" onclick="help.showModal()">contact us</span>.</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@else
|
||||
<div class="px-10">Resale license is not active. Please contact your instance admin.</div>
|
||||
<div class="px-10" >Resale license is not active. Please contact your instance admin.</div>
|
||||
@endif
|
||||
</x-layout-subscription>
|
||||
|
Loading…
Reference in New Issue
Block a user