feat: implement comprehensive deployment script for Laravel with progress indicators and rollback functionality, enhancing deployment reliability and user experience
This commit is contained in:
@@ -13,6 +13,9 @@
|
|||||||
namespace Paymenter\Extensions\Others\CreditsPlus;
|
namespace Paymenter\Extensions\Others\CreditsPlus;
|
||||||
|
|
||||||
use App\Classes\Extension\Extension;
|
use App\Classes\Extension\Extension;
|
||||||
|
use Illuminate\Support\Facades\View;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class CreditsPlus extends Extension
|
class CreditsPlus extends Extension
|
||||||
{
|
{
|
||||||
@@ -28,7 +31,7 @@ class CreditsPlus extends Extension
|
|||||||
|
|
||||||
public function getVersion(): string
|
public function getVersion(): string
|
||||||
{
|
{
|
||||||
return '2.0.0';
|
return '2.1.0';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAuthor(): string
|
public function getAuthor(): string
|
||||||
@@ -52,50 +55,79 @@ class CreditsPlus extends Extension
|
|||||||
private function registerCreditsNavigation(): void
|
private function registerCreditsNavigation(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (function_exists('app') && app()->bound('view')) {
|
// Register the view namespace
|
||||||
// Register the view namespace for custom components
|
View::addNamespace('creditsplus', __DIR__ . '/resources/views');
|
||||||
app('view')->addNamespace('creditsplus', __DIR__ . '/resources/views');
|
|
||||||
|
// Store reference to this instance for use in closure
|
||||||
app('view')->composer('components.navigation.index', function ($view) {
|
$extension = $this;
|
||||||
try {
|
$extensionPath = __DIR__;
|
||||||
if (!function_exists('auth') || !auth()->check() || !auth()->user()) {
|
|
||||||
return;
|
// Use view composer to add credits data to navigation
|
||||||
}
|
View::composer('components.navigation.index', function ($view) use ($extension, $extensionPath) {
|
||||||
|
try {
|
||||||
$user = auth()->user();
|
if (!Auth::check()) {
|
||||||
$creditsDisplay = $this->calculateUserCreditsDisplay($user);
|
return;
|
||||||
$creditsUrl = function_exists('route') ? route('account.credits') : '/account/credits';
|
|
||||||
|
|
||||||
$customPath = __DIR__ . '/resources/views/components/navigation/index.blade.php';
|
|
||||||
if (file_exists($customPath)) {
|
|
||||||
$view->setPath($customPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
$view->with([
|
|
||||||
'userCreditsDisplay' => $creditsDisplay,
|
|
||||||
'creditsUrl' => $creditsUrl,
|
|
||||||
]);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
// Only show credits if the feature is enabled
|
||||||
|
if (!config('settings.credits_enabled', false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = Auth::user();
|
||||||
|
$creditsDisplay = $extension->calculateUserCreditsDisplay($user);
|
||||||
|
|
||||||
|
// Determine logo path using storage_url helper if available
|
||||||
|
$logoHtml = '';
|
||||||
|
if (config('settings.logo')) {
|
||||||
|
$logoUrl = function_exists('storage_url')
|
||||||
|
? storage_url(config('settings.logo'))
|
||||||
|
: Storage::url(config('settings.logo'));
|
||||||
|
$logoHtml = '<img src="' . e($logoUrl) . '" alt="' . e(config('app.name')) . '" class="h-10 w-auto inline-block mr-2 rtl:ml-2">';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject credits data, logo HTML, and credits URL into the view
|
||||||
|
$creditsUrlPath = '#';
|
||||||
|
try {
|
||||||
|
if (function_exists('route')) {
|
||||||
|
$creditsUrlPath = route('account.credits');
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$creditsUrlPath = '/account/credits';
|
||||||
|
}
|
||||||
|
|
||||||
|
$view->with([
|
||||||
|
'userCreditsDisplay' => $creditsDisplay,
|
||||||
|
'creditsLogoHtml' => $logoHtml,
|
||||||
|
'creditsUrl' => $creditsUrlPath,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Replace the view path with our custom navigation
|
||||||
|
$customPath = $extensionPath . '/resources/views/components/navigation/index.blade.php';
|
||||||
|
if (file_exists($customPath)) {
|
||||||
|
$view->setPath($customPath);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Silently fail
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
// Silently fail
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function calculateUserCreditsDisplay($user): string
|
public function calculateUserCreditsDisplay($user): string
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
// Check if the credits relationship exists
|
||||||
|
if (!method_exists($user, 'credits')) {
|
||||||
|
return $this->getDefaultCurrencyDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
$credits = $user->credits()->with('currency')->get();
|
$credits = $user->credits()->with('currency')->get();
|
||||||
|
|
||||||
if ($credits->isEmpty()) {
|
if ($credits->isEmpty()) {
|
||||||
$currencyCode = session('currency', config('settings.default_currency', 'USD'));
|
return $this->getDefaultCurrencyDisplay();
|
||||||
$currency = \App\Models\Currency::where('code', $currencyCode)->first();
|
|
||||||
if ($currency) {
|
|
||||||
return $currency->prefix . '0.00' . $currency->suffix;
|
|
||||||
} else {
|
|
||||||
return '0.00 ' . $currencyCode;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$creditsByCurrency = $credits->groupBy('currency_code');
|
$creditsByCurrency = $credits->groupBy('currency_code');
|
||||||
@@ -111,33 +143,42 @@ class CreditsPlus extends Extension
|
|||||||
if ($maxAmount === null || $totalAmount > $maxAmount) {
|
if ($maxAmount === null || $totalAmount > $maxAmount) {
|
||||||
$maxAmount = $totalAmount;
|
$maxAmount = $totalAmount;
|
||||||
$maxCurrencyCode = $currencyCode;
|
$maxCurrencyCode = $currencyCode;
|
||||||
$maxCurrency = $userCredits->first()->currency;
|
$maxCurrency = $userCredits->first()->currency ?? null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($maxAmount === null) {
|
if ($maxAmount === null) {
|
||||||
$currencyCode = session('currency', config('settings.default_currency', 'USD'));
|
return $this->getDefaultCurrencyDisplay();
|
||||||
$currency = \App\Models\Currency::where('code', $currencyCode)->first();
|
|
||||||
if ($currency) {
|
|
||||||
return $currency->prefix . '0.00' . $currency->suffix;
|
|
||||||
} else {
|
|
||||||
return '0.00 ' . $currencyCode;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($maxCurrency) {
|
if ($maxCurrency && isset($maxCurrency->prefix) && isset($maxCurrency->suffix)) {
|
||||||
return $maxCurrency->prefix . number_format($maxAmount, 2) . $maxCurrency->suffix;
|
return $maxCurrency->prefix . number_format($maxAmount, 2) . $maxCurrency->suffix;
|
||||||
} else {
|
} else {
|
||||||
return number_format($maxAmount, 2) . ' ' . $maxCurrencyCode;
|
return number_format($maxAmount, 2) . ' ' . ($maxCurrencyCode ?? '');
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
return $this->getDefaultCurrencyDisplay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get default currency display (0.00 with currency symbol)
|
||||||
|
*/
|
||||||
|
private function getDefaultCurrencyDisplay(): string
|
||||||
|
{
|
||||||
|
try {
|
||||||
$currencyCode = session('currency', config('settings.default_currency', 'USD'));
|
$currencyCode = session('currency', config('settings.default_currency', 'USD'));
|
||||||
$currency = class_exists('\App\Models\Currency') ? \App\Models\Currency::where('code', $currencyCode)->first() : null;
|
|
||||||
if ($currency) {
|
if (class_exists('\App\Models\Currency')) {
|
||||||
return $currency->prefix . '0.00' . $currency->suffix;
|
$currency = \App\Models\Currency::where('code', $currencyCode)->first();
|
||||||
} else {
|
if ($currency && isset($currency->prefix) && isset($currency->suffix)) {
|
||||||
return '0.00 ' . $currencyCode;
|
return $currency->prefix . '0.00' . $currency->suffix;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return '0.00 ' . $currencyCode;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return '0.00 USD';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,9 +43,7 @@ Unauthorized copying, modification, or distribution is prohibited.
|
|||||||
<!-- Logo and app name -->
|
<!-- Logo and app name -->
|
||||||
<div class="flex flex-row items-center">
|
<div class="flex flex-row items-center">
|
||||||
<a href="{{ route('home') }}" class="flex flex-row items-center h-10 gap-2" wire:navigate>
|
<a href="{{ route('home') }}" class="flex flex-row items-center h-10 gap-2" wire:navigate>
|
||||||
@if (config('settings.logo'))
|
{!! $creditsLogoHtml ?? '' !!}
|
||||||
<img src="{{ \Storage::url(config('settings.logo')) }}" alt="{{ config('app.name') }}" class="h-10 w-auto inline-block mr-2 rtl:ml-2">
|
|
||||||
@endif
|
|
||||||
<span class="text-xl font-bold leading-none flex items-center text-flow-effect">
|
<span class="text-xl font-bold leading-none flex items-center text-flow-effect">
|
||||||
{{ config('app.name') }}
|
{{ config('app.name') }}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -11,16 +11,16 @@ Unauthorized copying, modification, or distribution is prohibited.
|
|||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
@foreach (\App\Classes\Navigation::getLinks() as $nav)
|
@foreach (\App\Classes\Navigation::getLinks() as $nav)
|
||||||
@if (!empty($nav['children']))
|
@if (!empty($nav['children']))
|
||||||
<div x-data="{ activeAccordion: {{ $nav['active'] ? 'true' : 'false' }} }"
|
<div x-data="{ activeAccordion: {{ ($nav['active'] ?? false) ? 'true' : 'false' }} }"
|
||||||
class="relative w-full overflow-hidden text-sm font-medium">
|
class="relative w-full overflow-hidden text-sm font-medium">
|
||||||
<div class="cursor-pointer">
|
<div class="cursor-pointer">
|
||||||
<button @click="activeAccordion = !activeAccordion"
|
<button @click="activeAccordion = !activeAccordion"
|
||||||
class="flex items-center justify-between w-full p-3 text-sm font-semibold whitespace-nowrap rounded-lg
|
class="flex items-center justify-between w-full p-3 text-sm font-semibold whitespace-nowrap rounded-lg
|
||||||
hover:bg-primary/10 transition-colors duration-200 {{ $nav['active'] ? 'text-primary bg-primary/5' : 'text-color-base' }}">
|
hover:bg-primary/10 transition-colors duration-200 {{ ($nav['active'] ?? false) ? 'text-primary bg-primary/5' : 'text-color-base' }}">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
@isset($nav['icon'])
|
@isset($nav['icon'])
|
||||||
<x-dynamic-component :component="$nav['icon']"
|
<x-dynamic-component :component="$nav['icon']"
|
||||||
class="size-5 {{ $nav['active'] ? 'text-primary' : 'text-color-muted' }}" />
|
class="size-5 {{ ($nav['active'] ?? false) ? 'text-primary' : 'text-color-muted' }}" />
|
||||||
@endisset
|
@endisset
|
||||||
<span>{{ $nav['name'] }}</span>
|
<span>{{ $nav['name'] }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -34,7 +34,7 @@ Unauthorized copying, modification, or distribution is prohibited.
|
|||||||
<x-navigation.link :href="route($child['route'], $child['params'] ?? [])"
|
<x-navigation.link :href="route($child['route'], $child['params'] ?? [])"
|
||||||
:spa="$child['spa'] ?? true"
|
:spa="$child['spa'] ?? true"
|
||||||
class="block w-full p-2 rounded-md transition-colors duration-200
|
class="block w-full p-2 rounded-md transition-colors duration-200
|
||||||
{{ $child['active'] ? 'text-primary font-bold bg-primary/10' : 'text-color-muted hover:bg-primary/5' }}">
|
{{ ($child['active'] ?? false) ? 'text-primary font-bold bg-primary/10' : 'text-color-muted hover:bg-primary/5' }}">
|
||||||
{{ $child['name'] }}
|
{{ $child['name'] }}
|
||||||
</x-navigation.link>
|
</x-navigation.link>
|
||||||
</div>
|
</div>
|
||||||
@@ -47,10 +47,10 @@ Unauthorized copying, modification, or distribution is prohibited.
|
|||||||
<x-navigation.link :href="route($nav['route'], $nav['params'] ?? [])"
|
<x-navigation.link :href="route($nav['route'], $nav['params'] ?? [])"
|
||||||
:spa="$nav['spa'] ?? true"
|
:spa="$nav['spa'] ?? true"
|
||||||
class="flex items-center gap-3 p-3 rounded-lg transition-colors duration-200 w-full
|
class="flex items-center gap-3 p-3 rounded-lg transition-colors duration-200 w-full
|
||||||
{{ $nav['active'] ? 'bg-primary/10 text-primary font-semibold' : 'hover:bg-primary/5 text-color-base' }}">
|
{{ ($nav['active'] ?? false) ? 'bg-primary/10 text-primary font-semibold' : 'hover:bg-primary/5 text-color-base' }}">
|
||||||
@isset($nav['icon'])
|
@isset($nav['icon'])
|
||||||
<x-dynamic-component :component="$nav['icon']"
|
<x-dynamic-component :component="$nav['icon']"
|
||||||
class="size-5 {{ $nav['active'] ? 'text-primary' : 'text-color-muted' }}" />
|
class="size-5 {{ ($nav['active'] ?? false) ? 'text-primary' : 'text-color-muted' }}" />
|
||||||
@endisset
|
@endisset
|
||||||
<span>{{ $nav['name'] }}</span>
|
<span>{{ $nav['name'] }}</span>
|
||||||
</x-navigation.link>
|
</x-navigation.link>
|
||||||
@@ -70,16 +70,16 @@ Unauthorized copying, modification, or distribution is prohibited.
|
|||||||
<div class="flex flex-col gap-2 flex-grow">
|
<div class="flex flex-col gap-2 flex-grow">
|
||||||
@foreach (\App\Classes\Navigation::getDashboardLinks() as $nav)
|
@foreach (\App\Classes\Navigation::getDashboardLinks() as $nav)
|
||||||
@if (!empty($nav['children']))
|
@if (!empty($nav['children']))
|
||||||
<div x-data="{ activeAccordion: {{ $nav['active'] ? 'true' : 'false' }} }"
|
<div x-data="{ activeAccordion: {{ ($nav['active'] ?? false) ? 'true' : 'false' }} }"
|
||||||
class="relative w-full overflow-hidden text-sm font-medium">
|
class="relative w-full overflow-hidden text-sm font-medium">
|
||||||
<div class="cursor-pointer">
|
<div class="cursor-pointer">
|
||||||
<button @click="activeAccordion = !activeAccordion"
|
<button @click="activeAccordion = !activeAccordion"
|
||||||
class="flex items-center justify-between w-full p-3 text-sm font-semibold whitespace-nowrap rounded-lg
|
class="flex items-center justify-between w-full p-3 text-sm font-semibold whitespace-nowrap rounded-lg
|
||||||
hover:bg-primary/10 transition-colors duration-200 {{ $nav['active'] ? 'text-primary bg-primary/5' : 'text-color-base' }}">
|
hover:bg-primary/10 transition-colors duration-200 {{ ($nav['active'] ?? false) ? 'text-primary bg-primary/5' : 'text-color-base' }}">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
@isset($nav['icon'])
|
@isset($nav['icon'])
|
||||||
<x-dynamic-component :component="$nav['icon']"
|
<x-dynamic-component :component="$nav['icon']"
|
||||||
class="size-5 {{ $nav['active'] ? 'text-primary' : 'text-color-muted' }}" />
|
class="size-5 {{ ($nav['active'] ?? false) ? 'text-primary' : 'text-color-muted' }}" />
|
||||||
@endisset
|
@endisset
|
||||||
<span>{{ $nav['name'] }}</span>
|
<span>{{ $nav['name'] }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -94,7 +94,7 @@ Unauthorized copying, modification, or distribution is prohibited.
|
|||||||
<x-navigation.link :href="route($child['route'], $child['params'] ?? [])"
|
<x-navigation.link :href="route($child['route'], $child['params'] ?? [])"
|
||||||
:spa="$child['spa'] ?? true"
|
:spa="$child['spa'] ?? true"
|
||||||
class="block w-full p-2 rounded-md transition-colors duration-200
|
class="block w-full p-2 rounded-md transition-colors duration-200
|
||||||
{{ $child['active'] ? 'text-primary font-bold bg-primary/10' : 'text-color-muted hover:bg-primary/5' }}">
|
{{ ($child['active'] ?? false) ? 'text-primary font-bold bg-primary/10' : 'text-color-muted hover:bg-primary/5' }}">
|
||||||
{{ $child['name'] }}
|
{{ $child['name'] }}
|
||||||
</x-navigation.link>
|
</x-navigation.link>
|
||||||
</div>
|
</div>
|
||||||
@@ -108,10 +108,10 @@ Unauthorized copying, modification, or distribution is prohibited.
|
|||||||
<x-navigation.link :href="route($nav['route'], $nav['params'] ?? [])"
|
<x-navigation.link :href="route($nav['route'], $nav['params'] ?? [])"
|
||||||
:spa="$nav['spa'] ?? true"
|
:spa="$nav['spa'] ?? true"
|
||||||
class="flex items-center gap-3 p-3 rounded-lg transition-colors duration-200 w-full
|
class="flex items-center gap-3 p-3 rounded-lg transition-colors duration-200 w-full
|
||||||
{{ $nav['active'] ? 'bg-primary/10 text-primary font-semibold' : 'hover:bg-primary/5 text-color-base' }}">
|
{{ ($nav['active'] ?? false) ? 'bg-primary/10 text-primary font-semibold' : 'hover:bg-primary/5 text-color-base' }}">
|
||||||
@isset($nav['icon'])
|
@isset($nav['icon'])
|
||||||
<x-dynamic-component :component="$nav['icon']"
|
<x-dynamic-component :component="$nav['icon']"
|
||||||
class="size-5 {{ $nav['active'] ? 'text-primary' : 'text-color-muted' }}" />
|
class="size-5 {{ ($nav['active'] ?? false) ? 'text-primary' : 'text-color-muted' }}" />
|
||||||
@endisset
|
@endisset
|
||||||
<span>{{ $nav['name'] }}</span>
|
<span>{{ $nav['name'] }}</span>
|
||||||
</x-navigation.link>
|
</x-navigation.link>
|
||||||
@@ -152,57 +152,7 @@ Unauthorized copying, modification, or distribution is prohibited.
|
|||||||
@if(auth()->check())
|
@if(auth()->check())
|
||||||
<div class="border-t border-neutral/50 pt-4 mt-auto">
|
<div class="border-t border-neutral/50 pt-4 mt-auto">
|
||||||
{{-- Credits Display for Mobile --}}
|
{{-- Credits Display for Mobile --}}
|
||||||
@php
|
@if(isset($userCreditsDisplay))
|
||||||
try {
|
|
||||||
$user = auth()->user();
|
|
||||||
$credits = $user->credits()->with('currency')->get();
|
|
||||||
|
|
||||||
if ($credits->isEmpty()) {
|
|
||||||
$currencyCode = session('currency', config('settings.default_currency', 'USD'));
|
|
||||||
$currency = \App\Models\Currency::where('code', $currencyCode)->first();
|
|
||||||
if ($currency) {
|
|
||||||
$creditsDisplay = $currency->prefix . '0.00' . $currency->suffix;
|
|
||||||
} else {
|
|
||||||
$creditsDisplay = '0.00 ' . $currencyCode;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$creditsByCurrency = $credits->groupBy('currency_code');
|
|
||||||
$maxAmount = null;
|
|
||||||
$maxCurrencyCode = null;
|
|
||||||
$maxCurrency = null;
|
|
||||||
|
|
||||||
foreach ($creditsByCurrency as $currencyCode => $userCredits) {
|
|
||||||
$totalAmount = $userCredits->sum('amount');
|
|
||||||
if ($totalAmount <= 0) continue;
|
|
||||||
if ($maxAmount === null || $totalAmount > $maxAmount) {
|
|
||||||
$maxAmount = $totalAmount;
|
|
||||||
$maxCurrencyCode = $currencyCode;
|
|
||||||
$maxCurrency = $userCredits->first()->currency;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($maxAmount === null) {
|
|
||||||
$currencyCode = session('currency', config('settings.default_currency', 'USD'));
|
|
||||||
$currency = \App\Models\Currency::where('code', $currencyCode)->first();
|
|
||||||
if ($currency) {
|
|
||||||
$creditsDisplay = $currency->prefix . '0.00' . $currency->suffix;
|
|
||||||
} else {
|
|
||||||
$creditsDisplay = '0.00 ' . $currencyCode;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($maxCurrency) {
|
|
||||||
$creditsDisplay = $maxCurrency->prefix . number_format($maxAmount, 2) . $maxCurrency->suffix;
|
|
||||||
} else {
|
|
||||||
$creditsDisplay = number_format($maxAmount, 2) . ' ' . $maxCurrencyCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$creditsDisplay = '0.00';
|
|
||||||
}
|
|
||||||
@endphp
|
|
||||||
|
|
||||||
@if(isset($creditsDisplay))
|
|
||||||
<a href="{{ route('account.credits') }}" wire:navigate
|
<a href="{{ route('account.credits') }}" wire:navigate
|
||||||
class="flex items-center justify-between p-3 mb-3 bg-gradient-to-r from-primary/10 to-secondary/10 hover:from-primary/20 hover:to-secondary/20 border border-primary/30 rounded-xl transition-all duration-200">
|
class="flex items-center justify-between p-3 mb-3 bg-gradient-to-r from-primary/10 to-secondary/10 hover:from-primary/20 hover:to-secondary/20 border border-primary/30 rounded-xl transition-all duration-200">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
@@ -211,7 +161,7 @@ Unauthorized copying, modification, or distribution is prohibited.
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span class="text-xs text-color-muted">Account Credits</span>
|
<span class="text-xs text-color-muted">Account Credits</span>
|
||||||
<span class="text-sm font-bold text-primary">{{ $creditsDisplay }}</span>
|
<span class="text-sm font-bold text-primary">{{ $userCreditsDisplay }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<x-ri-arrow-right-line class="size-5 text-color-muted" />
|
<x-ri-arrow-right-line class="size-5 text-color-muted" />
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{{--
|
||||||
|
Credits Plus Navigation Component
|
||||||
|
|
||||||
|
Created by: Exp (Discord: exp_yt)
|
||||||
|
Company: ExpHost - https://www.exphost.net/
|
||||||
|
|
||||||
|
This code is the intellectual property of Exp and ExpHost.
|
||||||
|
Unauthorized copying, modification, or distribution is prohibited.
|
||||||
|
--}}
|
||||||
|
<style>
|
||||||
|
.credits-plus-text {
|
||||||
|
background: linear-gradient(75deg,
|
||||||
|
hsl(178, 46.40%, 11.00%) 0%,
|
||||||
|
hsl(211, 57.80%, 17.60%) 30%,
|
||||||
|
hsl(220, 98%, 61%) 45%,
|
||||||
|
hsl(207, 53.00%, 50.80%) 55%,
|
||||||
|
hsl(214, 82.50%, 31.40%) 70%,
|
||||||
|
hsl(222, 47%, 11%) 100%);
|
||||||
|
background-size: 400% auto;
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
animation: creditsFlowAnimation 8s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes creditsFlowAnimation {
|
||||||
|
to {
|
||||||
|
background-position: 400% center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<a href="{{ route('account.credits') }}"
|
||||||
|
wire:navigate
|
||||||
|
class="text-sm font-semibold px-3 py-2 whitespace-nowrap hover:opacity-80 transition-opacity duration-200 credits-plus-text">
|
||||||
|
Credits: {{ $creditsDisplay }}
|
||||||
|
</a>
|
||||||
|
|
||||||
118
gitsync.sh
118
gitsync.sh
@@ -1 +1,117 @@
|
|||||||
git pull && systemctl restart php8.3-fpm.service && ./cache.sh
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BAR_WIDTH=50
|
||||||
|
SLEEP_INTERVAL=0.05
|
||||||
|
CLEAR_LINE="\033[K"
|
||||||
|
LOG_FILE="/var/log/laravel_deploy.log"
|
||||||
|
|
||||||
|
GREEN="\033[32m"
|
||||||
|
RESET="\033[0m"
|
||||||
|
|
||||||
|
STEPS=(
|
||||||
|
"Pull latest code"
|
||||||
|
"Maintenance mode"
|
||||||
|
"Restart PHP-FPM"
|
||||||
|
"Clear caches"
|
||||||
|
"Optimize app"
|
||||||
|
"Set permissions"
|
||||||
|
"Ensure queue running"
|
||||||
|
"Reset OPcache"
|
||||||
|
"Bring app online"
|
||||||
|
"Check php-fpm"
|
||||||
|
"Check paymenter"
|
||||||
|
)
|
||||||
|
|
||||||
|
CMDS=(
|
||||||
|
"git pull --rebase --autostash --quiet"
|
||||||
|
"php artisan down --render='errors::503' --retry=60 --quiet || true"
|
||||||
|
"systemctl restart php8.3-fpm.service"
|
||||||
|
"php artisan optimize:clear --quiet"
|
||||||
|
"php artisan optimize --quiet"
|
||||||
|
"find storage bootstrap/cache -not -perm 775 -type d -exec chmod 775 {} + -o -not -perm 664 -type f -exec chmod 664 {} +; chown -R www-data:www-data storage bootstrap/cache"
|
||||||
|
"if ! pgrep -f 'artisan queue:work' >/dev/null; then nohup php artisan queue:work --daemon --quiet >> /dev/null 2>&1 & fi"
|
||||||
|
"php -r 'if(function_exists(\"opcache_reset\")) opcache_reset();' 2>/dev/null || true"
|
||||||
|
"php artisan up --quiet"
|
||||||
|
"systemctl is-active --quiet php8.3-fpm || systemctl restart php8.3-fpm"
|
||||||
|
"systemctl is-active --quiet paymenter.service || systemctl restart paymenter.service"
|
||||||
|
)
|
||||||
|
|
||||||
|
rollback(){
|
||||||
|
echo -e "\n\033[31m✘ Deployment failed. Rolling back...\033[0m"
|
||||||
|
git reset --hard HEAD@{1} >> "$LOG_FILE" 2>&1
|
||||||
|
php artisan up || true
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
trap rollback ERR
|
||||||
|
|
||||||
|
draw_bar() {
|
||||||
|
local percent=$1
|
||||||
|
local width=$2
|
||||||
|
local text="$3"
|
||||||
|
|
||||||
|
# Strip color codes for length calculation
|
||||||
|
local clean_text=$(echo -ne "$text" | sed 's/\x1b\[[0-9;]*m//g')
|
||||||
|
|
||||||
|
[ ${#clean_text} -gt $width ] && clean_text="${clean_text:0:$width}"
|
||||||
|
|
||||||
|
local filled=$(( percent * width / 100 ))
|
||||||
|
[ $filled -gt $width ] && filled=$width
|
||||||
|
|
||||||
|
local text_start=$(( (width - ${#clean_text}) / 2 ))
|
||||||
|
local text_end=$(( text_start + ${#clean_text} ))
|
||||||
|
|
||||||
|
local bar=""
|
||||||
|
for ((i=0;i<width;i++)); do
|
||||||
|
if (( i >= text_start && i < text_end )); then
|
||||||
|
bar+="${clean_text:i-text_start:1}"
|
||||||
|
else
|
||||||
|
if (( i < filled )); then
|
||||||
|
bar+="#"
|
||||||
|
else
|
||||||
|
bar+="-"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
printf "[%s] %3d%%" "$bar" "$percent"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_deployment() {
|
||||||
|
local total_steps=${#STEPS[@]}
|
||||||
|
|
||||||
|
for i in "${!STEPS[@]}"; do
|
||||||
|
step_name="${STEPS[$i]}"
|
||||||
|
cmd="${CMDS[$i]}"
|
||||||
|
step_percent_start=$(( i * 100 / total_steps ))
|
||||||
|
step_percent_end=$(( (i+1) * 100 / total_steps ))
|
||||||
|
|
||||||
|
bash -c "$cmd" > >(tee -a "$LOG_FILE") 2> >(tee -a "$LOG_FILE" >&2) &
|
||||||
|
pid=$!
|
||||||
|
|
||||||
|
percent=$step_percent_start
|
||||||
|
while kill -0 "$pid" 2>/dev/null; do
|
||||||
|
[ "$percent" -lt "$step_percent_end" ] && percent=$((percent+1))
|
||||||
|
echo -ne "\r$(draw_bar $percent $BAR_WIDTH "$step_name")$CLEAR_LINE"
|
||||||
|
sleep "$SLEEP_INTERVAL"
|
||||||
|
done
|
||||||
|
wait "$pid"
|
||||||
|
|
||||||
|
# Fill bar to end of step
|
||||||
|
echo -ne "\r$(draw_bar $step_percent_end $BAR_WIDTH "$step_name")$CLEAR_LINE"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Final green completion bar
|
||||||
|
final_text="Deployment Complete"
|
||||||
|
# Construct bar normally
|
||||||
|
bar=$(draw_bar 100 $BAR_WIDTH "$final_text")
|
||||||
|
# Replace the visible text in the middle with green text
|
||||||
|
clean_text_len=${#final_text}
|
||||||
|
start=$(( (BAR_WIDTH - clean_text_len) / 2 + 1 )) # +1 for the [
|
||||||
|
end=$(( start + clean_text_len - 1 ))
|
||||||
|
colored_bar="${bar:0:start-1}${GREEN}${final_text}${RESET}${bar:end}"
|
||||||
|
echo -e "\r$colored_bar$CLEAR_LINE"
|
||||||
|
# echo "Log: $LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_deployment
|
||||||
|
|||||||
Reference in New Issue
Block a user