Merge pull request #1718 from coollabsio/next

v4.0.0-beta.210
This commit is contained in:
Andras Bacsai 2024-02-06 11:41:17 +01:00 committed by GitHub
commit dadc7aaf08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 73 additions and 39 deletions

View File

@ -2,8 +2,10 @@
namespace App\Livewire; namespace App\Livewire;
use App\Models\InstanceSettings;
use DanHarrin\LivewireRateLimiting\WithRateLimiting; use DanHarrin\LivewireRateLimiting\WithRateLimiting;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Livewire\Component; use Livewire\Component;
@ -28,9 +30,8 @@ class Help extends Component
public function submit() public function submit()
{ {
try { try {
$this->rateLimit(3, 60); $this->rateLimit(3, 30);
$this->validate(); $this->validate();
$subscriptionType = auth()->user()?->subscription?->type() ?? 'Free';
$debug = "Route: {$this->path}"; $debug = "Route: {$this->path}";
$mail = new MailMessage(); $mail = new MailMessage();
$mail->view( $mail->view(
@ -40,9 +41,21 @@ class Help extends Component
'debug' => $debug 'debug' => $debug
] ]
); );
$mail->subject("[HELP - {$subscriptionType}]: {$this->subject}"); $mail->subject("[HELP]: {$this->subject}");
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io'); $settings = InstanceSettings::get();
$this->dispatch('success', 'Your message has been sent successfully. <br>We will get in touch with you as soon as possible.'); $type = set_transanctional_email_settings($settings);
if (!$type) {
$url = "https://app.coolify.io/api/feedback";
if (isDev()) {
$url = "http://localhost:80/api/feedback";
}
Http::post($url, [
'content' => "User: `" . auth()->user()?->email . "` with subject: `" . $this->subject . "` has the following problem: `" . $this->description . "`"
]);
} else {
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
}
$this->dispatch('success', 'Feedback sent.', 'We will get in touch with you as soon as possible.');
} catch (\Throwable $e) { } catch (\Throwable $e) {
return handleError($e, $this); return handleError($e, $this);
} }

View File

