Merge branch 'next' into fix-autocompletes

This commit is contained in:
🏔️ Peak
2024-10-31 13:41:00 +01:00
committed by GitHub
451 changed files with 14494 additions and 4813 deletions

View File

@@ -184,10 +184,6 @@ export function initializeTerminalComponent() {
// Copy and paste functionality
this.term.attachCustomKeyEventHandler((arg) => {
if (arg.ctrlKey && arg.code === "KeyV" && arg.type === "keydown") {
navigator.clipboard.readText()
.then(text => {
this.socket.send(JSON.stringify({ message: text }));
});
return false;
}

View File

@@ -5,6 +5,13 @@
Coolify
</a>
<div class="w-full bg-white shadow md:mt-0 sm:max-w-md xl:p-0 dark:bg-base ">
@if ($errors->any())
<div class="text-center text-error">
@foreach ($errors->all() as $error)
<p>{{ $error }}</p>
@endforeach
</div>
@endif
<div class="p-6 space-y-4 md:space-y-6 sm:p-8">
<form action="/login" method="POST" class="flex flex-col gap-2">
@csrf
@@ -27,28 +34,22 @@
{{ __('auth.forgot_password') }}?
</a>
@endenv
<x-forms.button class="mt-10" type="submit">{{ __('auth.login') }}</x-forms.button>
<x-forms.button class="mt-4" type="submit">{{ __('auth.login') }}</x-forms.button>
@if (session('error'))
<div class="mb-4 font-medium text-red-600">
{{ session('error') }}
</div>
@endif
@if (!$is_registration_enabled)
<div class="text-center text-neutral-500">{{ __('auth.registration_disabled') }}</div>
@endif
@if ($errors->any())
<div class="text-xs text-center text-error">
@foreach ($errors->all() as $error)
<p>{{ $error }}</p>
@endforeach
</div>
@endif
@if (session('status'))
<div class="mb-4 font-medium text-green-600">
{{ session('status') }}
</div>
@endif
@if (session('error'))
<div class="mb-4 font-medium text-red-600">
{{ session('error') }}
</div>
@endif
</form>
@if ($is_registration_enabled)
<a href="/register" class="button bg-coollabs-gradient">

View File

@@ -32,25 +32,19 @@ $email = getOldOrLocal('email', 'test3@example.com');
label="{{ __('input.name') }}" />
<x-forms.input id="email" required type="email" name="email" value="{{ $email }}"
label="{{ __('input.email') }}" />
<div class="flex gap-2">
<x-forms.input id="password" required type="password" name="password"
label="{{ __('input.password') }}" />
<x-forms.input id="password_confirmation" required type="password"
name="password_confirmation" label="{{ __('input.password.again') }}" />
<x-forms.input id="password" required type="password" name="password"
label="{{ __('input.password') }}" />
<x-forms.input id="password_confirmation" required type="password" name="password_confirmation"
label="{{ __('input.password.again') }}" />
<div class="text-xs w-full">Your password should be min 8 characters long and contain
at least one uppercase letter, one lowercase letter, one number, and one symbol.</div>
<div class="flex flex-col gap-4 pt-8 w-full">
<x-forms.button class="w-full" type="submit">Register</x-forms.button>
<a href="/login" class="w-full text-xs">
{{ __('auth.already_registered') }}
</a>
</div>
<x-forms.button class="mb-4" type="submit">Register</x-forms.button>
<a href="/login" class="button bg-coollabs-gradient">
{{ __('auth.already_registered') }}
</a>
</form>
@if ($errors->any())
<div class="text-xs text-center text-error">
@foreach ($errors->all() as $error)
<p>{{ $error }}</p>
@endforeach
</div>
@endif
</div>
</div>
</div>

View File

@@ -14,7 +14,7 @@
'w-full' => $fullWidth,
])>
@if (!$hideLabel)
<label class="flex gap-4 px-0 min-w-fit label">
<label @class(['flex gap-4 px-0 min-w-fit label', 'opacity-40' => $disabled])>
<span class="flex gap-2">
@if ($label)
{!! $label !!}
@@ -31,4 +31,5 @@
<input @disabled($disabled) type="checkbox" {{ $attributes->merge(['class' => $defaultClass]) }}
@if ($instantSave) wire:loading.attr="disabled" wire:click='{{ $instantSave === 'instantSave' || $instantSave == '1' ? 'instantSave' : $instantSave }}'
wire:model={{ $id }} @else wire:model={{ $value ?? $id }} @endif />
</div>

View File

@@ -1,6 +1,7 @@
@props([
'title' => 'Are you sure?',
'isErrorButton' => false,
'isHighlightedButton' => false,
'buttonTitle' => 'Confirm Action',
'buttonFullWidth' => false,
'customButton' => null,
@@ -22,18 +23,23 @@
'dispatchEventMessage' => '',
])
@php
use App\Models\InstanceSettings;
$disableTwoStepConfirmation = data_get(InstanceSettings::get(), 'disable_two_step_confirmation');
@endphp
<div x-data="{
modalOpen: false,
step: {{ empty($checkboxes) ? 2 : 1 }},
initialStep: {{ empty($checkboxes) ? 2 : 1 }},
finalStep: {{ $confirmWithPassword ? 3 : 2 }},
finalStep: {{ $confirmWithPassword && !$disableTwoStepConfirmation ? 3 : 2 }},
deleteText: '',
password: '',
actions: @js($actions),
confirmationText: @js($confirmationText),
userConfirmationText: '',
confirmWithText: @js($confirmWithText),
confirmWithPassword: @js($confirmWithPassword),
confirmWithText: @js($confirmWithText && !$disableTwoStepConfirmation),
confirmWithPassword: @js($confirmWithPassword && !$disableTwoStepConfirmation),
copied: false,
submitAction: @js($submitAction),
passwordError: '',
@@ -41,6 +47,7 @@
dispatchEvent: @js($dispatchEvent),
dispatchEventType: @js($dispatchEventType),
dispatchEventMessage: @js($dispatchEventMessage),
disableTwoStepConfirmation: @js($disableTwoStepConfirmation),
resetModal() {
this.step = this.initialStep;
this.deleteText = '';
@@ -137,6 +144,16 @@
{{ $buttonTitle }}
</x-forms.button>
@endif
@elseif($isHighlightedButton)
@if ($buttonFullWidth)
<x-forms.button @click="modalOpen=true" class="flex gap-2 w-full" isHighlighted wire:target>
{{ $buttonTitle }}
</x-forms.button>
@else
<x-forms.button @click="modalOpen=true" class="flex gap-2" isHighlighted wire:target>
{{ $buttonTitle }}
</x-forms.button>
@endif
@else
@if ($buttonFullWidth)
<x-forms.button @click="modalOpen=true" class="flex gap-2 w-full" wire:target>
@@ -153,8 +170,8 @@
<template x-teleport="body">
<div x-show="modalOpen"
class="fixed top-0 lg:pt-10 left-0 z-[99] flex items-start justify-center w-screen h-screen" x-cloak>
<div x-show="modalOpen"
class="absolute inset-0 w-full h-full bg-black bg-opacity-20 backdrop-blur-sm"></div>
<div x-show="modalOpen" class="absolute inset-0 w-full h-full bg-black bg-opacity-20 backdrop-blur-sm">
</div>
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
@@ -172,7 +189,7 @@
</svg>
</button>
</div>
<div class="relative pb-8 w-auto">
<div class="relative w-auto">
@if (!empty($checkboxes))
<!-- Step 1: Select actions -->
<div x-show="step === 1">
@@ -187,6 +204,16 @@
x-bind:checked="selectedActions.includes('{{ $checkbox['id'] }}')" />
</div>
@endforeach
<div class="flex flex-wrap gap-2 justify-between mt-4">
<x-forms.button @click="modalOpen = false; resetModal()"
class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">
Cancel
</x-forms.button>
<x-forms.button @click="step++" class="w-auto" isError>
<span x-text="step1ButtonText"></span>
</x-forms.button>
</div>
</div>
@endif
@@ -222,44 +249,45 @@
</template>
@endforeach
</ul>
@if ($confirmWithText)
<div class="mb-4">
<h4 class="mb-2 text-lg font-semibold">Confirm Actions</h4>
<p class="mb-2 text-sm">{{ $confirmationLabel }}</p>
<div class="relative mb-2">
<input type="text" x-model="confirmationText"
class="p-2 pr-10 w-full text-black rounded cursor-text input" readonly>
<button @click="copyConfirmationText()"
class="absolute right-2 top-1/2 text-gray-500 transform -translate-y-1/2 hover:text-gray-700"
title="Copy confirmation text" x-ref="copyButton">
<template x-if="!copied">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 20 20"
fill="currentColor">
<path d="M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z" />
<path
d="M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z" />
</svg>
</template>
<template x-if="copied">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-green-500"
viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd" />
</svg>
</template>
</button>
</div>
@if (!$disableTwoStepConfirmation)
@if ($confirmWithText)
<div class="mb-4">
<h4 class="mb-2 text-lg font-semibold">Confirm Actions</h4>
<p class="mb-2 text-sm">{{ $confirmationLabel }}</p>
<div class="relative mb-2">
<input type="text" x-model="confirmationText"
class="p-2 pr-10 w-full text-black rounded cursor-text input" readonly>
<button @click="copyConfirmationText()"
class="absolute right-2 top-1/2 text-gray-500 transform -translate-y-1/2 hover:text-gray-700"
title="Copy confirmation text" x-ref="copyButton">
<template x-if="!copied">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5"
viewBox="0 0 20 20" fill="currentColor">
<path d="M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z" />
<path
d="M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z" />
</svg>
</template>
<template x-if="copied">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-green-500"
viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd" />
</svg>
</template>
</button>
</div>
<label for="userConfirmationText"
class="block mt-4 text-sm font-medium text-gray-700 dark:text-gray-300">
{{ $shortConfirmationLabel }}
</label>
<input type="text" x-model="userConfirmationText"
class="p-2 mt-1 w-full text-black rounded input">
</div>
<label for="userConfirmationText"
class="block mt-4 text-sm font-medium text-gray-700 dark:text-gray-300">
{{ $shortConfirmationLabel }}
</label>
<input type="text" x-model="userConfirmationText"
class="p-2 mt-1 w-full text-black rounded input">
</div>
@endif
@endif
</div>
<!-- Step 3: Password confirmation -->
<div x-show="step === 3 && confirmWithPassword">
@@ -284,65 +312,87 @@
@error('password')
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
@enderror
</div>
<div class="flex flex-wrap gap-2 justify-between mt-4">
@if (!empty($checkboxes))
<x-forms.button @click="step--"
class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">
Back
</x-forms.button>
@else
<x-forms.button @click="modalOpen = false; resetModal()"
class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">
Cancel
</x-forms.button>
@endif
<x-forms.button
x-bind:disabled="!disableTwoStepConfirmation && confirmWithText && userConfirmationText !==
confirmationText"
class="w-auto" isError
@click="
if (dispatchEvent) {
$wire.dispatch(dispatchEventType, dispatchEventMessage);
}
if (confirmWithPassword && !disableTwoStepConfirmation) {
step++;
} else {
modalOpen = false;
resetModal();
submitForm();
}
">
<span x-text="step2ButtonText"></span>
</x-forms.button>
</div>
</div>
</div>
<!-- Navigation buttons -->
<div class="flex flex-wrap gap-2 justify-between mt-4">
<template x-if="step > initialStep">
<x-forms.button @click="step--" class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">
Back
</x-forms.button>
</template>
<template x-if="step === initialStep">
<x-forms.button @click="modalOpen = false; resetModal()"
class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">
Cancel
</x-forms.button>
</template>
<template x-if="step === 1">
<x-forms.button @click="step++" class="w-auto" isError>
<span x-text="step1ButtonText"></span>
</x-forms.button>
</template>
<!-- Step 3: Password confirmation -->
@if (!$disableTwoStepConfirmation)
<div x-show="step === 3 && confirmWithPassword">
<div class="p-4 mb-4 text-white border-l-4 border-red-500 bg-error" role="alert">
<p class="font-bold">Final Confirmation</p>
<p>Please enter your password to confirm this destructive action.</p>
</div>
<div class="flex flex-col gap-2 mb-4">
<label for="password-confirm"
class="block text-sm font-medium text-gray-700 dark:text-gray-300">
Your Password
</label>
<form @submit.prevent @keydown.enter.prevent>
<input type="password" id="password-confirm" x-model="password"
class="w-full input" placeholder="Enter your password">
</form>
<p x-show="passwordError" x-text="passwordError" class="mt-1 text-sm text-red-500">
</p>
@error('password')
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
@enderror
</div>
<template x-if="step === 2">
<x-forms.button x-bind:disabled="confirmWithText && userConfirmationText !== confirmationText"
class="w-auto" isError
@click="
if (dispatchEvent) {
$wire.dispatch(dispatchEventType, dispatchEventMessage);
}
if (confirmWithPassword) {
step++;
} else {
modalOpen = false;
resetModal();
submitForm();
}">
<span x-text="step2ButtonText"></span>
</x-forms.button>
</template>
<template x-if="step === 3 && confirmWithPassword">
<x-forms.button x-bind:disabled="!password" class="w-auto" isError
@click="
if (dispatchEvent) {
$wire.dispatch(dispatchEventType, dispatchEventMessage);
}
submitForm().then((result) => {
if (result === true) {
modalOpen = false;
resetModal();
} else {
passwordError = result;
}
});
">
<span x-text="step3ButtonText"></span>
</x-forms.button>
</template>
<div class="flex flex-wrap gap-2 justify-between mt-4">
<x-forms.button @click="step--"
class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">
Back
</x-forms.button>
<x-forms.button x-bind:disabled="!password" class="w-auto" isError
@click="
if (dispatchEvent) {
$wire.dispatch(dispatchEventType, dispatchEventMessage);
}
submitForm().then((result) => {
if (result === true) {
modalOpen = false;
resetModal();
} else {
passwordError = result;
password = ''; // Clear the password field
}
});
">
<span x-text="step3ButtonText"></span>
</x-forms.button>
</div>
</div>
@endif
</div>
</div>
</div>

View File

@@ -2,6 +2,7 @@
'title' => 'Are you sure?',
'buttonTitle' => 'Open Modal',
'isErrorButton' => false,
'isHighlightedButton' => false,
'disabled' => false,
'action' => 'delete',
'content' => null,
@@ -18,6 +19,8 @@
<x-forms.button isError disabled>{{ $buttonTitle }}</x-forms.button>
@elseif ($isErrorButton)
<x-forms.button isError @click="modalOpen=true">{{ $buttonTitle }}</x-forms.button>
@elseif ($isHighlightedButton)
<x-forms.button isHighlighted @click="modalOpen=true">{{ $buttonTitle }}</x-forms.button>
@else
<x-forms.button @click="modalOpen=true">{{ $buttonTitle }}</x-forms.button>
@endif

View File

@@ -44,7 +44,7 @@
@if ($resource->getMorphClass() == 'App\Models\Service')
<x-status.services :service="$resource" />
@else
<x-status.index :resource="$resource" :lastDeploymentInfo="$lastDeploymentInfo" :lastDeploymentLink="$lastDeploymentLink" />
<x-status.index :resource="$resource" :title="$lastDeploymentInfo" :lastDeploymentLink="$lastDeploymentLink" />
@endif
</ol>
</nav>

View File

@@ -1,5 +1,14 @@
<div class="pb-6">
<livewire:server.proxy.modal :server="$server" />
<x-modal modalId="startProxy">
<x-slot:modalBody>
<livewire:activity-monitor header="Proxy Startup Logs" />
</x-slot:modalBody>
<x-slot:modalSubmit>
<x-forms.button onclick="startProxy.close()" type="submit">
Close
</x-forms.button>
</x-slot:modalSubmit>
</x-modal>
<div class="flex items-center gap-2">
<h1>Server</h1>
@if ($server->proxySet())
@@ -11,42 +20,31 @@
<nav class="flex items-center gap-6 overflow-x-scroll sm:overflow-x-hidden scrollbar min-h-10 whitespace-nowrap">
<a class="{{ request()->routeIs('server.show') ? 'dark:text-white' : '' }}"
href="{{ route('server.show', [
'server_uuid' => data_get($parameters, 'server_uuid'),
'server_uuid' => data_get($server, 'uuid'),
]) }}">
<button>General</button>
</a>
<a class="{{ request()->routeIs('server.private-key') ? 'dark:text-white' : '' }}"
href="{{ route('server.private-key', [
'server_uuid' => data_get($parameters, 'server_uuid'),
]) }}">
<button>Private Key</button>
</a>
<a class="{{ request()->routeIs('server.resources') ? 'dark:text-white' : '' }}"
href="{{ route('server.resources', [
'server_uuid' => data_get($parameters, 'server_uuid'),
]) }}">
<button>Resources</button>
<button>Configuration</button>
</a>
@if (!$server->isSwarmWorker() && !$server->settings->is_build_server)
<a class="{{ request()->routeIs('server.proxy') ? 'dark:text-white' : '' }}"
href="{{ route('server.proxy', [
'server_uuid' => data_get($parameters, 'server_uuid'),
'server_uuid' => data_get($server, 'uuid'),
]) }}">
<button>Proxy</button>
</a>
<a class="{{ request()->routeIs('server.destinations') ? 'dark:text-white' : '' }}"
href="{{ route('server.destinations', [
'server_uuid' => data_get($parameters, 'server_uuid'),
]) }}">
<button>Destinations</button>
</a>
<a class="{{ request()->routeIs('server.log-drains') ? 'dark:text-white' : '' }}"
href="{{ route('server.log-drains', [
'server_uuid' => data_get($parameters, 'server_uuid'),
]) }}">
<button>Log Drains</button>
</a>
@endif
<a class="{{ request()->routeIs('server.resources') ? 'dark:text-white' : '' }}"
href="{{ route('server.resources', [
'server_uuid' => data_get($server, 'uuid'),
]) }}">
<button>Resources</button>
</a>
<a class="{{ request()->routeIs('server.command') ? 'dark:text-white' : '' }}"
href="{{ route('server.command', [
'server_uuid' => data_get($server, 'uuid'),
]) }}">
<button>Terminal</button>
</a>
</nav>
<div class="order-first sm:order-last">
<livewire:server.proxy.deploy :server="$server" />

