wip
This commit is contained in:
		
							parent
							
								
									e714e87ad6
								
							
						
					
					
						commit
						cac59e4873
					
				| @ -62,13 +62,23 @@ class Controller extends BaseController | |||||||
|     public function team() |     public function team() | ||||||
|     { |     { | ||||||
|         $invitations = []; |         $invitations = []; | ||||||
|         if (auth()->user()->isAdmin()) { |         if (auth()->user()->isAdminFromSession()) { | ||||||
|             $invitations = TeamInvitation::whereTeamId(auth()->user()->currentTeam()->id)->get(); |             $invitations = TeamInvitation::whereTeamId(auth()->user()->currentTeam()->id)->get(); | ||||||
|         } |         } | ||||||
|         return view('team.show', [ |         return view('team.show', [ | ||||||
|             'invitations' => $invitations, |             'invitations' => $invitations, | ||||||
|         ]); |         ]); | ||||||
|     } |     } | ||||||
|  |     public function members() | ||||||
|  |     { | ||||||
|  |         $invitations = []; | ||||||
|  |         if (auth()->user()->isAdminFromSession()) { | ||||||
|  |             $invitations = TeamInvitation::whereTeamId(auth()->user()->currentTeam()->id)->get(); | ||||||
|  |         } | ||||||
|  |         return view('team.members', [ | ||||||
|  |             'invitations' => $invitations, | ||||||
|  |         ]); | ||||||
|  |     } | ||||||
|     public function acceptInvitation() |     public function acceptInvitation() | ||||||
|     { |     { | ||||||
|         try { |         try { | ||||||
|  | |||||||
| @ -22,6 +22,7 @@ class Kernel extends HttpKernel | |||||||
|         \App\Http\Middleware\TrimStrings::class, |         \App\Http\Middleware\TrimStrings::class, | ||||||
|         \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, |         \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, | ||||||
|         \App\Http\Middleware\LicenseValid::class, |         \App\Http\Middleware\LicenseValid::class, | ||||||
|  | 
 | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -37,6 +38,8 @@ class Kernel extends HttpKernel | |||||||
|             \Illuminate\View\Middleware\ShareErrorsFromSession::class, |             \Illuminate\View\Middleware\ShareErrorsFromSession::class, | ||||||
|             \App\Http\Middleware\VerifyCsrfToken::class, |             \App\Http\Middleware\VerifyCsrfToken::class, | ||||||
|             \Illuminate\Routing\Middleware\SubstituteBindings::class, |             \Illuminate\Routing\Middleware\SubstituteBindings::class, | ||||||
|  |             \App\Http\Middleware\SubscriptionValid::class, | ||||||
|  | 
 | ||||||
|         ], |         ], | ||||||
| 
 | 
 | ||||||
|         'api' => [ |         'api' => [ | ||||||
|  | |||||||
| @ -16,10 +16,7 @@ class LicenseValid | |||||||
|      */ |      */ | ||||||
|     public function handle(Request $request, Closure $next): Response |     public function handle(Request $request, Closure $next): Response | ||||||
|     { |     { | ||||||
|         if (isCloud()) { |         if (isCloud() && !isDev()) { | ||||||
|             if (isDev()) { |  | ||||||
|                 return $next($request); |  | ||||||
|             } |  | ||||||
|             $value = Cache::get('license_key'); |             $value = Cache::get('license_key'); | ||||||
|             if (!$value) { |             if (!$value) { | ||||||
|                 ray($request->path()); |                 ray($request->path()); | ||||||
|  | |||||||
							
								
								
									
										36
									
								
								app/Http/Middleware/SubscriptionValid.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								app/Http/Middleware/SubscriptionValid.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Http\Middleware; | ||||||
|  | 
 | ||||||
|  | use Closure; | ||||||
|  | use Illuminate\Http\Request; | ||||||
|  | use Illuminate\Support\Facades\Cache; | ||||||
|  | use Symfony\Component\HttpFoundation\Response; | ||||||
|  | 
 | ||||||
|  | class SubscriptionValid | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Handle an incoming request. | ||||||
|  |      * | ||||||
|  |      * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next | ||||||
|  |      */ | ||||||
|  |     public function handle(Request $request, Closure $next): Response | ||||||
|  |     { | ||||||
|  |         $allowed_paths = [ | ||||||
|  |             'team', | ||||||
|  |             'livewire/message/team', | ||||||
|  |             'login', | ||||||
|  |             'register', | ||||||
|  |             'livewire/message/switch-team', | ||||||
|  |             'logout', | ||||||
|  |         ]; | ||||||
|  |         if (isCloud()) { | ||||||
|  |            if (!$request->user()?->currentTeam()?->subscription && $request->user()?->currentTeam()->subscription?->lemon_status !== 'active') { | ||||||
|  |             if (!in_array($request->path(), $allowed_paths)) { | ||||||
|  |                 return redirect('team'); | ||||||
|  |             } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return $next($request); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -61,8 +61,10 @@ class User extends Authenticatable implements SendsEmail | |||||||
|     { |     { | ||||||
|         return $this->email; |         return $this->email; | ||||||
|     } |     } | ||||||
| 
 |     public function isAdmin() { | ||||||
|     public function isAdmin() |         return $this->pivot->role === 'admin' || $this->pivot->role === 'owner'; | ||||||
|  |     } | ||||||
|  |     public function isAdminFromSession() | ||||||
|     { |     { | ||||||
|         if (auth()->user()->id === 0) { |         if (auth()->user()->id === 0) { | ||||||
|             return true; |             return true; | ||||||
| @ -89,6 +91,9 @@ class User extends Authenticatable implements SendsEmail | |||||||
|         }); |         }); | ||||||
|         return $found_root_team->count() > 0; |         return $found_root_team->count() > 0; | ||||||
|     } |     } | ||||||
|  |     public function personalTeam() { | ||||||
|  |         return $this->teams()->where('personal_team', true)->first(); | ||||||
|  |     } | ||||||
|     public function teams() |     public function teams() | ||||||
|     { |     { | ||||||
|         return $this->belongsToMany(Team::class)->withPivot('role'); |         return $this->belongsToMany(Team::class)->withPivot('role'); | ||||||
|  | |||||||
| @ -8,4 +8,7 @@ use Illuminate\Database\Eloquent\Model; | |||||||
| class Webhook extends Model | class Webhook extends Model | ||||||
| { | { | ||||||
|     protected $guarded = []; |     protected $guarded = []; | ||||||
|  |     protected $casts = [ | ||||||
|  |         'payload' => 'encrypted', | ||||||
|  |     ]; | ||||||
| } | } | ||||||
| @ -132,20 +132,3 @@ function isCloud() | |||||||
| { | { | ||||||
|     return !config('coolify.self_hosted'); |     return !config('coolify.self_hosted'); | ||||||
| } | } | ||||||
| function getSubscriptionLink() |  | ||||||
| { |  | ||||||
|     $user_id = auth()->user()->id; |  | ||||||
|     $email = auth()->user()->email ?? null; |  | ||||||
|     $name = auth()->user()->name ?? null; |  | ||||||
|     $url = "https://store.coollabs.io/checkout/buy/d0b28c6a-9b57-40bf-8b84-89fbafde6526?"; |  | ||||||
|     if ($user_id) { |  | ||||||
|         $url .= "&checkout[custom][user_id]={$user_id}"; |  | ||||||
|     } |  | ||||||
|     if ($email) { |  | ||||||
|         $url .= "&checkout[email]={$email}"; |  | ||||||
|     } |  | ||||||
|     if ($name) { |  | ||||||
|         $url .= "&checkout[name]={$name}"; |  | ||||||
|     } |  | ||||||
|     return $url; |  | ||||||
| } |  | ||||||
|  | |||||||
							
								
								
									
										34
									
								
								bootstrap/helpers/subscriptions.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								bootstrap/helpers/subscriptions.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | use Illuminate\Support\Carbon; | ||||||
|  | 
 | ||||||
|  | function getSubscriptionLink() | ||||||
|  | { | ||||||
|  |     $user_id = auth()->user()->id; | ||||||
|  |     $team_id = auth()->user()->currentTeam()->id ?? null; | ||||||
|  |     $email = auth()->user()->email ?? null; | ||||||
|  |     $name = auth()->user()->name ?? null; | ||||||
|  |     $url = "https://store.coollabs.io/checkout/buy/d0b28c6a-9b57-40bf-8b84-89fbafde6526?"; | ||||||
|  |     if ($user_id) { | ||||||
|  |         $url .= "&checkout[custom][user_id]={$user_id}"; | ||||||
|  |     } | ||||||
|  |     if (isset($team_id)) { | ||||||
|  |         $url .= "&checkout[custom][team_id]={$team_id}"; | ||||||
|  |     } | ||||||
|  |     if ($email) { | ||||||
|  |         $url .= "&checkout[email]={$email}"; | ||||||
|  |     } | ||||||
|  |     if ($name) { | ||||||
|  |         $url .= "&checkout[name]={$name}"; | ||||||
|  |     } | ||||||
|  |     return $url; | ||||||
|  | } | ||||||
|  | function getPaymentLink() { | ||||||
|  |     return auth()->user()->currentTeam()->subscription->lemon_update_payment_menthod_url; | ||||||
|  | } | ||||||
|  | function getRenewDate() { | ||||||
|  |     return Carbon::parse(auth()->user()->currentTeam()->subscription->lemon_renews_at)->format('Y-M-d H:i:s'); | ||||||
|  | } | ||||||
|  | function isSubscribed() { | ||||||
|  |     return isCloud() && auth()->user()->currentTeam()->subscription?->lemon_status === 'active'; | ||||||
|  | } | ||||||
| @ -13,14 +13,16 @@ return new class extends Migration | |||||||
|     { |     { | ||||||
|         Schema::create('subscriptions', function (Blueprint $table) { |         Schema::create('subscriptions', function (Blueprint $table) { | ||||||
|             $table->id(); |             $table->id(); | ||||||
|  |             $table->string('lemon_subscription_id'); | ||||||
|             $table->string('lemon_order_id'); |             $table->string('lemon_order_id'); | ||||||
|             $table->string('lemon_product_id'); |             $table->string('lemon_product_id'); | ||||||
|             $table->string('lemon_variant_id'); |             $table->string('lemon_variant_id'); | ||||||
|  |             $table->string('lemon_variant_name'); | ||||||
|             $table->string('lemon_customer_id'); |             $table->string('lemon_customer_id'); | ||||||
|             $table->string('lemon_status'); |             $table->string('lemon_status'); | ||||||
|             $table->string('lemon_trial_ends_at'); |             $table->string('lemon_trial_ends_at')->nullable(); | ||||||
|             $table->string('lemon_renews_at'); |             $table->string('lemon_renews_at'); | ||||||
|             $table->string('lemon_ends_at'); |             $table->string('lemon_ends_at')->nullable(); | ||||||
|             $table->string('lemon_update_payment_menthod_url'); |             $table->string('lemon_update_payment_menthod_url'); | ||||||
|             $table->foreignId('team_id'); |             $table->foreignId('team_id'); | ||||||
|             $table->timestamps(); |             $table->timestamps(); | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ | |||||||
|     @endif |     @endif | ||||||
|     <select {{ $attributes->merge(['class' => $defaultClass]) }} @required($required) |     <select {{ $attributes->merge(['class' => $defaultClass]) }} @required($required) | ||||||
|         wire:dirty.class="text-black bg-warning" wire:loading.attr="disabled" name={{ $id }} |         wire:dirty.class="text-black bg-warning" wire:loading.attr="disabled" name={{ $id }} | ||||||
|         wire:model.defer={{ $id }}> |         @if ($attributes->whereStartsWith('wire:model')->first()) {{ $attributes->whereStartsWith('wire:model')->first() }} @else wire:model.defer={{ $id }} @endif> | ||||||
|         {{ $slot }} |         {{ $slot }} | ||||||
|     </select> |     </select> | ||||||
| </div> | </div> | ||||||
|  | |||||||
| @ -30,9 +30,11 @@ | |||||||
|     @auth |     @auth | ||||||
|         <x-toaster-hub /> |         <x-toaster-hub /> | ||||||
|         <x-navbar /> |         <x-navbar /> | ||||||
|  |         @if (isSubscribed()) | ||||||
|             <div class="fixed top-3 left-4" id="vue"> |             <div class="fixed top-3 left-4" id="vue"> | ||||||
|                 <magic-bar></magic-bar> |                 <magic-bar></magic-bar> | ||||||
|             </div> |             </div> | ||||||
|  |         @endif | ||||||
|         <main class="main"> |         <main class="main"> | ||||||
|             {{ $slot }} |             {{ $slot }} | ||||||
|         </main> |         </main> | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| @auth | @auth | ||||||
| <nav class="fixed h-full overflow-hidden overflow-y-auto pt-14 scrollbar"> |     <nav class="fixed h-full overflow-hidden overflow-y-auto scrollbar" :class="{ isSubscribed() ? 'pt-14' : '' }"> | ||||||
|         <ul class="flex flex-col h-full gap-4 menu flex-nowrap"> |         <ul class="flex flex-col h-full gap-4 menu flex-nowrap"> | ||||||
|             <li title="Dashboard"> |             <li title="Dashboard"> | ||||||
|                 <a class="hover:bg-transparent" @if (!request()->is('/')) href="/" @endif> |                 <a class="hover:bg-transparent" @if (!request()->is('/')) href="/" @endif> | ||||||
| @ -10,12 +10,13 @@ | |||||||
|                     </svg> |                     </svg> | ||||||
|                 </a> |                 </a> | ||||||
|             </li> |             </li> | ||||||
|  |             @if (isSubscribed()) | ||||||
|                 <li title="Projects"> |                 <li title="Projects"> | ||||||
|                     <a class="hover:bg-transparent" @if (!request()->is('projects')) href="/projects" @endif> |                     <a class="hover:bg-transparent" @if (!request()->is('projects')) href="/projects" @endif> | ||||||
|                         <svg xmlns="http://www.w3.org/2000/svg" |                         <svg xmlns="http://www.w3.org/2000/svg" | ||||||
|                             class="{{ request()->is('project/*') || request()->is('projects') ? 'text-warning icon' : 'icon' }}" |                             class="{{ request()->is('project/*') || request()->is('projects') ? 'text-warning icon' : 'icon' }}" | ||||||
|                     viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" |                             viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" | ||||||
|                     stroke-linejoin="round"> |                             stroke-linecap="round" stroke-linejoin="round"> | ||||||
|                             <path stroke="none" d="M0 0h24v24H0z" fill="none" /> |                             <path stroke="none" d="M0 0h24v24H0z" fill="none" /> | ||||||
|                             <path d="M12 4l-8 4l8 4l8 -4l-8 -4" /> |                             <path d="M12 4l-8 4l8 4l8 -4l-8 -4" /> | ||||||
|                             <path d="M4 12l8 4l8 -4" /> |                             <path d="M4 12l8 4l8 -4" /> | ||||||
| @ -28,8 +29,8 @@ | |||||||
|                     <a class="hover:bg-transparent" @if (!request()->is('servers')) href="/servers" @endif> |                     <a class="hover:bg-transparent" @if (!request()->is('servers')) href="/servers" @endif> | ||||||
|                         <svg xmlns="http://www.w3.org/2000/svg" |                         <svg xmlns="http://www.w3.org/2000/svg" | ||||||
|                             class="{{ request()->is('server/*') || request()->is('servers') ? 'text-warning icon' : 'icon' }}" |                             class="{{ request()->is('server/*') || request()->is('servers') ? 'text-warning icon' : 'icon' }}" | ||||||
|                     viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" |                             viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" | ||||||
|                     stroke-linejoin="round"> |                             stroke-linecap="round" stroke-linejoin="round"> | ||||||
|                             <path stroke="none" d="M0 0h24v24H0z" fill="none" /> |                             <path stroke="none" d="M0 0h24v24H0z" fill="none" /> | ||||||
|                             <path d="M3 4m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z" /> |                             <path d="M3 4m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z" /> | ||||||
|                             <path d="M15 20h-9a3 3 0 0 1 -3 -3v-2a3 3 0 0 1 3 -3h12" /> |                             <path d="M15 20h-9a3 3 0 0 1 -3 -3v-2a3 3 0 0 1 3 -3h12" /> | ||||||
| @ -43,8 +44,9 @@ | |||||||
|                     <li title="Command Center"> |                     <li title="Command Center"> | ||||||
|                         <a class="hover:bg-transparent" @if (!request()->is('command-center')) href="/command-center" @endif> |                         <a class="hover:bg-transparent" @if (!request()->is('command-center')) href="/command-center" @endif> | ||||||
|                             <svg xmlns="http://www.w3.org/2000/svg" |                             <svg xmlns="http://www.w3.org/2000/svg" | ||||||
|                     class="{{ request()->is('command-center') ? 'text-warning icon' : 'icon' }}" viewBox="0 0 24 24" |                                 class="{{ request()->is('command-center') ? 'text-warning icon' : 'icon' }}" | ||||||
|                     stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> |                                 viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" | ||||||
|  |                                 stroke-linecap="round" stroke-linejoin="round"> | ||||||
|                                 <path stroke="none" d="M0 0h24v24H0z" fill="none" /> |                                 <path stroke="none" d="M0 0h24v24H0z" fill="none" /> | ||||||
|                                 <path d="M5 7l5 5l-5 5" /> |                                 <path d="M5 7l5 5l-5 5" /> | ||||||
|                                 <path d="M12 19l7 0" /> |                                 <path d="M12 19l7 0" /> | ||||||
| @ -81,8 +83,9 @@ | |||||||
|                     <li title="Settings" class="mt-auto"> |                     <li title="Settings" class="mt-auto"> | ||||||
|                         <a class="hover:bg-transparent" @if (!request()->is('settings')) href="/settings" @endif> |                         <a class="hover:bg-transparent" @if (!request()->is('settings')) href="/settings" @endif> | ||||||
|                             <svg xmlns="http://www.w3.org/2000/svg" |                             <svg xmlns="http://www.w3.org/2000/svg" | ||||||
|                     class="{{ request()->is('settings*') ? 'text-warning icon' : 'icon' }}" viewBox="0 0 24 24" |                                 class="{{ request()->is('settings*') ? 'text-warning icon' : 'icon' }}" | ||||||
|                     stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> |                                 viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" | ||||||
|  |                                 stroke-linecap="round" stroke-linejoin="round"> | ||||||
|                                 <path stroke="none" d="M0 0h24v24H0z" fill="none" /> |                                 <path stroke="none" d="M0 0h24v24H0z" fill="none" /> | ||||||
|                                 <path |                                 <path | ||||||
|                                     d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z" /> |                                     d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z" /> | ||||||
| @ -90,6 +93,9 @@ | |||||||
|                             </svg> |                             </svg> | ||||||
|                         </a> |                         </a> | ||||||
|                     </li> |                     </li> | ||||||
|  |                 @endif | ||||||
|  |             @endif | ||||||
|  | 
 | ||||||
|             <li class="pb-6" title="Logout"> |             <li class="pb-6" title="Logout"> | ||||||
|                 <form action="/logout" method="POST" class=" hover:bg-transparent"> |                 <form action="/logout" method="POST" class=" hover:bg-transparent"> | ||||||
|                     @csrf |                     @csrf | ||||||
| @ -104,7 +110,6 @@ | |||||||
|                         </svg></button> |                         </svg></button> | ||||||
|                 </form> |                 </form> | ||||||
|             </li> |             </li> | ||||||
|         @endif |  | ||||||
|         </ul> |         </ul> | ||||||
| </nav> |     </nav> | ||||||
| @endauth | @endauth | ||||||
| @ -20,15 +20,22 @@ | |||||||
|             @endif |             @endif | ||||||
|         </ol> |         </ol> | ||||||
|     </nav> |     </nav> | ||||||
|  |     @if (isSubscribed()) | ||||||
|         <nav class="flex items-end gap-4 py-2 border-b-2 border-solid border-coolgray-200"> |         <nav class="flex items-end gap-4 py-2 border-b-2 border-solid border-coolgray-200"> | ||||||
|             <a class="{{ request()->routeIs('team.show') ? 'text-white' : '' }}" href="{{ route('team.show') }}"> |             <a class="{{ request()->routeIs('team.show') ? 'text-white' : '' }}" href="{{ route('team.show') }}"> | ||||||
|                 <button>General</button> |                 <button>General</button> | ||||||
|             </a> |             </a> | ||||||
|  |             <a class="{{ request()->routeIs('team.members') ? 'text-white' : '' }}" href="{{ route('team.members') }}"> | ||||||
|  |                 <button>Members</button> | ||||||
|  |             </a> | ||||||
|             <a class="{{ request()->routeIs('team.notifications') ? 'text-white' : '' }}" |             <a class="{{ request()->routeIs('team.notifications') ? 'text-white' : '' }}" | ||||||
|                 href="{{ route('team.notifications') }}"> |                 href="{{ route('team.notifications') }}"> | ||||||
|                 <button>Notifications</button> |                 <button>Notifications</button> | ||||||
|             </a> |             </a> | ||||||
|             <div class="flex-1"></div> |             <div class="flex-1"></div> | ||||||
|             <livewire:switch-team /> |             <livewire:switch-team /> | ||||||
|  |         @else | ||||||
|  |             <livewire:switch-team /> | ||||||
|  |     @endif | ||||||
|     </nav> |     </nav> | ||||||
| </div> | </div> | ||||||
|  | |||||||
| @ -18,6 +18,4 @@ | |||||||
|             <div class="stat-desc">Applications, databases, etc...</div> |             <div class="stat-desc">Applications, databases, etc...</div> | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|     {{-- <a href="{{ getSubscriptionLink() }}">Subscribe</a> --}} |  | ||||||
| 
 |  | ||||||
| </x-layout> | </x-layout> | ||||||
|  | |||||||
| @ -8,6 +8,8 @@ | |||||||
|         <div>This is the default team. You can't delete it.</div> |         <div>This is the default team. You can't delete it.</div> | ||||||
|     @elseif(auth()->user()->teams()->get()->count() === 1) |     @elseif(auth()->user()->teams()->get()->count() === 1) | ||||||
|         <div>You can't delete your last team.</div> |         <div>You can't delete your last team.</div> | ||||||
|  |     @elseif(auth()->user()->currentTeam()->subscription?->lemon_status !== 'cancelled') | ||||||
|  |         <div>Please cancel your subscription before delete this team (Manage My Subscription button).</div> | ||||||
|     @else |     @else | ||||||
|         @if (session('currentTeam')->isEmpty()) |         @if (session('currentTeam')->isEmpty()) | ||||||
|             <div class="pb-4">This will delete your team. Beware! There is no coming back!</div> |             <div class="pb-4">This will delete your team. Beware! There is no coming back!</div> | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ | |||||||
|         {{ data_get($member, 'pivot.role') }}</td> |         {{ data_get($member, 'pivot.role') }}</td> | ||||||
|     <td> |     <td> | ||||||
|         {{-- TODO: This is not good --}} |         {{-- TODO: This is not good --}} | ||||||
|         @if (auth()->user()->isAdmin()) |         @if (auth()->user()->isAdminFromSession()) | ||||||
|             @if ($member->id !== auth()->user()->id) |             @if ($member->id !== auth()->user()->id) | ||||||
|                 @if (data_get($member, 'pivot.role') !== 'owner') |                 @if (data_get($member, 'pivot.role') !== 'owner') | ||||||
|                     @if (data_get($member, 'pivot.role') !== 'admin') |                     @if (data_get($member, 'pivot.role') !== 'admin') | ||||||
|  | |||||||
							
								
								
									
										43
									
								
								resources/views/team/members.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								resources/views/team/members.blade.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | <x-layout> | ||||||
|  |     <x-team.navbar :team="session('currentTeam')" /> | ||||||
|  |     <h3>Members</h3> | ||||||
|  |     <div class="pt-4 overflow-hidden"> | ||||||
|  |         <table> | ||||||
|  |             <thead> | ||||||
|  |                 <tr> | ||||||
|  |                     <th>Name</th> | ||||||
|  |                     <th>Email</th> | ||||||
|  |                     <th>Role</th> | ||||||
|  |                     <th>Actions</th> | ||||||
|  |                 </tr> | ||||||
|  |             </thead> | ||||||
|  |             <tbody> | ||||||
|  |                 @foreach (auth()->user()->currentTeam()->members->sortBy('name') as $member) | ||||||
|  |                     <livewire:team.member :member="$member" :wire:key="$member->id" /> | ||||||
|  |                 @endforeach | ||||||
|  |             </tbody> | ||||||
|  |         </table> | ||||||
|  |     </div> | ||||||
|  |     @if (auth()->user()->isAdminFromSession()) | ||||||
|  |         <div class="py-4"> | ||||||
|  |             @if (is_transactional_emails_active()) | ||||||
|  |                 <h3 class="pb-4">Invite a new member</h3> | ||||||
|  |             @else | ||||||
|  |                 <h3>Invite a new member</h3> | ||||||
|  |                 @if (auth()->user()->isInstanceAdmin()) | ||||||
|  |                     <div class="pb-4 text-xs text-warning">You need to configure <a href="/settings/emails" | ||||||
|  |                             class="underline text-warning">Transactional Emails</a> | ||||||
|  |                         before | ||||||
|  |                         you can invite a | ||||||
|  |                         new | ||||||
|  |                         member | ||||||
|  |                         via | ||||||
|  |                         email. | ||||||
|  |                     </div> | ||||||
|  |                 @endif | ||||||
|  |             @endif | ||||||
|  |             <livewire:team.invite-link /> | ||||||
|  |         </div> | ||||||
|  |         <livewire:team.invitations :invitations="$invitations" /> | ||||||
|  |     @endif | ||||||
|  | </x-layout> | ||||||
| @ -1,46 +1,35 @@ | |||||||
| <x-layout> | <x-layout> | ||||||
|     <x-team.navbar :team="session('currentTeam')" /> |     <x-team.navbar :team="session('currentTeam')" /> | ||||||
|  |     @if (isSubscribed()) | ||||||
|         <livewire:team.form /> |         <livewire:team.form /> | ||||||
|     <h3>Members</h3> |     @endif | ||||||
|     <div class="pt-4 overflow-hidden"> |     @if (isCloud()) | ||||||
|         <table> |         <div class="pb-8"> | ||||||
|             <thead> |             <h3>Subscription</h3> | ||||||
|                 <tr> |             @if (data_get(auth()->user()->currentTeam(), | ||||||
|                     <th>Name</th> |                     'subscription')) | ||||||
|                     <th>Email</th> |                 <div>Status: {{ auth()->user()->currentTeam()->subscription->lemon_status }}</div> | ||||||
|                     <th>Role</th> |                 <div>Type: {{ auth()->user()->currentTeam()->subscription->lemon_variant_name }}</div> | ||||||
|                     <th>Actions</th> |                 @if (auth()->user()->currentTeam()->subscription->lemon_status === 'cancelled') | ||||||
|                 </tr> |                     <div class="pb-4">Subscriptions ends at: {{ getRenewDate() }}</div> | ||||||
|             </thead> |                     <x-forms.button><a class="text-white" href="{{ getSubscriptionLink() }}">Subscribe | ||||||
|             <tbody> |                             Again</a> | ||||||
|                 @foreach (auth()->user()->currentTeam()->members->sortBy('name') as $member) |                     </x-forms.button> | ||||||
|                     <livewire:team.member :member="$member" :wire:key="$member->id" /> |  | ||||||
|                 @endforeach |  | ||||||
|             </tbody> |  | ||||||
|         </table> |  | ||||||
|     </div> |  | ||||||
|     @if (auth()->user()->isAdmin()) |  | ||||||
|         <div class="py-4"> |  | ||||||
|             @if (is_transactional_emails_active()) |  | ||||||
|                 <h3 class="pb-4">Invite a new member</h3> |  | ||||||
|                 @else |                 @else | ||||||
|                 <h3>Invite a new member</h3> |                     <div class="pb-4">Renews at: {{ getRenewDate() }}</div> | ||||||
|                 @if (auth()->user()->isInstanceAdmin()) |                 @endif | ||||||
|                     <div class="pb-4 text-xs text-warning">You need to configure <a href="/settings/emails" |                 <x-forms.button><a class="text-white" href="{{ getPaymentLink() }}">Update Payment Details</a> | ||||||
|                             class="underline text-warning">Transactional Emails</a> |                 </x-forms.button> | ||||||
|                         before |             @else | ||||||
|                         you can invite a |                 <x-forms.button class="mt-4"><a class="text-white" href="{{ getSubscriptionLink() }}">Subscribe Now</a> | ||||||
|                         new |                 </x-forms.button> | ||||||
|                         member |             @endif | ||||||
|                         via |             <x-forms.button><a class="text-white" href="https://app.lemonsqueezy.com/my-orders">Manage My | ||||||
|                         email. |                     Subscription</a> | ||||||
|  |             </x-forms.button> | ||||||
|         </div> |         </div> | ||||||
|     @endif |     @endif | ||||||
|             @endif |     @if (isSubscribed()) | ||||||
|             <livewire:team.invite-link /> |  | ||||||
|         </div> |  | ||||||
|         <livewire:team.invitations :invitations="$invitations" /> |  | ||||||
|     @endif |  | ||||||
| 
 |  | ||||||
|         <livewire:team.delete /> |         <livewire:team.delete /> | ||||||
|  |     @endif | ||||||
| </x-layout> | </x-layout> | ||||||
|  | |||||||
| @ -91,6 +91,7 @@ Route::middleware(['auth'])->group(function () { | |||||||
|     Route::get('/team', [Controller::class, 'team'])->name('team.show'); |     Route::get('/team', [Controller::class, 'team'])->name('team.show'); | ||||||
|     Route::get('/team/new', fn () => view('team.create'))->name('team.create'); |     Route::get('/team/new', fn () => view('team.create'))->name('team.create'); | ||||||
|     Route::get('/team/notifications', fn () => view('team.notifications'))->name('team.notifications'); |     Route::get('/team/notifications', fn () => view('team.notifications'))->name('team.notifications'); | ||||||
|  |     Route::get('/team/members', [Controller::class, 'members'])->name('team.members'); | ||||||
|     Route::get('/command-center', fn () => view('command-center', ['servers' => Server::validated()->get()]))->name('command-center'); |     Route::get('/command-center', fn () => view('command-center', ['servers' => Server::validated()->get()]))->name('command-center'); | ||||||
|     Route::get('/invitations/{uuid}', [Controller::class, 'acceptInvitation'])->name('team.invitation.accept'); |     Route::get('/invitations/{uuid}', [Controller::class, 'acceptInvitation'])->name('team.invitation.accept'); | ||||||
|     Route::get('/invitations/{uuid}/revoke', [Controller::class, 'revokeInvitation'])->name('team.invitation.revoke'); |     Route::get('/invitations/{uuid}/revoke', [Controller::class, 'revokeInvitation'])->name('team.invitation.revoke'); | ||||||
|  | |||||||
| @ -5,6 +5,9 @@ use App\Models\ApplicationPreview; | |||||||
| use App\Models\PrivateKey; | use App\Models\PrivateKey; | ||||||
| use App\Models\GithubApp; | use App\Models\GithubApp; | ||||||
| use App\Models\Webhook; | use App\Models\Webhook; | ||||||
|  | use App\Models\User; | ||||||
|  | use App\Models\Team; | ||||||
|  | use App\Models\Subscription; | ||||||
| use Illuminate\Support\Facades\Http; | use Illuminate\Support\Facades\Http; | ||||||
| use Illuminate\Support\Facades\Route; | use Illuminate\Support\Facades\Route; | ||||||
| use Illuminate\Support\Str; | use Illuminate\Support\Str; | ||||||
| @ -172,12 +175,13 @@ Route::post('/source/github/events', function () { | |||||||
|     } |     } | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| Route::post('/subscriptions/events', function () { | if (isCloud()) { | ||||||
|  |     Route::post('/subscriptions/events', function () { | ||||||
|         try { |         try { | ||||||
|             $secret    = config('coolify.lemon_squeezy_webhook_secret'); |             $secret    = config('coolify.lemon_squeezy_webhook_secret'); | ||||||
|             $payload   = request()->collect(); |             $payload   = request()->collect(); | ||||||
|             $hash      = hash_hmac('sha256', $payload, $secret); |             $hash      = hash_hmac('sha256', $payload, $secret); | ||||||
|         $signature = ''; |             $signature = request()->header('X-Signature'); | ||||||
| 
 | 
 | ||||||
|             if (!hash_equals($hash, $signature)) { |             if (!hash_equals($hash, $signature)) { | ||||||
|                 return response('Invalid signature.', 400); |                 return response('Invalid signature.', 400); | ||||||
| @ -187,21 +191,67 @@ Route::post('/subscriptions/events', function () { | |||||||
|                 'type' => 'lemonsqueezy', |                 'type' => 'lemonsqueezy', | ||||||
|                 'payload' => $payload |                 'payload' => $payload | ||||||
|             ]); |             ]); | ||||||
| 
 |  | ||||||
|             $event = data_get($payload, 'meta.event_name'); |             $event = data_get($payload, 'meta.event_name'); | ||||||
|             $email = data_get($payload, 'data.attributes.user_email'); |             $email = data_get($payload, 'data.attributes.user_email'); | ||||||
|  |             $team_id = data_get($payload, 'meta.custom_data.team_id'); | ||||||
|  |             $subscription_id = data_get($payload, 'data.id'); | ||||||
|  |             $order_id = data_get($payload, 'data.attributes.order_id'); | ||||||
|  |             $product_id = data_get($payload, 'data.attributes.product_id'); | ||||||
|  |             $variant_id = data_get($payload, 'data.attributes.variant_id'); | ||||||
|  |             $variant_name = data_get($payload, 'data.attributes.variant_name'); | ||||||
|  |             $customer_id = data_get($payload, 'data.attributes.customer_id'); | ||||||
|  |             $status = data_get($payload, 'data.attributes.status'); | ||||||
|  |             $trial_ends_at = data_get($payload, 'data.attributes.trial_ends_at'); | ||||||
|  |             $renews_at = data_get($payload, 'data.attributes.renews_at'); | ||||||
|  |             $ends_at = data_get($payload, 'data.attributes.ends_at'); | ||||||
|             $update_payment_method = data_get($payload, 'data.attributes.urls.update_payment_method'); |             $update_payment_method = data_get($payload, 'data.attributes.urls.update_payment_method'); | ||||||
|  |             $team = Team::find($team_id); | ||||||
|  |             $found = $team->members->where('email', $email)->first(); | ||||||
|  |             if (!$found->isAdmin()) { | ||||||
|  |                 throw new \Exception("User {$email} is not an admin or owner of team {$team->id}."); | ||||||
|  |             } | ||||||
|             switch ($event) { |             switch ($event) { | ||||||
|                 case 'subscription_created': |                 case 'subscription_created': | ||||||
| 
 |                 case 'subscription_updated': | ||||||
|  |                 case 'subscription_resumed': | ||||||
|  |                 case 'subscription_unpaused': | ||||||
|  |                     $subscription = Subscription::updateOrCreate([ | ||||||
|  |                         'team_id' => $team_id, | ||||||
|  |                     ], [ | ||||||
|  |                         'lemon_subscription_id'=> $subscription_id, | ||||||
|  |                         'lemon_customer_id' => $customer_id, | ||||||
|  |                         'lemon_order_id' => $order_id, | ||||||
|  |                         'lemon_product_id' => $product_id, | ||||||
|  |                         'lemon_variant_id' => $variant_id, | ||||||
|  |                         'lemon_status' => $status, | ||||||
|  |                         'lemon_variant_name' => $variant_name, | ||||||
|  |                         'lemon_trial_ends_at' => $trial_ends_at, | ||||||
|  |                         'lemon_renews_at' => $renews_at, | ||||||
|  |                         'lemon_ends_at' => $ends_at, | ||||||
|  |                         'lemon_update_payment_menthod_url' => $update_payment_method, | ||||||
|  |                     ]); | ||||||
|  |                     break; | ||||||
|  |                 case 'subscription_cancelled': | ||||||
|  |                 case 'subscription_paused': | ||||||
|  |                 case 'subscription_expired': | ||||||
|  |                     $subscription = Subscription::where('team_id', $team_id)->where('lemon_order_id', $order_id)->first(); | ||||||
|  |                     if ($subscription) { | ||||||
|  |                         $subscription->update([ | ||||||
|  |                             'lemon_status' => $status, | ||||||
|  |                             'lemon_trial_ends_at' => $trial_ends_at, | ||||||
|  |                             'lemon_renews_at' => $renews_at, | ||||||
|  |                             'lemon_ends_at' => $ends_at, | ||||||
|  |                             'lemon_update_payment_menthod_url' => $update_payment_method, | ||||||
|  |                         ]); | ||||||
|  |                     } | ||||||
|                     break; |                     break; | ||||||
|             } |             } | ||||||
| 
 |             ray('Subscription event: ' . $event); | ||||||
|         ray($payload); |  | ||||||
|             $webhook->update([ |             $webhook->update([ | ||||||
|                 'status' => 'success', |                 'status' => 'success', | ||||||
|             ]); |             ]); | ||||||
|         } catch (\Exception $e) { |         } catch (\Exception $e) { | ||||||
|  |             ray($e->getMessage()); | ||||||
|             $webhook->update([ |             $webhook->update([ | ||||||
|                 'status' => 'failed', |                 'status' => 'failed', | ||||||
|                 'failure_reason' => $e->getMessage() |                 'failure_reason' => $e->getMessage() | ||||||
| @ -209,4 +259,5 @@ Route::post('/subscriptions/events', function () { | |||||||
|         } finally { |         } finally { | ||||||
|             return response('OK'); |             return response('OK'); | ||||||
|         } |         } | ||||||
| }); |     }); | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user