Merge branch 'next' into plane-app
This commit is contained in:
@@ -208,7 +208,6 @@ class GetContainersStatus
|
|||||||
$foundServices[] = "$service->id-$service->name";
|
$foundServices[] = "$service->id-$service->name";
|
||||||
$statusFromDb = $service->status;
|
$statusFromDb = $service->status;
|
||||||
if ($statusFromDb !== $containerStatus) {
|
if ($statusFromDb !== $containerStatus) {
|
||||||
// ray('Updating status: ' . $containerStatus);
|
|
||||||
$service->update(['status' => $containerStatus]);
|
$service->update(['status' => $containerStatus]);
|
||||||
} else {
|
} else {
|
||||||
$service->update(['last_online_at' => now()]);
|
$service->update(['last_online_at' => now()]);
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class CloudCleanupSubscriptions extends Command
|
|||||||
} else {
|
} else {
|
||||||
$subscription = $stripe->subscriptions->retrieve(data_get($team, 'subscription.stripe_subscription_id'), []);
|
$subscription = $stripe->subscriptions->retrieve(data_get($team, 'subscription.stripe_subscription_id'), []);
|
||||||
$status = data_get($subscription, 'status');
|
$status = data_get($subscription, 'status');
|
||||||
if ($status === 'active' || $status === 'past_due') {
|
if ($status === 'active') {
|
||||||
$team->subscription->update([
|
$team->subscription->update([
|
||||||
'stripe_invoice_paid' => true,
|
'stripe_invoice_paid' => true,
|
||||||
'stripe_trial_already_ended' => false,
|
'stripe_trial_already_ended' => false,
|
||||||
|
|||||||
@@ -1291,11 +1291,6 @@ class ApplicationsController extends Controller
|
|||||||
$dockerCompose = base64_decode($request->docker_compose_raw);
|
$dockerCompose = base64_decode($request->docker_compose_raw);
|
||||||
$dockerComposeRaw = Yaml::dump(Yaml::parse($dockerCompose), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
|
$dockerComposeRaw = Yaml::dump(Yaml::parse($dockerCompose), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
|
||||||
|
|
||||||
// $isValid = validateComposeFile($dockerComposeRaw, $server_id);
|
|
||||||
// if ($isValid !== 'OK') {
|
|
||||||
// return $this->dispatch('error', "Invalid docker-compose file.\n$isValid");
|
|
||||||
// }
|
|
||||||
|
|
||||||
$service = new Service;
|
$service = new Service;
|
||||||
removeUnnecessaryFieldsFromRequest($request);
|
removeUnnecessaryFieldsFromRequest($request);
|
||||||
$service->fill($request->all());
|
$service->fill($request->all());
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class Controller extends BaseController
|
|||||||
'email' => Str::lower($arrayOfRequest['email']),
|
'email' => Str::lower($arrayOfRequest['email']),
|
||||||
]);
|
]);
|
||||||
$type = set_transanctional_email_settings();
|
$type = set_transanctional_email_settings();
|
||||||
if (! $type) {
|
if (blank($type)) {
|
||||||
return response()->json(['message' => 'Transactional emails are not active'], 400);
|
return response()->json(['message' => 'Transactional emails are not active'], 400);
|
||||||
}
|
}
|
||||||
$request->validate([Fortify::email() => 'required|email']);
|
$request->validate([Fortify::email() => 'required|email']);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class Bitbucket extends Controller
|
|||||||
$full_name = data_get($payload, 'repository.full_name');
|
$full_name = data_get($payload, 'repository.full_name');
|
||||||
$commit = data_get($payload, 'push.changes.0.new.target.hash');
|
$commit = data_get($payload, 'push.changes.0.new.target.hash');
|
||||||
|
|
||||||
if (!$branch) {
|
if (! $branch) {
|
||||||
return response([
|
return response([
|
||||||
'status' => 'failed',
|
'status' => 'failed',
|
||||||
'message' => 'Nothing to do. No branch found in the request.',
|
'message' => 'Nothing to do. No branch found in the request.',
|
||||||
|
|||||||
@@ -202,7 +202,6 @@ class Gitea extends Controller
|
|||||||
if ($found) {
|
if ($found) {
|
||||||
$found->delete();
|
$found->delete();
|
||||||
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
// ray('Stopping container: ' . $container_name);
|
|
||||||
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
|
|||||||
@@ -208,7 +208,6 @@ class Github extends Controller
|
|||||||
if ($found) {
|
if ($found) {
|
||||||
$found->delete();
|
$found->delete();
|
||||||
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
// ray('Stopping container: ' . $container_name);
|
|
||||||
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
|
|||||||
@@ -227,7 +227,6 @@ class Gitlab extends Controller
|
|||||||
if ($found) {
|
if ($found) {
|
||||||
$found->delete();
|
$found->delete();
|
||||||
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||||
// ray('Stopping container: ' . $container_name);
|
|
||||||
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||||
$return_payloads->push([
|
$return_payloads->push([
|
||||||
'application' => $application->name,
|
'application' => $application->name,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use App\Notifications\Application\DeploymentFailed;
|
|||||||
use App\Notifications\Application\DeploymentSuccess;
|
use App\Notifications\Application\DeploymentSuccess;
|
||||||
use App\Traits\ExecuteRemoteCommand;
|
use App\Traits\ExecuteRemoteCommand;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Exception;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
@@ -1207,7 +1208,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
if ($this->application->custom_healthcheck_found) {
|
if ($this->application->custom_healthcheck_found) {
|
||||||
$this->application_deployment_queue->addLogEntry('Custom healthcheck found, skipping default healthcheck.');
|
$this->application_deployment_queue->addLogEntry('Custom healthcheck found, skipping default healthcheck.');
|
||||||
}
|
}
|
||||||
// ray('New container name: ', $this->container_name);
|
|
||||||
if ($this->container_name) {
|
if ($this->container_name) {
|
||||||
$counter = 1;
|
$counter = 1;
|
||||||
$this->application_deployment_queue->addLogEntry('Waiting for healthcheck to pass on the new container.');
|
$this->application_deployment_queue->addLogEntry('Waiting for healthcheck to pass on the new container.');
|
||||||
@@ -1410,7 +1410,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// ray('Deploying to additional destination: ', $server->name);
|
|
||||||
$deployment_uuid = new Cuid2;
|
$deployment_uuid = new Cuid2;
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: $deployment_uuid,
|
||||||
@@ -2023,6 +2022,8 @@ LABEL coolify.deploymentId={$this->deployment_uuid}
|
|||||||
COPY . .
|
COPY . .
|
||||||
RUN rm -f /usr/share/nginx/html/nginx.conf
|
RUN rm -f /usr/share/nginx/html/nginx.conf
|
||||||
RUN rm -f /usr/share/nginx/html/Dockerfile
|
RUN rm -f /usr/share/nginx/html/Dockerfile
|
||||||
|
RUN rm -f /usr/share/nginx/html/docker-compose.yaml
|
||||||
|
RUN rm -f /usr/share/nginx/html/.env
|
||||||
COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||||
if (str($this->application->custom_nginx_configuration)->isNotEmpty()) {
|
if (str($this->application->custom_nginx_configuration)->isNotEmpty()) {
|
||||||
$nginx_config = base64_encode($this->application->custom_nginx_configuration);
|
$nginx_config = base64_encode($this->application->custom_nginx_configuration);
|
||||||
|
|||||||
@@ -73,19 +73,21 @@ class StripeProcessJob implements ShouldQueue
|
|||||||
}
|
}
|
||||||
$subscription = Subscription::where('team_id', $teamId)->first();
|
$subscription = Subscription::where('team_id', $teamId)->first();
|
||||||
if ($subscription) {
|
if ($subscription) {
|
||||||
send_internal_notification('Old subscription activated for team: '.$teamId);
|
// send_internal_notification('Old subscription activated for team: '.$teamId);
|
||||||
$subscription->update([
|
$subscription->update([
|
||||||
'stripe_subscription_id' => $subscriptionId,
|
'stripe_subscription_id' => $subscriptionId,
|
||||||
'stripe_customer_id' => $customerId,
|
'stripe_customer_id' => $customerId,
|
||||||
'stripe_invoice_paid' => true,
|
'stripe_invoice_paid' => true,
|
||||||
|
'stripe_past_due' => false,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
send_internal_notification('New subscription for team: '.$teamId);
|
// send_internal_notification('New subscription for team: '.$teamId);
|
||||||
Subscription::create([
|
Subscription::create([
|
||||||
'team_id' => $teamId,
|
'team_id' => $teamId,
|
||||||
'stripe_subscription_id' => $subscriptionId,
|
'stripe_subscription_id' => $subscriptionId,
|
||||||
'stripe_customer_id' => $customerId,
|
'stripe_customer_id' => $customerId,
|
||||||
'stripe_invoice_paid' => true,
|
'stripe_invoice_paid' => true,
|
||||||
|
'stripe_past_due' => false,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -100,6 +102,7 @@ class StripeProcessJob implements ShouldQueue
|
|||||||
if ($subscription) {
|
if ($subscription) {
|
||||||
$subscription->update([
|
$subscription->update([
|
||||||
'stripe_invoice_paid' => true,
|
'stripe_invoice_paid' => true,
|
||||||
|
'stripe_past_due' => false,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
throw new \RuntimeException("No subscription found for customer: {$customerId}");
|
throw new \RuntimeException("No subscription found for customer: {$customerId}");
|
||||||
@@ -119,9 +122,7 @@ class StripeProcessJob implements ShouldQueue
|
|||||||
}
|
}
|
||||||
if (! $subscription->stripe_invoice_paid) {
|
if (! $subscription->stripe_invoice_paid) {
|
||||||
SubscriptionInvoiceFailedJob::dispatch($team);
|
SubscriptionInvoiceFailedJob::dispatch($team);
|
||||||
send_internal_notification('Invoice payment failed: '.$customerId);
|
// send_internal_notification('Invoice payment failed: '.$customerId);
|
||||||
} else {
|
|
||||||
send_internal_notification('Invoice payment failed but already paid: '.$customerId);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'payment_intent.payment_failed':
|
case 'payment_intent.payment_failed':
|
||||||
@@ -136,7 +137,7 @@ class StripeProcessJob implements ShouldQueue
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
send_internal_notification('Subscription payment failed for customer: '.$customerId);
|
// send_internal_notification('Subscription payment failed for customer: '.$customerId);
|
||||||
break;
|
break;
|
||||||
case 'customer.subscription.created':
|
case 'customer.subscription.created':
|
||||||
$customerId = data_get($data, 'customer');
|
$customerId = data_get($data, 'customer');
|
||||||
@@ -158,7 +159,7 @@ class StripeProcessJob implements ShouldQueue
|
|||||||
}
|
}
|
||||||
$subscription = Subscription::where('team_id', $teamId)->first();
|
$subscription = Subscription::where('team_id', $teamId)->first();
|
||||||
if ($subscription) {
|
if ($subscription) {
|
||||||
send_internal_notification("Subscription already exists for team: {$teamId}");
|
// send_internal_notification("Subscription already exists for team: {$teamId}");
|
||||||
throw new \RuntimeException("Subscription already exists for team: {$teamId}");
|
throw new \RuntimeException("Subscription already exists for team: {$teamId}");
|
||||||
} else {
|
} else {
|
||||||
Subscription::create([
|
Subscription::create([
|
||||||
@@ -182,7 +183,7 @@ class StripeProcessJob implements ShouldQueue
|
|||||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||||
if (! $subscription) {
|
if (! $subscription) {
|
||||||
if ($status === 'incomplete_expired') {
|
if ($status === 'incomplete_expired') {
|
||||||
send_internal_notification('Subscription incomplete expired');
|
// send_internal_notification('Subscription incomplete expired');
|
||||||
throw new \RuntimeException('Subscription incomplete expired');
|
throw new \RuntimeException('Subscription incomplete expired');
|
||||||
}
|
}
|
||||||
if ($teamId) {
|
if ($teamId) {
|
||||||
@@ -224,9 +225,33 @@ class StripeProcessJob implements ShouldQueue
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($status === 'past_due') {
|
||||||
|
if ($subscription->stripe_subscription_id === $subscriptionId) {
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_past_due' => true,
|
||||||
|
]);
|
||||||
|
send_internal_notification('Past Due: '.$customerId.'Subscription ID: '.$subscriptionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($status === 'unpaid') {
|
||||||
|
if ($subscription->stripe_subscription_id === $subscriptionId) {
|
||||||
|
$subscription->update([
|
||||||
|
'stripe_invoice_paid' => false,
|
||||||
|
]);
|
||||||
|
send_internal_notification('Unpaid: '.$customerId.'Subscription ID: '.$subscriptionId);
|
||||||
|
}
|
||||||
|
$team = data_get($subscription, 'team');
|
||||||
|
if ($team) {
|
||||||
|
$team->subscriptionEnded();
|
||||||
|
} else {
|
||||||
|
send_internal_notification('Subscription unpaid but no team found in Coolify for customer: '.$customerId);
|
||||||
|
throw new \RuntimeException("No team found in Coolify for customer: {$customerId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
if ($status === 'active') {
|
if ($status === 'active') {
|
||||||
if ($subscription->stripe_subscription_id === $subscriptionId) {
|
if ($subscription->stripe_subscription_id === $subscriptionId) {
|
||||||
$subscription->update([
|
$subscription->update([
|
||||||
|
'stripe_past_due' => false,
|
||||||
'stripe_invoice_paid' => true,
|
'stripe_invoice_paid' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class Help extends Component
|
|||||||
$type = set_transanctional_email_settings($settings);
|
$type = set_transanctional_email_settings($settings);
|
||||||
|
|
||||||
// Sending feedback through Cloud API
|
// Sending feedback through Cloud API
|
||||||
if ($type === false) {
|
if (blank($type)) {
|
||||||
$url = 'https://app.coolify.io/api/feedback';
|
$url = 'https://app.coolify.io/api/feedback';
|
||||||
Http::post($url, [
|
Http::post($url, [
|
||||||
'content' => 'User: `'.auth()->user()?->email.'` with subject: `'.$this->subject.'` has the following problem: `'.$this->description.'`',
|
'content' => 'User: `'.auth()->user()?->email.'` with subject: `'.$this->subject.'` has the following problem: `'.$this->description.'`',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
//use Livewire\Component;
|
// use Livewire\Component;
|
||||||
use Illuminate\View\Component;
|
use Illuminate\View\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
|
|||||||
@@ -52,12 +52,6 @@ class DockerCompose extends Component
|
|||||||
'dockerComposeRaw' => 'required',
|
'dockerComposeRaw' => 'required',
|
||||||
]);
|
]);
|
||||||
$this->dockerComposeRaw = Yaml::dump(Yaml::parse($this->dockerComposeRaw), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
|
$this->dockerComposeRaw = Yaml::dump(Yaml::parse($this->dockerComposeRaw), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
|
||||||
|
|
||||||
$isValid = validateComposeFile($this->dockerComposeRaw, $server_id);
|
|
||||||
if ($isValid !== 'OK') {
|
|
||||||
return $this->dispatch('error', "Invalid docker-compose file.\n$isValid");
|
|
||||||
}
|
|
||||||
|
|
||||||
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
|
||||||
$environment = $project->load(['environments'])->environments->where('uuid', $this->parameters['environment_uuid'])->first();
|
$environment = $project->load(['environments'])->environments->where('uuid', $this->parameters['environment_uuid'])->first();
|
||||||
|
|
||||||
|
|||||||
@@ -31,12 +31,22 @@ class EditCompose extends Component
|
|||||||
|
|
||||||
public function refreshEnvs()
|
public function refreshEnvs()
|
||||||
{
|
{
|
||||||
$this->service = Service::find($this->serviceId);
|
$this->service = Service::ownedByCurrentTeam()->find($this->serviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->service = Service::find($this->serviceId);
|
$this->service = Service::ownedByCurrentTeam()->find($this->serviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateCompose()
|
||||||
|
{
|
||||||
|
$isValid = validateComposeFile($this->service->docker_compose_raw, $this->service->server_id);
|
||||||
|
if ($isValid !== 'OK') {
|
||||||
|
$this->dispatch('error', "Invalid docker-compose file.\n$isValid");
|
||||||
|
} else {
|
||||||
|
$this->dispatch('success', 'Docker compose is valid.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveEditedCompose()
|
public function saveEditedCompose()
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class StackForm extends Component
|
|||||||
public function saveCompose($raw)
|
public function saveCompose($raw)
|
||||||
{
|
{
|
||||||
$this->service->docker_compose_raw = $raw;
|
$this->service->docker_compose_raw = $raw;
|
||||||
$this->submit(notify: false);
|
$this->submit(notify: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
@@ -76,10 +76,6 @@ class StackForm extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->validate();
|
$this->validate();
|
||||||
$isValid = validateComposeFile($this->service->docker_compose_raw, $this->service->server->id);
|
|
||||||
if ($isValid !== 'OK') {
|
|
||||||
throw new \Exception("Invalid docker-compose file.\n$isValid");
|
|
||||||
}
|
|
||||||
$this->service->save();
|
$this->service->save();
|
||||||
$this->service->saveExtraFields($this->fields);
|
$this->service->saveExtraFields($this->fields);
|
||||||
$this->service->parse();
|
$this->service->parse();
|
||||||
|
|||||||
@@ -2,15 +2,22 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Project\Shared\ScheduledTask;
|
namespace App\Livewire\Project\Shared\ScheduledTask;
|
||||||
|
|
||||||
|
use App\Models\ScheduledTask;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Livewire\Attributes\Locked;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Add extends Component
|
class Add extends Component
|
||||||
{
|
{
|
||||||
public $parameters;
|
public $parameters;
|
||||||
|
|
||||||
|
#[Locked]
|
||||||
|
public string $id;
|
||||||
|
|
||||||
|
#[Locked]
|
||||||
public string $type;
|
public string $type;
|
||||||
|
|
||||||
|
#[Locked]
|
||||||
public Collection $containerNames;
|
public Collection $containerNames;
|
||||||
|
|
||||||
public string $name;
|
public string $name;
|
||||||
@@ -21,8 +28,6 @@ class Add extends Component
|
|||||||
|
|
||||||
public ?string $container = '';
|
public ?string $container = '';
|
||||||
|
|
||||||
protected $listeners = ['clearScheduledTask' => 'clear'];
|
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'name' => 'required|string',
|
'name' => 'required|string',
|
||||||
'command' => 'required|string',
|
'command' => 'required|string',
|
||||||
@@ -60,18 +65,42 @@ class Add extends Component
|
|||||||
$this->container = $this->subServiceName;
|
$this->container = $this->subServiceName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->dispatch('saveScheduledTask', [
|
$this->saveScheduledTask();
|
||||||
'name' => $this->name,
|
|
||||||
'command' => $this->command,
|
|
||||||
'frequency' => $this->frequency,
|
|
||||||
'container' => $this->container,
|
|
||||||
]);
|
|
||||||
$this->clear();
|
$this->clear();
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function saveScheduledTask()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$task = new ScheduledTask();
|
||||||
|
$task->name = $this->name;
|
||||||
|
$task->command = $this->command;
|
||||||
|
$task->frequency = $this->frequency;
|
||||||
|
$task->container = $this->container;
|
||||||
|
$task->team_id = currentTeam()->id;
|
||||||
|
|
||||||
|
switch ($this->type) {
|
||||||
|
case 'application':
|
||||||
|
$task->application_id = $this->id;
|
||||||
|
break;
|
||||||
|
case 'standalone-postgresql':
|
||||||
|
$task->standalone_postgresql_id = $this->id;
|
||||||
|
break;
|
||||||
|
case 'service':
|
||||||
|
$task->service_id = $this->id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$task->save();
|
||||||
|
$this->dispatch('refreshTasks');
|
||||||
|
$this->dispatch('success', 'Scheduled task added.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function clear()
|
public function clear()
|
||||||
{
|
{
|
||||||
$this->name = '';
|
$this->name = '';
|
||||||
|
|||||||
@@ -4,20 +4,22 @@ namespace App\Livewire\Project\Shared\ScheduledTask;
|
|||||||
|
|
||||||
use App\Models\ScheduledTask;
|
use App\Models\ScheduledTask;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Livewire\Attributes\Locked;
|
||||||
|
use Livewire\Attributes\On;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class All extends Component
|
class All extends Component
|
||||||
{
|
{
|
||||||
|
#[Locked]
|
||||||
public $resource;
|
public $resource;
|
||||||
|
|
||||||
|
#[Locked]
|
||||||
|
public array $parameters;
|
||||||
|
|
||||||
public Collection $containerNames;
|
public Collection $containerNames;
|
||||||
|
|
||||||
public ?string $variables = null;
|
public ?string $variables = null;
|
||||||
|
|
||||||
public array $parameters;
|
|
||||||
|
|
||||||
protected $listeners = ['refreshTasks', 'saveScheduledTask' => 'submit'];
|
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
@@ -35,37 +37,10 @@ class All extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[On('refreshTasks')]
|
||||||
public function refreshTasks()
|
public function refreshTasks()
|
||||||
{
|
{
|
||||||
$this->resource->refresh();
|
$this->resource->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submit($data)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$task = new ScheduledTask;
|
|
||||||
$task->name = $data['name'];
|
|
||||||
$task->command = $data['command'];
|
|
||||||
$task->frequency = $data['frequency'];
|
|
||||||
$task->container = $data['container'];
|
|
||||||
$task->team_id = currentTeam()->id;
|
|
||||||
|
|
||||||
switch ($this->resource->type()) {
|
|
||||||
case 'application':
|
|
||||||
$task->application_id = $this->resource->id;
|
|
||||||
break;
|
|
||||||
case 'standalone-postgresql':
|
|
||||||
$task->standalone_postgresql_id = $this->resource->id;
|
|
||||||
break;
|
|
||||||
case 'service':
|
|
||||||
$task->service_id = $this->resource->id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$task->save();
|
|
||||||
$this->refreshTasks();
|
|
||||||
$this->dispatch('success', 'Scheduled task added.');
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,9 +133,9 @@ class Show extends Component
|
|||||||
$this->task->delete();
|
$this->task->delete();
|
||||||
|
|
||||||
if ($this->type === 'application') {
|
if ($this->type === 'application') {
|
||||||
return redirect()->route('project.application.configuration', $this->parameters, $this->task->name);
|
return redirect()->route('project.application.scheduled-tasks.show', $this->parameters);
|
||||||
} else {
|
} else {
|
||||||
return redirect()->route('project.service.configuration', $this->parameters, $this->task->name);
|
return redirect()->route('project.service.scheduled-tasks.show', $this->parameters);
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return handleError($e);
|
return handleError($e);
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ class Webhooks extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
// ray()->clearAll();
|
|
||||||
// ray()->showQueries();
|
|
||||||
$this->deploywebhook = generateDeployWebhook($this->resource);
|
$this->deploywebhook = generateDeployWebhook($this->resource);
|
||||||
|
|
||||||
$this->githubManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_github');
|
$this->githubManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_github');
|
||||||
|
|||||||
@@ -105,7 +105,6 @@ class Deploy extends Component
|
|||||||
|
|
||||||
$startTime = Carbon::now()->getTimestamp();
|
$startTime = Carbon::now()->getTimestamp();
|
||||||
while ($process->running()) {
|
while ($process->running()) {
|
||||||
ray('running');
|
|
||||||
if (Carbon::now()->getTimestamp() - $startTime >= $timeout) {
|
if (Carbon::now()->getTimestamp() - $startTime >= $timeout) {
|
||||||
$this->forceStopContainer($containerName);
|
$this->forceStopContainer($containerName);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class SettingsEmail extends Component
|
|||||||
public ?int $smtpPort = null;
|
public ?int $smtpPort = null;
|
||||||
|
|
||||||
#[Validate(['nullable', 'string', 'in:starttls,tls,none'])]
|
#[Validate(['nullable', 'string', 'in:starttls,tls,none'])]
|
||||||
public ?string $smtpEncryption = null;
|
public ?string $smtpEncryption = 'starttls';
|
||||||
|
|
||||||
#[Validate(['nullable', 'string'])]
|
#[Validate(['nullable', 'string'])]
|
||||||
public ?string $smtpUsername = null;
|
public ?string $smtpUsername = null;
|
||||||
|
|||||||
@@ -101,7 +101,6 @@ class Change extends Component
|
|||||||
// ]);
|
// ]);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// ray($runners_by_repository);
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Stringable;
|
use Illuminate\Support\Stringable;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
@@ -24,6 +25,7 @@ use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
|||||||
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
#[OA\Schema(
|
#[OA\Schema(
|
||||||
description: 'Server model',
|
description: 'Server model',
|
||||||
@@ -101,11 +103,13 @@ class Server extends BaseModel
|
|||||||
'server_id' => $server->id,
|
'server_id' => $server->id,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
StandaloneDocker::create([
|
$standaloneDocker = new StandaloneDocker([
|
||||||
'name' => 'coolify',
|
'name' => 'coolify',
|
||||||
|
'uuid' => (string) new Cuid2,
|
||||||
'network' => 'coolify',
|
'network' => 'coolify',
|
||||||
'server_id' => $server->id,
|
'server_id' => $server->id,
|
||||||
]);
|
]);
|
||||||
|
$standaloneDocker->saveQuietly();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! isset($server->proxy->redirect_enabled)) {
|
if (! isset($server->proxy->redirect_enabled)) {
|
||||||
@@ -437,10 +441,6 @@ class Server extends BaseModel
|
|||||||
"mkdir -p $dynamic_config_path",
|
"mkdir -p $dynamic_config_path",
|
||||||
"echo '$base64' | base64 -d | tee $file > /dev/null",
|
"echo '$base64' | base64 -d | tee $file > /dev/null",
|
||||||
], $this);
|
], $this);
|
||||||
|
|
||||||
if (config('app.env') === 'local') {
|
|
||||||
// ray($yaml);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} elseif ($this->proxyType() === 'CADDY') {
|
} elseif ($this->proxyType() === 'CADDY') {
|
||||||
$file = "$dynamic_config_path/coolify.caddy";
|
$file = "$dynamic_config_path/coolify.caddy";
|
||||||
@@ -709,22 +709,6 @@ $schema://$host {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getContainersWithSentinel(): Collection
|
|
||||||
{
|
|
||||||
$sentinel_found = instant_remote_process(['docker inspect coolify-sentinel'], $this, false);
|
|
||||||
$sentinel_found = json_decode($sentinel_found, true);
|
|
||||||
$status = data_get($sentinel_found, '0.State.Status', 'exited');
|
|
||||||
if ($status === 'running') {
|
|
||||||
$containers = instant_remote_process(['docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/containers"'], $this, false);
|
|
||||||
if (is_null($containers)) {
|
|
||||||
return collect([]);
|
|
||||||
}
|
|
||||||
$containers = data_get(json_decode($containers, true), 'containers', []);
|
|
||||||
|
|
||||||
return collect($containers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function loadAllContainers(): Collection
|
public function loadAllContainers(): Collection
|
||||||
{
|
{
|
||||||
if ($this->isFunctional()) {
|
if ($this->isFunctional()) {
|
||||||
@@ -970,10 +954,8 @@ $schema://$host {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if ($supported->count() === 1) {
|
if ($supported->count() === 1) {
|
||||||
// ray('supported');
|
|
||||||
return str($supported->first());
|
return str($supported->first());
|
||||||
} else {
|
} else {
|
||||||
// ray('not supported');
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1042,7 +1024,7 @@ $schema://$host {
|
|||||||
$unreachableNotificationSent = (bool) $this->unreachable_notification_sent;
|
$unreachableNotificationSent = (bool) $this->unreachable_notification_sent;
|
||||||
$isReachable = (bool) $this->settings->is_reachable;
|
$isReachable = (bool) $this->settings->is_reachable;
|
||||||
|
|
||||||
\Log::debug('Server reachability check', [
|
Log::debug('Server reachability check', [
|
||||||
'server_id' => $this->id,
|
'server_id' => $this->id,
|
||||||
'is_reachable' => $isReachable,
|
'is_reachable' => $isReachable,
|
||||||
'notification_sent' => $unreachableNotificationSent,
|
'notification_sent' => $unreachableNotificationSent,
|
||||||
@@ -1054,7 +1036,7 @@ $schema://$host {
|
|||||||
$this->save();
|
$this->save();
|
||||||
|
|
||||||
if ($unreachableNotificationSent === true) {
|
if ($unreachableNotificationSent === true) {
|
||||||
\Log::debug('Server is now reachable, sending notification', [
|
Log::debug('Server is now reachable, sending notification', [
|
||||||
'server_id' => $this->id,
|
'server_id' => $this->id,
|
||||||
]);
|
]);
|
||||||
$this->sendReachableNotification();
|
$this->sendReachableNotification();
|
||||||
@@ -1064,7 +1046,7 @@ $schema://$host {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->increment('unreachable_count');
|
$this->increment('unreachable_count');
|
||||||
\Log::debug('Incremented unreachable count', [
|
Log::debug('Incremented unreachable count', [
|
||||||
'server_id' => $this->id,
|
'server_id' => $this->id,
|
||||||
'new_count' => $this->unreachable_count,
|
'new_count' => $this->unreachable_count,
|
||||||
]);
|
]);
|
||||||
@@ -1072,7 +1054,7 @@ $schema://$host {
|
|||||||
if ($this->unreachable_count === 1) {
|
if ($this->unreachable_count === 1) {
|
||||||
$this->settings->is_reachable = true;
|
$this->settings->is_reachable = true;
|
||||||
$this->settings->save();
|
$this->settings->save();
|
||||||
\Log::debug('First unreachable attempt, marking as reachable', [
|
Log::debug('First unreachable attempt, marking as reachable', [
|
||||||
'server_id' => $this->id,
|
'server_id' => $this->id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -1083,7 +1065,7 @@ $schema://$host {
|
|||||||
$failedChecks = 0;
|
$failedChecks = 0;
|
||||||
for ($i = 0; $i < 3; $i++) {
|
for ($i = 0; $i < 3; $i++) {
|
||||||
$status = $this->serverStatus();
|
$status = $this->serverStatus();
|
||||||
\Log::debug('Additional reachability check', [
|
Log::debug('Additional reachability check', [
|
||||||
'server_id' => $this->id,
|
'server_id' => $this->id,
|
||||||
'attempt' => $i + 1,
|
'attempt' => $i + 1,
|
||||||
'status' => $status,
|
'status' => $status,
|
||||||
@@ -1095,7 +1077,7 @@ $schema://$host {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($failedChecks === 3 && ! $unreachableNotificationSent) {
|
if ($failedChecks === 3 && ! $unreachableNotificationSent) {
|
||||||
\Log::debug('Server confirmed unreachable after 3 attempts, sending notification', [
|
Log::debug('Server confirmed unreachable after 3 attempts, sending notification', [
|
||||||
'server_id' => $this->id,
|
'server_id' => $this->id,
|
||||||
]);
|
]);
|
||||||
$this->sendUnreachableNotification();
|
$this->sendUnreachableNotification();
|
||||||
|
|||||||
@@ -93,6 +93,15 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
|
|||||||
return $servers >= $serverLimit;
|
return $servers >= $serverLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function subscriptionPastOverDue()
|
||||||
|
{
|
||||||
|
if (isCloud()) {
|
||||||
|
return $this->subscription?->stripe_past_due;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function serverOverflow()
|
public function serverOverflow()
|
||||||
{
|
{
|
||||||
if ($this->serverLimit() < $this->servers->count()) {
|
if ($this->serverLimit() < $this->servers->count()) {
|
||||||
@@ -185,6 +194,7 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
|
|||||||
'stripe_cancel_at_period_end' => false,
|
'stripe_cancel_at_period_end' => false,
|
||||||
'stripe_invoice_paid' => false,
|
'stripe_invoice_paid' => false,
|
||||||
'stripe_trial_already_ended' => false,
|
'stripe_trial_already_ended' => false,
|
||||||
|
'stripe_past_due' => false,
|
||||||
]);
|
]);
|
||||||
foreach ($this->servers as $server) {
|
foreach ($this->servers as $server) {
|
||||||
$server->settings()->update([
|
$server->settings()->update([
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class DeploymentSuccess extends CustomEmailNotification
|
|||||||
if (str($this->fqdn)->explode(',')->count() > 1) {
|
if (str($this->fqdn)->explode(',')->count() > 1) {
|
||||||
$this->fqdn = str($this->fqdn)->explode(',')->first();
|
$this->fqdn = str($this->fqdn)->explode(',')->first();
|
||||||
}
|
}
|
||||||
$this->deployment_url = base_url()."/project/{$this->project_uuid}/environments/{$this->environment_uuid}/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}";
|
$this->deployment_url = base_url()."/project/{$this->project_uuid}/environment/{$this->environment_uuid}/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class StatusChanged extends CustomEmailNotification
|
|||||||
if (str($this->fqdn)->explode(',')->count() > 1) {
|
if (str($this->fqdn)->explode(',')->count() > 1) {
|
||||||
$this->fqdn = str($this->fqdn)->explode(',')->first();
|
$this->fqdn = str($this->fqdn)->explode(',')->first();
|
||||||
}
|
}
|
||||||
$this->resource_url = base_url()."/project/{$this->project_uuid}/environments/{$this->environment_uuid}/application/{$this->resource->uuid}";
|
$this->resource_url = base_url()."/project/{$this->project_uuid}/environment/{$this->environment_uuid}/application/{$this->resource->uuid}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
|
|||||||
@@ -50,11 +50,9 @@ class EmailChannel
|
|||||||
|
|
||||||
if ($emailSettings->use_instance_email_settings) {
|
if ($emailSettings->use_instance_email_settings) {
|
||||||
$type = set_transanctional_email_settings();
|
$type = set_transanctional_email_settings();
|
||||||
if (! $type) {
|
if (blank($type)) {
|
||||||
throw new Exception('No email settings found.');
|
throw new Exception('No email settings found.');
|
||||||
}
|
}
|
||||||
config()->set('mail.default', $type);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class TransactionalEmailChannel
|
|||||||
private function bootConfigs(): void
|
private function bootConfigs(): void
|
||||||
{
|
{
|
||||||
$type = set_transanctional_email_settings();
|
$type = set_transanctional_email_settings();
|
||||||
if (! $type) {
|
if (blank($type)) {
|
||||||
throw new Exception('No email settings found.');
|
throw new Exception('No email settings found.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class PushoverMessage
|
|||||||
if ($buttonUrl && str_contains($buttonUrl, 'http://localhost')) {
|
if ($buttonUrl && str_contains($buttonUrl, 'http://localhost')) {
|
||||||
$buttonUrl = str_replace('http://localhost', config('app.url'), $buttonUrl);
|
$buttonUrl = str_replace('http://localhost', config('app.url'), $buttonUrl);
|
||||||
}
|
}
|
||||||
$payload['message'] .= " <a href='" . $buttonUrl . "'>" . $text . '</a>';
|
$payload['message'] .= " <a href='".$buttonUrl."'>".$text.'</a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::info('Pushover message', $payload);
|
Log::info('Pushover message', $payload);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Notifications\TransactionalEmails;
|
namespace App\Notifications\TransactionalEmails;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
|
use Exception;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
use Illuminate\Notifications\Notification;
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
@@ -35,8 +36,8 @@ class ResetPassword extends Notification
|
|||||||
public function via($notifiable)
|
public function via($notifiable)
|
||||||
{
|
{
|
||||||
$type = set_transanctional_email_settings();
|
$type = set_transanctional_email_settings();
|
||||||
if (! $type) {
|
if (blank($type)) {
|
||||||
throw new \Exception('No email settings found.');
|
throw new Exception('No email settings found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return ['mail'];
|
return ['mail'];
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ namespace App\Traits;
|
|||||||
|
|
||||||
use App\Notifications\Channels\DiscordChannel;
|
use App\Notifications\Channels\DiscordChannel;
|
||||||
use App\Notifications\Channels\EmailChannel;
|
use App\Notifications\Channels\EmailChannel;
|
||||||
|
use App\Notifications\Channels\PushoverChannel;
|
||||||
use App\Notifications\Channels\SlackChannel;
|
use App\Notifications\Channels\SlackChannel;
|
||||||
use App\Notifications\Channels\TelegramChannel;
|
use App\Notifications\Channels\TelegramChannel;
|
||||||
use App\Notifications\Channels\PushoverChannel;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
trait HasNotificationSettings
|
trait HasNotificationSettings
|
||||||
|
|||||||
@@ -91,8 +91,6 @@ function next_queuable(string $server_id, string $application_id): bool
|
|||||||
$server = Server::find($server_id);
|
$server = Server::find($server_id);
|
||||||
$concurrent_builds = $server->settings->concurrent_builds;
|
$concurrent_builds = $server->settings->concurrent_builds;
|
||||||
|
|
||||||
// ray("serverId:{$server->id}", "concurrentBuilds:{$concurrent_builds}", "deployments:{$deployments->count()}", "sameApplicationDeployments:{$same_application_deployments->count()}")->green();
|
|
||||||
|
|
||||||
if ($deployments->count() > $concurrent_builds) {
|
if ($deployments->count() > $concurrent_builds) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -778,7 +778,6 @@ function convertDockerRunToCompose(?string $custom_docker_run_options = null)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ray($payload);
|
|
||||||
$compose_options->put('deploy', [
|
$compose_options->put('deploy', [
|
||||||
'resources' => [
|
'resources' => [
|
||||||
'reservations' => [
|
'reservations' => [
|
||||||
@@ -829,26 +828,29 @@ function generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker
|
|||||||
|
|
||||||
function validateComposeFile(string $compose, int $server_id): string|Throwable
|
function validateComposeFile(string $compose, int $server_id): string|Throwable
|
||||||
{
|
{
|
||||||
return 'OK';
|
$uuid = Str::random(18);
|
||||||
|
$server = Server::ownedByCurrentTeam()->find($server_id);
|
||||||
try {
|
try {
|
||||||
$uuid = Str::random(10);
|
if (! $server) {
|
||||||
$server = Server::findOrFail($server_id);
|
throw new \Exception('Server not found');
|
||||||
|
}
|
||||||
$base64_compose = base64_encode($compose);
|
$base64_compose = base64_encode($compose);
|
||||||
$output = instant_remote_process([
|
instant_remote_process([
|
||||||
"echo {$base64_compose} | base64 -d | tee /tmp/{$uuid}.yml > /dev/null",
|
"echo {$base64_compose} | base64 -d | tee /tmp/{$uuid}.yml > /dev/null",
|
||||||
"docker compose -f /tmp/{$uuid}.yml config",
|
"chmod 600 /tmp/{$uuid}.yml",
|
||||||
|
"docker compose -f /tmp/{$uuid}.yml config --no-interpolate --no-path-resolution -q",
|
||||||
|
"rm /tmp/{$uuid}.yml",
|
||||||
], $server);
|
], $server);
|
||||||
ray($output);
|
|
||||||
|
|
||||||
return 'OK';
|
return 'OK';
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e);
|
|
||||||
|
|
||||||
return $e->getMessage();
|
return $e->getMessage();
|
||||||
} finally {
|
} finally {
|
||||||
instant_remote_process([
|
if (filled($server)) {
|
||||||
"rm /tmp/{$uuid}.yml",
|
instant_remote_process([
|
||||||
], $server);
|
"rm /tmp/{$uuid}.yml",
|
||||||
|
], $server, throwError: false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null
|
|||||||
{
|
{
|
||||||
$settings = instanceSettings();
|
$settings = instanceSettings();
|
||||||
$type = set_transanctional_email_settings($settings);
|
$type = set_transanctional_email_settings($settings);
|
||||||
if (! $type) {
|
if (blank($type)) {
|
||||||
throw new Exception('No email settings found.');
|
throw new Exception('No email settings found.');
|
||||||
}
|
}
|
||||||
if ($cc) {
|
if ($cc) {
|
||||||
@@ -54,15 +54,19 @@ function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string //
|
function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string // returns null|resend|smtp and defaults to array based on mail.php config
|
||||||
{
|
{
|
||||||
if (! $settings) {
|
if (! $settings) {
|
||||||
$settings = instanceSettings();
|
$settings = instanceSettings();
|
||||||
}
|
}
|
||||||
config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
|
if (! data_get($settings, 'smtp_enabled') && ! data_get($settings, 'resend_enabled')) {
|
||||||
config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (data_get($settings, 'resend_enabled')) {
|
if (data_get($settings, 'resend_enabled')) {
|
||||||
config()->set('mail.default', 'resend');
|
config()->set('mail.default', 'resend');
|
||||||
|
config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
|
||||||
|
config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
|
||||||
config()->set('resend.api_key', data_get($settings, 'resend_api_key'));
|
config()->set('resend.api_key', data_get($settings, 'resend_api_key'));
|
||||||
|
|
||||||
return 'resend';
|
return 'resend';
|
||||||
@@ -76,6 +80,8 @@ function set_transanctional_email_settings(?InstanceSettings $settings = null):
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (data_get($settings, 'smtp_enabled')) {
|
if (data_get($settings, 'smtp_enabled')) {
|
||||||
|
config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
|
||||||
|
config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
|
||||||
config()->set('mail.default', 'smtp');
|
config()->set('mail.default', 'smtp');
|
||||||
config()->set('mail.mailers.smtp', [
|
config()->set('mail.mailers.smtp', [
|
||||||
'transport' => 'smtp',
|
'transport' => 'smtp',
|
||||||
@@ -91,6 +97,4 @@ function set_transanctional_email_settings(?InstanceSettings $settings = null):
|
|||||||
|
|
||||||
return 'smtp';
|
return 'smtp';
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -748,6 +748,7 @@ function parseCommandFromMagicEnvVariable(Str|string $key): Stringable
|
|||||||
{
|
{
|
||||||
$value = str($key);
|
$value = str($key);
|
||||||
$count = substr_count($value->value(), '_');
|
$count = substr_count($value->value(), '_');
|
||||||
|
$command = null;
|
||||||
if ($count === 2) {
|
if ($count === 2) {
|
||||||
if ($value->startsWith('SERVICE_FQDN') || $value->startsWith('SERVICE_URL')) {
|
if ($value->startsWith('SERVICE_FQDN') || $value->startsWith('SERVICE_URL')) {
|
||||||
// SERVICE_FQDN_UMAMI
|
// SERVICE_FQDN_UMAMI
|
||||||
@@ -800,7 +801,6 @@ function parseEnvVariable(Str|string $value)
|
|||||||
} else {
|
} else {
|
||||||
// SERVICE_BASE64_64_UMAMI
|
// SERVICE_BASE64_64_UMAMI
|
||||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||||
ray($command);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -952,7 +952,6 @@ function validate_dns_entry(string $fqdn, Server $server)
|
|||||||
$type = \PurplePixie\PhpDns\DNSTypes::NAME_A;
|
$type = \PurplePixie\PhpDns\DNSTypes::NAME_A;
|
||||||
foreach ($dns_servers as $dns_server) {
|
foreach ($dns_servers as $dns_server) {
|
||||||
try {
|
try {
|
||||||
ray("Checking $host on $dns_server");
|
|
||||||
$query = new DNSQuery($dns_server);
|
$query = new DNSQuery($dns_server);
|
||||||
$results = $query->query($host, $type);
|
$results = $query->query($host, $type);
|
||||||
if ($results === false || $query->hasError()) {
|
if ($results === false || $query->hasError()) {
|
||||||
@@ -961,13 +960,10 @@ function validate_dns_entry(string $fqdn, Server $server)
|
|||||||
foreach ($results as $result) {
|
foreach ($results as $result) {
|
||||||
if ($result->getType() == $type) {
|
if ($result->getType() == $type) {
|
||||||
if (ip_match($result->getData(), $cloudflare_ips->toArray(), $match)) {
|
if (ip_match($result->getData(), $cloudflare_ips->toArray(), $match)) {
|
||||||
ray("Found match in Cloudflare IPs: $match");
|
|
||||||
$found_matching_ip = true;
|
$found_matching_ip = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ($result->getData() === $ip) {
|
if ($result->getData() === $ip) {
|
||||||
ray($host.' has IP address '.$result->getData());
|
|
||||||
ray($result->getString());
|
|
||||||
$found_matching_ip = true;
|
$found_matching_ip = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -977,7 +973,6 @@ function validate_dns_entry(string $fqdn, Server $server)
|
|||||||
} catch (\Exception) {
|
} catch (\Exception) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ray("Found match: $found_matching_ip");
|
|
||||||
|
|
||||||
return $found_matching_ip;
|
return $found_matching_ip;
|
||||||
}
|
}
|
||||||
@@ -1331,7 +1326,6 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull
|
|||||||
$isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
|
$isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
|
||||||
if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) {
|
if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) {
|
||||||
// if isDirectory is not set (or false) & content is also not set, we assume it is a directory
|
// if isDirectory is not set (or false) & content is also not set, we assume it is a directory
|
||||||
ray('setting isDirectory to true');
|
|
||||||
$isDirectory = true;
|
$isDirectory = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1499,7 +1493,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
$serviceLabels->push("$removedLabelName=$removedLabel");
|
$serviceLabels->push("$removedLabelName=$removedLabel");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$containerName = "$serviceName-{$resource->uuid}";
|
$containerName = "$serviceName-{$resource->uuid}";
|
||||||
|
|
||||||
// Decide if the service is a database
|
// Decide if the service is a database
|
||||||
@@ -1662,7 +1655,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
}
|
}
|
||||||
if (is_null($isDirectory) && is_null($content)) {
|
if (is_null($isDirectory) && is_null($content)) {
|
||||||
// if isDirectory is not set & content is also not set, we assume it is a directory
|
// if isDirectory is not set & content is also not set, we assume it is a directory
|
||||||
ray('setting isDirectory to true');
|
|
||||||
$isDirectory = true;
|
$isDirectory = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2529,9 +2521,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($collectedPorts->count() > 0) {
|
|
||||||
ray($collectedPorts->implode(','));
|
|
||||||
}
|
|
||||||
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||||
return $value == $definedNetwork;
|
return $value == $definedNetwork;
|
||||||
});
|
});
|
||||||
@@ -2956,7 +2945,6 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
}
|
}
|
||||||
|
|
||||||
$parsedServices = collect([]);
|
$parsedServices = collect([]);
|
||||||
// ray()->clearAll();
|
|
||||||
|
|
||||||
$allMagicEnvironments = collect([]);
|
$allMagicEnvironments = collect([]);
|
||||||
foreach ($services as $serviceName => $service) {
|
foreach ($services as $serviceName => $service) {
|
||||||
@@ -3016,7 +3004,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$environment = $environment->merge($buildArgs);
|
$environment = $environment->merge($buildArgs);
|
||||||
|
|
||||||
// convert environment variables to one format
|
// convert environment variables to one format
|
||||||
$environment = convertComposeEnvironmentToArray($environment);
|
$environment = convertToKeyValueCollection($environment);
|
||||||
|
|
||||||
// Add Coolify defined environments
|
// Add Coolify defined environments
|
||||||
$allEnvironments = $resource->environment_variables()->get(['key', 'value']);
|
$allEnvironments = $resource->environment_variables()->get(['key', 'value']);
|
||||||
@@ -3197,7 +3185,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$buildArgs = collect(data_get($service, 'build.args', []));
|
$buildArgs = collect(data_get($service, 'build.args', []));
|
||||||
$environment = $environment->merge($buildArgs);
|
$environment = $environment->merge($buildArgs);
|
||||||
|
|
||||||
$environment = convertComposeEnvironmentToArray($environment);
|
$environment = convertToKeyValueCollection($environment);
|
||||||
$coolifyEnvironments = collect([]);
|
$coolifyEnvironments = collect([]);
|
||||||
|
|
||||||
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
||||||
@@ -3934,7 +3922,7 @@ function add_coolify_default_environment_variables(StandaloneRedis|StandalonePos
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertComposeEnvironmentToArray($environment)
|
function convertToKeyValueCollection($environment)
|
||||||
{
|
{
|
||||||
$convertedServiceVariables = collect([]);
|
$convertedServiceVariables = collect([]);
|
||||||
if (isAssociativeArray($environment)) {
|
if (isAssociativeArray($environment)) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see https://github.com/pionl/laravel-chunk-upload
|
* @see https://github.com/pionl/laravel-chunk-upload
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
'coolify' => [
|
'coolify' => [
|
||||||
'version' => '4.0.0-beta.396',
|
'version' => '4.0.0-beta.398',
|
||||||
'helper_version' => '1.0.7',
|
'helper_version' => '1.0.7',
|
||||||
'realtime_version' => '1.0.6',
|
'realtime_version' => '1.0.6',
|
||||||
'self_hosted' => env('SELF_HOSTED', true),
|
'self_hosted' => env('SELF_HOSTED', true),
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ return [
|
|||||||
'pgsql' => [
|
'pgsql' => [
|
||||||
'driver' => 'pgsql',
|
'driver' => 'pgsql',
|
||||||
'url' => env('DATABASE_URL'),
|
'url' => env('DATABASE_URL'),
|
||||||
'host' => env('DB_HOST', 'postgres'),
|
'host' => env('DB_HOST', 'coolify-db'),
|
||||||
'port' => env('DB_PORT', '5432'),
|
'port' => env('DB_PORT', '5432'),
|
||||||
'database' => env('DB_DATABASE', 'coolify'),
|
'database' => env('DB_DATABASE', 'coolify'),
|
||||||
'username' => env('DB_USERNAME', 'coolify'),
|
'username' => env('DB_USERNAME', 'coolify'),
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ return [
|
|||||||
],
|
],
|
||||||
'views' => [
|
'views' => [
|
||||||
'timeline' => false, // Add the views to the timeline (Experimental)
|
'timeline' => false, // Add the views to the timeline (Experimental)
|
||||||
'data' => false, //true for all data, 'keys' for only names, false for no parameters.
|
'data' => false, // true for all data, 'keys' for only names, false for no parameters.
|
||||||
'group' => 50, // Group duplicate views. Pass value to auto-group, or true/false to force
|
'group' => 50, // Group duplicate views. Pass value to auto-group, or true/false to force
|
||||||
'exclude_paths' => [ // Add the paths which you don't want to appear in the views
|
'exclude_paths' => [ // Add the paths which you don't want to appear in the views
|
||||||
'vendor/filament', // Exclude Filament components by default
|
'vendor/filament', // Exclude Filament components by default
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ return [
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'default' => env('MAIL_MAILER', null),
|
'default' => env('MAIL_MAILER', 'array'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('scheduled_task_executions', function (Blueprint $table) {
|
||||||
|
$table->index(['scheduled_task_id', 'created_at'], 'scheduled_task_executions_task_id_created_at_index');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('scheduled_database_backup_executions', function (Blueprint $table) {
|
||||||
|
$table->index(
|
||||||
|
['scheduled_database_backup_id', 'created_at'],
|
||||||
|
'scheduled_db_backup_executions_backup_id_created_at_index'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('scheduled_task_executions', function (Blueprint $table) {
|
||||||
|
$table->dropIndex('scheduled_task_executions_task_id_created_at_index');
|
||||||
|
});
|
||||||
|
Schema::table('scheduled_database_backup_executions', function (Blueprint $table) {
|
||||||
|
$table->dropIndex('scheduled_db_backup_executions_backup_id_created_at_index');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('subscriptions', function (Blueprint $table) {
|
||||||
|
$table->boolean('stripe_past_due')->default(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('subscriptions', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('stripe_past_due');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -61,7 +61,7 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
timeout: 2s
|
timeout: 2s
|
||||||
soketi:
|
soketi:
|
||||||
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.5'
|
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.6'
|
||||||
ports:
|
ports:
|
||||||
- "${SOKETI_PORT:-6001}:6001"
|
- "${SOKETI_PORT:-6001}:6001"
|
||||||
- "6002:6002"
|
- "6002:6002"
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"coolify": {
|
"coolify": {
|
||||||
"v4": {
|
"v4": {
|
||||||
"version": "4.0.0-beta.382"
|
"version": "4.0.0-beta.397"
|
||||||
},
|
},
|
||||||
"nightly": {
|
"nightly": {
|
||||||
"version": "4.0.0-beta.383"
|
"version": "4.0.0-beta.398"
|
||||||
},
|
},
|
||||||
"helper": {
|
"helper": {
|
||||||
"version": "1.0.7"
|
"version": "1.0.7"
|
||||||
|
|||||||
@@ -79,6 +79,16 @@
|
|||||||
</x-slot:button-text>
|
</x-slot:button-text>
|
||||||
</x-popup>
|
</x-popup>
|
||||||
</span>
|
</span>
|
||||||
|
@if (currentTeam()->subscriptionPastOverDue())
|
||||||
|
<x-banner :closable=false>
|
||||||
|
<div><span class="font-bold text-red-500">WARNING:</span> Your subscription is in over-due. If your latest
|
||||||
|
payment is not paid within a week, all automations <span class="font-bold text-red-500">will
|
||||||
|
be deactivated</span>. Visit <a href="{{ route('subscription.show') }}"
|
||||||
|
class="underline dark:text-white">/subscription</a> to check your subscription status or pay your
|
||||||
|
invoice (or check your email for the invoice).
|
||||||
|
</div>
|
||||||
|
</x-banner>
|
||||||
|
@endif
|
||||||
@if (currentTeam()->serverOverflow())
|
@if (currentTeam()->serverOverflow())
|
||||||
<x-banner :closable=false>
|
<x-banner :closable=false>
|
||||||
<div><span class="font-bold text-red-500">WARNING:</span> The number of active servers exceeds the limit
|
<div><span class="font-bold text-red-500">WARNING:</span> The number of active servers exceeds the limit
|
||||||
|
|||||||
@@ -176,7 +176,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="pb-4">Persistent storage to preserve data between deployments.</div>
|
<div class="pb-4">Persistent storage to preserve data between deployments.</div>
|
||||||
<div class="pb-4 dark:text-warning text-coollabs">If you would like to add a volume, you must add it to
|
<div class="pb-4 dark:text-warning text-coollabs">If you would like to add a volume, you must add it to
|
||||||
your compose file (Service Stack tab).</div>
|
your compose file (<a class="underline"
|
||||||
|
href="{{ route('project.service.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}"
|
||||||
|
wire:navigate>General tab</a>).</div>
|
||||||
@foreach ($applications as $application)
|
@foreach ($applications as $application)
|
||||||
<livewire:project.service.storage wire:key="application-{{ $application->id }}"
|
<livewire:project.service.storage wire:key="application-{{ $application->id }}"
|
||||||
:resource="$application" />
|
:resource="$application" />
|
||||||
|
|||||||
@@ -20,27 +20,29 @@
|
|||||||
</x-forms.textarea>
|
</x-forms.textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="pt-2 flex gap-2">
|
<div class="pt-2 flex gap-2">
|
||||||
<x-forms.checkbox label="Escape special characters in labels?"
|
<div class="flex flex-col gap-2">
|
||||||
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
|
<x-forms.checkbox label="Escape special characters in labels?"
|
||||||
id="service.is_container_label_escape_enabled" instantSave></x-forms.checkbox>
|
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
|
||||||
<div class="flex-1"></div>
|
id="service.is_container_label_escape_enabled" instantSave></x-forms.checkbox>
|
||||||
<div x-cloak x-show="raw">
|
<x-forms.checkbox label="Show Normal Textarea" x-model="showNormalTextarea"></x-forms.checkbox>
|
||||||
<x-forms.button class="w-32" @click.prevent="showNormalTextarea = !showNormalTextarea">Switch
|
|
||||||
Textarea</x-forms.button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-end w-full gap-2 pt-4">
|
<div class="flex w-full gap-2 pt-4">
|
||||||
<div class="flex items-end gap-2">
|
<div x-cloak x-show="raw">
|
||||||
<div x-cloak x-show="raw">
|
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Deployable Compose</x-forms.button>
|
||||||
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Deployable Compose</x-forms.button>
|
</div>
|
||||||
</div>
|
<div x-cloak x-show="raw === false">
|
||||||
<div x-cloak x-show="raw === false">
|
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Source
|
||||||
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Source
|
Compose</x-forms.button>
|
||||||
Compose</x-forms.button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
<x-forms.button class="w-64" wire:click.prevent='saveEditedCompose'>
|
@if (blank($service->service_type))
|
||||||
|
<x-forms.button class="w-28" wire:click.prevent='validateCompose'>
|
||||||
|
Validate
|
||||||
|
</x-forms.button>
|
||||||
|
@endif
|
||||||
|
<x-forms.button class="w-28" wire:click.prevent='saveEditedCompose'>
|
||||||
Save
|
Save
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,15 +21,6 @@
|
|||||||
]" confirmationText="{{ $fs_path }}"
|
]" confirmationText="{{ $fs_path }}"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
||||||
shortConfirmationLabel="Filepath" :confirmWithPassword="false" step2ButtonText="Convert to file" />
|
shortConfirmationLabel="Filepath" :confirmWithPassword="false" step2ButtonText="Convert to file" />
|
||||||
@else
|
|
||||||
<x-modal-confirmation title="Confirm File Conversion to Directory?" buttonTitle="Convert to directory"
|
|
||||||
submitAction="convertToDirectory" :actions="[
|
|
||||||
'The selected file will be permanently deleted and an empty directory will be created in its place.',
|
|
||||||
]" confirmationText="{{ $fs_path }}"
|
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
|
||||||
shortConfirmationLabel="Filepath" :confirmWithPassword="false" step2ButtonText="Convert to directory" />
|
|
||||||
@endif
|
|
||||||
@if ($fileStorage->is_directory)
|
|
||||||
<x-modal-confirmation title="Confirm Directory Deletion?" buttonTitle="Delete Directory" isErrorButton
|
<x-modal-confirmation title="Confirm Directory Deletion?" buttonTitle="Delete Directory" isErrorButton
|
||||||
submitAction="delete" :checkboxes="$directoryDeletionCheckboxes" :actions="[
|
submitAction="delete" :checkboxes="$directoryDeletionCheckboxes" :actions="[
|
||||||
'The selected directory and all its contents will be permanently deleted from the container.',
|
'The selected directory and all its contents will be permanently deleted from the container.',
|
||||||
@@ -37,13 +28,18 @@
|
|||||||
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
||||||
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
|
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
|
||||||
@else
|
@else
|
||||||
|
<x-modal-confirmation title="Confirm File Conversion to Directory?" buttonTitle="Convert to directory"
|
||||||
|
submitAction="convertToDirectory" :actions="[
|
||||||
|
'The selected file will be permanently deleted and an empty directory will be created in its place.',
|
||||||
|
]" confirmationText="{{ $fs_path }}"
|
||||||
|
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
||||||
|
shortConfirmationLabel="Filepath" :confirmWithPassword="false" step2ButtonText="Convert to directory" />
|
||||||
<x-modal-confirmation title="Confirm File Deletion?" buttonTitle="Delete File" isErrorButton
|
<x-modal-confirmation title="Confirm File Deletion?" buttonTitle="Delete File" isErrorButton
|
||||||
submitAction="delete" :checkboxes="$fileDeletionCheckboxes" :actions="['The selected file will be permanently deleted from the container.']" confirmationText="{{ $fs_path }}"
|
submitAction="delete" :checkboxes="$fileDeletionCheckboxes" :actions="['The selected file will be permanently deleted from the container.']" confirmationText="{{ $fs_path }}"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
||||||
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
|
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
|
||||||
@endif
|
@endif
|
||||||
|
{{-- @if (!$fileStorage->is_based_on_git)
|
||||||
@if (!$fileStorage->is_based_on_git)
|
|
||||||
<x-modal-confirmation isErrorButton buttonTitle="Delete">
|
<x-modal-confirmation isErrorButton buttonTitle="Delete">
|
||||||
<div class="px-2">This storage will be deleted. It is not reversible. <strong
|
<div class="px-2">This storage will be deleted. It is not reversible. <strong
|
||||||
class="text-error">Please
|
class="text-error">Please
|
||||||
@@ -58,7 +54,7 @@
|
|||||||
label="Permanently delete file from the server?"></x-forms.checkbox>
|
label="Permanently delete file from the server?"></x-forms.checkbox>
|
||||||
@endif
|
@endif
|
||||||
</x-modal-confirmation>
|
</x-modal-confirmation>
|
||||||
@endif
|
@endif --}}
|
||||||
</div>
|
</div>
|
||||||
@if (!$fileStorage->is_directory)
|
@if (!$fileStorage->is_directory)
|
||||||
@if (data_get($resource, 'settings.is_preserve_repository_enabled'))
|
@if (data_get($resource, 'settings.is_preserve_repository_enabled'))
|
||||||
@@ -75,6 +71,5 @@
|
|||||||
<x-forms.button class="w-full" type="submit">Save</x-forms.button>
|
<x-forms.button class="w-full" type="submit">Save</x-forms.button>
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
<h2>Scheduled Tasks</h2>
|
<h2>Scheduled Tasks</h2>
|
||||||
<x-modal-input buttonTitle="+ Add" title="New Scheduled Task" :closeOutside="false">
|
<x-modal-input buttonTitle="+ Add" title="New Scheduled Task" :closeOutside="false">
|
||||||
@if ($resource->type() == 'application')
|
@if ($resource->type() == 'application')
|
||||||
<livewire:project.shared.scheduled-task.add :type="$resource->type()" :containerNames="$containerNames" />
|
<livewire:project.shared.scheduled-task.add :type="$resource->type()" :id="$resource->id" :containerNames="$containerNames" />
|
||||||
@elseif ($resource->type() == 'service')
|
@elseif ($resource->type() == 'service')
|
||||||
<livewire:project.shared.scheduled-task.add :type="$resource->type()" :containerNames="$containerNames" />
|
<livewire:project.shared.scheduled-task.add :type="$resource->type()" :id="$resource->id" :containerNames="$containerNames" />
|
||||||
@endif
|
@endif
|
||||||
</x-modal-input>
|
</x-modal-input>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -132,12 +132,12 @@
|
|||||||
<h4 class="py-4">Confirmation Settings</h4>
|
<h4 class="py-4">Confirmation Settings</h4>
|
||||||
|
|
||||||
@if ($disable_two_step_confirmation)
|
@if ($disable_two_step_confirmation)
|
||||||
<div class="md:w-96 pb-4">
|
<div class="md:w-96 pb-4" wire:key="two-step-confirmation-enabled">
|
||||||
<x-forms.checkbox instantSave id="disable_two_step_confirmation" label="Disable Two Step Confirmation"
|
<x-forms.checkbox instantSave id="disable_two_step_confirmation" label="Disable Two Step Confirmation"
|
||||||
helper="When disabled, you will not need to confirm actions with a text and user password. This significantly reduces security and may lead to accidental deletions or unwanted changes. Use with extreme caution, especially on production servers." />
|
helper="When disabled, you will not need to confirm actions with a text and user password. This significantly reduces security and may lead to accidental deletions or unwanted changes. Use with extreme caution, especially on production servers." />
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
<div class="md:w-96 pb-4">
|
<div class="md:w-96 pb-4" wire:key="two-step-confirmation-disabled">
|
||||||
<x-modal-confirmation title="Disable Two Step Confirmation?"
|
<x-modal-confirmation title="Disable Two Step Confirmation?"
|
||||||
buttonTitle="Disable Two Step Confirmation" isErrorButton submitAction="toggleTwoStepConfirmation"
|
buttonTitle="Disable Two Step Confirmation" isErrorButton submitAction="toggleTwoStepConfirmation"
|
||||||
:actions="[
|
:actions="[
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class LoginTest extends DuskTestCase
|
|||||||
*
|
*
|
||||||
* @throws Throwable
|
* @throws Throwable
|
||||||
*/
|
*/
|
||||||
public function testLogin()
|
public function test_login()
|
||||||
{
|
{
|
||||||
$this->browse(callback: function (Browser $browser) {
|
$this->browse(callback: function (Browser $browser) {
|
||||||
$browser->loginWithRootUser()
|
$browser->loginWithRootUser()
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class ProjectAddNewTest extends DuskTestCase
|
|||||||
*
|
*
|
||||||
* @throws Throwable
|
* @throws Throwable
|
||||||
*/
|
*/
|
||||||
public function testLogin()
|
public function test_login()
|
||||||
{
|
{
|
||||||
$this->browse(function (Browser $browser) {
|
$this->browse(function (Browser $browser) {
|
||||||
$browser->loginWithRootUser()
|
$browser->loginWithRootUser()
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class ProjectSearchTest extends DuskTestCase
|
|||||||
*
|
*
|
||||||
* @throws Throwable
|
* @throws Throwable
|
||||||
*/
|
*/
|
||||||
public function testLogin()
|
public function test_login()
|
||||||
{
|
{
|
||||||
$this->browse(function (Browser $browser) {
|
$this->browse(function (Browser $browser) {
|
||||||
$browser->loginWithRootUser()
|
$browser->loginWithRootUser()
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class ProjectTest extends DuskTestCase
|
|||||||
*
|
*
|
||||||
* @throws Throwable
|
* @throws Throwable
|
||||||
*/
|
*/
|
||||||
public function testLogin()
|
public function test_login()
|
||||||
{
|
{
|
||||||
$this->browse(function (Browser $browser) {
|
$this->browse(function (Browser $browser) {
|
||||||
$browser->loginWithRootUser()
|
$browser->loginWithRootUser()
|
||||||
|
|||||||
@@ -358,7 +358,7 @@ use Symfony\Component\Yaml\Yaml;
|
|||||||
// expect($output)->toContain('Docker version');
|
// expect($output)->toContain('Docker version');
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// test('ConvertComposeEnvironmentToArray', function () {
|
// test('convertToKeyValueCollection', function () {
|
||||||
// ray()->clearAll();
|
// ray()->clearAll();
|
||||||
// $yaml = '
|
// $yaml = '
|
||||||
// services:
|
// services:
|
||||||
@@ -374,9 +374,9 @@ use Symfony\Component\Yaml\Yaml;
|
|||||||
// - POSTGRES_DB: activepieces
|
// - POSTGRES_DB: activepieces
|
||||||
// ';
|
// ';
|
||||||
// $parsedYaml = Yaml::parse($yaml);
|
// $parsedYaml = Yaml::parse($yaml);
|
||||||
// $output = convertComposeEnvironmentToArray($parsedYaml['services']['activepieces']['environment']);
|
// $output = convertToKeyValueCollection($parsedYaml['services']['activepieces']['environment']);
|
||||||
// $output2 = convertComposeEnvironmentToArray($parsedYaml['services']['activepieces2']['environment']);
|
// $output2 = convertToKeyValueCollection($parsedYaml['services']['activepieces2']['environment']);
|
||||||
// $dboutput = convertComposeEnvironmentToArray($parsedYaml['services']['postgres']['environment']);
|
// $dboutput = convertToKeyValueCollection($parsedYaml['services']['postgres']['environment']);
|
||||||
// ray($output);
|
// ray($output);
|
||||||
// ray($output2);
|
// ray($output2);
|
||||||
// ray($dboutput);
|
// ray($dboutput);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Test Case
|
| Test Case
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"coolify": {
|
"coolify": {
|
||||||
"v4": {
|
"v4": {
|
||||||
"version": "4.0.0-beta.396"
|
"version": "4.0.0-beta.398"
|
||||||
},
|
},
|
||||||
"nightly": {
|
"nightly": {
|
||||||
"version": "4.0.0-beta.397"
|
"version": "4.0.0-beta.399"
|
||||||
},
|
},
|
||||||
"helper": {
|
"helper": {
|
||||||
"version": "1.0.7"
|
"version": "1.0.7"
|
||||||
|
|||||||
Reference in New Issue
Block a user