@ -104,7 +104,7 @@ function handleError(?Throwable $error = null, ?Livewire\Component $livewire = n
ray($error); ray($error);
if ($error instanceof TooManyRequestsException) { if ($error instanceof TooManyRequestsException) {
if (isset($livewire)) { if (isset($livewire)) {
return $livewire->dispatch('error', "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds."); return $livewire->dispatch('error', "Too many requests.","Please try again in {$error->secondsUntilAvailable} seconds.");
} }
return "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds."; return "Too many requests. Please try again in {$error->secondsUntilAvailable} seconds.";
} }
@ -298,10 +298,8 @@ function validate_cron_expression($expression_to_validate): bool
function send_internal_notification(string $message): void function send_internal_notification(string $message): void
{ {
try { try {
$baseUrl = config('app.name');
$team = Team::find(0); $team = Team::find(0);
$team?->notify(new GeneralNotification("👀 {$baseUrl}: " . $message)); $team?->notify(new GeneralNotification($message));
ray("👀 {$baseUrl}: " . $message);
} catch (\Throwable $e) { } catch (\Throwable $e) {
ray($e->getMessage()); ray($e->getMessage());
} }

View File

@ -3,6 +3,7 @@
return [ return [
'docs' => 'https://coolify.io/docs/', 'docs' => 'https://coolify.io/docs/',
'contact' => 'https://coolify.io/docs/contact', 'contact' => 'https://coolify.io/docs/contact',
'feedback_discord_webhook' => env('FEEDBACK_DISCORD_WEBHOOK'),
'self_hosted' => env('SELF_HOSTED', true), 'self_hosted' => env('SELF_HOSTED', true),
'waitlist' => env('WAITLIST', false), 'waitlist' => env('WAITLIST', false),
'license_url' => 'https://licenses.coollabs.io', 'license_url' => 'https://licenses.coollabs.io',

View File

@ -7,7 +7,7 @@ return [
// The release version of your application // The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.209', 'release' => '4.0.0-beta.210',
// When left empty or `null` the Laravel environment will be used // When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'), 'environment' => config('app.env'),

View File

@ -1,3 +1,3 @@
<?php <?php
return '4.0.0-beta.209'; return '4.0.0-beta.210';

View File

@ -46,6 +46,7 @@ services:
- PUSHER_APP_SECRET - PUSHER_APP_SECRET
- AUTOUPDATE - AUTOUPDATE
- SELF_HOSTED - SELF_HOSTED
- FEEDBACK_DISCORD_WEBHOOK
- WAITLIST - WAITLIST
- SUBSCRIPTION_PROVIDER - SUBSCRIPTION_PROVIDER
- STRIPE_API_KEY - STRIPE_API_KEY

View File

@ -26,11 +26,10 @@
</li> </li>
<li title="Send us feedback or get help!" class="fixed top-0 right-0 p-2 px-4 pt-4 mt-auto text-xs"> <li title="Send us feedback or get help!" class="fixed top-0 right-0 p-2 px-4 pt-4 mt-auto text-xs">
<div class="justify-center" wire:click="help" onclick="help.showModal()"> <div class="justify-center" wire:click="help" onclick="help.showModal()">
<svg class="w-5 h-5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg class="icon" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" <path fill="currentColor"
d="M22 5.5H9c-1.1 0-2 .9-2 2v9a2 2 0 0 0 2 2h13c1.11 0 2-.89 2-2v-9a2 2 0 0 0-2-2m0 11H9V9.17l6.5 3.33L22 9.17v7.33m-6.5-5.69L9 7.5h13l-6.5 3.31M5 16.5c0 .17.03.33.05.5H1c-.552 0-1-.45-1-1s.448-1 1-1h4v1.5M3 7h2.05c-.02.17-.05.33-.05.5V9H3c-.55 0-1-.45-1-1s.45-1 1-1m-2 5c0-.55.45-1 1-1h3v2H2c-.55 0-1-.45-1-1Z" /> d="M144 180a16 16 0 1 1-16-16a16 16 0 0 1 16 16m92-52A108 108 0 1 1 128 20a108.12 108.12 0 0 1 108 108m-24 0a84 84 0 1 0-84 84a84.09 84.09 0 0 0 84-84m-84-64c-24.26 0-44 17.94-44 40v4a12 12 0 0 0 24 0v-4c0-8.82 9-16 20-16s20 7.18 20 16s-9 16-20 16a12 12 0 0 0-12 12v8a12 12 0 0 0 23.73 2.56C158.31 137.88 172 122.37 172 104c0-22.06-19.74-40-44-40" />
</svg> </svg>
Feedback
</div> </div>
</li> </li>
<li class="pb-6" title="Logout"> <li class="pb-6" title="Logout">

View File

@ -142,13 +142,12 @@
</li> </li>
@endif @endif
@if (isSubscriptionActive() || isDev()) @if (isSubscriptionActive() || isDev())
<li title="Send us feedback or get help!" class="fixed top-0 right-0 p-2 px-4 pt-4 mt-auto text-xs"> <li title="Send us feedback or get help!" class="hover:bg-transparent">
<div class="justify-center" wire:click="help" onclick="help.showModal()"> <div class="justify-center" wire:click="help" onclick="help.showModal()">
<svg class="w-5 h-5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg class="icon" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" <path fill="currentColor"
d="M22 5.5H9c-1.1 0-2 .9-2 2v9a2 2 0 0 0 2 2h13c1.11 0 2-.89 2-2v-9a2 2 0 0 0-2-2m0 11H9V9.17l6.5 3.33L22 9.17v7.33m-6.5-5.69L9 7.5h13l-6.5 3.31M5 16.5c0 .17.03.33.05.5H1c-.552 0-1-.45-1-1s.448-1 1-1h4v1.5M3 7h2.05c-.02.17-.05.33-.05.5V9H3c-.55 0-1-.45-1-1s.45-1 1-1m-2 5c0-.55.45-1 1-1h3v2H2c-.55 0-1-.45-1-1Z" /> d="M144 180a16 16 0 1 1-16-16a16 16 0 0 1 16 16m92-52A108 108 0 1 1 128 20a108.12 108.12 0 0 1 108 108m-24 0a84 84 0 1 0-84 84a84.09 84.09 0 0 0 84-84m-84-64c-24.26 0-44 17.94-44 40v4a12 12 0 0 0 24 0v-4c0-8.82 9-16 20-16s20 7.18 20 16s-9 16-20 16a12 12 0 0 0-12 12v8a12 12 0 0 0 23.73 2.56C158.31 137.88 172 122.37 172 104c0-22.06-19.74-40-44-40" />
</svg> </svg>
Feedback
</div> </div>
</li> </li>
@endif @endif

View File

@ -3,11 +3,10 @@
@if (isSubscriptionActive() || isDev()) @if (isSubscriptionActive() || isDev())
<div title="Send us feedback or get help!" class="fixed top-0 right-0 p-2 px-4 pt-4 mt-auto text-xs"> <div title="Send us feedback or get help!" class="fixed top-0 right-0 p-2 px-4 pt-4 mt-auto text-xs">
<button class="flex items-center justify-center gap-2" wire:click="help" onclick="help.showModal()"> <button class="flex items-center justify-center gap-2" wire:click="help" onclick="help.showModal()">
<svg class="w-5 h-5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg class="icon" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" <path fill="currentColor"
d="M22 5.5H9c-1.1 0-2 .9-2 2v9a2 2 0 0 0 2 2h13c1.11 0 2-.89 2-2v-9a2 2 0 0 0-2-2m0 11H9V9.17l6.5 3.33L22 9.17v7.33m-6.5-5.69L9 7.5h13l-6.5 3.31M5 16.5c0 .17.03.33.05.5H1c-.552 0-1-.45-1-1s.448-1 1-1h4v1.5M3 7h2.05c-.02.17-.05.33-.05.5V9H3c-.55 0-1-.45-1-1s.45-1 1-1m-2 5c0-.55.45-1 1-1h3v2H2c-.55 0-1-.45-1-1Z" /> d="M144 180a16 16 0 1 1-16-16a16 16 0 0 1 16 16m92-52A108 108 0 1 1 128 20a108.12 108.12 0 0 1 108 108m-24 0a84 84 0 1 0-84 84a84.09 84.09 0 0 0 84-84m-84-64c-24.26 0-44 17.94-44 40v4a12 12 0 0 0 24 0v-4c0-8.82 9-16 20-16s20 7.18 20 16s-9 16-20 16a12 12 0 0 0-12 12v8a12 12 0 0 0 23.73 2.56C158.31 137.88 172 122.37 172 104c0-22.06-19.74-40-44-40" />
</svg> </svg>
Feedback
</button> </button>
</div> </div>
@endif @endif

View File

@ -6,6 +6,6 @@
<x-forms.textarea rows="10" id="description" label="Description" <x-forms.textarea rows="10" id="description" label="Description"
placeholder="Please provide as much information as possible."></x-forms.textarea> placeholder="Please provide as much information as possible."></x-forms.textarea>
<div></div> <div></div>
<x-forms.button class="w-full mt-4" type="submit" onclick="help.close()">Send Email</x-forms.button> <x-forms.button class="w-full mt-4" type="submit" onclick="help.close()">Send</x-forms.button>
</form> </form>
</div> </div>

View File

@ -12,6 +12,7 @@ use App\Models\Tag;
use App\Models\User; use App\Models\User;
use App\Providers\RouteServiceProvider; use App\Providers\RouteServiceProvider;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Visus\Cuid2\Cuid2; use Visus\Cuid2\Cuid2;
@ -34,6 +35,16 @@ if (isDev()) {
Route::get('/health', function () { Route::get('/health', function () {
return 'OK'; return 'OK';
}); });
Route::post('/feedback', function (Request $request) {
$content = $request->input('content');
$webhook_url = config('coolify.feedback_discord_webhook');
if ($webhook_url) {
Http::post($webhook_url, [
'content' => $content
]);
}
return response()->json(['message' => 'Feedback sent.'], 200);
});
// Route::group([ // Route::group([
// 'middleware' => $middlewares, // 'middleware' => $middlewares,
// 'prefix' => 'v1' // 'prefix' => 'v1'
@ -53,6 +64,8 @@ Route::group([
'prefix' => 'v1' 'prefix' => 'v1'
], function () { ], function () {
Route::get('/deploy', [Deploy::class, 'deploy']); Route::get('/deploy', [Deploy::class, 'deploy']);
}); });
Route::middleware(['throttle:5'])->group(function () { Route::middleware(['throttle:5'])->group(function () {

View File

@ -764,7 +764,6 @@ Route::post('/payments/stripe/events', function () {
]); ]);
$type = data_get($event, 'type'); $type = data_get($event, 'type');
$data = data_get($event, 'data.object'); $data = data_get($event, 'data.object');
ray('Event: ' . $type);
switch ($type) { switch ($type) {
case 'checkout.session.completed': case 'checkout.session.completed':
$clientReferenceId = data_get($data, 'client_reference_id'); $clientReferenceId = data_get($data, 'client_reference_id');
@ -779,7 +778,8 @@ Route::post('/payments/stripe/events', function () {
$team = Team::find($teamId); $team = Team::find($teamId);
$found = $team->members->where('id', $userId)->first(); $found = $team->members->where('id', $userId)->first();
if (!$found->isAdmin()) { if (!$found->isAdmin()) {
throw new Exception("User {$userId} is not an admin or owner of team {$team->id}."); send_internal_notification("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}.");
throw new Exception("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}.");
} }
$subscription = Subscription::where('team_id', $teamId)->first(); $subscription = Subscription::where('team_id', $teamId)->first();
if ($subscription) { if ($subscription) {
@ -819,25 +819,35 @@ Route::post('/payments/stripe/events', function () {
break; break;
case 'invoice.payment_failed': case 'invoice.payment_failed':
$customerId = data_get($data, 'customer'); $customerId = data_get($data, 'customer');
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); $subscription = Subscription::where('stripe_customer_id', $customerId)->first();
if (!$subscription) {
send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: ' . $customerId);
return response('No subscription found in Coolify.');
}
$team = data_get($subscription, 'team'); $team = data_get($subscription, 'team');
if (!$team) { if (!$team) {
throw new Exception('No team found for subscription: ' . $subscription->id); send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: ' . $customerId);
return response('No team found in Coolify.');
} }
if (!$subscription->stripe_invoice_paid) { if (!$subscription->stripe_invoice_paid) {
SubscriptionInvoiceFailedJob::dispatch($team); SubscriptionInvoiceFailedJob::dispatch($team);
send_internal_notification('Invoice payment failed: ' . $subscription->team->id); send_internal_notification('Invoice payment failed: ' . $customerId);
} else { } else {
send_internal_notification('Invoice payment failed but already paid: ' . $subscription->team->id); send_internal_notification('Invoice payment failed but already paid: ' . $customerId);
} }
break; break;
case 'payment_intent.payment_failed': case 'payment_intent.payment_failed':
$customerId = data_get($data, 'customer'); $customerId = data_get($data, 'customer');
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); $subscription = Subscription::where('stripe_customer_id', $customerId)->first();
$subscription->update([ if (!$subscription) {
'stripe_invoice_paid' => false, send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: ' . $customerId);
]); return response('No subscription found in Coolify.');
send_internal_notification('Subscription payment failed: ' . $subscription->team->id); }
if ($subscription->stripe_invoice_paid) {
send_internal_notification('payment_intent.payment_failed but invoice is active for customer: ' . $customerId);
return;
}
send_internal_notification('Subscription payment failed for customer: ' . $customerId);
break; break;
case 'customer.subscription.updated': case 'customer.subscription.updated':
$customerId = data_get($data, 'customer'); $customerId = data_get($data, 'customer');
@ -868,7 +878,7 @@ Route::post('/payments/stripe/events', function () {
$subscription->update([ $subscription->update([
'stripe_invoice_paid' => false, 'stripe_invoice_paid' => false,
]); ]);
send_internal_notification('Subscription paused or incomplete for team: ' . $subscription->team->id); send_internal_notification('Subscription paused or incomplete for customer: ' . $customerId);
} }
// Trial ended but subscribed, reactive servers // Trial ended but subscribed, reactive servers
@ -878,7 +888,7 @@ Route::post('/payments/stripe/events', function () {
} }
if ($feedback) { if ($feedback) {
$reason = "Cancellation feedback for {$subscription->team->id}: '" . $feedback . "'"; $reason = "Cancellation feedback for {$customerId}: '" . $feedback . "'";
if ($comment) { if ($comment) {
$reason .= ' with comment: \'' . $comment . "'"; $reason .= ' with comment: \'' . $comment . "'";
} }
@ -888,7 +898,7 @@ Route::post('/payments/stripe/events', function () {
if ($cancelAtPeriodEnd) { if ($cancelAtPeriodEnd) {
// send_internal_notification('Subscription cancelled at period end for team: ' . $subscription->team->id); // send_internal_notification('Subscription cancelled at period end for team: ' . $subscription->team->id);
} else { } else {
send_internal_notification('Subscription resumed for team: ' . $subscription->team->id); send_internal_notification('customer.subscription.updated for customer: ' . $customerId);
} }
} }
break; break;
@ -905,9 +915,10 @@ Route::post('/payments/stripe/events', function () {
'stripe_invoice_paid' => false, 'stripe_invoice_paid' => false,
'stripe_trial_already_ended' => true, 'stripe_trial_already_ended' => true,
]); ]);
// send_internal_notification('Subscription cancelled: ' . $subscription->team->id); send_internal_notification('customer.subscription.deleted for customer: ' . $customerId);
break; break;
case 'customer.subscription.trial_will_end': case 'customer.subscription.trial_will_end':
// Not used for now
$customerId = data_get($data, 'customer'); $customerId = data_get($data, 'customer');
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
$team = data_get($subscription, 'team'); $team = data_get($subscription, 'team');
@ -929,7 +940,7 @@ Route::post('/payments/stripe/events', function () {
'stripe_invoice_paid' => false, 'stripe_invoice_paid' => false,
]); ]);
SubscriptionTrialEndedJob::dispatch($team); SubscriptionTrialEndedJob::dispatch($team);
send_internal_notification('Subscription paused for team: ' . $subscription->team->id); send_internal_notification('Subscription paused for customer: ' . $customerId);
break; break;
default: default:
// Unhandled event type // Unhandled event type

View File

@ -4,7 +4,7 @@
"version": "3.12.36" "version": "3.12.36"
}, },
"v4": { "v4": {
"version": "4.0.0-beta.209" "version": "4.0.0-beta.210"
} }
} }
} }