Merge branch 'next' into plane-app
This commit is contained in:
@@ -208,7 +208,6 @@ class GetContainersStatus
|
||||
$foundServices[] = "$service->id-$service->name";
|
||||
$statusFromDb = $service->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
// ray('Updating status: ' . $containerStatus);
|
||||
$service->update(['status' => $containerStatus]);
|
||||
} else {
|
||||
$service->update(['last_online_at' => now()]);
|
||||
|
||||
@@ -50,7 +50,7 @@ class CloudCleanupSubscriptions extends Command
|
||||
} else {
|
||||
$subscription = $stripe->subscriptions->retrieve(data_get($team, 'subscription.stripe_subscription_id'), []);
|
||||
$status = data_get($subscription, 'status');
|
||||
if ($status === 'active' || $status === 'past_due') {
|
||||
if ($status === 'active') {
|
||||
$team->subscription->update([
|
||||
'stripe_invoice_paid' => true,
|
||||
'stripe_trial_already_ended' => false,
|
||||
|
||||
@@ -1291,11 +1291,6 @@ class ApplicationsController extends Controller
|
||||
$dockerCompose = base64_decode($request->docker_compose_raw);
|
||||
$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;
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
$service->fill($request->all());
|
||||
|
||||
@@ -54,7 +54,7 @@ class Controller extends BaseController
|
||||
'email' => Str::lower($arrayOfRequest['email']),
|
||||
]);
|
||||
$type = set_transanctional_email_settings();
|
||||
if (! $type) {
|
||||
if (blank($type)) {
|
||||
return response()->json(['message' => 'Transactional emails are not active'], 400);
|
||||
}
|
||||
$request->validate([Fortify::email() => 'required|email']);
|
||||
|
||||
@@ -202,7 +202,6 @@ class Gitea extends Controller
|
||||
if ($found) {
|
||||
$found->delete();
|
||||
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||
// ray('Stopping container: ' . $container_name);
|
||||
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
|
||||
@@ -208,7 +208,6 @@ class Github extends Controller
|
||||
if ($found) {
|
||||
$found->delete();
|
||||
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||
// ray('Stopping container: ' . $container_name);
|
||||
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
|
||||
@@ -227,7 +227,6 @@ class Gitlab extends Controller
|
||||
if ($found) {
|
||||
$found->delete();
|
||||
$container_name = generateApplicationContainerName($application, $pull_request_id);
|
||||
// ray('Stopping container: ' . $container_name);
|
||||
instant_remote_process(["docker rm -f $container_name"], $application->destination->server);
|
||||
$return_payloads->push([
|
||||
'application' => $application->name,
|
||||
|
||||
@@ -19,6 +19,7 @@ use App\Notifications\Application\DeploymentFailed;
|
||||
use App\Notifications\Application\DeploymentSuccess;
|
||||
use App\Traits\ExecuteRemoteCommand;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@@ -1207,7 +1208,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($this->application->custom_healthcheck_found) {
|
||||
$this->application_deployment_queue->addLogEntry('Custom healthcheck found, skipping default healthcheck.');
|
||||
}
|
||||
// ray('New container name: ', $this->container_name);
|
||||
if ($this->container_name) {
|
||||
$counter = 1;
|
||||
$this->application_deployment_queue->addLogEntry('Waiting for healthcheck to pass on the new container.');
|
||||
@@ -1410,7 +1410,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
continue;
|
||||
}
|
||||
// ray('Deploying to additional destination: ', $server->name);
|
||||
$deployment_uuid = new Cuid2;
|
||||
queue_application_deployment(
|
||||
deployment_uuid: $deployment_uuid,
|
||||
@@ -2023,6 +2022,8 @@ LABEL coolify.deploymentId={$this->deployment_uuid}
|
||||
COPY . .
|
||||
RUN rm -f /usr/share/nginx/html/nginx.conf
|
||||
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");
|
||||
if (str($this->application->custom_nginx_configuration)->isNotEmpty()) {
|
||||
$nginx_config = base64_encode($this->application->custom_nginx_configuration);
|
||||
|
||||
@@ -73,19 +73,21 @@ class StripeProcessJob implements ShouldQueue
|
||||
}
|
||||
$subscription = Subscription::where('team_id', $teamId)->first();
|
||||
if ($subscription) {
|
||||
send_internal_notification('Old subscription activated for team: '.$teamId);
|
||||
// send_internal_notification('Old subscription activated for team: '.$teamId);
|
||||
$subscription->update([
|
||||
'stripe_subscription_id' => $subscriptionId,
|
||||
'stripe_customer_id' => $customerId,
|
||||
'stripe_invoice_paid' => true,
|
||||
'stripe_past_due' => false,
|
||||
]);
|
||||
} else {
|
||||
send_internal_notification('New subscription for team: '.$teamId);
|
||||
// send_internal_notification('New subscription for team: '.$teamId);
|
||||
Subscription::create([
|
||||
'team_id' => $teamId,
|
||||
'stripe_subscription_id' => $subscriptionId,
|
||||
'stripe_customer_id' => $customerId,
|
||||
'stripe_invoice_paid' => true,
|
||||
'stripe_past_due' => false,
|
||||
]);
|
||||
}
|
||||
break;
|
||||
@@ -100,6 +102,7 @@ class StripeProcessJob implements ShouldQueue
|
||||
if ($subscription) {
|
||||
$subscription->update([
|
||||
'stripe_invoice_paid' => true,
|
||||
'stripe_past_due' => false,
|
||||
]);
|
||||
} else {
|
||||
throw new \RuntimeException("No subscription found for customer: {$customerId}");
|
||||
@@ -119,9 +122,7 @@ class StripeProcessJob implements ShouldQueue
|
||||
}
|
||||
if (! $subscription->stripe_invoice_paid) {
|
||||
SubscriptionInvoiceFailedJob::dispatch($team);
|
||||
send_internal_notification('Invoice payment failed: '.$customerId);
|
||||
} else {
|
||||
send_internal_notification('Invoice payment failed but already paid: '.$customerId);
|
||||
// send_internal_notification('Invoice payment failed: '.$customerId);
|
||||
}
|
||||
break;
|
||||
case 'payment_intent.payment_failed':
|
||||
@@ -136,7 +137,7 @@ class StripeProcessJob implements ShouldQueue
|
||||
|
||||
return;
|
||||
}
|
||||
send_internal_notification('Subscription payment failed for customer: '.$customerId);
|
||||
// send_internal_notification('Subscription payment failed for customer: '.$customerId);
|
||||
break;
|
||||
case 'customer.subscription.created':
|
||||
$customerId = data_get($data, 'customer');
|
||||
@@ -158,7 +159,7 @@ class StripeProcessJob implements ShouldQueue
|
||||
}
|
||||
$subscription = Subscription::where('team_id', $teamId)->first();
|
||||
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}");
|
||||
} else {
|
||||
Subscription::create([
|
||||
@@ -182,7 +183,7 @@ class StripeProcessJob implements ShouldQueue
|
||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
|
||||
if (! $subscription) {
|
||||
if ($status === 'incomplete_expired') {
|
||||
send_internal_notification('Subscription incomplete expired');
|
||||
// send_internal_notification('Subscription incomplete expired');
|
||||
throw new \RuntimeException('Subscription incomplete expired');
|
||||
}
|
||||
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 ($subscription->stripe_subscription_id === $subscriptionId) {
|
||||
$subscription->update([
|
||||
'stripe_past_due' => false,
|
||||
'stripe_invoice_paid' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class Help extends Component
|
||||
$type = set_transanctional_email_settings($settings);
|
||||
|
||||
// Sending feedback through Cloud API
|
||||
if ($type === false) {
|
||||
if (blank($type)) {
|
||||
$url = 'https://app.coolify.io/api/feedback';
|
||||
Http::post($url, [
|
||||
'content' => 'User: `'.auth()->user()?->email.'` with subject: `'.$this->subject.'` has the following problem: `'.$this->description.'`',
|
||||
|
||||
@@ -52,12 +52,6 @@ class DockerCompose extends Component
|
||||
'dockerComposeRaw' => 'required',
|
||||
]);
|
||||
$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();
|
||||
$environment = $project->load(['environments'])->environments->where('uuid', $this->parameters['environment_uuid'])->first();
|
||||
|
||||
|
||||
@@ -31,12 +31,22 @@ class EditCompose extends Component
|
||||
|
||||
public function refreshEnvs()
|
||||
{
|
||||
$this->service = Service::find($this->serviceId);
|
||||
$this->service = Service::ownedByCurrentTeam()->find($this->serviceId);
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -63,7 +63,7 @@ class StackForm extends Component
|
||||
public function saveCompose($raw)
|
||||
{
|
||||
$this->service->docker_compose_raw = $raw;
|
||||
$this->submit(notify: false);
|
||||
$this->submit(notify: true);
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
@@ -76,10 +76,6 @@ class StackForm extends Component
|
||||
{
|
||||
try {
|
||||
$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->saveExtraFields($this->fields);
|
||||
$this->service->parse();
|
||||
|
||||
@@ -2,15 +2,22 @@
|
||||
|
||||
namespace App\Livewire\Project\Shared\ScheduledTask;
|
||||
|
||||
use App\Models\ScheduledTask;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Component;
|
||||
|
||||
class Add extends Component
|
||||
{
|
||||
public $parameters;
|
||||
|
||||
#[Locked]
|
||||
public string $id;
|
||||
|
||||
#[Locked]
|
||||
public string $type;
|
||||
|
||||
#[Locked]
|
||||
public Collection $containerNames;
|
||||
|
||||
public string $name;
|
||||
@@ -21,8 +28,6 @@ class Add extends Component
|
||||
|
||||
public ?string $container = '';
|
||||
|
||||
protected $listeners = ['clearScheduledTask' => 'clear'];
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|string',
|
||||
'command' => 'required|string',
|
||||
@@ -60,18 +65,42 @@ class Add extends Component
|
||||
$this->container = $this->subServiceName;
|
||||
}
|
||||
}
|
||||
$this->dispatch('saveScheduledTask', [
|
||||
'name' => $this->name,
|
||||
'command' => $this->command,
|
||||
'frequency' => $this->frequency,
|
||||
'container' => $this->container,
|
||||
]);
|
||||
$this->saveScheduledTask();
|
||||
$this->clear();
|
||||
} catch (\Exception $e) {
|
||||
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()
|
||||
{
|
||||
$this->name = '';
|
||||
|
||||
@@ -4,20 +4,22 @@ namespace App\Livewire\Project\Shared\ScheduledTask;
|
||||
|
||||
use App\Models\ScheduledTask;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\On;
|
||||
use Livewire\Component;
|
||||
|
||||
class All extends Component
|
||||
{
|
||||
#[Locked]
|
||||
public $resource;
|
||||
|
||||
#[Locked]
|
||||
public array $parameters;
|
||||
|
||||
public Collection $containerNames;
|
||||
|
||||
public ?string $variables = null;
|
||||
|
||||
public array $parameters;
|
||||
|
||||
protected $listeners = ['refreshTasks', 'saveScheduledTask' => 'submit'];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
@@ -35,37 +37,10 @@ class All extends Component
|
||||
}
|
||||
}
|
||||
|
||||
#[On('refreshTasks')]
|
||||
public function refreshTasks()
|
||||
{
|
||||
$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();
|
||||
|
||||
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 {
|
||||
return redirect()->route('project.service.configuration', $this->parameters, $this->task->name);
|
||||
return redirect()->route('project.service.scheduled-tasks.show', $this->parameters);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e);
|
||||
|
||||
@@ -29,8 +29,6 @@ class Webhooks extends Component
|
||||
|
||||
public function mount()
|
||||
{
|
||||
// ray()->clearAll();
|
||||
// ray()->showQueries();
|
||||
$this->deploywebhook = generateDeployWebhook($this->resource);
|
||||
|
||||
$this->githubManualWebhookSecret = data_get($this->resource, 'manual_webhook_secret_github');
|
||||
|
||||
@@ -105,7 +105,6 @@ class Deploy extends Component
|
||||
|
||||
$startTime = Carbon::now()->getTimestamp();
|
||||
while ($process->running()) {
|
||||
ray('running');
|
||||
if (Carbon::now()->getTimestamp() - $startTime >= $timeout) {
|
||||
$this->forceStopContainer($containerName);
|
||||
break;
|
||||
|
||||
@@ -36,7 +36,7 @@ class SettingsEmail extends Component
|
||||
public ?int $smtpPort = null;
|
||||
|
||||
#[Validate(['nullable', 'string', 'in:starttls,tls,none'])]
|
||||
public ?string $smtpEncryption = null;
|
||||
public ?string $smtpEncryption = 'starttls';
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $smtpUsername = null;
|
||||
|
||||
@@ -101,7 +101,6 @@ class Change extends Component
|
||||
// ]);
|
||||
// }
|
||||
|
||||
// ray($runners_by_repository);
|
||||
// }
|
||||
|
||||
public function mount()
|
||||
|
||||
@@ -17,6 +17,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Stringable;
|
||||
use OpenApi\Attributes as OA;
|
||||
@@ -24,6 +25,7 @@ use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
||||
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
||||
use Spatie\Url\Url;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
#[OA\Schema(
|
||||
description: 'Server model',
|
||||
@@ -101,11 +103,13 @@ class Server extends BaseModel
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
} else {
|
||||
StandaloneDocker::create([
|
||||
$standaloneDocker = new StandaloneDocker([
|
||||
'name' => 'coolify',
|
||||
'uuid' => (string) new Cuid2,
|
||||
'network' => 'coolify',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
$standaloneDocker->saveQuietly();
|
||||
}
|
||||
}
|
||||
if (! isset($server->proxy->redirect_enabled)) {
|
||||
@@ -437,10 +441,6 @@ class Server extends BaseModel
|
||||
"mkdir -p $dynamic_config_path",
|
||||
"echo '$base64' | base64 -d | tee $file > /dev/null",
|
||||
], $this);
|
||||
|
||||
if (config('app.env') === 'local') {
|
||||
// ray($yaml);
|
||||
}
|
||||
}
|
||||
} elseif ($this->proxyType() === '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
|
||||
{
|
||||
if ($this->isFunctional()) {
|
||||
@@ -970,10 +954,8 @@ $schema://$host {
|
||||
}
|
||||
});
|
||||
if ($supported->count() === 1) {
|
||||
// ray('supported');
|
||||
return str($supported->first());
|
||||
} else {
|
||||
// ray('not supported');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1042,7 +1024,7 @@ $schema://$host {
|
||||
$unreachableNotificationSent = (bool) $this->unreachable_notification_sent;
|
||||
$isReachable = (bool) $this->settings->is_reachable;
|
||||
|
||||
\Log::debug('Server reachability check', [
|
||||
Log::debug('Server reachability check', [
|
||||
'server_id' => $this->id,
|
||||
'is_reachable' => $isReachable,
|
||||
'notification_sent' => $unreachableNotificationSent,
|
||||
@@ -1054,7 +1036,7 @@ $schema://$host {
|
||||
$this->save();
|
||||
|
||||
if ($unreachableNotificationSent === true) {
|
||||
\Log::debug('Server is now reachable, sending notification', [
|
||||
Log::debug('Server is now reachable, sending notification', [
|
||||
'server_id' => $this->id,
|
||||
]);
|
||||
$this->sendReachableNotification();
|
||||
@@ -1064,7 +1046,7 @@ $schema://$host {
|
||||
}
|
||||
|
||||
$this->increment('unreachable_count');
|
||||
\Log::debug('Incremented unreachable count', [
|
||||
Log::debug('Incremented unreachable count', [
|
||||
'server_id' => $this->id,
|
||||
'new_count' => $this->unreachable_count,
|
||||
]);
|
||||
@@ -1072,7 +1054,7 @@ $schema://$host {
|
||||
if ($this->unreachable_count === 1) {
|
||||
$this->settings->is_reachable = true;
|
||||
$this->settings->save();
|
||||
\Log::debug('First unreachable attempt, marking as reachable', [
|
||||
Log::debug('First unreachable attempt, marking as reachable', [
|
||||
'server_id' => $this->id,
|
||||
]);
|
||||
|
||||
@@ -1083,7 +1065,7 @@ $schema://$host {
|
||||
$failedChecks = 0;
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$status = $this->serverStatus();
|
||||
\Log::debug('Additional reachability check', [
|
||||
Log::debug('Additional reachability check', [
|
||||
'server_id' => $this->id,
|
||||
'attempt' => $i + 1,
|
||||
'status' => $status,
|
||||
@@ -1095,7 +1077,7 @@ $schema://$host {
|
||||
}
|
||||
|
||||
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,
|
||||
]);
|
||||
$this->sendUnreachableNotification();
|
||||
|
||||
@@ -93,6 +93,15 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
|
||||
return $servers >= $serverLimit;
|
||||
}
|
||||
|
||||
public function subscriptionPastOverDue()
|
||||
{
|
||||
if (isCloud()) {
|
||||
return $this->subscription?->stripe_past_due;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function serverOverflow()
|
||||
{
|
||||
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_invoice_paid' => false,
|
||||
'stripe_trial_already_ended' => false,
|
||||
'stripe_past_due' => false,
|
||||
]);
|
||||
foreach ($this->servers as $server) {
|
||||
$server->settings()->update([
|
||||
|
||||
@@ -44,7 +44,7 @@ class DeploymentSuccess extends CustomEmailNotification
|
||||
if (str($this->fqdn)->explode(',')->count() > 1) {
|
||||
$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
|
||||
|
||||
@@ -34,7 +34,7 @@ class StatusChanged extends CustomEmailNotification
|
||||
if (str($this->fqdn)->explode(',')->count() > 1) {
|
||||
$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
|
||||
|
||||
@@ -50,11 +50,9 @@ class EmailChannel
|
||||
|
||||
if ($emailSettings->use_instance_email_settings) {
|
||||
$type = set_transanctional_email_settings();
|
||||
if (! $type) {
|
||||
if (blank($type)) {
|
||||
throw new Exception('No email settings found.');
|
||||
}
|
||||
config()->set('mail.default', $type);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class TransactionalEmailChannel
|
||||
private function bootConfigs(): void
|
||||
{
|
||||
$type = set_transanctional_email_settings();
|
||||
if (! $type) {
|
||||
if (blank($type)) {
|
||||
throw new Exception('No email settings found.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Notifications\TransactionalEmails;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use Exception;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
@@ -35,8 +36,8 @@ class ResetPassword extends Notification
|
||||
public function via($notifiable)
|
||||
{
|
||||
$type = set_transanctional_email_settings();
|
||||
if (! $type) {
|
||||
throw new \Exception('No email settings found.');
|
||||
if (blank($type)) {
|
||||
throw new Exception('No email settings found.');
|
||||
}
|
||||
|
||||
return ['mail'];
|
||||
|
||||
@@ -4,9 +4,9 @@ namespace App\Traits;
|
||||
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\PushoverChannel;
|
||||
use App\Notifications\Channels\SlackChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\PushoverChannel;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
trait HasNotificationSettings
|
||||
|
||||
@@ -91,8 +91,6 @@ function next_queuable(string $server_id, string $application_id): bool
|
||||
$server = Server::find($server_id);
|
||||
$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) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -778,7 +778,6 @@ function convertDockerRunToCompose(?string $custom_docker_run_options = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
ray($payload);
|
||||
$compose_options->put('deploy', [
|
||||
'resources' => [
|
||||
'reservations' => [
|
||||
@@ -829,26 +828,29 @@ function generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker
|
||||
|
||||
function validateComposeFile(string $compose, int $server_id): string|Throwable
|
||||
{
|
||||
return 'OK';
|
||||
$uuid = Str::random(18);
|
||||
$server = Server::ownedByCurrentTeam()->find($server_id);
|
||||
try {
|
||||
$uuid = Str::random(10);
|
||||
$server = Server::findOrFail($server_id);
|
||||
if (! $server) {
|
||||
throw new \Exception('Server not found');
|
||||
}
|
||||
$base64_compose = base64_encode($compose);
|
||||
$output = instant_remote_process([
|
||||
instant_remote_process([
|
||||
"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);
|
||||
ray($output);
|
||||
|
||||
return 'OK';
|
||||
} catch (\Throwable $e) {
|
||||
ray($e);
|
||||
|
||||
return $e->getMessage();
|
||||
} finally {
|
||||
if (filled($server)) {
|
||||
instant_remote_process([
|
||||
"rm /tmp/{$uuid}.yml",
|
||||
], $server);
|
||||
], $server, throwError: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null
|
||||
{
|
||||
$settings = instanceSettings();
|
||||
$type = set_transanctional_email_settings($settings);
|
||||
if (! $type) {
|
||||
if (blank($type)) {
|
||||
throw new Exception('No email settings found.');
|
||||
}
|
||||
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) {
|
||||
$settings = instanceSettings();
|
||||
}
|
||||
config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
|
||||
config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
|
||||
if (! data_get($settings, 'smtp_enabled') && ! data_get($settings, 'resend_enabled')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (data_get($settings, 'resend_enabled')) {
|
||||
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'));
|
||||
|
||||
return 'resend';
|
||||
@@ -76,6 +80,8 @@ function set_transanctional_email_settings(?InstanceSettings $settings = null):
|
||||
};
|
||||
|
||||
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.mailers.smtp', [
|
||||
'transport' => 'smtp',
|
||||
@@ -91,6 +97,4 @@ function set_transanctional_email_settings(?InstanceSettings $settings = null):
|
||||
|
||||
return 'smtp';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -748,6 +748,7 @@ function parseCommandFromMagicEnvVariable(Str|string $key): Stringable
|
||||
{
|
||||
$value = str($key);
|
||||
$count = substr_count($value->value(), '_');
|
||||
$command = null;
|
||||
if ($count === 2) {
|
||||
if ($value->startsWith('SERVICE_FQDN') || $value->startsWith('SERVICE_URL')) {
|
||||
// SERVICE_FQDN_UMAMI
|
||||
@@ -800,7 +801,6 @@ function parseEnvVariable(Str|string $value)
|
||||
} else {
|
||||
// SERVICE_BASE64_64_UMAMI
|
||||
$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;
|
||||
foreach ($dns_servers as $dns_server) {
|
||||
try {
|
||||
ray("Checking $host on $dns_server");
|
||||
$query = new DNSQuery($dns_server);
|
||||
$results = $query->query($host, $type);
|
||||
if ($results === false || $query->hasError()) {
|
||||
@@ -961,13 +960,10 @@ function validate_dns_entry(string $fqdn, Server $server)
|
||||
foreach ($results as $result) {
|
||||
if ($result->getType() == $type) {
|
||||
if (ip_match($result->getData(), $cloudflare_ips->toArray(), $match)) {
|
||||
ray("Found match in Cloudflare IPs: $match");
|
||||
$found_matching_ip = true;
|
||||
break;
|
||||
}
|
||||
if ($result->getData() === $ip) {
|
||||
ray($host.' has IP address '.$result->getData());
|
||||
ray($result->getString());
|
||||
$found_matching_ip = true;
|
||||
break;
|
||||
}
|
||||
@@ -977,7 +973,6 @@ function validate_dns_entry(string $fqdn, Server $server)
|
||||
} catch (\Exception) {
|
||||
}
|
||||
}
|
||||
ray("Found match: $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);
|
||||
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
|
||||
ray('setting isDirectory to true');
|
||||
$isDirectory = true;
|
||||
}
|
||||
}
|
||||
@@ -1499,7 +1493,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
$serviceLabels->push("$removedLabelName=$removedLabel");
|
||||
}
|
||||
}
|
||||
|
||||
$containerName = "$serviceName-{$resource->uuid}";
|
||||
|
||||
// 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 isDirectory is not set & content is also not set, we assume it is a directory
|
||||
ray('setting isDirectory to 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) {
|
||||
return $value == $definedNetwork;
|
||||
});
|
||||
@@ -2956,7 +2945,6 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
}
|
||||
|
||||
$parsedServices = collect([]);
|
||||
// ray()->clearAll();
|
||||
|
||||
$allMagicEnvironments = collect([]);
|
||||
foreach ($services as $serviceName => $service) {
|
||||
@@ -3016,7 +3004,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
$environment = $environment->merge($buildArgs);
|
||||
|
||||
// convert environment variables to one format
|
||||
$environment = convertComposeEnvironmentToArray($environment);
|
||||
$environment = convertToKeyValueCollection($environment);
|
||||
|
||||
// Add Coolify defined environments
|
||||
$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', []));
|
||||
$environment = $environment->merge($buildArgs);
|
||||
|
||||
$environment = convertComposeEnvironmentToArray($environment);
|
||||
$environment = convertToKeyValueCollection($environment);
|
||||
$coolifyEnvironments = collect([]);
|
||||
|
||||
$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([]);
|
||||
if (isAssociativeArray($environment)) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @see https://github.com/pionl/laravel-chunk-upload
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
return [
|
||||
'coolify' => [
|
||||
'version' => '4.0.0-beta.396',
|
||||
'version' => '4.0.0-beta.398',
|
||||
'helper_version' => '1.0.7',
|
||||
'realtime_version' => '1.0.6',
|
||||
'self_hosted' => env('SELF_HOSTED', true),
|
||||
|
||||
@@ -38,7 +38,7 @@ return [
|
||||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'url' => env('DATABASE_URL'),
|
||||
'host' => env('DB_HOST', 'postgres'),
|
||||
'host' => env('DB_HOST', 'coolify-db'),
|
||||
'port' => env('DB_PORT', '5432'),
|
||||
'database' => env('DB_DATABASE', 'coolify'),
|
||||
'username' => env('DB_USERNAME', 'coolify'),
|
||||
|
||||
@@ -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
|
||||
timeout: 2s
|
||||
soketi:
|
||||
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.5'
|
||||
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.6'
|
||||
ports:
|
||||
- "${SOKETI_PORT:-6001}:6001"
|
||||
- "6002:6002"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.382"
|
||||
"version": "4.0.0-beta.397"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0-beta.383"
|
||||
"version": "4.0.0-beta.398"
|
||||
},
|
||||
"helper": {
|
||||
"version": "1.0.7"
|
||||
|
||||
@@ -79,6 +79,16 @@
|
||||
</x-slot:button-text>
|
||||
</x-popup>
|
||||
</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())
|
||||
<x-banner :closable=false>
|
||||
<div><span class="font-bold text-red-500">WARNING:</span> The number of active servers exceeds the limit
|
||||
|
||||
@@ -176,7 +176,9 @@
|
||||
</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
|
||||
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)
|
||||
<livewire:project.service.storage wire:key="application-{{ $application->id }}"
|
||||
:resource="$application" />
|
||||
|
||||
@@ -20,17 +20,15 @@
|
||||
</x-forms.textarea>
|
||||
</div>
|
||||
<div class="pt-2 flex gap-2">
|
||||
<div class="flex flex-col gap-2">
|
||||
<x-forms.checkbox label="Escape special characters in labels?"
|
||||
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."
|
||||
id="service.is_container_label_escape_enabled" instantSave></x-forms.checkbox>
|
||||
<div class="flex-1"></div>
|
||||
<div x-cloak x-show="raw">
|
||||
<x-forms.button class="w-32" @click.prevent="showNormalTextarea = !showNormalTextarea">Switch
|
||||
Textarea</x-forms.button>
|
||||
<x-forms.checkbox label="Show Normal Textarea" x-model="showNormalTextarea"></x-forms.checkbox>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex justify-end w-full gap-2 pt-4">
|
||||
<div class="flex items-end gap-2">
|
||||
<div class="flex w-full gap-2 pt-4">
|
||||
<div x-cloak x-show="raw">
|
||||
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Deployable Compose</x-forms.button>
|
||||
</div>
|
||||
@@ -38,9 +36,13 @@
|
||||
<x-forms.button class="w-64" @click.prevent="raw = !raw">Show Source
|
||||
Compose</x-forms.button>
|
||||
</div>
|
||||
</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
|
||||
</x-forms.button>
|
||||
</div>
|
||||
|
||||
@@ -21,15 +21,6 @@
|
||||
]" confirmationText="{{ $fs_path }}"
|
||||
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
|
||||
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
|
||||
submitAction="delete" :checkboxes="$directoryDeletionCheckboxes" :actions="[
|
||||
'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"
|
||||
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
|
||||
@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
|
||||
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"
|
||||
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
|
||||
@endif
|
||||
|
||||
@if (!$fileStorage->is_based_on_git)
|
||||
{{-- @if (!$fileStorage->is_based_on_git)
|
||||
<x-modal-confirmation isErrorButton buttonTitle="Delete">
|
||||
<div class="px-2">This storage will be deleted. It is not reversible. <strong
|
||||
class="text-error">Please
|
||||
@@ -58,7 +54,7 @@
|
||||
label="Permanently delete file from the server?"></x-forms.checkbox>
|
||||
@endif
|
||||
</x-modal-confirmation>
|
||||
@endif
|
||||
@endif --}}
|
||||
</div>
|
||||
@if (!$fileStorage->is_directory)
|
||||
@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>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<h2>Scheduled Tasks</h2>
|
||||
<x-modal-input buttonTitle="+ Add" title="New Scheduled Task" :closeOutside="false">
|
||||
@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')
|
||||
<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
|
||||
</x-modal-input>
|
||||
</div>
|
||||
|
||||
@@ -132,12 +132,12 @@
|
||||
<h4 class="py-4">Confirmation Settings</h4>
|
||||
|
||||
@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"
|
||||
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>
|
||||
@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?"
|
||||
buttonTitle="Disable Two Step Confirmation" isErrorButton submitAction="toggleTwoStepConfirmation"
|
||||
:actions="[
|
||||
|
||||
@@ -16,7 +16,7 @@ class LoginTest extends DuskTestCase
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function testLogin()
|
||||
public function test_login()
|
||||
{
|
||||
$this->browse(callback: function (Browser $browser) {
|
||||
$browser->loginWithRootUser()
|
||||
|
||||
@@ -16,7 +16,7 @@ class ProjectAddNewTest extends DuskTestCase
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function testLogin()
|
||||
public function test_login()
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser->loginWithRootUser()
|
||||
|
||||
@@ -16,7 +16,7 @@ class ProjectSearchTest extends DuskTestCase
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function testLogin()
|
||||
public function test_login()
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser->loginWithRootUser()
|
||||
|
||||
@@ -16,7 +16,7 @@ class ProjectTest extends DuskTestCase
|
||||
*
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function testLogin()
|
||||
public function test_login()
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser->loginWithRootUser()
|
||||
|
||||
@@ -358,7 +358,7 @@ use Symfony\Component\Yaml\Yaml;
|
||||
// expect($output)->toContain('Docker version');
|
||||
// });
|
||||
|
||||
// test('ConvertComposeEnvironmentToArray', function () {
|
||||
// test('convertToKeyValueCollection', function () {
|
||||
// ray()->clearAll();
|
||||
// $yaml = '
|
||||
// services:
|
||||
@@ -374,9 +374,9 @@ use Symfony\Component\Yaml\Yaml;
|
||||
// - POSTGRES_DB: activepieces
|
||||
// ';
|
||||
// $parsedYaml = Yaml::parse($yaml);
|
||||
// $output = convertComposeEnvironmentToArray($parsedYaml['services']['activepieces']['environment']);
|
||||
// $output2 = convertComposeEnvironmentToArray($parsedYaml['services']['activepieces2']['environment']);
|
||||
// $dboutput = convertComposeEnvironmentToArray($parsedYaml['services']['postgres']['environment']);
|
||||
// $output = convertToKeyValueCollection($parsedYaml['services']['activepieces']['environment']);
|
||||
// $output2 = convertToKeyValueCollection($parsedYaml['services']['activepieces2']['environment']);
|
||||
// $dboutput = convertToKeyValueCollection($parsedYaml['services']['postgres']['environment']);
|
||||
// ray($output);
|
||||
// ray($output2);
|
||||
// ray($dboutput);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Test Case
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.396"
|
||||
"version": "4.0.0-beta.398"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0-beta.397"
|
||||
"version": "4.0.0-beta.399"
|
||||
},
|
||||
"helper": {
|
||||
"version": "1.0.7"
|
||||
|
||||
Reference in New Issue
Block a user