View File

@@ -0,0 +1,16 @@
@if ($server->proxySet())
<div class="flex flex-col items-start gap-2 min-w-fit">
<a class="{{ request()->routeIs('server.proxy') ? 'menu-item menu-item-active' : 'menu-item' }}"
href="{{ route('server.proxy', $parameters) }}">
<button>Configuration</button>
</a>
<a class="{{ request()->routeIs('server.proxy.dynamic-confs') ? 'menu-item menu-item-active' : 'menu-item' }}"
href="{{ route('server.proxy.dynamic-confs', $parameters) }}">
<button>Dynamic Configurations</button>
</a>
<a class="{{ request()->routeIs('server.proxy.logs') ? 'menu-item menu-item-active' : 'menu-item' }}"
href="{{ route('server.proxy.logs', $parameters) }}">
<button>Logs</button>
</a>
</div>
@endif

View File

@@ -1,18 +1,29 @@
@if ($server->proxySet())
<div class="flex h-full pr-4">
<div class="flex flex-col w-48 gap-4 min-w-fit">
<a class="{{ request()->routeIs('server.proxy') ? 'dark:text-white' : '' }}"
href="{{ route('server.proxy', $parameters) }}">
<button>Configuration</button>
</a>
<a class="{{ request()->routeIs('server.proxy.dynamic-confs') ? 'dark:text-white' : '' }}"
href="{{ route('server.proxy.dynamic-confs', $parameters) }}">
<button>Dynamic Configurations</button>
</a>
<a class="{{ request()->routeIs('server.proxy.logs') ? 'dark:text-white' : '' }}"
href="{{ route('server.proxy.logs', $parameters) }}">
<button>Logs</button>
</a>
</div>
</div>
@endif
<div class="flex flex-col items-start gap-2 min-w-fit">
<a class="menu-item {{ $activeMenu === 'general' ? 'menu-item-active' : '' }}"
href="{{ route('server.show', ['server_uuid' => $server->uuid]) }}" wire:navigate>General</a>
@if ($server->isFunctional())
<a class="menu-item {{ $activeMenu === 'advanced' ? 'menu-item-active' : '' }}"
href="{{ route('server.advanced', ['server_uuid' => $server->uuid]) }}" wire:navigate>Advanced
</a>
@endif
<a class="menu-item {{ $activeMenu === 'private-key' ? 'menu-item-active' : '' }}"
href="{{ route('server.private-key', ['server_uuid' => $server->uuid]) }}" wire:navigate>Private Key
</a>
@if ($server->isFunctional())
<a class="menu-item {{ $activeMenu === 'cloudflare-tunnels' ? 'menu-item-active' : '' }}"
href="{{ route('server.cloudflare-tunnels', ['server_uuid' => $server->uuid]) }}" wire:navigate>Cloudflare
Tunnels</a>
<a class="menu-item {{ $activeMenu === 'destinations' ? 'menu-item-active' : '' }}"
href="{{ route('server.destinations', ['server_uuid' => $server->uuid]) }}" wire:navigate>Destinations
</a>
<a class="menu-item {{ $activeMenu === 'log-drains' ? 'menu-item-active' : '' }}"
href="{{ route('server.log-drains', ['server_uuid' => $server->uuid]) }}" wire:navigate>Log
Drains</a>
<a class="menu-item {{ $activeMenu === 'metrics' ? 'menu-item-active' : '' }}"
href="{{ route('server.charts', ['server_uuid' => $server->uuid]) }}">Metrics</a>
@endif
@if (!$server->isLocalhost())
<a class="menu-item {{ $activeMenu === 'danger' ? 'menu-item-active' : '' }}"
href="{{ route('server.delete', ['server_uuid' => $server->uuid]) }}" wire:navigate>Danger</a>
@endif
</div>

View File

@@ -1,14 +1,14 @@
@props([
'lastDeploymentInfo' => null,
'title' => null,
'lastDeploymentLink' => null,
'resource' => null,
])
@if (str($resource->status)->startsWith('running'))
<x-status.running :status="$resource->status" :lastDeploymentInfo="$lastDeploymentInfo" :lastDeploymentLink="$lastDeploymentLink" />
<x-status.running :status="$resource->status" :title="$title" :lastDeploymentLink="$lastDeploymentLink" />
@elseif(str($resource->status)->startsWith('restarting') ||
str($resource->status)->startsWith('starting') ||
str($resource->status)->startsWith('degraded'))
<x-status.restarting :status="$resource->status" :lastDeploymentInfo="$lastDeploymentInfo" :lastDeploymentLink="$lastDeploymentLink" />
<x-status.restarting :status="$resource->status" :title="$title" :lastDeploymentLink="$lastDeploymentLink" />
@else
<x-status.stopped :status="$resource->status" />
@endif

View File

@@ -1,6 +1,6 @@
@props([
'status' => 'Restarting',
'lastDeploymentInfo' => null,
'title' => null,
'lastDeploymentLink' => null,
'noLoading' => false,
])
@@ -10,7 +10,7 @@
@endif
<span wire:loading.remove.delay.longer class="flex items-center">
<div class="badge badge-warning "></div>
<div class="pl-2 pr-1 text-xs font-bold tracking-wider dark:text-warning" @if($lastDeploymentInfo) title="{{$lastDeploymentInfo}}" @endif>
<div class="pl-2 pr-1 text-xs font-bold tracking-wider dark:text-warning" @if($title) title="{{$title}}" @endif>
@if ($lastDeploymentLink)
<a href="{{ $lastDeploymentLink }}" target="_blank" class="underline cursor-pointer">
{{ str($status)->before(':')->headline() }}

View File

@@ -1,6 +1,6 @@
@props([
'status' => 'Running',
'lastDeploymentInfo' => null,
'title' => null,
'lastDeploymentLink' => null,
'noLoading' => false,
])
@@ -10,7 +10,7 @@
@endif
<span wire:loading.remove.delay.longer class="flex items-center">
<div class="badge badge-success "></div>
<div class="pl-2 pr-1 text-xs font-bold tracking-wider text-success" @if($lastDeploymentInfo) title="{{$lastDeploymentInfo}}" @endif>
<div class="pl-2 pr-1 text-xs font-bold tracking-wider text-success" @if($title) title="{{$title}}" @endif>
@if ($lastDeploymentLink)
<a href="{{ $lastDeploymentLink }}" target="_blank" class="underline cursor-pointer">
{{ str($status)->before(':')->headline() }}

View File

@@ -1,2 +1,4 @@
<a {{ $attributes->merge(['class' => 'text-xs cursor-pointer opacity-90 hover:opacity-100 dark:hover:text-white hover:text-black']) }}
href="https://github.com/coollabsio/coolify/releases/tag/v{{ config('version') }}">v{{ config('version') }}</a>
href="https://github.com/coollabsio/coolify/releases/tag/v{{ config('version') }}" target="_blank">
v{{ config('version') }}
</a>

View File

@@ -17,12 +17,12 @@
@elseif ($currentState === 'explanation')
<x-boarding-step title="What is Coolify?">
<x-slot:question>
Coolify is an all-in-one application to automate tasks on your servers, deploy application with
Coolify is an all-in-one application to automate tasks on your servers, deploy applications with
Git
integrations, deploy databases and services, monitor these resources with notifications and
alerts
without vendor lock-in
and <a href="https://coolify.io" class="dark:text-white hover:underline">much much more</a>.
without vendor lock-in. <br/>
<a href="https://coolify.io" class="dark:text-white hover:underline">Coolify Home</a>.
<br><br>
<span class="text-xl">
<x-highlighted text="Self-hosting with superpowers!" /></span>

View File

@@ -5,10 +5,10 @@
<x-modal-input buttonTitle="+ Add" title="New Destination">
<livewire:destination.new.docker :server_id="$server->id" />
</x-modal-input>
<x-forms.button wire:click='scan'>Scan Destinations</x-forms.button>
<x-forms.button wire:click='scan'>Scan for Destinations</x-forms.button>
</div>
<div class="pt-2 pb-6 ">Destinations are used to segregate resources by network.</div>
<div class="flex gap-2 ">
<div>Destinations are used to segregate resources by network.</div>
<div class="flex gap-2 pt-6">
Available for using:
@forelse ($server->standaloneDockers as $docker)
<a href="{{ route('destination.show', ['destination_uuid' => data_get($docker, 'uuid')]) }}">

View File

@@ -37,6 +37,8 @@
label="Backup Status" />
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_scheduled_tasks"
label="Scheduled Tasks Status" />
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_server_disk_usage"
label="Server Disk Usage" />
</div>
@endif
</div>

View File

@@ -116,6 +116,8 @@
label="Backup Status" />
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_scheduled_tasks"
label="Scheduled Tasks Status" />
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_server_disk_usage"
label="Server Disk Usage" />
</div>
@endif
</div>

View File

@@ -72,7 +72,11 @@
helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
id="team.telegram_notifications_scheduled_tasks_thread_id" label="Custom Topic ID" />
</div>
<div class="flex flex-col">
<h4>Server Disk Usage</h4>
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_server_disk_usage"
label="Enabled" />
</div>
</div>
@endif
</form>

View File

@@ -33,20 +33,66 @@
Please finish configuring two factor authentication below. Read the QR code or enter the secret key
manually.
</div>
<div class="flex flex-col gap-2">
<div class="flex flex-col gap-4">
<form action="/user/confirmed-two-factor-authentication" method="POST" class="flex items-end gap-2">
@csrf
<x-forms.input type="number" id="code" label="One-time code" required />
<x-forms.input type="text" inputmode="numeric" pattern="[0-9]*" id="code" label="One time (OTP) code" required />
<x-forms.button type="submit">Validate 2FA</x-forms.button>
</form>
<div>
<div class="flex items-center justify-center w-64 h-64 bg-transparent">{!! request()->user()->twoFactorQrCodeSvg() !!}</div>
<div x-data="{ showCode: false }" class="py-2">
<template x-if="showCode">
<div class="py-2 ">{!! decrypt(request()->user()->two_factor_secret) !!}</div>
</template>
<x-forms.button x-on:click="showCode = !showCode">Show secret key to manually
enter</x-forms.button>
<div class="flex flex-col items-start">
<div class="flex items-center justify-center w-80 h-80 bg-white p-4 border-4 border-gray-300 rounded-lg shadow-lg">
{!! request()->user()->twoFactorQrCodeSvg() !!}
</div>
<div x-data="{
showCode: false,
secretKey: '{{ decrypt(request()->user()->two_factor_secret) }}',
otpUrl: '{{ request()->user()->twoFactorQrCodeUrl() }}',
copiedSecretKey: false,
copiedOtpUrl: false
}" class="py-4 w-full">
<div class="flex flex-col gap-2" x-show="showCode">
<div class="relative">
<x-forms.input
x-model="secretKey"
label="Secret Key"
readonly
class="font-mono pr-10"
/>
<button
@click="navigator.clipboard.writeText(secretKey); copiedSecretKey = true; setTimeout(() => copiedSecretKey = false, 2000)"
class="absolute right-2 bottom-1 p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700"
>
<svg x-show="!copiedSecretKey" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 01-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 011.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 00-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 01-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 00-3.375-3.375h-1.5a1.125 1.125 0 01-1.125-1.125v-1.5a3.375 3.375 0 00-3.375-3.375H9.75" />
</svg>
<svg x-show="copiedSecretKey" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 text-green-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" />
</svg>
</button>
</div>
<div class="relative" >
<x-forms.input
x-model="otpUrl"
label="OTP URL"
readonly
class="font-mono pr-10"
/>
<button
@click="navigator.clipboard.writeText(otpUrl); copiedOtpUrl = true; setTimeout(() => copiedOtpUrl = false, 2000)"
class="absolute right-2 bottom-1 p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700"
>
<svg x-show="!copiedOtpUrl" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 01-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 011.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 00-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 01-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 00-3.375-3.375h-1.5a1.125 1.125 0 01-1.125-1.125v-1.5a3.375 3.375 0 00-3.375-3.375H9.75" />
</svg>
<svg x-show="copiedOtpUrl" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 text-green-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" />
</svg>
</button>
</div>
</div>
<x-forms.button x-on:click="showCode = !showCode" class="mt-2">
<span x-text="showCode ? 'Hide Secret Key and OTP URL' : 'Show Secret Key and OTP URL'"></span>
</x-forms.button>
</div>
</div>
</div>

View File

