wip
This commit is contained in:
parent
e714e87ad6
commit
cac59e4873
@ -62,13 +62,23 @@ public function emails()
|
|||||||
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 @@ public function routeNotificationForEmail()
|
|||||||
{
|
{
|
||||||
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 @@ public function isInstanceAdmin()
|
|||||||
});
|
});
|
||||||
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 @@
|
|||||||
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 @@ public function up(): void
|
|||||||
{
|
{
|
||||||
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 @@ class="flex items-center gap-1 mb-2 text-sm font-medium text-neutral-400">{{ $la
|
|||||||
@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 @@ class="{{ request()->is('project/*') || request()->is('projects') ? 'text-warnin
|
|||||||
<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 @@ class="{{ request()->is('server/*') || request()->is('servers') ? 'text-warning
|
|||||||
<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 @@ class="{{ request()->is('command-center') ? 'text-warning icon' : 'icon' }}" vie
|
|||||||
<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 @@ class="{{ request()->is('settings*') ? 'text-warning icon' : 'icon' }}" viewBox=
|
|||||||
</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 @@ class="{{ request()->is('settings*') ? 'text-warning icon' : 'icon' }}" viewBox=
|
|||||||
</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::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\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('/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 @@
|
|||||||
'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 @@
|
|||||||
} finally {
|
} finally {
|
||||||
return response('OK');
|
return response('OK');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user