@@ -5,7 +5,7 @@
<x-forms.button type="submit">
Save
</x-forms.button>
{{--
{{--
<x-forms.button wire:click="downloadConfig">
Download Config
<x-modal-input buttonTitle="Upload Config" title="Upload Config" :closeOutside="false">
@@ -63,7 +63,7 @@
@endif
@if ($application->build_pack !== 'dockercompose')
<div class="flex items-end gap-2">
<x-forms.input placeholder="https://coolify.io" id="application.fqdn" label="Domains"
<x-forms.input placeholder="https://coolify.io" wire:model.blur="application.fqdn" label="Domains"
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io,https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. " />
<x-forms.button wire:click="getWildcardDomain">Generate Domain
</x-forms.button>
@@ -238,9 +238,9 @@
@if ($application->build_pack !== 'dockercompose')
<div class="pt-2 w-96">
<x-forms.checkbox
helper="Use a build server to build your application. You can configure your build server in the Server settings. This is experimental. For more info, check the <a href='https://coolify.io/docs/knowledge-base/server/build-server' class='underline' target='_blank'>documentation</a>."
helper="Use a build server to build your application. You can configure your build server in the Server settings. For more info, check the <a href='https://coolify.io/docs/knowledge-base/server/build-server' class='underline' target='_blank'>documentation</a>."
instantSave id="application.settings.is_build_server_enabled"
label="Use a Build Server? (experimental)" />
label="Use a Build Server?" />
</div>
@endif
@if ($application->could_set_build_commands())
@@ -312,7 +312,7 @@
id="application.settings.is_container_label_readonly_enabled" instantSave></x-forms.checkbox>
</div>
<x-modal-confirmation title="Confirm Labels Reset to Coolify Defaults?"
buttonTitle="Reset Labels to Coolify Defaults" buttonFullWidth submitAction="resetDefaultLabels"
buttonTitle="Reset Labels to Defaults" buttonFullWidth submitAction="resetDefaultLabels(true)"
:actions="[
'All your custom proxy labels will be lost.',
'Proxy labels (traefik, caddy, etc) will be reset to the coolify defaults.',

View File

@@ -1,5 +1,5 @@
<nav wire:poll.10000ms="check_status">
<x-resources.breadcrumbs :resource="$application" :parameters="$parameters" :lastDeploymentInfo="$lastDeploymentInfo" :lastDeploymentLink="$lastDeploymentLink" />
<x-resources.breadcrumbs :resource="$application" :parameters="$parameters" :title="$lastDeploymentInfo" :lastDeploymentLink="$lastDeploymentLink" />
<div class="navbar-main">
<nav class="flex flex-shrink-0 gap-6 items-center whitespace-nowrap scrollbar min-h-10">
<a href="{{ route('project.application.configuration', $parameters) }}">

View File

@@ -35,7 +35,7 @@
<div class="flex flex-col gap-2">
<h3>Settings</h3>
<div class="flex gap-2 flex-col ">
@if ($backup->database_type === 'App\Models\StandalonePostgresql')
@if ($backup->database_type === 'App\Models\StandalonePostgresql' && $backup->database_id !== 0)
<div class="w-48">
<x-forms.checkbox label="Backup All Databases" id="backup.dump_all" instantSave />
</div>

View File

@@ -9,8 +9,25 @@
<div class="flex gap-2">
<x-forms.input label="Name" id="database.name" />
<x-forms.input label="Description" id="database.description" />
<x-forms.input label="Image" id="database.image" required
helper="For all available images, check here:<br><br><a target='_blank' href='https://hub.docker.com/_/redis'>https://hub.docker.com/_/redis</a>" />
<x-forms.input label="Image" id="database.image" required helper="For all available images, check here:<br><br><a target='_blank' href='https://hub.docker.com/_/redis'>https://hub.docker.com/_/redis</a>" />
</div>
<div class="flex flex-col gap-2">
@if (version_compare($redis_version, '6.0', '>='))
<x-forms.input label="Username" id="redis_username" required
helper="You can change the Redis Username in the input field below or by editing the value of the REDIS_USERNAME environment variable.
<br><br>
If you change the Redis Username in the database, please sync it here, otherwise automations (like backups) won't work.
<br><br>
Note: If the environment variable REDIS_USERNAME is set as a shared variable (environment, project, or team-based), this input field will become read-only."
:disabled="$this->isSharedVariable('REDIS_USERNAME')" />
@endif
<x-forms.input label="Password" id="redis_password" type="password" required
helper="You can change the Redis Password in the input field below or by editing the value of the REDIS_PASSWORD environment variable.
<br><br>
If you change the Redis Password in the database, please sync it here, otherwise automations (like backups) won't work.
<br><br>
Note: If the environment variable REDIS_PASSWORD is set as a shared variable (environment, project, or team-based), this input field will become read-only."
:disabled="$this->isSharedVariable('REDIS_PASSWORD')" />
</div>
<x-forms.input
helper="You can add custom docker run options that will be used when your container is started.<br>Note: Not all options are supported, as they could mess up Coolify's automation and could cause bad experience for users.<br><br>Check the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/custom-commands'>docs.</a>"
@@ -19,42 +36,32 @@
<div class="flex flex-col gap-2">
<h3 class="py-2">Network</h3>
<div class="flex items-end gap-2">
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings"
helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings" helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
</div>
<x-forms.input label="Redis URL (internal)"
helper="If you change the user/password/port, this could be different. This is with the default values."
type="password" readonly wire:model="db_url" />
<x-forms.input label="Redis URL (internal)" helper="If you change the user/password/port, this could be different. This is with the default values." type="password" readonly wire:model="db_url" />
@if ($db_url_public)
<x-forms.input label="Redis URL (public)"
helper="If you change the user/password/port, this could be different. This is with the default values."
type="password" readonly wire:model="db_url_public" />
<x-forms.input label="Redis URL (public)" helper="If you change the user/password/port, this could be different. This is with the default values." type="password" readonly wire:model="db_url_public" />
@endif
</div>
<div>
<h3 class="py-2">Proxy</h3>
<div class="flex items-end gap-2">
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}" id="database.public_port" label="Public Port" />
<x-slide-over fullScreen>
<x-slot:title>Proxy Logs</x-slot:title>
<x-slot:content>
<livewire:project.shared.get-logs :server="$server" :resource="$database"
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
<livewire:project.shared.get-logs :server="$server" :resource="$database" container="{{ data_get($database, 'uuid') }}-proxy" lazy />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true" class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
</div>
<x-forms.textarea
helper="<a target='_blank' class='underline dark:text-white' href='https://raw.githubusercontent.com/redis/redis/7.2/redis.conf'>Redis Default Configuration</a>"
label="Custom Redis Configuration" rows="10" id="database.redis_conf" />
<x-forms.textarea helper="<a target='_blank' class='underline dark:text-white' href='https://raw.githubusercontent.com/redis/redis/7.2/redis.conf'>Redis Default Configuration</a>" label="Custom Redis Configuration" rows="10" id="database.redis_conf" />
<h3 class="pt-4">Advanced</h3>
<div class="flex flex-col">
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" />
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings." instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" />
</div>
</form>
</div>

View File

@@ -24,6 +24,12 @@
<div x-text="item.description"></div>
</div>
</div>
<div class="flex items-center justify-center gap-2 pt-4 pb-2 mr-4 text-xs lg:py-0 lg:justify-normal">
<a class="mx-4 font-bold hover:underline"
:href="item.settingsRoute">
Settings
</a>
</div>
</div>
</template>
</div>

View File

@@ -159,10 +159,11 @@
});
},
filterAndSort(items, isSort = true) {
if (this.search === '') {
const searchLower = this.search.trim().toLowerCase();
if (searchLower === '') {
return isSort ? Object.values(items).sort(sortFn) : Object.values(items);
}
const searchLower = this.search.toLowerCase();
const filtered = Object.values(items).filter(item => {
return (item.name?.toLowerCase().includes(searchLower) ||
item.description?.toLowerCase().includes(searchLower))

View File

@@ -7,15 +7,15 @@
<h1>Resources</h1>
@if ($environment->isEmpty())
<a class="button"
href="{{ route('project.clone-me', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => request()->route('environment_name')]) }}">
href="{{ route('project.clone-me', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($parameters, 'environment_name')]) }}">
Clone
</a>
@else
<a href="{{ route('project.resource.create', ['project_uuid' => request()->route('project_uuid'), 'environment_name' => request()->route('environment_name')]) }} "
<a href="{{ route('project.resource.create', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_name' => data_get($parameters, 'environment_name')]) }} "
class="button">+
New</a>
<a class="button"
href="{{ route('project.clone-me', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => request()->route('environment_name')]) }}">
href="{{ route('project.clone-me', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => data_get($parameters, 'environment_name')]) }}">
Clone
</a>
@endif
@@ -25,7 +25,7 @@
<ol class="flex items-center">
<li class="inline-flex items-center">
<a class="text-xs truncate lg:text-sm"
href="{{ route('project.show', ['project_uuid' => request()->route('project_uuid')]) }}">
href="{{ route('project.show', ['project_uuid' => data_get($parameters, 'project_uuid')]) }}">
{{ $project->name }}</a>
</li>
<li>
@@ -44,7 +44,7 @@
</nav>
</div>
@if ($environment->isEmpty())
<a href="{{ route('project.resource.create', ['project_uuid' => request()->route('project_uuid'), 'environment_name' => request()->route('environment_name')]) }} "
<a href="{{ route('project.resource.create', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_name' => data_get($parameters, 'environment_name')]) }} "
class="items-center justify-center box">+ Add New Resource</a>
@else
<div x-data="searchComponent()">

View File

@@ -1,4 +1,4 @@
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'service-stack' }" x-init="$wire.check_status">
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'service-stack' }">
<x-slot:title>
{{ data_get_str($service, 'name')->limit(10) }} > Configuration | Coolify
</x-slot>

View File

@@ -17,7 +17,6 @@
label="Image Tag" id="database.image"></x-forms.input>
</div>
<div class="flex items-end gap-2">
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
label="Public Port" />
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />

View File

@@ -20,127 +20,143 @@
</a>
<x-services.links :service="$service" />
</nav>
<div class="flex flex-wrap order-first gap-2 items-center sm:order-last">
@if (str($service->status())->contains('running'))
<x-dropdown>
<x-slot:title>
Advanced
</x-slot>
<div class="dropdown-item" @click="$wire.dispatch('pullAndRestartEvent')">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
@if ($service->isDeployable)
<div class="flex flex-wrap order-first gap-2 items-center sm:order-last">
@if (str($service->status())->contains('running'))
<x-dropdown>
<x-slot:title>
Advanced
</x-slot>
<div class="dropdown-item" @click="$wire.dispatch('pullAndRestartEvent')">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M12.983 8.978c3.955 -.182 7.017 -1.446 7.017 -2.978c0 -1.657 -3.582 -3 -8 -3c-1.661 0 -3.204 .19 -4.483 .515m-2.783 1.228c-.471 .382 -.734 .808 -.734 1.257c0 1.22 1.944 2.271 4.734 2.74" />
<path
d="M4 6v6c0 1.657 3.582 3 8 3c.986 0 1.93 -.067 2.802 -.19m3.187 -.82c1.251 -.53 2.011 -1.228 2.011 -1.99v-6" />
<path d="M4 12v6c0 1.657 3.582 3 8 3c3.217 0 5.991 -.712 7.261 -1.74m.739 -3.26v-4" />
<path d="M3 3l18 18" />
</svg>
Pull Latest Images & Restart
</div>
</x-dropdown>
<x-forms.button title="Restart" @click="$wire.dispatch('restartEvent')">
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2">
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
<path d="M20 4v5h-5" />
</g>
</svg>
Restart
</x-forms.button>
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" submitAction="stop"
:checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]" :confirmWithText="false" :confirmWithPassword="false"
step1ButtonText="Continue" step2ButtonText="Stop Service" :dispatchEvent="true"
dispatchEventType="stopEvent">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
<path
d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
</svg>
Stop
</x-slot:button-title>
</x-modal-confirmation>
@elseif (str($service->status())->contains('degraded'))
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2">
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
<path d="M20 4v5h-5" />
</g>
</svg>
Restart Degraded Services
</button>
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" submitAction="stop"
:checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]" :confirmWithText="false" :confirmWithPassword="false"
step1ButtonText="Continue" step2ButtonText="Stop Service" :dispatchEvent="true"
dispatchEventType="stopEvent">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
<path
d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
</svg>
Stop
</x-slot:button-title>
</x-modal-confirmation>
@elseif (str($service->status())->contains('exited'))
<button wire:click='stop(true)' class="gap-2 button">
<svg class="w-5 h-5" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<path fill="red" d="M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z" />
<path fill="red"
d="M17.003 20a4.895 4.895 0 0 0-2.404-4.173L22 3l-1.73-1l-7.577 13.126a5.699 5.699 0 0 0-5.243 1.503C3.706 20.24 3.996 28.682 4.01 29.04a1 1 0 0 0 1 .96h14.991a1 1 0 0 0 .6-1.8c-3.54-2.656-3.598-8.146-3.598-8.2Zm-5.073-3.003A3.11 3.11 0 0 1 15.004 20c0 .038.002.208.017.469l-5.9-2.624a3.8 3.8 0 0 1 2.809-.848ZM15.45 28A5.2 5.2 0 0 1 14 25h-2a6.5 6.5 0 0 0 .968 3h-2.223A16.617 16.617 0 0 1 10 24H8a17.342 17.342 0 0 0 .665 4H6c.031-1.836.29-5.892 1.803-8.553l7.533 3.35A13.025 13.025 0 0 0 17.596 28Z" />
</svg>
Force Cleanup Containers
</button>
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M12.983 8.978c3.955 -.182 7.017 -1.446 7.017 -2.978c0 -1.657 -3.582 -3 -8 -3c-1.661 0 -3.204 .19 -4.483 .515m-2.783 1.228c-.471 .382 -.734 .808 -.734 1.257c0 1.22 1.944 2.271 4.734 2.74" />
<path
d="M4 6v6c0 1.657 3.582 3 8 3c.986 0 1.93 -.067 2.802 -.19m3.187 -.82c1.251 -.53 2.011 -1.228 2.011 -1.99v-6" />
<path d="M4 12v6c0 1.657 3.582 3 8 3c3.217 0 5.991 -.712 7.261 -1.74m.739 -3.26v-4" />
<path d="M3 3l18 18" />
<path d="M7 4v16l13 -8z" />
</svg>
Pull Latest Images & Restart
</div>
</x-dropdown>
<x-forms.button title="Restart" @click="$wire.dispatch('restartEvent')">
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2">
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
<path d="M20 4v5h-5" />
</g>
</svg>
Restart
</x-forms.button>
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" submitAction="stop"
:checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]" :confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue"
step2ButtonText="Stop Service" :dispatchEvent="true" dispatchEventType="stopEvent">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
Deploy
</button>
@else
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" submitAction="stop"
:checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]" :confirmWithText="false" :confirmWithPassword="false"
step1ButtonText="Continue" step2ButtonText="Stop Service" :dispatchEvent="true"
dispatchEventType="stopEvent">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
<path
d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
</svg>
Stop
</x-slot:button-title>
</x-modal-confirmation>
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M7 4v16l13 -8z" />
</svg>
Stop
</x-slot:button-title>
</x-modal-confirmation>
@elseif (str($service->status())->contains('degraded'))
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2">
<path d="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
<path d="M20 4v5h-5" />
</g>
</svg>
Restart Degraded Services
</button>
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" submitAction="stop"
:checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]" :confirmWithText="false" :confirmWithPassword="false"
step1ButtonText="Continue" step2ButtonText="Stop Service" :dispatchEvent="true"
dispatchEventType="stopEvent">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
</svg>
Stop
</x-slot:button-title>
</x-modal-confirmation>
@elseif (str($service->status())->contains('exited'))
<button wire:click='stop(true)' class="gap-2 button">
<svg class="w-5 h-5" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<path fill="red" d="M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z" />
<path fill="red"
d="M17.003 20a4.895 4.895 0 0 0-2.404-4.173L22 3l-1.73-1l-7.577 13.126a5.699 5.699 0 0 0-5.243 1.503C3.706 20.24 3.996 28.682 4.01 29.04a1 1 0 0 0 1 .96h14.991a1 1 0 0 0 .6-1.8c-3.54-2.656-3.598-8.146-3.598-8.2Zm-5.073-3.003A3.11 3.11 0 0 1 15.004 20c0 .038.002.208.017.469l-5.9-2.624a3.8 3.8 0 0 1 2.809-.848ZM15.45 28A5.2 5.2 0 0 1 14 25h-2a6.5 6.5 0 0 0 .968 3h-2.223A16.617 16.617 0 0 1 10 24H8a17.342 17.342 0 0 0 .665 4H6c.031-1.836.29-5.892 1.803-8.553l7.533 3.35A13.025 13.025 0 0 0 17.596 28Z" />
</svg>
Force Cleanup Containers
</button>
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M7 4v16l13 -8z" />
</svg>
Deploy
</button>
@else
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" submitAction="stop"
:checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]" :confirmWithText="false" :confirmWithPassword="false"
step1ButtonText="Continue" step2ButtonText="Stop Service" :dispatchEvent="true"
dispatchEventType="stopEvent">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
<path d="M14 5m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v12a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z">
</path>
</svg>
Stop
</x-slot:button-title>
</x-modal-confirmation>
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M7 4v16l13 -8z" />
</svg>
Deploy
</button>
@endif
</div>
Deploy
</button>
@endif
</div>
@else
<div class="flex flex-wrap order-first gap-2 items-center sm:order-last">
<div class="text-error">
Unable to deploy. <a
class="underline font-bold cursor-pointer"
@click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'">
Required environment variables missing.</a>
</div>
</div>
@endif
</div>
@script
<script>

View File

@@ -24,8 +24,13 @@
<div class="grid grid-cols-2 gap-2">
@foreach ($fields as $serviceName => $field)
<div class="flex items-center gap-2"><span
class="font-bold">{{ data_get($field, 'serviceName') }}</span>{{ data_get($field, 'name') }}<x-helper
helper="Variable name: {{ $serviceName }}" /></div>
class="font-bold">{{ data_get($field, 'serviceName') }}</span>{{ data_get($field, 'name') }}
@if (data_get($field, 'customHelper'))
<x-helper helper="{{ data_get($field, 'customHelper') }}" />
@else
<x-helper helper="Variable name: {{ $serviceName }}" />
@endif
</div>
<x-forms.input type="{{ data_get($field, 'isPassword') ? 'password' : 'text' }}"
required="{{ str(data_get($field, 'rules'))?->contains('required') }}"
id="fields.{{ $serviceName }}.value"></x-forms.input>

View File

@@ -37,7 +37,14 @@
<h3>Production Environment Variables</h3>
<div>Environment (secrets) variables for Production.</div>
</div>
@forelse ($resource->environment_variables as $env)
@php
$requiredEmptyVars = $resource->environment_variables->filter(function($env) {
return $env->is_required && empty($env->value);
});
$otherVars = $resource->environment_variables->diff($requiredEmptyVars);
@endphp
@forelse ($requiredEmptyVars->merge($otherVars) as $env)
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
:env="$env" :type="$resource->type()" />
@empty

View File

@@ -1,6 +1,11 @@
<div>
<form wire:submit='submit'
class="flex flex-col items-center gap-4 p-4 bg-white border lg:items-start dark:bg-base dark:border-coolgray-300">
@class([
'flex flex-col items-center gap-4 p-4 bg-white border lg:items-start dark:bg-base',
'border-error' => $env->is_really_required,
'dark:border-coolgray-300' => !$env->is_really_required,
])
>
@if ($isLocked)
<div class="flex flex-1 w-full gap-2">
<x-forms.input disabled id="env.key" />

View File

@@ -2,24 +2,36 @@
<x-slot:title>
{{ data_get_str($resource, 'name')->limit(10) }} > Commands | Coolify
</x-slot>
<livewire:project.shared.configuration-checker :resource="$resource" />
@if ($type === 'application')
<livewire:project.shared.configuration-checker :resource="$resource" />
<h1>Terminal</h1>
<livewire:project.application.heading :application="$resource" />
@elseif ($type === 'database')
<livewire:project.shared.configuration-checker :resource="$resource" />
<h1>Terminal</h1>
<livewire:project.database.heading :database="$resource" />
@elseif ($type === 'service')
<livewire:project.shared.configuration-checker :resource="$resource" />
<livewire:project.service.navbar :service="$resource" :parameters="$parameters" title="Terminal" />
@elseif ($type === 'server')
<x-server.navbar :server="$server" :parameters="$parameters" />
@endif
<div x-init="$wire.loadContainers">
<div class="pt-4" wire:loading wire:target='loadContainers'>
Loading resources...
@if ($type === 'server')
<form class="w-full" wire:submit="$dispatchSelf('connectToServer')" wire:init="$dispatchSelf('connectToServer')">
<x-forms.button class="w-full" type="submit">Reconnect</x-forms.button>
</form>
<div class="mx-auto w-full">
<livewire:project.shared.terminal />
</div>
<div wire:loading.remove wire:target='loadContainers'>
@if (count($containers) > 0)
<form class="flex flex-col gap-2 justify-center pt-4 xl:items-end xl:flex-row"
wire:submit="$dispatchSelf('connectToContainer')">
@else
@if (count($containers) > 0)
@if (count($containers) === 1)
<form class="w-full pt-4"
wire:submit="$dispatchSelf('connectToContainer')" wire:init="$dispatchSelf('connectToContainer')">
<x-forms.button class="w-full" type="submit">Reconnect</x-forms.button>
</form>
@else
<form class="w-full pt-4 flex gap-2 flex-col" wire:submit="$dispatchSelf('connectToContainer')">
<x-forms.select label="Container" id="container" required wire:model="selected_container">
@foreach ($containers as $container)
@if ($loop->first)
@@ -31,14 +43,16 @@
</option>
@endforeach
</x-forms.select>
<x-forms.button type="submit">Connect</x-forms.button>
<x-forms.button class="w-full" type="submit">
Connect
</x-forms.button>
</form>
@else
<div class="pt-4">No containers are running.</div>
@endif
</div>
</div>
<div class="mx-auto w-full">
<livewire:project.shared.terminal />
</div>
<div class="mx-auto w-full">
<livewire:project.shared.terminal />
</div>
@else
<div class="pt-4">No containers are running.</div>
@endif
@endif
</div>

View File

@@ -48,8 +48,8 @@
</div>
<form wire:submit='getLogs(true)' class="flex gap-2 items-end">
<div class="w-96">
<x-forms.input label="Only Show Number of Lines" placeholder="1000" type="number" required
id="numberOfLines"></x-forms.input>
<x-forms.input label="Only Show Number of Lines" placeholder="100" type="number" required
id="numberOfLines" :readonly="$streamLogs"></x-forms.input>
</div>
<x-forms.button type="submit">Refresh</x-forms.button>
<x-forms.checkbox instantSave label="Stream Logs" id="streamLogs"></x-forms.checkbox>

View File

@@ -25,13 +25,13 @@
<x-forms.input id="resource.health_check_response_text" placeholder="OK" label="Response Text" />
</div>
<div class="flex gap-2">
<x-forms.input min=1 type="number" id="resource.health_check_interval" placeholder="30" label="Interval"
<x-forms.input min=1 type="number" id="resource.health_check_interval" placeholder="30" label="Interval (s)"
required />
<x-forms.input type="number" id="resource.health_check_timeout" placeholder="30" label="Timeout"
<x-forms.input type="number" id="resource.health_check_timeout" placeholder="30" label="Timeout (s)"
required />
<x-forms.input type="number" id="resource.health_check_retries" placeholder="3" label="Retries" required />
<x-forms.input min=1 type="number" id="resource.health_check_start_period" placeholder="30"
label="Start Period" required />
label="Start Period (s)" required />
</div>
</div>
</form>

View File

@@ -5,8 +5,8 @@
<div class="pb-4">Basic metrics for your container.</div>
@if ($resource->getMorphClass() === 'App\Models\Application' && $resource->build_pack === 'dockercompose')
<div class="alert alert-warning">Metrics are not available for Docker Compose applications yet!</div>
@elseif(!$resource->destination->server->isSentinelEnabled())
<div class="alert alert-warning">Metrics are only available for servers with Sentinel enabled!</div>
@elseif(!$resource->destination->server->isMetricsEnabled())
<div class="alert alert-warning">Metrics are only available for servers with Sentinel & Metrics enabled!</div>
<div> Go to <a class="underline dark:text-white"
href="{{ route('server.show', $resource->destination->server->uuid) }}">Server settings</a> to
enable

View File

@@ -2,11 +2,11 @@
<div class="flex items-center gap-2">
<h2>Webhooks</h2>
<x-helper
helper="For more details goto our <a class='underline dark:text-white' href='https://coolify.io/docs/api-reference/deploy-webhook' target='_blank'>docs</a>." />
helper="For more details goto our <a class='underline dark:text-white' href='https://coolify.io/docs/api/operations/deploy-by-tag-or-uuid' target='_blank'>docs</a>." />
</div>
<div>
<x-forms.input readonly
helper="See details in our <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/api-reference/deploy-webhook'>documentation</a>."
helper="See details in our <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/api/operations/deploy-by-tag-or-uuid'>documentation</a>."
label="Deploy Webhook (auth required)" id="deploywebhook"></x-forms.input>
</div>
@if ($resource->type() === 'application')

View File

@@ -1,10 +1,11 @@
<div>
<div class="pb-0 subtitle">
<div >Private Keys are used to connect to your servers without passwords.</div>
<div class="font-bold">You should not use passphrase protected keys.</div>
<div>Private Keys are used to connect to your servers without passwords.</div>
<div class="font-bold">You should not use passphrase protected keys.</div>
</div>
<div class="flex gap-2 mb-4">
<x-forms.button wire:click="generateNewEDKey">Generate new ED25519 SSH Key (Recommended, fastest and most secure)</x-forms.button>
<div class="flex gap-2 mb-4 w-full">
<x-forms.button wire:click="generateNewEDKey" isHighlighted class="w-full">Generate new ED25519 SSH
Key</x-forms.button>
<x-forms.button wire:click="generateNewRSAKey">Generate new RSA SSH Key</x-forms.button>
</div>
<form class="flex flex-col gap-2" wire:submit='createPrivateKey'>

View File

@@ -0,0 +1,97 @@
<div>
<x-server.navbar :server="$server" />
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 sm:flex-row">
<x-server.sidebar :server="$server" activeMenu="advanced" />
<form wire:submit='submit' class="w-full">
<div>
<div class="flex items-center gap-2">
<h2>Advanced</h2>
<x-forms.button type="submit">Save</x-forms.button>
<x-modal-confirmation title="Confirm Docker Cleanup?" buttonTitle="Trigger Manual Cleanup"
isHighlightedButton submitAction="manualCleanup" :actions="[
'Permanently deletes all stopped containers managed by Coolify (as containers are non-persistent, no data will be lost)',
'Permanently deletes all unused images',
'Clears build cache',
'Removes old versions of the Coolify helper image',
'Optionally permanently deletes all unused volumes (if enabled in advanced options).',
'Optionally permanently deletes all unused networks (if enabled in advanced options).',
]" :confirmWithText="false"
:confirmWithPassword="false" step2ButtonText="Trigger Docker Cleanup" />
</div>
<div>Advanced configuration for your server.</div>
</div>
<div class="flex flex-col gap-4">
<div class="flex flex-col">
<div class="flex flex-wrap gap-2 sm:flex-nowrap pt-4">
<x-forms.input id="serverDiskUsageNotificationThreshold"
label="Server disk usage notification threshold (%)" required
helper="If the server disk usage exceeds this threshold, Coolify will send a notification to the team members." />
</div>
</div>
<div class="flex flex-col gap-2">
<div class="flex items-center gap-2">
<h3>Docker Cleanup</h3>
</div>
<div class="flex flex-wrap items-center gap-4">
@if ($forceDockerCleanup)
<x-forms.input placeholder="*/10 * * * *" id="dockerCleanupFrequency"
label="Docker cleanup frequency" required
helper="Cron expression for Docker Cleanup.<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every night at midnight." />
@else
<x-forms.input id="dockerCleanupThreshold" label="Docker cleanup threshold (%)" required
helper="The Docker cleanup tasks will run when the disk usage exceeds this threshold." />
@endif
<div class="w-96">
<x-forms.checkbox
helper="Enabling Force Docker Cleanup or manually triggering a cleanup will perform the following actions:
<ul class='list-disc pl-4 mt-2'>
<li>Removes stopped containers managed by Coolify (as containers are none persistent, no data will be lost).</li>
<li>Deletes unused images.</li>
<li>Clears build cache.</li>
<li>Removes old versions of the Coolify helper image.</li>
<li>Optionally delete unused volumes (if enabled in advanced options).</li>
<li>Optionally remove unused networks (if enabled in advanced options).</li>
</ul>"
instantSave id="forceDockerCleanup" label="Force Docker Cleanup" />
</div>
</div>
<p class="text-sm text-gray-600 dark:text-gray-400 mb-2">
<span class="dark:text-warning font-bold">Warning: Enable these
options only if you fully understand their implications and
consequences!</span><br>Improper use will result in data loss and could cause
functional issues.
</p>
<div class="w-96">
<x-forms.checkbox instantSave id="deleteUnusedVolumes" label="Delete Unused Volumes"
helper="This option will remove all unused Docker volumes during cleanup.<br><br><strong>Warning: Data form stopped containers will be lost!</strong><br><br>Consequences include:<br>
<ul class='list-disc pl-4 mt-2'>
<li>Volumes not attached to running containers will be deleted and data will be permanently lost (stopped containers are affected).</li>
<li>Data from stopped containers volumes will be permanently lost.</li>
<li>No way to recover deleted volume data.</li>
</ul>" />
<x-forms.checkbox instantSave id="deleteUnusedNetworks" label="Delete Unused Networks"
helper="This option will remove all unused Docker networks during cleanup.<br><br><strong>Warning: Functionality may be lost and containers may not be able to communicate with each other!</strong><br><br>Consequences include:<br>
<ul class='list-disc pl-4 mt-2'>
<li>Networks not attached to running containers will be permanently deleted (stopped containers are affected).</li>
<li>Custom networks for stopped containers will be permanently deleted.</li>
<li>Functionality may be lost and containers may not be able to communicate with each other.</li>
</ul>" />
</div>
</div>
<div class="flex flex-col">
<h3>Builds</h3>
<div>Customize the build process.</div>
<div class="flex flex-wrap gap-2 sm:flex-nowrap pt-4">
<x-forms.input id="concurrentBuilds" label="Number of concurrent builds" required
helper="You can specify the number of simultaneous build processes/deployments that should run concurrently." />
<x-forms.input id="dynamicTimeout" label="Deployment timeout (seconds)" required
helper="You can define the maximum duration for a deployment to run before timing it out." />
</div>
</div>
</div>
</form>
</div>
</div>

View File

@@ -1,240 +1,253 @@
<div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()">
<h3>CPU (%)</h3>
<x-forms.select label="Interval" wire:change="setInterval" id="interval">
<option value="5">5 minutes (live)</option>
<option value="10">10 minutes (live)</option>
<option value="30">30 minutes</option>
<option value="60">1 hour</option>
<option value="720">12 hours</option>
<option value="10080">1 week</option>
<option value="43200">30 days</option>
</x-forms.select>
<div wire:ignore id="{!! $chartId !!}-cpu"></div>
<div>
<x-slot:title>
{{ data_get_str($server, 'name')->limit(10) }} > Server Destinations | Coolify
</x-slot>
<x-server.navbar :server="$server" />
<div class="flex flex-col h-full gap-8 sm:flex-row">
<x-server.sidebar :server="$server" activeMenu="metrics" />
<div class="w-full">
<h2>Metrics</h2>
<div class="pb-4">Basic metrics for your container.</div>
<div @if ($poll) wire:poll.5000ms='pollData' @endif x-init="$wire.loadData()">
<x-forms.select label="Interval" wire:change="setInterval" id="interval">
<option value="5">5 minutes (live)</option>
<option value="10">10 minutes (live)</option>
<option value="30">30 minutes</option>
<option value="60">1 hour</option>
<option value="720">12 hours</option>
<option value="10080">1 week</option>
<option value="43200">30 days</option>
</x-forms.select>
<h4 class="pt-4">CPU (%)</h4>
<div wire:ignore id="{!! $chartId !!}-cpu"></div>
<script>
checkTheme();
const optionsServerCpu = {
stroke: {
curve: 'straight',
},
chart: {
height: '150px',
id: '{!! $chartId !!}-cpu',
type: 'area',
toolbar: {
show: true,
tools: {
download: false,
selection: false,
zoom: true,
zoomin: false,
zoomout: false,
pan: false,
reset: true
},
},
animations: {
enabled: false,
},
},
fill: {
type: 'gradient',
},
dataLabels: {
enabled: false,
offsetY: -10,
style: {
colors: ['#FCD452'],
},
background: {
enabled: false,
}
},
grid: {
show: true,
borderColor: '',
},
colors: [baseColor],
xaxis: {
type: 'datetime',
},
series: [{
name: 'CPU %',
data: []
}],
noData: {
text: 'Loading...',
style: {
color: textColor,
}
},
tooltip: {
enabled: true,
marker: {
show: false,
}
},
legend: {
show: false
}
}
const serverCpuChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-cpu`),
optionsServerCpu);
serverCpuChart.render();
document.addEventListener('livewire:init', () => {
Livewire.on('refreshChartData-{!! $chartId !!}-cpu', (chartData) => {
checkTheme();
serverCpuChart.updateOptions({
series: [{
data: chartData[0].seriesData,
}],
colors: [baseColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
style: {
colors: textColor,
}
}
},
yaxis: {
show: true,
labels: {
show: true,
style: {
colors: textColor,
}
}
},
noData: {
text: 'Loading...',
style: {
color: textColor,
}
}
});
});
});
</script>
<div>
<h3>Memory (%)</h3>
<div wire:ignore id="{!! $chartId !!}-memory"></div>
<script>
checkTheme();
const optionsServerMemory = {
stroke: {
curve: 'straight',
},
chart: {
height: '150px',
id: '{!! $chartId !!}-memory',
type: 'area',
toolbar: {
show: true,
tools: {
download: false,
selection: false,
zoom: true,
zoomin: false,
zoomout: false,
pan: false,
reset: true
},
},
animations: {
enabled: false,
},
},
fill: {
type: 'gradient',
},
dataLabels: {
enabled: false,
offsetY: -10,
style: {
colors: ['#FCD452'],
},
background: {
enabled: false,
}
},
grid: {
show: true,
borderColor: '',
},
colors: [baseColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
style: {
colors: textColor,
}
}
},
series: [{
name: "Memory (%)",
data: []
}],
noData: {
text: 'Loading...',
style: {
color: textColor,
}
},
tooltip: {
enabled: true,
marker: {
show: false,
}
},
legend: {
show: false
}
}
const serverMemoryChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-memory`),
optionsServerMemory);
serverMemoryChart.render();
document.addEventListener('livewire:init', () => {
Livewire.on('refreshChartData-{!! $chartId !!}-memory', (chartData) => {
<script>
checkTheme();
serverMemoryChart.updateOptions({
series: [{
data: chartData[0].seriesData,
}],
const optionsServerCpu = {
stroke: {
curve: 'straight',
},
chart: {
height: '150px',
id: '{!! $chartId !!}-cpu',
type: 'area',
toolbar: {
show: true,
tools: {
download: false,
selection: false,
zoom: true,
zoomin: false,
zoomout: false,
pan: false,
reset: true
},
},
animations: {
enabled: false,
},
},
fill: {
type: 'gradient',
},
dataLabels: {
enabled: false,
offsetY: -10,
style: {
colors: ['#FCD452'],
},
background: {
enabled: false,
}
},
grid: {
show: true,
borderColor: '',
},
colors: [baseColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
style: {
colors: textColor,
}
}
},
yaxis: {
min: 0,
show: true,
labels: {
show: true,
style: {
colors: textColor,
}
}
},
series: [{
name: 'CPU %',
data: []
}],
noData: {
text: 'Loading...',
style: {
color: textColor,
}
},
tooltip: {
enabled: true,
marker: {
show: false,
}
},
legend: {
show: false
}
}
const serverCpuChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-cpu`),
optionsServerCpu);
serverCpuChart.render();
document.addEventListener('livewire:init', () => {
Livewire.on('refreshChartData-{!! $chartId !!}-cpu', (chartData) => {
checkTheme();
serverCpuChart.updateOptions({
series: [{
data: chartData[0].seriesData,
}],
colors: [baseColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
style: {
colors: textColor,
}
}
},
yaxis: {
show: true,
labels: {
show: true,
style: {
colors: textColor,
}
}
},
noData: {
text: 'Loading...',
style: {
color: textColor,
}
}
});
});
});
});
});
</script>
</script>
<div>
<h4>Memory (%)</h4>
<div wire:ignore id="{!! $chartId !!}-memory"></div>
<script>
checkTheme();
const optionsServerMemory = {
stroke: {
curve: 'straight',
},
chart: {
height: '150px',
id: '{!! $chartId !!}-memory',
type: 'area',
toolbar: {
show: true,
tools: {
download: false,
selection: false,
zoom: true,
zoomin: false,
zoomout: false,
pan: false,
reset: true
},
},
animations: {
enabled: false,
},
},
fill: {
type: 'gradient',
},
dataLabels: {
enabled: false,
offsetY: -10,
style: {
colors: ['#FCD452'],
},
background: {
enabled: false,
}
},
grid: {
show: true,
borderColor: '',
},
colors: [baseColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
style: {
colors: textColor,
}
}
},
series: [{
name: "Memory (%)",
data: []
}],
noData: {
text: 'Loading...',
style: {
color: textColor,
}
},
tooltip: {
enabled: true,
marker: {
show: false,
}
},
legend: {
show: false
}
}
const serverMemoryChart = new ApexCharts(document.getElementById(`{!! $chartId !!}-memory`),
optionsServerMemory);
serverMemoryChart.render();
document.addEventListener('livewire:init', () => {
Livewire.on('refreshChartData-{!! $chartId !!}-memory', (chartData) => {
checkTheme();
serverMemoryChart.updateOptions({
series: [{
data: chartData[0].seriesData,
}],
colors: [baseColor],
xaxis: {
type: 'datetime',
labels: {
show: true,
style: {
colors: textColor,
}
}
},
yaxis: {
min: 0,
show: true,
labels: {
show: true,
style: {
colors: textColor,
}
}
},
noData: {
text: 'Loading...',
style: {
color: textColor,
}
}
});
});
});
</script>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,54 @@
<div>
<x-slot:title>
{{ data_get_str($server, 'name')->limit(10) }} > Cloudflare Tunnels | Coolify
</x-slot>
<x-server.navbar :server="$server" />
<div class="flex flex-col h-full gap-8 sm:flex-row">
<x-server.sidebar :server="$server" activeMenu="cloudflare-tunnels" />
<div class="w-full">
<div class="flex flex-col">
<div class="flex gap-1 items-center">
<h2>Cloudflare Tunnels</h2>
<x-helper class="inline-flex"
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all SSH requests to your server through Cloudflare.<br> You then can close your server's SSH port in the firewall of your hosting provider.<br><span class='dark:text-warning'>If you choose manual configuration, Coolify does not install or set up Cloudflare (cloudflared) on your server.</span>" />
</div>
<div>Secure your servers with Cloudflare Tunnels</div>
</div>
<div class="flex flex-col gap-2 pt-6">
@if ($isCloudflareTunnelsEnabled)
<div class="w-64">
<x-forms.checkbox instantSave id="isCloudflareTunnelsEnabled" label="Enabled" />
</div>
@elseif (!$server->isFunctional())
<div
class="p-4 mb-4 w-full text-sm text-yellow-800 bg-yellow-100 rounded dark:bg-yellow-900 dark:text-yellow-300">
To <span class="font-semibold">automatically</span> configure Cloudflare Tunnels, please
validate your server first.</span> Then you will need a Cloudflare token and an SSH
domain configured.
<br />
To <span class="font-semibold">manually</span> configure Cloudflare Tunnels, please
click <span wire:click="manualCloudflareConfig" class="underline cursor-pointer">here</span>,
then you should validate the server.
<br /><br />
For more information, please read our <a
href="https://coolify.io/docs/knowledge-base/cloudflare/tunnels/" target="_blank"
class="font-medium underline hover:text-yellow-600 dark:hover:text-yellow-200">documentation</a>.
</div>
@endif
@if (!$isCloudflareTunnelsEnabled && $server->isFunctional())
<h4>Configuration</h4>
<div class="flex gap-2">
<x-modal-input buttonTitle="Automated" title="Cloudflare Tunnels" :closeOutside="false"
isHighlightedButton>
<livewire:server.configure-cloudflare-tunnels :server_id="$server->id" />
</x-modal-input>
<x-forms.button wire:click="manualCloudflareConfig" class="w-20">
Manual
</x-forms.button>
</div>
@endif
</div>
</div>
</div>
</div>

View File

@@ -1,22 +1,31 @@
<div>
@if ($server->id !== 0)
<h2 class="pt-4">Danger Zone</h2>
<div class="">Woah. I hope you know what are you doing.</div>
<h4 class="pt-4">Delete Server</h4>
<div class="pb-4">This will remove this server from Coolify. Beware! There is no coming
back!
<x-slot:title>
{{ data_get_str($server, 'name')->limit(10) }} > Delete Server | Coolify
</x-slot>
<x-server.navbar :server="$server" />
<div class="flex flex-col h-full gap-8 sm:flex-row">
<x-server.sidebar :server="$server" activeMenu="danger" />
<div class="w-full">
@if ($server->id !== 0)
<h2>Danger Zone</h2>
<div class="">Woah. I hope you know what are you doing.</div>
<h4 class="pt-4">Delete Server</h4>
<div class="pb-4">This will remove this server from Coolify. Beware! There is no coming
back!
</div>
@if ($server->definedResources()->count() > 0)
<div class="pb-2 text-red-500">You need to delete all resources before deleting this server.</div>
<x-modal-confirmation title="Confirm Server Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="['This server will be permanently deleted.']" confirmationText="{{ $server->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
shortConfirmationLabel="Server Name" step3ButtonText="Permanently Delete" />
@else
<x-modal-confirmation title="Confirm Server Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="['This server will be permanently deleted.']" confirmationText="{{ $server->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
shortConfirmationLabel="Server Name" step3ButtonText="Permanently Delete" />
@endif
@endif
</div>
@if ($server->definedResources()->count() > 0)
<div class="pb-2 text-red-500">You need to delete all resources before deleting this server.</div>
<x-modal-confirmation title="Confirm Server Deletion?" isErrorButton buttonTitle="Delete" submitAction="delete"
:actions="['This server will be permanently deleted.']" confirmationText="{{ $server->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
shortConfirmationLabel="Server Name" step3ButtonText="Permanently Delete" />
@else
<x-modal-confirmation title="Confirm Server Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="['This server will be permanently deleted.']" confirmationText="{{ $server->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
shortConfirmationLabel="Server Name" step2ButtonText="Continue" step3ButtonText="Permanently Delete" />
@endif
@endif
</div>
</div>

View File

@@ -2,6 +2,48 @@
<x-slot:title>
{{ data_get_str($server, 'name')->limit(10) }} > Server Destinations | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
<livewire:destination.show :server="$server" />
<x-server.navbar :server="$server" />
<div class="flex flex-col h-full gap-8 sm:flex-row">
<x-server.sidebar :server="$server" activeMenu="destinations" />
<div class="w-full">
@if ($server->isFunctional())
<div class="flex items-end gap-2">
<h2>Destinations</h2>
<x-modal-input buttonTitle="+ Add" title="New Destination">
<livewire:destination.new.docker :server_id="$server->id" />
</x-modal-input>
<x-forms.button isHighlighted wire:click='scan'>Scan for Destinations</x-forms.button>
</div>
<div>Destinations are used to segregate resources by network.</div>
<h4 class="pt-4 pb-2">Available Destinations</h4>
<div class="flex gap-2">
@foreach ($server->standaloneDockers as $docker)
<a href="{{ route('destination.show', ['destination_uuid' => data_get($docker, 'uuid')]) }}">
<x-forms.button>{{ data_get($docker, 'network') }} </x-forms.button>
</a>
@endforeach
@foreach ($server->swarmDockers as $docker)
<a href="{{ route('destination.show', ['destination_uuid' => data_get($docker, 'uuid')]) }}">
<x-forms.button>{{ data_get($docker, 'network') }} </x-forms.button>
</a>
@endforeach
</div>
@if ($networks->count() > 0)
<div class="pt-2">
<h3 class="pb-4">Found Destinations</h3>
<div class="flex flex-wrap gap-2 ">
@foreach ($networks as $network)
<div class="min-w-fit">
<x-forms.button wire:click="add('{{ data_get($network, 'Name') }}')">Add
{{ data_get($network, 'Name') }}</x-forms.button>
</div>
@endforeach
</div>
</div>
@endif
@else
<div>Server is not validated. Validate first.</div>
@endif
</div>
</div>
</div>

View File

@@ -68,7 +68,8 @@
</div>
<div class="flex flex-col gap-2 w-full lg:flex-row">
<x-forms.input type="password" id="server.ip" label="IP Address/Domain"
helper="An IP Address (127.0.0.1) or domain (example.com). Make sure there is no protocol like http(s):// so you provide a FQDN not a URL." required />
helper="An IP Address (127.0.0.1) or domain (example.com). Make sure there is no protocol like http(s):// so you provide a FQDN not a URL."
required />
<div class="flex gap-2">
<x-forms.input id="server.user" label="User" required />
<x-forms.input type="number" id="server.port" label="Port" required />
@@ -94,7 +95,8 @@
</div>
<div class="relative">
<div class="inline-flex relative items-center w-64">
<input autocomplete="off" wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
<input autocomplete="off"
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" x-model="search"
@focus="open = true" @click.away="open = false" @input="open = true" class="w-full input"
:placeholder="placeholder" wire:model.debounce.300ms="server.settings.server_timezone">
@@ -117,173 +119,108 @@
</div>
</div>
<div class="{{ $server->isFunctional() ? 'w-96' : 'w-full' }}">
<div class="w-full">
@if (!$server->isLocalhost())
<x-forms.checkbox instantSave id="server.settings.is_build_server"
label="Use it as a build server?" />
<div class="flex flex-col gap-2 pt-6">
<div class="flex gap-1 items-center">
<h3 class="text-lg font-semibold">Cloudflare Tunnels</h3>
<x-helper class="inline-flex"
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all SSH requests to your server through Cloudflare.<br> You then can close your server's SSH port in the firewall of your hosting provider.<br><span class='dark:text-warning'>If you choose manual configuration, Coolify does not install or set up Cloudflare (cloudflared) on your server.</span>" />
</div>
@if ($server->settings->is_cloudflare_tunnel)
<div class="w-64">
<x-forms.checkbox instantSave id="server.settings.is_cloudflare_tunnel" label="Enabled" />
</div>
@elseif (!$server->isFunctional())
<div class="p-4 mb-4 w-full text-sm text-yellow-800 bg-yellow-100 rounded dark:bg-yellow-900 dark:text-yellow-300">
To <span class="font-semibold">automatically</span> configure Cloudflare Tunnels, please validate your server first.</span> Then you will need a Cloudflare token and an SSH domain configured.
<br/>
To <span class="font-semibold">manually</span> configure Cloudflare Tunnels, please click <span wire:click="manualCloudflareConfig" class="underline cursor-pointer">here</span>, then you should validate the server.
<br/><br/>
For more information, please read our <a href="https://coolify.io/docs/knowledge-base/cloudflare/tunnels/" target="_blank" class="font-medium underline hover:text-yellow-600 dark:hover:text-yellow-200">documentation</a>.
</div>
@endif
@if (!$server->settings->is_cloudflare_tunnel && $server->isFunctional())
<x-modal-input buttonTitle="Automated Configuration" title="Cloudflare Tunnels" class="w-full" :closeOutside="false">
<livewire:server.configure-cloudflare-tunnels :server_id="$server->id" />
</x-modal-input>
@endif
@if ($server->isFunctional() &&!$server->settings->is_cloudflare_tunnel)
<div wire:click="manualCloudflareConfig" class="w-full underline cursor-pointer">
I have configured Cloudflare Tunnels manually
</div>
@endif
<div class="w-96">
<x-forms.checkbox instantSave id="server.settings.is_build_server"
label="Use it as a build server?" />
</div>
@if (!$server->isBuildServer() && !$server->settings->is_cloudflare_tunnel)
<h3 class="pt-6">Swarm <span class="text-xs text-neutral-500">(experimental)</span></h3>
<div class="pb-4">Read the docs <a class='underline dark:text-white'
href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>.
</div>
@if ($server->settings->is_swarm_worker)
<x-forms.checkbox disabled instantSave type="checkbox"
id="server.settings.is_swarm_manager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@else
<x-forms.checkbox instantSave type="checkbox" id="server.settings.is_swarm_manager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@endif
<div class="w-96">
@if ($server->settings->is_swarm_worker)
<x-forms.checkbox disabled instantSave type="checkbox"
id="server.settings.is_swarm_manager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@else
<x-forms.checkbox instantSave type="checkbox" id="server.settings.is_swarm_manager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@endif
@if ($server->settings->is_swarm_manager)
<x-forms.checkbox disabled instantSave type="checkbox"
id="server.settings.is_swarm_worker"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@else
<x-forms.checkbox instantSave type="checkbox" id="server.settings.is_swarm_worker"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@endif
@if ($server->settings->is_swarm_manager)
<x-forms.checkbox disabled instantSave type="checkbox"
id="server.settings.is_swarm_worker"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@else
<x-forms.checkbox instantSave type="checkbox" id="server.settings.is_swarm_worker"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@endif
</div>
@endif
@endif
</div>
</div>
@if ($server->isFunctional())
<h3 class="pt-4">Settings</h3>
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<div class="flex flex-wrap items-center gap-4">
<div class="w-64">
<x-forms.checkbox
helper="Enabling Force Docker Cleanup or manually triggering a cleanup will perform the following actions:
<ul class='list-disc pl-4 mt-2'>
<li>Removes stopped containers manged by Coolify (as containers are none persistent, no data will be lost).</li>
<li>Deletes unused images.</li>
<li>Clears build cache.</li>
<li>Removes old versions of the Coolify helper image.</li>
<li>Optionally delete unused volumes (if enabled in advanced options).</li>
<li>Optionally remove unused networks (if enabled in advanced options).</li>
</ul>"
instantSave id="server.settings.force_docker_cleanup" label="Force Docker Cleanup" />
</div>
<x-modal-confirmation
title="Confirm Docker Cleanup?"
buttonTitle="Trigger Docker Cleanup"
submitAction="manualCleanup"
:actions="[
'Permanently deletes all stopped containers managed by Coolify (as containers are non-persistent, no data will be lost)',
'Permanently deletes all unused images',
'Clears build cache',
'Removes old versions of the Coolify helper image',
'Optionally permanently deletes all unused volumes (if enabled in advanced options).',
'Optionally permanently deletes all unused networks (if enabled in advanced options).'
]"
:confirmWithText="false"
:confirmWithPassword="false"
step2ButtonText="Trigger Docker Cleanup"
/>
</div>
@if ($server->settings->force_docker_cleanup)
<x-forms.input placeholder="*/10 * * * *" id="server.settings.docker_cleanup_frequency"
label="Docker cleanup frequency" required
helper="Cron expression for Docker Cleanup.<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every night at midnight." />
@else
<x-forms.input id="server.settings.docker_cleanup_threshold"
label="Docker cleanup threshold (%)" required
helper="The Docker cleanup tasks will run when the disk usage exceeds this threshold." />
@endif
<div x-data="{ open: false }" class="mt-4 max-w-md">
<button @click="open = !open" type="button" class="flex items-center justify-between w-full text-left text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100">
<span>Advanced Options</span>
<svg :class="{'rotate-180': open}" class="w-5 h-5 transition-transform duration-200" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
<div x-show="open" class="mt-2 space-y-2">
<p class="text-sm text-gray-600 dark:text-gray-400 mb-2"><strong>Warning: Enable these options only if you fully understand their implications and consequences!</strong><br>Improper use will result in data loss and could cause functional issues.</p>
<x-forms.checkbox instantSave id="server.settings.delete_unused_volumes" label="Delete Unused Volumes"
helper="This option will remove all unused Docker volumes during cleanup.<br><br><strong>Warning: Data form stopped containers will be lost!</strong><br><br>Consequences include:<br>
<ul class='list-disc pl-4 mt-2'>
<li>Volumes not attached to running containers will be deleted and data will be permanently lost (stopped containers are affected).</li>
<li>Data from stopped containers volumes will be permanently lost.</li>
<li>No way to recover deleted volume data.</li>
</ul>"
/>
<x-forms.checkbox instantSave id="server.settings.delete_unused_networks" label="Delete Unused Networks"
helper="This option will remove all unused Docker networks during cleanup.<br><br><strong>Warning: Functionality may be lost and containers may not be able to communicate with each other!</strong><br><br>Consequences include:<br>
<ul class='list-disc pl-4 mt-2'>
<li>Networks not attached to running containers will be permanently deleted (stopped containers are affected).</li>
<li>Custom networks for stopped containers will be permanently deleted.</li>
<li>Functionality may be lost and containers may not be able to communicate with each other.</li>
</ul>"
/>
</div>
</div>
</div>
<div class="flex flex-wrap gap-4 sm:flex-nowrap">
<x-forms.input id="server.settings.concurrent_builds" label="Number of concurrent builds" required
helper="You can specify the number of simultaneous build processes/deployments that should run concurrently." />
<x-forms.input id="server.settings.dynamic_timeout" label="Deployment timeout (seconds)" required
helper="You can define the maximum duration for a deployment to run before timing it out." />
</div>
</div>
</form>
@if ($server->isFunctional() && !$server->isSwarm() && !$server->isBuildServer())
<form wire:submit.prevent='saveSentinel'>
<div class="flex gap-2 items-center pt-4 pb-2">
<h3>Sentinel</h3>
{{-- @if ($server->isSentinelEnabled()) --}}
{{-- <x-forms.button wire:click='restartSentinel'>Restart</x-forms.button> --}}
{{-- @endif --}}
@if ($server->isSentinelEnabled())
<div class="flex gap-2 items-center">
@if ($server->isSentinelLive())
<x-status.running status="In sync" noLoading title="{{ $server->sentinel_updated_at }}" />
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click='restartSentinel'>Restart</x-forms.button>
@else
<x-status.stopped status="Out of sync" noLoading
title="{{ $server->sentinel_updated_at }}" />
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click='restartSentinel'>Sync</x-forms.button>
@endif
</div>
@endif
</div>
<div>Metrics are disabled until a few bugs are fixed.</div>
{{-- <div class="w-64">
<x-forms.checkbox instantSave id="server.settings.is_metrics_enabled" label="Enable Metrics" />
</div>
<div class="pt-4">
<div class="flex flex-wrap gap-2 sm:flex-nowrap">
<x-forms.input type="password" id="server.settings.metrics_token" label="Metrics token" required
helper="Token for collector (Sentinel)." />
<x-forms.input id="server.settings.metrics_refresh_rate_seconds" label="Metrics rate (seconds)"
required
helper="The interval for gathering metrics. Lower means more disk space will be used." />
<x-forms.input id="server.settings.metrics_history_days" label="Metrics history (days)" required
helper="How many days should the metrics data should be reserved." />
<div class="flex flex-col gap-2">
<div class="flex gap-2">Experimental feature <x-helper
helper="Sentinel reports your server's & container's health and collects metrics." /></div>
<div class="w-64">
<x-forms.checkbox wire:model.live="server.settings.is_sentinel_enabled" label="Enable Sentinel" />
@if ($server->isSentinelEnabled())
<x-forms.checkbox id="server.settings.is_sentinel_debug_enabled" label="Enable Sentinel Debug"
instantSave />
<x-forms.checkbox instantSave id="server.settings.is_metrics_enabled"
label="Enable Metrics" />
@else
<x-forms.checkbox id="server.settings.is_sentinel_debug_enabled" label="Enable Sentinel Debug"
disabled instantSave />
<x-forms.checkbox instantSave disabled id="server.settings.is_metrics_enabled"
label="Enable Metrics" />
@endif
</div>
</div> --}}
@endif
</form>
@if ($server->isSentinelEnabled())
<div class="flex flex-wrap gap-2 sm:flex-nowrap items-end">
<x-forms.input type="password" id="server.settings.sentinel_token" label="Sentinel token"
required helper="Token for Sentinel." />
<x-forms.button wire:click="regenerateSentinelToken">Regenerate</x-forms.button>
</div>
<x-forms.input id="server.settings.sentinel_custom_url" required label="Coolify URL"
helper="URL to your Coolify instance. If it is empty that means you do not have a FQDN set for your Coolify instance." />
<div class="flex flex-col gap-2">
<div class="flex flex-wrap gap-2 sm:flex-nowrap">
<x-forms.input id="server.settings.sentinel_metrics_refresh_rate_seconds"
label="Metrics rate (seconds)" required
helper="The interval for gathering metrics. Lower means more disk space will be used." />
<x-forms.input id="server.settings.sentinel_metrics_history_days"
label="Metrics history (days)" required
helper="How many days should the metrics data should be reserved." />
<x-forms.input id="server.settings.sentinel_push_interval_seconds"
label="Push interval (seconds)" required
helper="How many seconds should the metrics data should be pushed to the collector." />
</div>
</div>
@endif
</div>
</form>
@endif
</div>

View File

@@ -1,120 +1,118 @@
<div>
<x-slot:title>
{{ data_get_str($server, 'name')->limit(10) }} > Server LogDrains | Coolify
{{ data_get_str($server, 'name')->limit(10) }} > Server Log Drains | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
@if ($server->isFunctional())
<h2>Log Drains</h2>
<div class="pb-4">Sends service logs to 3rd party tools.</div>
<div class="flex flex-col gap-4 pt-4">
<div class="p-4 border dark:border-coolgray-300">
<form wire:submit='submit("newrelic")' class="flex flex-col">
<h3>New Relic</h3>
<div class="w-32">
<x-forms.checkbox instantSave='instantSave("newrelic")'
id="server.settings.is_logdrain_newrelic_enabled" label="Enabled" />
</div>
<div class="flex flex-col gap-4">
<div class="flex flex-col w-full gap-2 xl:flex-row">
@if ($server->isLogDrainEnabled())
<x-forms.input disabled type="password" required
id="server.settings.logdrain_newrelic_license_key" label="License Key" />
<x-forms.input disabled required id="server.settings.logdrain_newrelic_base_uri"
placeholder="https://log-api.eu.newrelic.com/log/v1"
helper="For EU use: https://log-api.eu.newrelic.com/log/v1<br>For US use: https://log-api.newrelic.com/log/v1"
label="Endpoint" />
<x-server.navbar :server="$server" />
<div class="flex flex-col h-full gap-8 sm:flex-row">
<x-server.sidebar :server="$server" activeMenu="log-drains" />
<div class="w-full">
@if ($server->isFunctional())
<div class="flex gap-2 items-center">
<h2>Log Drains</h2>
<x-loading wire:target="instantSave" wire:loading.delay />
</div>
<div class="">Sends service logs to 3rd party tools.</div>
<div class="flex flex-col gap-4 pt-4">
<div class="p-4 border dark:border-coolgray-300">
<form wire:submit='submit("newrelic")' class="flex flex-col">
<h3>New Relic</h3>
<div class="w-32">
@if ($isLogDrainAxiomEnabled || $isLogDrainCustomEnabled)
<x-forms.checkbox disabled id="isLogDrainNewRelicEnabled" label="Enabled" />
@else
<x-forms.checkbox instantSave id="isLogDrainNewRelicEnabled" label="Enabled" />
@endif
</div>
<div class="flex flex-col gap-4">
<div class="flex flex-col w-full gap-2 xl:flex-row">
@if ($server->isLogDrainEnabled())
<x-forms.input disabled type="password" required id="logDrainNewRelicLicenseKey"
label="License Key" />
<x-forms.input disabled required id="logDrainNewRelicBaseUri"
placeholder="https://log-api.eu.newrelic.com/log/v1"
helper="For EU use: https://log-api.eu.newrelic.com/log/v1<br>For US use: https://log-api.newrelic.com/log/v1"
label="Endpoint" />
@else
<x-forms.input type="password" required id="logDrainNewRelicLicenseKey"
label="License Key" />
<x-forms.input required id="logDrainNewRelicBaseUri"
placeholder="https://log-api.eu.newrelic.com/log/v1"
helper="For EU use: https://log-api.eu.newrelic.com/log/v1<br>For US use: https://log-api.newrelic.com/log/v1"
label="Endpoint" />
@endif
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
</form>
<h3>Axiom</h3>
<div class="w-32">
@if ($isLogDrainNewRelicEnabled || $isLogDrainCustomEnabled)
<x-forms.checkbox disabled id="isLogDrainAxiomEnabled" label="Enabled" />
@else
<x-forms.input type="password" required
id="server.settings.logdrain_newrelic_license_key" label="License Key" />
<x-forms.input required id="server.settings.logdrain_newrelic_base_uri"
placeholder="https://log-api.eu.newrelic.com/log/v1"
helper="For EU use: https://log-api.eu.newrelic.com/log/v1<br>For US use: https://log-api.newrelic.com/log/v1"
label="Endpoint" />
<x-forms.checkbox instantSave id="isLogDrainAxiomEnabled" label="Enabled" />
@endif
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
</form>
<h3>Axiom</h3>
<div class="w-32">
<x-forms.checkbox instantSave='instantSave("axiom")' id="server.settings.is_logdrain_axiom_enabled"
label="Enabled" />
</div>
<form wire:submit='submit("axiom")' class="flex flex-col">
<div class="flex flex-col gap-4">
<div class="flex flex-col w-full gap-2 xl:flex-row">
@if ($server->isLogDrainEnabled())
<x-forms.input disabled type="password" required
id="server.settings.logdrain_axiom_api_key" label="API Key" />
<x-forms.input disabled required id="server.settings.logdrain_axiom_dataset_name"
label="Dataset Name" />
<form wire:submit='submit("axiom")' class="flex flex-col">
<div class="flex flex-col gap-4">
<div class="flex flex-col w-full gap-2 xl:flex-row">
@if ($server->isLogDrainEnabled())
<x-forms.input disabled type="password" required id="logDrainAxiomApiKey"
label="API Key" />
<x-forms.input disabled required id="logDrainAxiomDatasetName"
label="Dataset Name" />
@else
<x-forms.input type="password" required id="logDrainAxiomApiKey"
label="API Key" />
<x-forms.input required id="logDrainAxiomDatasetName" label="Dataset Name" />
@endif
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
</form>
<h3>Custom FluentBit</h3>
<div class="w-32">
@if ($isLogDrainNewRelicEnabled || $isLogDrainAxiomEnabled)
<x-forms.checkbox disabled id="isLogDrainCustomEnabled" label="Enabled" />
@else
<x-forms.input type="password" required id="server.settings.logdrain_axiom_api_key"
label="API Key" />
<x-forms.input required id="server.settings.logdrain_axiom_dataset_name"
label="Dataset Name" />
<x-forms.checkbox instantSave id="isLogDrainCustomEnabled" label="Enabled" />
@endif
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
</form>
{{-- <h3>Highlight.io</h3>
<div class="w-32">
<x-forms.checkbox instantSave='instantSave("highlight")'
id="server.settings.is_logdrain_highlight_enabled" label="Enabled" />
</div>
<form wire:submit='submit("highlight")' class="flex flex-col">
<div class="flex flex-col gap-4">
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input type="password" required id="server.settings.logdrain_highlight_project_id"
label="Project Id" />
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
</form> --}}
<h3>Custom FluentBit configuration</h3>
<div class="w-32">
<x-forms.checkbox instantSave='instantSave("custom")'
id="server.settings.is_logdrain_custom_enabled" label="Enabled" />
</div>
<form wire:submit='submit("custom")' class="flex flex-col">
<div class="flex flex-col gap-4">
@if ($server->isLogDrainEnabled())
<x-forms.textarea disabled rows="6" required
id="server.settings.logdrain_custom_config" label="Custom FluentBit Configuration" />
<x-forms.textarea disabled id="server.settings.logdrain_custom_config_parser"
label="Custom Parser Configuration" />
@else
<x-forms.textarea rows="6" required id="server.settings.logdrain_custom_config"
label="Custom FluentBit Configuration" />
<x-forms.textarea id="server.settings.logdrain_custom_config_parser"
label="Custom Parser Configuration" />
@endif
<form wire:submit='submit("custom")' class="flex flex-col">
<div class="flex flex-col gap-4">
@if ($server->isLogDrainEnabled())
<x-forms.textarea disabled rows="6" required id="logDrainCustomConfig"
label="Custom FluentBit Configuration" />
<x-forms.textarea disabled id="logDrainCustomConfigParser"
label="Custom Parser Configuration" />
@else
<x-forms.textarea rows="6" required id="logDrainCustomConfig"
label="Custom FluentBit Configuration" />
<x-forms.textarea id="logDrainCustomConfigParser"
label="Custom Parser Configuration" />
@endif
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
</form>
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
</form>
</div>
</div>
@else
<div>Server is not validated. Validate first.</div>
@endif
</div>
@else
<div>Server is not validated. Validate first.</div>
@endif
</div>
</div>

View File

@@ -1,7 +1,43 @@
<div>
<x-slot:title>
Server Connection | Coolify
{{ data_get_str($server, 'name')->limit(10) }} > Server Connection | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
<livewire:server.show-private-key :server="$server" :privateKeys="$privateKeys" />
<x-server.navbar :server="$server" />
<div class="flex flex-col h-full gap-8 sm:flex-row">
<x-server.sidebar :server="$server" activeMenu="private-key" />
<div class="w-full">
<div class="flex items-end gap-2">
<h2>Private Key</h2>
<x-modal-input buttonTitle="+ Add" title="New Private Key">
<livewire:security.private-key.create />
</x-modal-input>
<x-forms.button isHighlighted wire:click.prevent='checkConnection'>
Check connection
</x-forms.button>
</div>
<div class="pb-4">Change your server's private key.</div>
<div class="grid xl:grid-cols-2 grid-cols-1 gap-2">
@forelse ($privateKeys as $private_key)
<div
class="box-without-bg justify-between dark:bg-coolgray-100 bg-white items-center flex flex-col gap-2">
<div class="flex flex-col w-full">
<div class="box-title">{{ $private_key->name }}</div>
<div class="box-description">{{ $private_key->description }}</div>
</div>
@if (data_get($server, 'privateKey.uuid') !== $private_key->uuid)
<x-forms.button class="w-full" wire:click='setPrivateKey({{ $private_key->id }})'>
Use this key
</x-forms.button>
@else
<x-forms.button class="w-full" disabled>
Currently used
</x-forms.button>
@endif
</div>
@empty
<div>No private keys found. </div>
@endforelse
</div>
</div>
</div>
</div>

View File

@@ -3,8 +3,8 @@
Proxy Dynamic Configuration | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
<div class="flex gap-2">
<x-server.sidebar :server="$server" :parameters="$parameters" />
<div class="flex flex-col h-full gap-8 sm:flex-row">
<x-server.sidebar-proxy :server="$server" :parameters="$parameters" />
<div class="w-full">
@if ($server->isFunctional())
<div class="flex gap-2">

View File

@@ -3,8 +3,8 @@
Proxy Logs | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
<div class="flex gap-2">
<x-server.sidebar :server="$server" :parameters="$parameters" />
<div class="flex flex-col h-full gap-8 sm:flex-row">
<x-server.sidebar-proxy :server="$server" :parameters="$parameters" />
<div class="w-full">
<h2 class="pb-4">Logs</h2>
<livewire:project.shared.get-logs :server="$server" container="coolify-proxy" />

View File

@@ -1,12 +0,0 @@
<div>
<x-modal submitWireAction="proxyStatusUpdated" modalId="startProxy">
<x-slot:modalBody>
<livewire:activity-monitor header="Proxy Startup Logs" />
</x-slot:modalBody>
<x-slot:modalSubmit>
<x-forms.button onclick="startProxy.close()" type="submit">
Close
</x-forms.button>
</x-slot:modalSubmit>
</x-modal>
</div>

View File

@@ -4,13 +4,13 @@
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
@if ($server->isFunctional())
<div class="flex gap-2">
<x-server.sidebar :server="$server" :parameters="$parameters" />
<div class="flex flex-col h-full gap-8 sm:flex-row">
<x-server.sidebar-proxy :server="$server" :parameters="$parameters" />
<div class="w-full">
<livewire:server.proxy :server="$server" />
</div>
</div>
@else
@else
<div>Server is not validated. Validate first.</div>
@endif
</div>

View File

@@ -3,23 +3,37 @@
{{ data_get_str($server, 'name')->limit(10) }} > Server Resources | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'managed' }" class="flex flex-col h-full gap-8 md:flex-row">
<div class="flex flex-row gap-4 md:flex-col">
<a :class="activeTab === 'managed' && 'dark:text-white'"
@click.prevent="activeTab = 'managed'; window.location.hash = 'managed'" href="#">Managed</a>
<a :class="activeTab === 'unmanaged' && 'dark:text-white'"
@click.prevent="activeTab = 'unmanaged'; window.location.hash = 'unmanaged'" href="#">Unmanaged</a>
</div>
<div x-data="{ activeTab: 'managed' }" class="flex flex-col h-full gap-8 md:flex-row">
<div class="w-full">
<div x-cloak x-show="activeTab === 'managed'" class="h-full">
<div class="flex flex-col">
<div class="flex gap-2">
<h2>Resources</h2>
<x-forms.button wire:click="refreshStatus">Refresh</x-forms.button>
</div>
<div class="subtitle">Here you can find all resources that are managed by Coolify.</div>
<div class="flex flex-col">
<div class="flex gap-2">
<h2>Resources</h2>
<x-forms.button wire:click="refreshStatus">Refresh</x-forms.button>
</div>
@if ($server->definedResources()->count() > 0)
<div>Here you can find all resources that are managed by Coolify.</div>
<div class="flex flex-row gap-4 py-10">
<div @class([
'box-without-bg cursor-pointer bg-coolgray-100 text-white w-full text-center items-center justify-center',
'bg-coollabs' => $activeTab === 'managed',
]) wire:click="loadManagedContainers">
Managed
<div class="flex flex-col items-center justify-center">
<x-loading wire:loading wire:target="loadManagedContainers" />
</div>
</div>
<div @class([
'box-without-bg cursor-pointer bg-coolgray-100 text-white w-full text-center items-center justify-center',
'bg-coollabs' => $activeTab === 'unmanaged',
]) wire:click="loadUnmanagedContainers">
Unmanaged
<div class="flex flex-col items-center justify-center">
<x-loading wire:loading wire:target="loadUnmanagedContainers" />
</div>
</div>
</div>
</div>
@if ($containers->count() > 0)
@if ($activeTab === 'managed')
<div class="flex flex-col">
<div class="flex flex-col">
<div class="overflow-x-auto">
@@ -78,19 +92,7 @@
</div>
</div>
</div>
@else
<div>No resources found.</div>
@endif
</div>
<div x-cloak x-show="activeTab === 'unmanaged'" class="h-full">
<div class="flex flex-col" x-init="$wire.loadUnmanagedContainers()">
<div class="flex gap-2">
<h2>Resources</h2>
<x-forms.button wire:click="refreshStatus">Refresh</x-forms.button>
</div>
<div class="subtitle">Here you can find all other containers running on the server.</div>
</div>
@if ($unmanagedContainers->count() > 0)
@elseif ($activeTab === 'unmanaged')
<div class="flex flex-col">
<div class="flex flex-col">
<div class="overflow-x-auto">
@@ -114,7 +116,7 @@
</tr>
</thead>
<tbody>
@forelse ($unmanagedContainers->sortBy('name',SORT_NATURAL) as $resource)
@forelse ($containers->sortBy('name',SORT_NATURAL) as $resource)
<tr>
<td class="px-5 py-4 text-sm whitespace-nowrap">
{{ data_get($resource, 'Names') }}
@@ -152,11 +154,14 @@
</div>
</div>
</div>
</div>
@else
<div>No resources found.</div>
@endif
</div>
@else
@if ($activeTab === 'managed')
<div>No managed resources found.</div>
@elseif ($activeTab === 'unmanaged')
<div>No unmanaged resources found.</div>
@endif
@endif
</div>
</div>
</div>

View File

@@ -1,40 +0,0 @@
<div>
<div class="flex items-end gap-2 pb-6 ">
<h2>Private Key</h2>
<x-modal-input buttonTitle="+ Add" title="New Private Key">
<livewire:security.private-key.create />
</x-modal-input>
<x-forms.button wire:click.prevent='checkConnection'>
Check connection
</x-forms.button>
</div>
<div class="flex flex-col gap-2 pb-6">
@if (data_get($server, 'privateKey.uuid'))
<div>
Currently attached Private Key:
<a
href="{{ route('security.private-key.show', ['private_key_uuid' => data_get($server, 'privateKey.uuid')]) }}">
<button class="dark:text-white btn-link">{{ data_get($server, 'privateKey.name') }}</button>
</a>
</div>
@else
<div class="">No private key attached.</div>
@endif
</div>
<h3 class="pb-4">Choose another Key</h3>
<div class="grid grid-cols-3 gap-2">
@forelse ($privateKeys as $private_key)
<div class="box group cursor-pointer"
wire:click='setPrivateKey({{ $private_key->id }})'>
<div class="flex flex-col ">
<div class="box-title">{{ $private_key->name }}</div>
<div class="box-description">{{ $private_key->description }}</div>
</div>
</div>
@empty
<div>No private keys found. </div>
@endforelse
</div>
</div>

View File

@@ -2,12 +2,238 @@
<x-slot:title>
{{ data_get_str($server, 'name')->limit(10) }} > Server Configurations | Coolify
</x-slot>
<x-server.navbar :server="$server" :parameters="$parameters" />
<livewire:server.form :server="$server" />
@if ($server->isFunctional() && $server->isMetricsEnabled())
<div class="pt-10">
<livewire:server.charts :server="$server" />
<x-server.navbar :server="$server" />
<div class="flex flex-col h-full gap-8 sm:flex-row">
<x-server.sidebar :server="$server" activeMenu="general" />
<div class="w-full">
<form wire:submit.prevent='submit' class="flex flex-col">
<div class="flex gap-2">
<h2>General</h2>
@if ($server->id === 0)
<x-modal-confirmation title="Confirm Server Settings Change?" buttonTitle="Save"
submitAction="submit" :actions="[
'If you missconfigure the server, you could lose a lot of functionalities of Coolify.',
]" :confirmWithText="false" :confirmWithPassword="false"
step2ButtonText="Save" />
@else
<x-forms.button type="submit">Save</x-forms.button>
@if ($server->isFunctional())
<x-slide-over closeWithX fullScreen>
<x-slot:title>Validate & configure</x-slot:title>
<x-slot:content>
<livewire:server.validate-and-install :server="$server" ask />
</x-slot:content>
<x-forms.button @click="slideOverOpen=true" wire:click.prevent='validateServer'
isHighlighted>
Revalidate server
</x-forms.button>
</x-slide-over>
@endif
@endif
</div>
@if ($server->isFunctional())
Server is reachable and validated.
@else
You can't use this server until it is validated.
@endif
@if ((!$isReachable || !$isUsable) && $server->id !== 0)
<x-slide-over closeWithX fullScreen>
<x-slot:title>Validate & configure</x-slot:title>
<x-slot:content>
<livewire:server.validate-and-install :server="$server" />
</x-slot:content>
<x-forms.button @click="slideOverOpen=true"
class="mt-8 mb-4 w-full font-bold box-without-bg bg-coollabs hover:bg-coollabs-100"
wire:click.prevent='validateServer' isHighlighted>
Validate Server & Install Docker Engine
</x-forms.button>
</x-slide-over>
@if ($server->validation_logs)
<h4>Previous Validation Logs</h4>
<div class="pb-8">
{!! $server->validation_logs !!}
</div>
@endif
@endif
@if ((!$isReachable || !$isUsable) && $server->id === 0)
<x-forms.button class="mt-8 mb-4 font-bold box-without-bg bg-coollabs hover:bg-coollabs-100"
wire:click.prevent='checkLocalhostConnection' isHighlighted>
Validate Server
</x-forms.button>
@endif
@if ($server->isForceDisabled() && isCloud())
<div class="pt-4 font-bold text-red-500">The system has disabled the server because you have
exceeded the
number of servers for which you have paid.</div>
@endif
<div class="flex flex-col gap-2 pt-4">
<div class="flex flex-col gap-2 w-full lg:flex-row">
<x-forms.input id="name" label="Name" required />
<x-forms.input id="description" label="Description" />
@if (!$isSwarmWorker && !$isBuildServer)
<x-forms.input placeholder="https://example.com" id="wildcard_domain"
label="Wildcard Domain"
helper='A wildcard domain allows you to receive a randomly generated domain for your new applications. <br><br>For instance, if you set "https://example.com" as your wildcard domain, your applications will receive domains like "https://randomId.example.com".' />
@endif
</div>
<div class="flex flex-col gap-2 w-full lg:flex-row">
<x-forms.input type="password" id="ip" label="IP Address/Domain"
helper="An IP Address (127.0.0.1) or domain (example.com). Make sure there is no protocol like http(s):// so you provide a FQDN not a URL."
required />
<div class="flex gap-2">
<x-forms.input id="user" label="User" required />
<x-forms.input type="number" id="port" label="Port" required />
</div>
</div>
<div class="w-full" x-data="{
open: false,
search: '{{ $serverTimezone ?: '' }}',
timezones: @js($timezones),
placeholder: '{{ $serverTimezone ? 'Search timezone...' : 'Select Server Timezone' }}',
init() {
this.$watch('search', value => {
if (value === '') {
this.open = true;
}
})
}
}">
<div class="flex items-center mb-1">
<label for="serverTimezone">Server
Timezone</label>
<x-helper class="ml-2"
helper="Server's timezone. This is used for backups, cron jobs, etc." />
</div>
<div class="relative">
<div class="inline-flex relative items-center w-64">
<input autocomplete="off"
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" x-model="search"
@focus="open = true" @click.away="open = false" @input="open = true"
class="w-full input" :placeholder="placeholder"
wire:model.debounce.300ms="serverTimezone">
<svg class="absolute right-0 mr-2 w-4 h-4" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
@click="open = true">
<path stroke-linecap="round" stroke-linejoin="round"
d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
</svg>
</div>
<div x-show="open"
class="overflow-auto overflow-x-hidden absolute z-50 mt-1 w-64 max-h-60 bg-white rounded-md border shadow-lg dark:bg-coolgray-100 dark:border-coolgray-200 scrollbar">
<template
x-for="timezone in timezones.filter(tz => tz.toLowerCase().includes(search.toLowerCase()))"
:key="timezone">
<div @click="search = timezone; open = false; $wire.set('server.settings.server_timezone', timezone)"
class="px-4 py-2 text-gray-800 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 dark:text-gray-200"
x-text="timezone"></div>
</template>
</div>
</div>
</div>
<div class="w-full">
@if (!$server->isLocalhost())
<div class="w-96">
<x-forms.checkbox instantSave id="isBuildServer" label="Use it as a build server?" />
</div>
@if (!$server->isBuildServer() && !$server->settings->is_cloudflare_tunnel)
<h3 class="pt-6">Swarm <span class="text-xs text-neutral-500">(experimental)</span>
</h3>
<div class="pb-4">Read the docs <a class='underline dark:text-white'
href='https://coolify.io/docs/knowledge-base/docker/swarm'
target='_blank'>here</a>.
</div>
<div class="w-96">
@if ($server->settings->is_swarm_worker)
<x-forms.checkbox disabled instantSave type="checkbox" id="isSwarmManager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@else
<x-forms.checkbox instantSave type="checkbox" id="isSwarmManager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@endif
@if ($server->settings->is_swarm_manager)
<x-forms.checkbox disabled instantSave type="checkbox" id="isSwarmWorker"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@else
<x-forms.checkbox instantSave type="checkbox" id="isSwarmWorker"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Worker?" />
@endif
</div>
@endif
@endif
</div>
</div>
</form>
@if ($server->isFunctional() && !$server->isSwarm() && !$server->isBuildServer())
<form wire:submit.prevent='submit'>
<div class="flex gap-2 items-center pt-4 pb-2">
<h3>Sentinel</h3>
@if ($server->isSentinelEnabled())
<div class="flex gap-2 items-center">
@if ($server->isSentinelLive())
<x-status.running status="In sync" noLoading title="{{ $sentinelUpdatedAt }}" />
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click='restartSentinel'>Restart</x-forms.button>
@else
<x-status.stopped status="Out of sync" noLoading
title="{{ $sentinelUpdatedAt }}" />
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click='restartSentinel'>Sync</x-forms.button>
@endif
</div>
@endif
</div>
<div class="flex flex-col gap-2">
<div class="flex gap-2">Experimental feature <x-helper
helper="Sentinel reports your server's & container's health and collects metrics." />
</div>
<div class="w-64">
<x-forms.checkbox wire:model.live="isSentinelEnabled" label="Enable Sentinel" />
@if ($server->isSentinelEnabled())
<x-forms.checkbox id="isSentinelDebugEnabled" label="Enable Sentinel Debug"
instantSave />
<x-forms.checkbox instantSave id="isMetricsEnabled" label="Enable Metrics" />
@else
<x-forms.checkbox id="isSentinelDebugEnabled" label="Enable Sentinel Debug" disabled
instantSave />
<x-forms.checkbox instantSave disabled id="isMetricsEnabled" label="Enable Metrics" />
label="Enable Metrics" />
@endif
</div>
@if ($server->isSentinelEnabled())
<div class="flex flex-wrap gap-2 sm:flex-nowrap items-end">
<x-forms.input type="password" id="sentinelToken" label="Sentinel token" required
helper="Token for Sentinel." />
<x-forms.button wire:click="regenerateSentinelToken">Regenerate</x-forms.button>
</div>
<x-forms.input id="sentinelCustomUrl" required label="Coolify URL"
helper="URL to your Coolify instance. If it is empty that means you do not have a FQDN set for your Coolify instance." />
<div class="flex flex-col gap-2">
<div class="flex flex-wrap gap-2 sm:flex-nowrap">
<x-forms.input id="sentinelMetricsRefreshRateSeconds"
label="Metrics rate (seconds)" required
helper="The interval for gathering metrics. Lower means more disk space will be used." />
<x-forms.input id="sentinelMetricsHistoryDays" label="Metrics history (days)"
required helper="How many days should the metrics data should be reserved." />
<x-forms.input id="sentinelPushIntervalSeconds" label="Push interval (seconds)"
required
helper="How many seconds should the metrics data should be pushed to the collector." />
</div>
</div>
@endif
</div>
</form>
@endif
</div>
@endif
<livewire:server.delete :server="$server" />
</div>
</div>

View File

@@ -17,13 +17,13 @@
@if (isset($database) && isset($backup))
<div class="flex flex-col gap-3 pb-4">
<div class="flex gap-2">
<x-forms.input label="UUID" readonly id="database.uuid" />
<x-forms.input label="Name" readonly id="database.name" />
<x-forms.input label="Description" id="database.description" />
<x-forms.input label="UUID" readonly id="uuid" />
<x-forms.input label="Name" readonly id="name" />
<x-forms.input label="Description" id="description" />
</div>
<div class="flex gap-2">
<x-forms.input label="User" readonly id="database.postgres_user" />
<x-forms.input type="password" label="Password" readonly id="database.postgres_password" />
<x-forms.input label="User" readonly id="postgres_user" />
<x-forms.input type="password" label="Password" readonly id="postgres_password" />
</div>
</div>
<livewire:project.database.backup-edit :backup="$backup" :s3s="$s3s" :status="data_get($database, 'status')" />
@@ -33,9 +33,8 @@
@else
To configure automatic backup for your Coolify instance, you first need to add a database resource
into Coolify.
<x-forms.button class="mt-2" wire:click="add_coolify_database">Add Database</x-forms.button>
<x-forms.button class="mt-2" wire:click="addCoolifyDatabase">Configure Backup</x-forms.button>
@endif
</div>
</div>
</div>

View File

@@ -16,10 +16,10 @@
<h4 class="pt-6">Instance Settings</h4>
<div class="flex flex-wrap items-end gap-2">
<div class="flex gap-2 md:flex-row flex-col w-full">
<x-forms.input id="settings.fqdn" label="Instance's Domain"
<x-forms.input id="fqdn" label="Instance's Domain"
helper="Enter the full domain name (FQDN) of the instance, including 'https://' if you want to secure the dashboard with HTTPS. Setting this will make the dashboard accessible via this domain, secured by HTTPS, instead of just the IP address."
placeholder="https://coolify.yourdomain.com" />
<x-forms.input id="settings.instance_name" label="Instance's Name" placeholder="Coolify" />
<x-forms.input id="instance_name" label="Instance's Name" placeholder="Coolify" />
<div class="w-full" x-data="{
open: false,
search: '{{ $settings->instance_timezone ?: '' }}',
@@ -34,18 +34,19 @@
}
}">
<div class="flex items-center mb-1">
<label for="settings.instance_timezone">Instance
<label for="instance_timezone">Instance
Timezone</label>
<x-helper class="ml-2"
helper="Timezone for the Coolify instance. This is used for the update check and automatic update frequency." />
</div>
<div class="relative">
<div class="inline-flex items-center relative w-full">
<input autocomplete="off" wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
<input autocomplete="off"
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" x-model="search"
@focus="open = true" @click.away="open = false" @input="open = true"
class="w-full input " :placeholder="placeholder"
wire:model.debounce.300ms="settings.instance_timezone">
wire:model.debounce.300ms="instance_timezone">
<svg class="absolute right-0 w-4 h-4 mr-2" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
@click="open = true">
@@ -58,35 +59,43 @@
<template
x-for="timezone in timezones.filter(tz => tz.toLowerCase().includes(search.toLowerCase()))"
:key="timezone">
<div @click="search = timezone; open = false; $wire.set('settings.instance_timezone', timezone)"
<div @click="search = timezone; open = false; $wire.set('instance_timezone', timezone)"
class="px-4 py-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 text-gray-800 dark:text-gray-200"
x-text="timezone"></div>
</template>
</div>
</div>
</div>
</div>
</div>
<div class="flex gap-2">
<x-forms.input id="public_ipv4" type="password" label="Instance's IPv4"
helper="Enter the IPv4 address of the instance.<br><br>It is useful if you have several IPv4 addresses and Coolify could not detect the correct one."
placeholder="1.2.3.4" />
<x-forms.input id="public_ipv6" type="password" label="Instance's IPv6"
helper="Enter the IPv6 address of the instance.<br><br>It is useful if you have several IPv6 addresses and Coolify could not detect the correct one."
placeholder="2001:db8::1" />
</div>
<h4 class="w-full pt-6">DNS Validation</h4>
<div class="md:w-96">
<x-forms.checkbox instantSave id="is_dns_validation_enabled" label="Enabled" />
</div>
<x-forms.input id="settings.custom_dns_servers" label="DNS Servers"
<x-forms.input id="custom_dns_servers" label="DNS Servers"
helper="DNS servers to validate FQDNs against. A comma separated list of DNS servers."
placeholder="1.1.1.1,8.8.8.8" />
</div>
{{-- <div class="flex gap-2 ">
<x-forms.input type="number" id="settings.public_port_min" label="Public Port Min" />
<x-forms.input type="number" id="settings.public_port_max" label="Public Port Max" />
<x-forms.input type="number" id="public_port_min" label="Public Port Min" />
<x-forms.input type="number" id="public_port_max" label="Public Port Max" />
</div> --}}
</div>
<h4 class="pt-6">API</h4>
<div class="md:w-96">
<div class="md:w-96 pb-2">
<x-forms.checkbox instantSave id="is_api_enabled" label="Enabled" />
</div>
<x-forms.input id="settings.allowed_ips" label="Allowed IPs"
<x-forms.input id="allowed_ips" label="Allowed IPs"
helper="Allowed IP lists for the API. A comma separated list of IPs. Empty means you allow from everywhere."
placeholder="1.1.1.1,8.8.8.8" />
@@ -95,7 +104,7 @@
<x-forms.checkbox instantSave id="is_registration_enabled" label="Registration Allowed" />
<x-forms.checkbox instantSave id="do_not_track" label="Do Not Track" />
</div>
<h5 class="pt-4 font-bold text-white">Update</h5>
<h4 class="pt-6">Update</h4>
<div class="text-right md:w-96">
@if (!is_null(env('AUTOUPDATE', null)))
<div class="text-right md:w-96">
@@ -119,6 +128,37 @@
helper="Cron expression for auto update frequency (automatically update coolify).<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every day at 00:00" />
@endif
</div>
</form>
<h4 class="pt-6">Advanced</h4>
<div class="text-right md:w-96">
<x-forms.checkbox instantSave id="is_registration_enabled" label="Registration Allowed" />
<x-forms.checkbox instantSave id="do_not_track" label="Do Not Track" />
</div>
<h5 class="py-4 font-bold text-white">Confirmation Settings</h5>
@if ($disable_two_step_confirmation)
<div class="md:w-96 pb-4">
<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">
<x-modal-confirmation title="Disable Two Step Confirmation?"
buttonTitle="Disable Two Step Confirmation" isErrorButton submitAction="toggleTwoStepConfirmation"
:actions="[
'Tow Step confimation will be disabled globally.',
'Disabling two step confirmation reduces security (as anyone can easily delete anything).',
'The risk of accidental actions will increase.',
]" confirmationText="DISABLE TWO STEP CONFIRMATION"
confirmationLabel="Please type the confirmation text to disable two step confirmation."
shortConfirmationLabel="Confirmation text" step3ButtonText="Disable Two Step Confirmation" />
</div>
<div class="p-4 mb-4 text-white border-l-4 border-red-500 bg-error md:w-[40rem] w-full mb-32">
<p class="font-bold">Warning!</p>
<p>Disabling two step confirmation reduces security (as anyone can easily delete anything) and increases
the
risk of accidental actions. This is not recommended for production servers.</p>
</div>
@endif
</form>
</div>

View File

@@ -1,5 +1,5 @@
<div wire:poll.2000ms="get_deployments" wire:init='get_deployments'>
@forelse ($deployments_per_tag_per_server as $server_name => $deployments)
<div wire:poll.2000ms="getDeployments" wire:init='getDeployments'>
@forelse ($deploymentsPerTagPerServer as $server_name => $deployments)
<h4 class="py-4">{{ $server_name }}</h4>
<div class="grid grid-cols-1 gap-2">
@foreach ($deployments as $deployment)

View File

@@ -1,15 +1,12 @@
<div>
<x-slot:title>
Tags | Coolify
</x-slot>
<h1>Tags</h1>
<div class="flex flex-col pb-6 ">
<div class="flex flex-col pb-6">
<div class="subtitle">Tags help you to perform actions on multiple resources.</div>
<div class="">
@if ($tags->count() === 0)
<div>No tags yet defined yet. Go to a resource and add a tag there.</div>
@else
<x-forms.datalist wire:model="tag" onUpdate='tag_updated'>
<x-forms.datalist wire:model="tag" onUpdate='tagUpdated'>
@foreach ($tags as $oneTag)
<option value="{{ $oneTag->name }}">{{ $oneTag->name }}</option>
@endforeach
@@ -21,7 +18,7 @@
<x-forms.input readonly label="Deploy Webhook URL" id="webhook" />
</div>
<x-modal-confirmation title="Redeploy all resources with this tag?" isHighlighted
buttonTitle="Redeploy All" submitAction="redeploy_all" :actions="[
buttonTitle="Redeploy All" submitAction="redeployAll" :actions="[
'All resources with this tag will be redeployed.',
'During redeploy resources will be temporarily unavailable.',
]"
@@ -31,34 +28,34 @@
</div>
<div class="grid grid-cols-1 gap-2 pt-4 lg:grid-cols-2 xl:grid-cols-3">
@foreach ($applications as $application)
<div class="box group">
<a href="{{ $application->link() }}" class="flex flex-col ">
<a href="{{ $application->link() }}"class="box group">
<div class="flex flex-col">
<div class="box-title">{{ $application->name }}</div>
<div class="box-description">
{{ $application->project()->name }}/{{ $application->environment->name }}
</div>
<div class="box-description">{{ $application->description }}</div>
</a>
</div>
</div>
</a>
@endforeach
@foreach ($services as $service)
<div class="box group">
<a href="{{ $service->link() }}" class="flex flex-col ">
<a href="{{ $service->link() }}" class="box group">
<div class="flex flex-col ">
<div class="box-title">{{ $service->name }}</div>
<div class="box-description">
{{ $service->project()->name }}/{{ $service->environment->name }}</div>
<div class="box-description">{{ $service->description }}</div>
</a>
</div>
</div>
</a>
@endforeach
</div>
<div class="flex items-center gap-2">
<h3 class="py-4">Deployments</h3>
@if (count($deployments_per_tag_per_server) > 0)
@if (count($deploymentsPerTagPerServer) > 0)
<x-loading />
@endif
</div>
<livewire:tags.deployments :deployments_per_tag_per_server="$deployments_per_tag_per_server" :resource_ids="$applications->pluck('id')" />
<livewire:tags.deployments :deploymentsPerTagPerServer="$deploymentsPerTagPerServer" :resourceIds="$applications->pluck('id')" />
</div>
@endif
@endif

View File

@@ -1,7 +1,4 @@
<div>
<x-slot:title>
Tag | Coolify
</x-slot>
<div class="flex items-start gap-2">
<div>
<h1>Tags</h1>

View File

@@ -1,8 +1,10 @@
<form wire:submit='viaLink' class="flex flex-col items-start gap-2 lg:items-end lg:flex-row">
<div class="flex flex-1 gap-2">
<x-forms.input id="email" type="email" label="Email" name="email" placeholder="Email" required />
<x-forms.input id="email" type="email" label="Email" name="email" placeholder="Email" required />
<x-forms.select id="role" name="role" label="Role">
<option value="owner">Owner</option>
@if (auth()->user()->role() === 'owner')
<option value="owner">Owner</option>
@endif
<option value="admin">Admin</option>
<option value="member">Member</option>
</x-forms.select>