Merge branch 'next' into feature/authentik-provider

This commit is contained in:
🏔️ Peak
2024-12-11 15:24:26 +01:00
committed by GitHub
814 changed files with 31372 additions and 13434 deletions

View File

@@ -4,11 +4,9 @@
@tailwind components;
@tailwind utilities;
html,
body {
@apply h-full bg-neutral-50 text-neutral-800 dark:bg-base dark:text-neutral-400;
@apply h-full bg-neutral-50 text-neutral-800 dark:bg-base dark:text-neutral-400 w-full;
}
body {
@@ -32,6 +30,14 @@ body {
@apply text-black dark:bg-coolgray-100 dark:text-white ring-neutral-200 dark:ring-coolgray-300;
}
.input-sticky {
@apply text-black dark:bg-coolgray-100 dark:text-white ring-neutral-200 dark:ring-coolgray-300 focus:ring-2 dark:focus:ring-coolgray-300 focus:ring-neutral-400 block w-full py-1.5 rounded border-0 text-sm ring-1 ring-inset;
}
.input-sticky-active {
@apply border-2 border-coollabs text-black dark:text-white focus:bg-neutral-200 dark:focus:bg-coolgray-400 focus:border-coollabs;
}
/* Readonly */
.input {
@apply dark:read-only:text-neutral-500 dark:read-only:ring-0 dark:read-only:bg-coolgray-100/40 placeholder:text-neutral-300 dark:placeholder:text-neutral-700 read-only:text-neutral-500 read-only:bg-neutral-200;
@@ -69,7 +75,7 @@ button[isHighlighted]:not(:disabled) {
}
h1 {
@apply text-2xl font-bold dark:text-white;
@apply text-3xl font-bold dark:text-white;
}
h2 {
@@ -124,6 +130,10 @@ tr td:first-child {
@apply pl-4 pr-3 font-bold sm:pl-6;
}
section {
@apply mb-12;
}
.alert-success {
@apply flex items-center gap-2 text-success;
}
@@ -168,10 +178,6 @@ tr td:first-child {
@apply bg-error;
}
/* [type='checkbox']:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='black' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e");
} */
.menu {
@apply flex items-center gap-1;
}
@@ -193,7 +199,7 @@ tr td:first-child {
}
.scrollbar {
@apply scrollbar-thumb-coollabs-100 dark:scrollbar-track-coolgray-200 scrollbar-track-neutral-200 scrollbar-w-2;
@apply scrollbar-thumb-coollabs-100 dark:scrollbar-track-coolgray-200 scrollbar-track-neutral-200 scrollbar-thin;
}
.main {
@@ -217,7 +223,7 @@ tr td:first-child {
}
.box {
@apply relative flex lg:flex-row flex-col p-2 transition-colors cursor-pointer min-h-[4rem] dark:bg-coolgray-100 bg-white border text-black dark:text-white hover:text-black border-neutral-200 dark:border-black hover:bg-neutral-100 dark:hover:bg-coollabs-100 dark:hover:text-white hover:no-underline;
@apply relative flex lg:flex-row flex-col p-2 transition-colors cursor-pointer min-h-[4rem] dark:bg-coolgray-100 shadow bg-white border text-black dark:text-white hover:text-black border-neutral-200 dark:border-black hover:bg-neutral-100 dark:hover:bg-coollabs-100 dark:hover:text-white hover:no-underline;
}
.box-boarding {
@@ -316,4 +322,4 @@ tr td:first-child {
.dz-button {
@apply w-full p-4 py-10 my-4 font-bold bg-white border dark:border-coolgray-400 dark:text-white dark:bg-transparent hover:dark:bg-coolgray-400;
}
}

View File

@@ -1,32 +0,0 @@
/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
// import axios from 'axios';
// window.axios = axios;
// window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
// import Echo from 'laravel-echo';
// import Pusher from 'pusher-js';
// window.Pusher = Pusher;
// window.Echo = new Echo({
// broadcaster: 'pusher',
// key: import.meta.env.VITE_PUSHER_APP_KEY,
// cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1',
// wsHost: import.meta.env.VITE_PUSHER_HOST ? import.meta.env.VITE_PUSHER_HOST : `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`,
// wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,
// wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,
// forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',
// enabledTransports: ['ws', 'wss'],
// });

View File

@@ -60,7 +60,22 @@ export function initializeTerminalComponent() {
};
},
resetTerminal() {
if (this.term) {
this.$wire.dispatch('error', 'Terminal websocket connection lost.');
this.term.reset();
this.term.clear();
this.pendingWrites = 0;
this.paused = false;
this.commandBuffer = '';
// Force a refresh
this.$nextTick(() => {
this.resizeTerminal();
this.term.focus();
});
}
},
setupTerminal() {
const terminalElement = document.getElementById('terminal');
if (terminalElement) {
@@ -69,9 +84,15 @@ export function initializeTerminalComponent() {
rows: 30,
fontFamily: '"Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"',
cursorBlink: true,
rendererType: 'canvas',
convertEol: true,
disableStdin: false
});
this.fitAddon = new FitAddon();
this.term.loadAddon(this.fitAddon);
this.$nextTick(() => {
this.resizeTerminal();
});
}
},
@@ -101,12 +122,19 @@ export function initializeTerminalComponent() {
`${connectionString.protocol}://${connectionString.host}${connectionString.port}${connectionString.path}`
this.socket = new WebSocket(url);
this.socket.onopen = () => {
console.log('[Terminal] WebSocket connection established. Cool cool cool cool cool cool.');
};
this.socket.onmessage = this.handleSocketMessage.bind(this);
this.socket.onerror = (e) => {
console.error('WebSocket error:', e);
console.error('[Terminal] WebSocket error.');
};
this.socket.onclose = () => {
console.log('WebSocket connection closed');
console.warn('[Terminal] WebSocket connection closed.');
this.resetTerminal();
this.message = '(connection closed)';
this.terminalActive = false;
this.reconnect();
};
}
@@ -117,19 +145,18 @@ export function initializeTerminalComponent() {
clearInterval(this.reconnectInterval);
}
this.reconnectInterval = setInterval(() => {
console.log('Attempting to reconnect...');
console.warn('[Terminal] Attempting to reconnect...');
this.initializeWebSocket();
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
console.log('Reconnected successfully');
console.log('[Terminal] Reconnected successfully');
clearInterval(this.reconnectInterval);
this.reconnectInterval = null;
window.location.reload();
}
}, 2000);
},
handleSocketMessage(event) {
this.message = '(connection closed)';
if (event.data === 'pty-ready') {
if (!this.term._initialized) {
this.term.open(document.getElementById('terminal'));
@@ -150,8 +177,17 @@ export function initializeTerminalComponent() {
this.term.reset();
this.commandBuffer = '';
} else {
this.pendingWrites++;
this.term.write(event.data, this.flowControlCallback.bind(this));
try {
this.pendingWrites++;
this.term.write(event.data, (err) => {
if (err) {
console.error('[Terminal] Write error:', err);
}
this.flowControlCallback();
});
} catch (error) {
console.error('[Terminal] Write operation failed:', error);
}
}
},
@@ -173,11 +209,15 @@ export function initializeTerminalComponent() {
if (!this.term) return;
this.term.onData((data) => {
this.socket.send(JSON.stringify({ message: data }));
if (data === '\r') {
this.commandBuffer = '';
if (this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify({ message: data }));
if (data === '\r') {
this.commandBuffer = '';
} else {
this.commandBuffer += data;
}
} else {
this.commandBuffer += data;
console.warn('[Terminal] WebSocket not ready, data not sent');
}
});

View File

@@ -9,7 +9,7 @@
<div
class="w-full bg-white shadow md:mt-0 sm:max-w-md xl:p-0 dark:bg-base ">
<div class="p-6 space-y-4 md:space-y-6 sm:p-8">
@if (is_transactional_emails_active())
@if (is_transactional_emails_enabled())
<form action="/forgot-password" method="POST" class="flex flex-col gap-2">
@csrf
<x-forms.input required type="email" name="email" label="{{ __('input.email') }}" />
@@ -18,7 +18,7 @@
@else
<div>Transactional emails are not active on this instance.</div>
<div>See how to set it in our <a class="dark:text-white" target="_blank"
href="{{ config('constants.docs.base_url') }}">docs</a>, or how to
href="{{ config('constants.urls.docs') }}">docs</a>, or how to
manually reset password.
</div>
@endif

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

@@ -5,30 +5,41 @@
'disabled' => false,
'instantSave' => false,
'value' => null,
'hideLabel' => false,
'domValue' => null,
'checked' => false,
'fullWidth' => false,
])
<div @class([
'flex flex-row items-center gap-4 px-2 py-1 form-control min-w-fit dark:hover:bg-coolgray-100',
'flex flex-row items-center gap-4 pr-2 py-1 form-control min-w-fit dark:hover:bg-coolgray-100',
'w-full' => $fullWidth,
])>
@if (!$hideLabel)
<label class="flex gap-4 px-0 min-w-fit label">
<span class="flex gap-2">
@if ($label)
{!! $label !!}
@else
{{ $id }}
@endif
@if ($helper)
<x-helper :helper="$helper" />
@endif
</span>
</label>
@endif
<span class="flex-grow"></span>
<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 />
<label @class([
'flex gap-4 items-center px-0 min-w-fit label w-full cursor-pointer',
])>
<span class="flex flex-grow gap-2">
@if ($label)
{!! $label !!}
@else
{{ $id }}
@endif
@if ($helper)
<x-helper :helper="$helper" />
@endif
</span>
@if ($instantSave)
<input type="checkbox" @disabled($disabled) {{ $attributes->merge(['class' => $defaultClass]) }}
wire:loading.attr="disabled"
wire:click='{{ $instantSave === 'instantSave' || $instantSave == '1' ? 'instantSave' : $instantSave }}'
wire:model={{ $id }} @if ($checked) checked @endif />
@else
@if ($domValue)
<input type="checkbox" @disabled($disabled) {{ $attributes->merge(['class' => $defaultClass]) }}
value={{ $domValue }} @if ($checked) checked @endif />
@else
<input type="checkbox" @disabled($disabled) {{ $attributes->merge(['class' => $defaultClass]) }}
wire:model={{ $value ?? $id }} @if ($checked) checked @endif />
@endif
@endif
</label>
</div>

View File

@@ -41,8 +41,9 @@
@if ($id !== 'null') wire:model={{ $id }} @endif
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" wire:loading.attr="disabled"
type="{{ $type }}" @disabled($disabled)
min="{{ $attributes->get('min') }}" max="{{ $attributes->get('max') }}"
type="{{ $type }}" @disabled($disabled) min="{{ $attributes->get('min') }}"
max="{{ $attributes->get('max') }}" minlength="{{ $attributes->get('minlength') }}"
maxlength="{{ $attributes->get('maxlength') }}"
@if ($id !== 'null') id={{ $id }} @endif name="{{ $name }}"
placeholder="{{ $attributes->get('placeholder') }}">
@endif

View File

@@ -51,8 +51,8 @@
type="{{ $type }}" @readonly($readonly) @disabled($disabled) id="{{ $id }}"
name="{{ $name }}" placeholder="{{ $attributes->get('placeholder') }}"
aria-placeholder="{{ $attributes->get('placeholder') }}">
<textarea x-cloak x-show="type !== 'password'" placeholder="{{ $placeholder }}"
{{ $attributes->merge(['class' => $defaultClass]) }}
<textarea minlength="{{ $minlength }}" maxlength="{{ $maxlength }}" x-cloak x-show="type !== 'password'"
placeholder="{{ $placeholder }}" {{ $attributes->merge(['class' => $defaultClass]) }}
@if ($realtimeValidation) wire:model.debounce.200ms="{{ $id }}"
@else
wire:model={{ $value ?? $id }}
@@ -62,7 +62,8 @@
</div>
@else
<textarea {{ $allowTab ? '@keydown.tab=handleKeydown' : '' }} placeholder="{{ $placeholder }}"
<textarea minlength="{{ $minlength }}" maxlength="{{ $maxlength }}"
{{ $allowTab ? '@keydown.tab=handleKeydown' : '' }} placeholder="{{ $placeholder }}"
{{ !$spellcheck ? 'spellcheck=false' : '' }} {{ $attributes->merge(['class' => $defaultClass]) }}
@if ($realtimeValidation) wire:model.debounce.200ms="{{ $id }}"
@else

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' => '',
])
<div x-data="{
@php
use App\Models\InstanceSettings;
$disableTwoStepConfirmation = data_get(InstanceSettings::get(), 'disable_two_step_confirmation');
@endphp
<div wire:ignore 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 = '';
@@ -99,8 +106,8 @@
this.selectedActions.push(id);
}
}
}" @keydown.escape.window="modalOpen = false; resetModal()" :class="{ 'z-40': modalOpen }"
class="relative w-auto h-auto">
}" @keydown.escape.window="modalOpen = false; resetModal()"
:class="{ 'z-40': modalOpen }" class="relative w-auto h-auto">
@if ($customButton)
@if ($buttonFullWidth)
<x-forms.button @click="modalOpen=true" class="w-full">
@@ -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,123 +249,133 @@
</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 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>
<!-- Step 3: Password confirmation -->
<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>
@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">
@php
$passwordConfirm = Str::uuid();
@endphp
<label for="password-confirm-{{ $passwordConfirm }}"
class="block text-sm font-medium text-gray-700 dark:text-gray-300">
Your Password
</label>
<form @submit.prevent="false" @keydown.enter.prevent>
<input type="text" name="username" autocomplete="username"
value="{{ auth()->user()->email }}" style="display: none;">
<input type="password" id="password-confirm-{{ $passwordConfirm }}"
x-model="password" class="w-full input" placeholder="Enter your password"
autocomplete="current-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>
<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>
<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>
</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>
<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>
@endif
</div>
</div>
</div>

View File

@@ -2,13 +2,15 @@
'title' => 'Are you sure?',
'buttonTitle' => 'Open Modal',
'isErrorButton' => false,
'isHighlightedButton' => false,
'disabled' => false,
'action' => 'delete',
'content' => null,
'closeOutside' => true,
'minWidth' => '36rem',
])
<div x-data="{ modalOpen: false }" :class="{ 'z-40': modalOpen }" @keydown.window.escape="modalOpen=false"
class="relative w-auto h-auto">
class="relative w-auto h-auto" wire:ignore>
@if ($content)
<div @click="modalOpen=true">
{{ $content }}
@@ -18,13 +20,15 @@
<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
@endif
<template x-teleport="body">
<div x-show="modalOpen"
class="fixed top-0 left-0 lg:px-0 px-4 z-[99] flex items-center justify-center w-screen h-screen" x-cloak>
class="fixed top-0 left-0 lg:px-0 px-4 z-[99] flex items-center justify-center w-screen h-screen">
<div x-show="modalOpen" x-transition:enter="ease-out duration-100" x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-100"
x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"
@@ -37,7 +41,7 @@
x-transition:leave="ease-in duration-100"
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave-end="opacity-0 -translate-y-2 sm:scale-95"
class="relative w-full py-6 border rounded drop-shadow min-w-full lg:min-w-[36rem] max-w-fit bg-white border-neutral-200 dark:bg-base px-6 dark:border-coolgray-300">
class="relative w-full py-6 border rounded drop-shadow min-w-full lg:min-w-[{{ $minWidth }}] max-w-fit bg-white border-neutral-200 dark:bg-base px-6 dark:border-coolgray-300">
<div class="flex items-center justify-between pb-3">
<h3 class="text-2xl font-bold">{{ $title }}</h3>
<button @click="modalOpen=false"

View File

@@ -1,4 +1,4 @@
<nav class="flex flex-col flex-1 bg-white border-r dark:border-coolgray-200 dark:bg-base" x-data="{
<nav class="flex flex-col flex-1 px-2 bg-white border-r dark:border-coolgray-200 dark:bg-base" x-data="{
switchWidth() {
if (this.full === 'full') {
localStorage.setItem('pageWidth', 'center');
@@ -7,8 +7,13 @@
}
window.location.reload();
},
setZoom(zoom) {
localStorage.setItem('zoom', zoom);
window.location.reload();
},
init() {
this.full = localStorage.getItem('pageWidth');
this.zoom = localStorage.getItem('zoom');
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
const userSettings = localStorage.getItem('theme');
if (userSettings !== 'system') {
@@ -21,6 +26,7 @@
}
});
this.queryTheme();
this.checkZoom();
},
setTheme(type) {
this.theme = type;
@@ -44,9 +50,33 @@
this.theme = 'system';
document.documentElement.classList.remove('dark');
}
},
checkZoom() {
if (this.zoom === null) {
this.setZoom(100);
}
if (this.zoom === '90') {
const style = document.createElement('style');
style.textContent = `
html {
font-size: 93.75%;
}
:root {
--vh: 1vh;
}
@media (min-width: 1024px) {
html {
font-size: 87.5%;
}
}
`;
document.head.appendChild(style);
}
}
}">
<div class="flex pt-6 pb-4 pl-3">
<div class="flex pt-6 pb-4 pl-2">
<div class="flex flex-col w-full">
<div class="text-2xl font-bold tracking-wide dark:text-white">Coolify</div>
<x-version />
@@ -69,7 +99,7 @@
</div>
</x-slot:title>
<div class="flex flex-col gap-1">
<div class="mb-1 font-bold border-b dark:border-coolgray-500 dark:text-white text-md">Color</div>
<div class="font-bold border-b dark:border-coolgray-500 dark:text-white text-md">Color</div>
<button @click="setTheme('dark')" class="px-1 dropdown-item-no-padding">Dark</button>
<button @click="setTheme('light')" class="px-1 dropdown-item-no-padding">Light</button>
<button @click="setTheme('system')" class="px-1 dropdown-item-no-padding">System</button>
@@ -78,6 +108,9 @@
x-show="full === 'full'">Center</button>
<button @click="switchWidth()" class="px-1 dropdown-item-no-padding"
x-show="full === 'center'">Full</button>
<div class="my-1 font-bold border-b dark:border-coolgray-500 dark:text-white text-md">Zoom</div>
<button @click="setZoom(100)" class="px-1 dropdown-item-no-padding">100%</button>
<button @click="setZoom(90)" class="px-1 dropdown-item-no-padding">90%</button>
</div>
</x-dropdown>
</div>
@@ -148,7 +181,7 @@
<li>
<a title="Destinations"
class="{{ request()->is('destination*') ? 'menu-item-active menu-item' : 'menu-item' }}"
href="{{ route('destination.all') }}">
href="{{ route('destination.index') }}">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
<path fill="none" stroke="currentColor" stroke-linecap="round"
@@ -163,8 +196,8 @@
class="{{ request()->is('storages*') ? 'menu-item-active menu-item' : 'menu-item' }}"
href="{{ route('storage.index') }}">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2">
<g fill="none" stroke="currentColor" stroke-linecap="round"
stroke-linejoin="round" stroke-width="2">
<path d="M4 6a8 3 0 1 0 16 0A8 3 0 1 0 4 6" />
<path d="M4 6v6a8 3 0 0 0 16 0V6" />
<path d="M4 12v6a8 3 0 0 0 16 0v-6" />
@@ -215,7 +248,7 @@
<li>
<a title="Tags"
class="{{ request()->is('tags*') ? 'menu-item-active menu-item' : 'menu-item' }}"
href="{{ route('tags.index') }}">
href="{{ route('tags.show') }}">
<svg class="icon" 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">

View File

@@ -15,6 +15,10 @@
href="{{ route('notifications.discord') }}">
<button>Discord</button>
</a>
<a class="{{ request()->routeIs('notifications.slack') ? 'dark:text-white' : '' }}"
href="{{ route('notifications.slack') }}">
<button>Slack</button>
</a>
</nav>
</div>
</div>
</div>

View File

@@ -262,7 +262,7 @@
your self-hosted instance?
<x-forms.button>
<a class="font-bold dark:text-white hover:no-underline"
href="{{ config('coolify.contact') }}">Contact
href="{{ config('constants.urls.contact') }}">Contact
Us</a>
</x-forms.button>
</div>

View File

@@ -8,7 +8,7 @@
<li class="inline-flex items-center">
<div class="flex items-center">
<a class="text-xs truncate lg:text-sm"
href="{{ route('project.show', ['project_uuid' => $this->parameters['project_uuid']]) }}">
href="{{ route('project.show', ['project_uuid' => data_get($resource, 'environment.project.uuid')]) }}">
{{ data_get($resource, 'environment.project.name', 'Undefined Name') }}</a>
<svg aria-hidden="true" class="w-4 h-4 mx-1 font-bold dark:text-warning" fill="currentColor"
viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
@@ -21,7 +21,7 @@
<li>
<div class="flex items-center">
<a class="text-xs truncate lg:text-sm"
href="{{ route('project.resource.index', ['environment_name' => $this->parameters['environment_name'], 'project_uuid' => $this->parameters['project_uuid']]) }}">{{ $this->parameters['environment_name'] }}</a>
href="{{ route('project.resource.index', ['environment_name' => data_get($resource, 'environment.name'), 'project_uuid' => data_get($resource, 'environment.project.uuid')]) }}">{{ data_get($resource, 'environment.name') }}</a>
<svg aria-hidden="true" class="w-4 h-4 mx-1 font-bold dark:text-warning" fill="currentColor"
viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
@@ -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,31 @@
@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]) }}">General</a>
@if ($server->isFunctional())
<a class="menu-item {{ $activeMenu === 'advanced' ? 'menu-item-active' : '' }}"
href="{{ route('server.advanced', ['server_uuid' => $server->uuid]) }}">Advanced
</a>
@endif
<a class="menu-item {{ $activeMenu === 'private-key' ? 'menu-item-active' : '' }}"
href="{{ route('server.private-key', ['server_uuid' => $server->uuid]) }}">Private Key
</a>
@if (!$server->isLocalhost())
<a class="menu-item {{ $activeMenu === 'cloudflare-tunnels' ? 'menu-item-active' : '' }}"
href="{{ route('server.cloudflare-tunnels', ['server_uuid' => $server->uuid]) }}">Cloudflare
Tunnels</a>
@endif
@if ($server->isFunctional())
<a class="menu-item {{ $activeMenu === 'destinations' ? 'menu-item-active' : '' }}"
href="{{ route('server.destinations', ['server_uuid' => $server->uuid]) }}">Destinations
</a>
<a class="menu-item {{ $activeMenu === 'log-drains' ? 'menu-item-active' : '' }}"
href="{{ route('server.log-drains', ['server_uuid' => $server->uuid]) }}">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]) }}">Danger</a>
@endif
</div>

View File

@@ -7,12 +7,6 @@
href="{{ route('settings.index') }}">
<button>Configuration</button>
</a>
@if (isCloud())
<a class="{{ request()->routeIs('settings.license') ? 'dark:text-white' : '' }}"
href="{{ route('settings.license') }}">
<button>Resale License</button>
</a>
@endif
<a class="{{ request()->routeIs('settings.backup') ? 'dark:text-white' : '' }}"
href="{{ route('settings.backup') }}">
<button>Backup</button>

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

@@ -11,16 +11,20 @@
toast(this.title, { description: this.description, type: this.type, position: this.position, html: html })
}
}" x-init="window.toast = function(message, options = {}) {
let description = '';
let type = 'default';
let position = 'top-center';
let html = '';
if (typeof options.description != 'undefined') description = options.description;
if (typeof options.type != 'undefined') type = options.type;
if (typeof options.position != 'undefined') position = options.position;
if (typeof options.html != 'undefined') html = options.html;
try {
let description = '';
let type = 'default';
let position = 'top-center';
let html = '';
if (typeof options.description != 'undefined') description = options.description;
if (typeof options.type != 'undefined') type = options.type;
if (typeof options.position != 'undefined') position = options.position;
if (typeof options.html != 'undefined') html = options.html;
window.dispatchEvent(new CustomEvent('toast-show', { detail: { type: type, message: message, description: description, position: position, html: html } }));
window.dispatchEvent(new CustomEvent('toast-show', { detail: { type: type, message: message, description: description, position: position, html: html } }));
} catch (error) {
console.error('Error showing toast:', error);
}
}" class="relative space-y-5">
<template x-teleport="body">
<ul x-data="{

View File

@@ -1,4 +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') }}" target="_blank">
v{{ config('version') }}
</a>
href="https://github.com/coollabsio/coolify/releases/tag/v{{ config('constants.coolify.version') }}" target="_blank">
v{{ config('constants.coolify.version') }}
</a>

View File

@@ -1,44 +0,0 @@
<x-layout>
<x-slot:title>
Destinations | Coolify
</x-slot>
<div class="flex items-start gap-2">
<h1>Destinations</h1>
@if ($servers->count() > 0)
<x-modal-input buttonTitle="+ Add" title="New Destination">
<livewire:destination.new.docker :server_id="$server_id" />
</x-modal-input>
@endif
</div>
<div class="subtitle">Network endpoints to deploy your resources.</div>
<div class="grid gap-2 lg:grid-cols-1">
@forelse ($destinations as $destination)
@if ($destination->getMorphClass() === 'App\Models\StandaloneDocker')
<a class="box group"
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}">
<div class="flex flex-col mx-6">
<div class="box-title">{{ $destination->name }}</div>
<div class="box-description">server: {{ $destination->server->name }}</div>
</div>
</a>
@endif
@if ($destination->getMorphClass() === 'App\Models\SwarmDocker')
<a class="box group"
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}">
<div class="flex flex-col mx-6">
<div class="box-title">{{ $destination->name }}</div>
<div class="box-description">server: {{ $destination->server->name }}</div>
</div>
</a>
@endif
@empty
<div>
@if ($servers->count() === 0)
<div>No servers found. Please add one first.</div>
@else
<div>No destinations found.</div>
@endif
</div>
@endforelse
</div>
</x-layout>

View File

@@ -1,3 +0,0 @@
<x-layout>
<livewire:destination.form :destination="$destination" />
</x-layout>

View File

@@ -0,0 +1,8 @@
<x-emails.layout>
Docker Cleanup on {{ $name }} FAILED with the following error:
<pre>
{{ $text }}
</pre>
</x-emails.layout>

View File

@@ -0,0 +1,9 @@
<x-emails.layout>
Docker Cleanup on {{ $name }} succeeded with the following message:
<pre>
{{ $text }}
</pre>
</x-emails.layout>

View File

@@ -2,4 +2,4 @@
{{ Illuminate\Mail\Markdown::parse('---') }}
{{ Illuminate\Mail\Markdown::parse($debug) }}
{{-- {{ Illuminate\Mail\Markdown::parse($debug) }} --}}

View File

@@ -0,0 +1,9 @@
<x-emails.layout>
Scheduled task ({{ $task->name }}) completed successfully with the following output:
<pre>
{{ $output }}
</pre>
Click [here]({{ $url }}) to view the task.
</x-emails.layout>

View File

@@ -1,7 +0,0 @@
<x-emails.layout>
Someone added this email to the Coolify Cloud's waitlist. [Click here]({{ $confirmation_url }}) to confirm!
The link will expire in {{ config('constants.waitlist.expiration') }} minutes.
You have no idea what [Coolify Cloud](https://coolify.io) is or this waitlist? [Click here]({{ $cancel_url }}) to remove you from the waitlist.
</x-emails.layout>

View File

@@ -1,3 +0,0 @@
<x-emails.layout>
You have been invited to join the Coolify Cloud: [Get Started]({{ $loginLink }})
</x-emails.layout>

View File

@@ -0,0 +1,23 @@
@extends('layouts.base')
<div class="flex flex-col items-center justify-center h-full">
<div>
<p class="font-mono font-semibold text-7xl dark:text-warning">400</p>
<h1 class="mt-4 font-bold tracking-tight dark:text-white">Bad Request</h1>
@if ($exception->getMessage())
<p class="text-base leading-7 text-red-500">{{ $exception->getMessage() }}</p>
@else
<p class="text-base leading-7 text-neutral-300">The request could not be understood by the server due to
malformed syntax.
</p>
@endif
<div class="flex items-center mt-10 gap-x-6">
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a target="_blank" class="text-xs" href="{{ config('constants.urls.contact') }}">Contact
support
<x-external-link />
</a>
</div>
</div>
</div>

View File

@@ -9,7 +9,7 @@
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a target="_blank" class="text-xs" href="{{ config('coolify.contact') }}">Contact
<a target="_blank" class="text-xs" href="{{ config('constants.urls.contact') }}">Contact
support
<x-external-link />
</a>

View File

@@ -8,7 +8,7 @@
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a target="_blank" class="text-xs" href="{{ config('coolify.contact') }}">Contact
<a target="_blank" class="text-xs" href="{{ config('constants.urls.contact') }}">Contact
support
<x-external-link />
</a>

View File

@@ -9,7 +9,7 @@
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a target="_blank" class="text-xs" href="{{ config('coolify.contact') }}">Contact
<a target="_blank" class="text-xs" href="{{ config('constants.urls.contact') }}">Contact
support
<x-external-link />
</a>

View File

@@ -10,7 +10,7 @@
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a target="_blank" class="text-xs" href="{{ config('coolify.contact') }}">Contact
<a target="_blank" class="text-xs" href="{{ config('constants.urls.contact') }}">Contact
support
<x-external-link />
</a>

View File

@@ -10,7 +10,7 @@
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a target="_blank" class="text-xs" href="{{ config('coolify.contact') }}">Contact
<a target="_blank" class="text-xs" href="{{ config('constants.urls.contact') }}">Contact
support
<x-external-link />
</a>

View File

@@ -10,7 +10,7 @@
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a target="_blank" class="text-xs" href="{{ config('coolify.contact') }}">Contact
<a target="_blank" class="text-xs" href="{{ config('constants.urls.contact') }}">Contact
support
<x-external-link />
</a>

View File

@@ -2,7 +2,7 @@
<div class="flex flex-col items-center justify-center h-full">
<div>
<p class="font-mono font-semibold text-red-500 text-7xl">500</p>
<h1 class="mt-4 font-bold tracking-tight dark:text-white">Something is not okay, are you okay?</h1>
<h1 class="mt-4 font-bold tracking-tight dark:text-white">Wait, this is not cool...</h1>
<p class="text-base leading-7 text-neutral-300">There has been an error, we are working on it.
</p>
@if ($exception->getMessage() !== '')
@@ -13,7 +13,7 @@
<a href="/">
<x-forms.button>Go back home</x-forms.button>
</a>
<a target="_blank" class="text-xs" href="{{ config('coolify.contact') }}">Contact
<a target="_blank" class="text-xs" href="{{ config('constants.urls.contact') }}">Contact
support
<x-external-link />
</a>

View File

@@ -7,7 +7,7 @@
patience.
</p>
<div class="flex items-center mt-10 gap-x-6">
<a target="_blank" class="text-xs" href="{{ config('coolify.contact') }}">Contact
<a target="_blank" class="text-xs" href="{{ config('constants.urls.contact') }}">Contact
support
<x-external-link />
</a>

View File

@@ -18,7 +18,7 @@
<div class="relative z-50 lg:hidden" :class="open ? 'block' : 'hidden'" role="dialog" aria-modal="true">
<div class="fixed inset-0 bg-black/80"></div>
<div class="fixed inset-0 flex">
<div class="relative flex flex-1 w-full mr-16 max-w-48 ">
<div class="relative flex flex-1 w-full mr-16 max-w-56 ">
<div class="absolute top-0 flex justify-center w-16 pt-5 left-full">
<button type="button" class="-m-2.5 p-2.5" x-on:click="open = !open">
<span class="sr-only">Close sidebar</span>
@@ -29,14 +29,14 @@
</button>
</div>
<div class="flex flex-col pb-2 overflow-y-auto min-w-48 dark:bg-coolgray-100 gap-y-5 scrollbar">
<div class="flex flex-col pb-2 overflow-y-auto min-w-56 dark:bg-coolgray-100 gap-y-5 scrollbar">
<x-navbar />
</div>
</div>
</div>
</div>
<div class="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-48 lg:flex-col">
<div class="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-56 lg:flex-col">
<div class="flex flex-col overflow-y-auto grow gap-y-5 scrollbar">
<x-navbar />
</div>
@@ -52,7 +52,7 @@
</button>
</div>
<main class="lg:pl-48">
<main class="lg:pl-56">
<div class="p-4 sm:px-6 lg:px-8 lg:py-6">
{{ $slot }}
</div>

View File

@@ -34,11 +34,13 @@
</style>
@if (config('app.name') == 'Coolify Cloud')
<script defer data-domain="app.coolify.io" src="https://analytics.coollabs.io/js/plausible.js"></script>
<script src="https://js.sentry-cdn.com/0f8593910512b5cdd48c6da78d4093be.min.js" crossorigin="anonymous"></script>
@endif
@auth
<script type="text/javascript" src="{{ URL::asset('js/echo.js') }}"></script>
<script type="text/javascript" src="{{ URL::asset('js/pusher.js') }}"></script>
<script type="text/javascript" src="{{ URL::asset('js/apexcharts.js') }}"></script>
@endauth
</head>
@section('body')
@@ -84,9 +86,9 @@
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'pusher',
cluster: "{{ env('PUSHER_HOST') }}" || window.location.hostname,
key: "{{ env('PUSHER_APP_KEY') }}" || 'coolify',
wsHost: "{{ env('PUSHER_HOST') }}" || window.location.hostname,
cluster: "{{ config('constants.pusher.host') }}" || window.location.hostname,
key: "{{ config('constants.pusher.app_key') }}" || 'coolify',
wsHost: "{{ config('constants.pusher.host') }}" || window.location.hostname,
wsPort: "{{ getRealtime() }}",
wssPort: "{{ getRealtime() }}",
forceTLS: false,
@@ -94,6 +96,20 @@
enableStats: false,
enableLogging: true,
enabledTransports: ['ws', 'wss'],
disableStats: true,
// Add auto reconnection settings
enabledTransports: ['ws', 'wss'],
disabledTransports: ['sockjs', 'xhr_streaming', 'xhr_polling'],
// Attempt to reconnect on connection lost
autoReconnect: true,
// Wait 1 second before first reconnect attempt
reconnectionDelay: 1000,
// Maximum delay between reconnection attempts
maxReconnectionDelay: 1000,
// Multiply delay by this number for each reconnection attempt
reconnectionDelayGrowth: 1,
// Maximum number of reconnection attempts
maxAttempts: 15
});
@endauth
let checkHealthInterval = null;

View File

@@ -6,26 +6,25 @@
<x-forms.input wire:model="search" placeholder="Search for a user" />
<x-forms.button type="submit">Search</x-forms.button>
</form>
<h3 class="pt-4">Active Subscribers</h3>
<div class="flex flex-wrap gap-2">
@forelse ($active_subscribers as $user)
<div class="flex gap-2 box" wire:click="switchUser('{{ $user->id }}')">
<p>{{ $user->name }}</p>
<p>{{ $user->email }}</p>
<div class="pt-4">Active Subscribers : {{ $activeSubscribers }}</div>
<div>Inactive Subscribers : {{ $inactiveSubscribers }}</div>
@if ($search)
@if ($foundUsers->count() > 0)
<div class="flex flex-wrap gap-2 pt-4">
@foreach ($foundUsers as $user)
<div class="box w-64 group" wire:click="switchUser({{ $user->id }})">
<div class="flex flex-col gap-2">
<div class="box-title">{{ $user->name }}</div>
<div class="box-description">{{ $user->email }}</div>
<div class="box-description">Active:
{{ $user->teams()->whereRelation('subscription', 'stripe_subscription_id', '!=', null)->exists() ? 'Yes' : 'No' }}
</div>
</div>
</div>
@endforeach
</div>
@empty
<p>No active subscribers</p>
@endforelse
</div>
<h3 class="pt-4">Inactive Subscribers</h3>
<div class="flex flex-col flex-wrap gap-2">
@forelse ($inactive_subscribers as $user)
<div class="flex gap-2 box" wire:click="switchUser('{{ $user->id }}')">
<p>{{ $user->name }}</p>
<p>{{ $user->email }}</p>
</div>
@empty
<p>No inactive subscribers</p>
@endforelse
</div>
@else
<div>No users found with {{ $search }}</div>
@endif
@endif
</div>

View File

@@ -323,7 +323,7 @@
</x-slot:actions>
<x-slot:explanation>
<p>This will install the latest Docker Engine on your server, configure a few things to be able
to run optimal.<br><br>Minimum Docker Engine version is: 22<br><br>To manually install
to run optimal.<br><br>Minimum Docker Engine version is: {{ $minDockerVersion }}<br><br>To manually install
Docker
Engine, check <a target="_blank" class="underline dark:text-warning"
href="https://docs.docker.com/engine/install/#server">this

View File

@@ -18,155 +18,154 @@
subscription is activated.<br> Please be patient.
</div>
@endif
<h3 class="pb-4">Projects</h3>
@if ($projects->count() > 0)
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
@foreach ($projects as $project)
<div class="gap-2 border border-transparent cursor-pointer box group"
onclick="gotoProject('{{ $project->uuid }}','{{ $project->default_environment }}')">
<div class="flex flex-1 mx-6">
<div class="flex flex-col justify-center flex-1">
<div class="box-title">{{ $project->name }}</div>
<div class="box-description">
{{ $project->description }}
<section>
<h3 class="pb-2">Projects</h3>
@if ($projects->count() > 0)
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
@foreach ($projects as $project)
<div class="gap-2 border border-transparent cursor-pointer box group"
onclick="gotoProject('{{ $project->uuid }}','{{ $project->default_environment }}')">
<div class="flex flex-1 mx-6">
<div class="flex flex-col justify-center flex-1">
<div class="box-title">{{ $project->name }}</div>
<div class="box-description">
{{ $project->description }}
</div>
</div>
<div class="flex items-center justify-center gap-2 text-xs font-bold">
<a class="hover:underline"
href="{{ route('project.resource.create', ['project_uuid' => $project->uuid, 'environment_name' => data_get($project, 'default_environment', 'production')]) }}">
<span class="p-2 font-bold">+ Add Resource</span>
</a>
<a class="hover:underline"
href="{{ route('project.edit', ['project_uuid' => $project->uuid]) }}">
Settings
</a>
</div>
</div>
<div class="flex items-center justify-center gap-2 text-xs font-bold">
<a class="hover:underline"
href="{{ route('project.resource.create', ['project_uuid' => $project->uuid, 'environment_name' => data_get($project, 'default_environment', 'production')]) }}">
<span class="p-2 font-bold">+ Add Resource</span>
</a>
<a class="hover:underline"
href="{{ route('project.edit', ['project_uuid' => $project->uuid]) }}">
Settings
</a>
</div>
</div>
</div>
@endforeach
</div>
@else
<div class="flex flex-col gap-1">
<div class='font-bold dark:text-warning'>No projects found.</div>
<div class="flex items-center gap-1">
<x-modal-input buttonTitle="Add" title="New Project">
<livewire:project.add-empty />
</x-modal-input> your first project or
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}">onboarding</a> page.
</div>
</div>
@endif
<h3 class="py-4">Servers</h3>
@if ($servers->count() > 0)
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
@foreach ($servers as $server)
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
@class([
'gap-2 border cursor-pointer box group',
'border-transparent' => $server->settings->is_reachable,
'border-red-500' => !$server->settings->is_reachable,
])>
<div class="flex flex-col justify-center mx-6">
<div class="box-title">
{{ $server->name }}
</div>
<div class="box-description">
{{ $server->description }}</div>
<div class="flex gap-1 text-xs text-error">
@if (!$server->settings->is_reachable)
Not reachable
@endif
@if (!$server->settings->is_reachable && !$server->settings->is_usable)
&
@endif
@if (!$server->settings->is_usable)
Not usable by Coolify
@endif
</div>
</div>
<div class="flex-1"></div>
</a>
@endforeach
</div>
@else
@if ($private_keys->count() === 0)
<div class="flex flex-col gap-1">
<div class='font-bold dark:text-warning'>No private keys found.</div>
<div class="flex items-center gap-1">Before you can add your server, first <x-modal-input
buttonTitle="add" title="New Private Key">
<livewire:security.private-key.create from="server" />
</x-modal-input> a private key
or
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}">onboarding</a>
page.
</div>
@endforeach
</div>
@else
<div class="flex flex-col gap-1">
<div class='font-bold dark:text-warning'>No servers found.</div>
<div class='font-bold dark:text-warning'>No projects found.</div>
<div class="flex items-center gap-1">
<x-modal-input buttonTitle="Add" title="New Server" :closeOutside="false">
<livewire:server.create />
</x-modal-input> your first server
or
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}">onboarding</a>
page.
<x-modal-input buttonTitle="Add" title="New Project">
<livewire:project.add-empty />
</x-modal-input> your first project or
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}">onboarding</a> page.
</div>
</div>
@endif
@endif
@if ($servers->count() > 0 && $projects->count() > 0)
<div class="flex items-center gap-2">
<h3 class="py-4">Deployments</h3>
@if (count($deployments_per_server) > 0)
<x-loading />
@endif
<x-modal-confirmation
title="Confirm Cleanup Queues?"
buttonTitle="Cleanup Queues"
isErrorButton
submitAction="cleanup_queue"
:actions="['All running Deployment Queues will be cleaned up.']"
:confirmWithText="false"
:confirmWithPassword="false"
step2ButtonText="Permanently Cleanup Deployment Queues"
:dispatchEvent="true"
dispatchEventType="success"
dispatchEventMessage="Deployment Queues cleanup started."
/>
</div>
<div wire:poll.3000ms="get_deployments" class="grid grid-cols-1">
@forelse ($deployments_per_server as $server_name => $deployments)
<h4 class="py-4">{{ $server_name }}</h4>
<div class="grid grid-cols-1 gap-2 lg:grid-cols-3">
@foreach ($deployments as $deployment)
<a href="{{ data_get($deployment, 'deployment_url') }}" @class([
'gap-2 cursor-pointer box group border-l-2 border-dotted',
'dark:border-coolgray-300' => data_get($deployment, 'status') === 'queued',
'border-yellow-500' => data_get($deployment, 'status') === 'in_progress',
</section>
<section>
<h3 class="pb-2">Servers</h3>
@if ($servers->count() > 0)
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
@foreach ($servers as $server)
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
@class([
'gap-2 border cursor-pointer box group',
'border-transparent' => $server->settings->is_reachable,
'border-red-500' => !$server->settings->is_reachable,
])>
<div class="flex flex-col justify-center mx-6">
<div class="box-title">
{{ data_get($deployment, 'application_name') }}
</div>
@if (data_get($deployment, 'pull_request_id') !== 0)
<div class="box-description">
PR #{{ data_get($deployment, 'pull_request_id') }}
</div>
@endif
<div class="box-description">
{{ str(data_get($deployment, 'status'))->headline() }}
</div>
<div class="flex flex-col justify-center mx-6">
<div class="box-title">
{{ $server->name }}
</div>
<div class="flex-1"></div>
</a>
@endforeach
<div class="box-description">
{{ $server->description }}</div>
<div class="flex gap-1 text-xs text-error">
@if (!$server->settings->is_reachable)
Not reachable
@endif
@if (!$server->settings->is_reachable && !$server->settings->is_usable)
&
@endif
@if (!$server->settings->is_usable)
Not usable by Coolify
@endif
</div>
</div>
<div class="flex-1"></div>
</a>
@endforeach
</div>
@else
@if ($privateKeys->count() === 0)
<div class="flex flex-col gap-1">
<div class='font-bold dark:text-warning'>No private keys found.</div>
<div class="flex items-center gap-1">Before you can add your server, first <x-modal-input
buttonTitle="add" title="New Private Key">
<livewire:security.private-key.create from="server" />
</x-modal-input> a private key
or
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}">onboarding</a>
page.
</div>
</div>
@empty
<div>No deployments running.</div>
@endforelse
</div>
@else
<div class="flex flex-col gap-1">
<div class='font-bold dark:text-warning'>No servers found.</div>
<div class="flex items-center gap-1">
<x-modal-input buttonTitle="Add" title="New Server" :closeOutside="false">
<livewire:server.create />
</x-modal-input> your first server
or
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}">onboarding</a>
page.
</div>
</div>
@endif
@endif
</section>
@if ($servers->count() > 0 && $projects->count() > 0)
<section>
<div class="flex items-center gap-2">
<h3 class="pb-2">Deployments</h3>
@if (count($deploymentsPerServer) > 0)
<x-loading />
@endif
<x-modal-confirmation title="Confirm Cleanup Queues?" buttonTitle="Cleanup Queues" isErrorButton
submitAction="cleanupQueue" :actions="['All running Deployment Queues will be cleaned up.']" :confirmWithText="false" :confirmWithPassword="false"
step2ButtonText="Permanently Cleanup Deployment Queues" :dispatchEvent="true"
dispatchEventType="success" dispatchEventMessage="Deployment Queues cleanup started." />
</div>
<div wire:poll.3000ms="loadDeployments" class="grid grid-cols-1">
@forelse ($deploymentsPerServer as $serverName => $deployments)
<h4 class="pb-2">{{ $serverName }}</h4>
<div class="grid grid-cols-1 gap-2 lg:grid-cols-3">
@foreach ($deployments as $deployment)
<a href="{{ data_get($deployment, 'deployment_url') }}" @class([
'gap-2 cursor-pointer box group border-l-2 border-dotted',
'dark:border-coolgray-300' => data_get($deployment, 'status') === 'queued',
'border-yellow-500' => data_get($deployment, 'status') === 'in_progress',
])>
<div class="flex flex-col justify-center mx-6">
<div class="box-title">
{{ data_get($deployment, 'application_name') }}
</div>
@if (data_get($deployment, 'pull_request_id') !== 0)
<div class="box-description">
PR #{{ data_get($deployment, 'pull_request_id') }}
</div>
@endif
<div class="box-description">
{{ str(data_get($deployment, 'status'))->headline() }}
</div>
</div>
<div class="flex-1"></div>
</a>
@endforeach
</div>
@empty
<div>No deployments running.</div>
@endforelse
</div>
</section>
@endif
@@ -179,7 +178,4 @@
}
}
</script>
{{-- <x-forms.button wire:click='getIptables'>Get IPTABLES</x-forms.button> --}}
</div>

View File

@@ -1,29 +0,0 @@
<div>
<form class="flex flex-col">
<div class="flex items-center gap-2">
<h1>Destination</h1>
<x-forms.button wire:click.prevent='submit' type="submit">
Save
</x-forms.button>
@if ($destination->network !== 'coolify')
<x-modal-confirmation title="Confirm Destination Deletion?" buttonTitle="Delete Destination" isErrorButton
submitAction="delete" :actions="['This will delete the selected destination/network.']" confirmationText="{{ $destination->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Destination Name below"
shortConfirmationLabel="Destination Name" :confirmWithPassword="false" step2ButtonText="Permanently Delete" />
@endif
</div>
@if ($destination->getMorphClass() === 'App\Models\StandaloneDocker')
<div class="subtitle ">A Docker network in a non-swarm environment.</div>
@else
<div class="subtitle ">Your swarm docker network. WIP</div>
@endif
<div class="flex gap-2">
<x-forms.input id="destination.name" label="Name" />
<x-forms.input id="destination.server.ip" label="Server IP" readonly />
@if ($destination->getMorphClass() === 'App\Models\StandaloneDocker')
<x-forms.input id="destination.network" label="Docker Network" readonly />
@endif
</div>
</form>
</div>

View File

@@ -0,0 +1,42 @@
<div>
<x-slot:title>
Destinations | Coolify
</x-slot>
<div class="flex items-start gap-2">
<h1>Destinations</h1>
@if ($servers->count() > 0)
<x-modal-input buttonTitle="+ Add" title="New Destination">
<livewire:destination.new.docker />
</x-modal-input>
@endif
</div>
<div class="subtitle">Network endpoints to deploy your resources.</div>
<div class="grid gap-2 lg:grid-cols-1">
@forelse ($servers as $server)
@forelse ($server->destinations() as $destination)
@if ($destination->getMorphClass() === 'App\Models\StandaloneDocker')
<a class="box group"
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}">
<div class="flex flex-col mx-6">
<div class="box-title">{{ $destination->name }}</div>
<div class="box-description">Server: {{ $destination->server->name }}</div>
</div>
</a>
@endif
@if ($destination->getMorphClass() === 'App\Models\SwarmDocker')
<a class="box group"
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}">
<div class="flex flex-col mx-6">
<div class="box-title">{{ $destination->name }}</div>
<div class="box-description">server: {{ $destination->server->name }}</div>
</div>
</a>
@endif
@empty
<div>No destinations found.</div>
@endforelse
@empty
<div>No servers found.</div>
@endforelse
</div>
</div>

View File

@@ -5,7 +5,7 @@
<x-forms.input id="name" label="Name" required />
<x-forms.input id="network" label="Network" required />
</div>
<x-forms.select id="server_id" label="Select a server" required wire:change="generate_name">
<x-forms.select id="serverId" label="Select a server" required wire:change="generateName">
<option disabled>Select a server</option>
@foreach ($servers as $server)
<option value="{{ $server->id }}">{{ $server->name }}</option>

View File

@@ -1,42 +1,29 @@
<div>
@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 wire:click='scan'>Scan 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 ">
Available for using:
@forelse ($server->standaloneDockers as $docker)
<a href="{{ route('destination.show', ['destination_uuid' => data_get($docker, 'uuid')]) }}">
<button class="dark:text-white btn-link">{{ data_get($docker, 'network') }} </button>
</a>
@empty
@endforelse
@forelse ($server->swarmDockers as $docker)
<a href="{{ route('destination.show', ['destination_uuid' => data_get($docker, 'uuid')]) }}">
<button class="dark:text-white btn-link">{{ data_get($docker, 'network') }} </button>
</a>
@empty
@endforelse
</div>
<div class="pt-2">
@if (count($networks) > 0)
<h3 class="pb-4">Found Destinations</h3>
<form class="flex flex-col">
<div class="flex items-center gap-2">
<h1>Destination</h1>
<x-forms.button wire:click.prevent='submit' type="submit">
Save
</x-forms.button>
@if ($network !== 'coolify')
<x-modal-confirmation title="Confirm Destination Deletion?" buttonTitle="Delete Destination" isErrorButton
submitAction="delete" :actions="['This will delete the selected destination/network.']" confirmationText="{{ $destination->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Destination Name below"
shortConfirmationLabel="Destination Name" :confirmWithPassword="false" step2ButtonText="Permanently Delete" />
@endif
<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>
@else
<div>Server is not validated. Validate first.</div>
@endif
@if ($destination->getMorphClass() === 'App\Models\StandaloneDocker')
<div class="subtitle ">A simple Docker network.</div>
@else
<div class="subtitle ">A swarm Docker network. WIP</div>
@endif
<div class="flex gap-2">
<x-forms.input id="name" label="Name" />
<x-forms.input id="serverIp" label="Server IP" readonly />
@if ($destination->getMorphClass() === 'App\Models\StandaloneDocker')
<x-forms.input id="network" label="Docker Network" readonly />
@endif
</div>
</form>
</div>

View File

@@ -1,13 +0,0 @@
<div class="pb-10" x-data>
<h1>Compose</h1>
<div>All kinds of compose files.</div>
<h3 class="pt-4">Services</h3>
@foreach ($services as $serviceName => $value)
<x-forms.button wire:click="setService('{{ $serviceName }}')">{{ Str::headline($serviceName) }}</x-forms.button>
@endforeach
<h3 class="pt-4">Base64 En/Decode</h3>
<x-forms.button x-on:click="copyToClipboard('{{ $base64 }}')">Copy Base64 Compose</x-forms.button>
<div class="pt-4">
<x-forms.textarea realtimeValidation rows="40" id="compose"></x-forms.textarea>
</div>
</div>

View File

@@ -1,15 +0,0 @@
<div>
<h2>S3 Test</h2>
<form wire:submit="save">
<input type="file" wire:model="file">
@error('file')
<span class="error">{{ $message }}</span>
@enderror
<div wire:loading wire:target="file">Uploading to server...</div>
@if ($file)
<x-forms.button type="submit">Upload file to s3:/files</x-forms.button>
@endif
</form>
<h4>Functions</h4>
<x-forms.button wire:click="get_files">Get s3:/files</x-forms.button>
</div>

View File

@@ -1,10 +1,11 @@
<div class="flex flex-col w-full gap-2">
<div>Your feedback helps us to improve Coolify. Thank you! 💜</div>
<form wire:submit="submit" class="flex flex-col gap-4 pt-4">
<x-forms.input id="subject" label="Subject" placeholder="Summary of your problem."></x-forms.input>
<x-forms.textarea rows="10" id="description" label="Description" class="font-sans" spellcheck
placeholder="Please provide as much information as possible."></x-forms.textarea>
<x-forms.input minlength="3" required id="subject" label="Subject" placeholder="Help with..."></x-forms.input>
<x-forms.textarea minlength="10" maxlength="1000" required rows="10" id="description" label="Description"
class="font-sans" spellcheck
placeholder="Having trouble with... Please provide as much information as possible."></x-forms.textarea>
<div></div>
<x-forms.button class="w-full mt-4" type="submit" @click="modalOpen=false">Send</x-forms.button>
<x-forms.button class="w-full mt-4" type="submit">Send</x-forms.button>
</form>
</div>

View File

@@ -11,16 +11,22 @@
let checkNumber = 1;
let checkPusherInterval = null;
let checkReconnectInterval = null;
if (!this.popups.realtime) {
checkPusherInterval = setInterval(() => {
if (window.Echo && window.Echo.connector.pusher.connection.state !== 'connected') {
checkNumber++;
if (checkNumber > 5) {
this.popups.realtime = true;
console.error(
'Coolify could not connect to its real-time service. This will cause unusual problems on the UI if not fixed! Please check the related documentation (https://coolify.io/docs/knowledge-base/cloudflare/tunnels) or get help on Discord (https://coollabs.io/discord).)'
);
clearInterval(checkPusherInterval);
if (window.Echo) {
if (window.Echo.connector.pusher.connection.state === 'connected') {
this.popups.realtime = false;
} else {
checkNumber++;
if (checkNumber > 5) {
this.popups.realtime = true;
console.error(
'Coolify could not connect to its real-time service. This will cause unusual problems on the UI if not fixed! Please check the related documentation (https://coolify.io/docs/knowledge-base/cloudflare/tunnels) or get help on Discord (https://coollabs.io/discord).)'
);
}
}
}
}, 2000);

View File

@@ -3,40 +3,79 @@
Notifications | Coolify
</x-slot>
<x-notification.navbar />
<form wire:submit='submit' class="flex flex-col gap-4">
<form wire:submit='submit' class="flex flex-col gap-4 pb-4">
<div class="flex items-center gap-2">
<h2>Discord</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
@if ($team->discord_enabled)
@if ($discordEnabled)
<x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
wire:click="sendTestNotification">
Send Test Notifications
Send Test Notification
</x-forms.button>
@else
<x-forms.button disabled class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
Send Test Notification
</x-forms.button>
@endif
</div>
<div class="w-32">
<x-forms.checkbox instantSave id="team.discord_enabled" label="Enabled" />
<x-forms.checkbox instantSave="instantSaveDiscordEnabled" id="discordEnabled" label="Enabled" />
</div>
<x-forms.input type="password"
helper="Generate a webhook in Discord.<br>Example: https://discord.com/api/webhooks/...." required
id="team.discord_webhook_url" label="Webhook" />
id="discordWebhookUrl" label="Webhook" />
</form>
@if (data_get($team, 'discord_enabled'))
<h2 class="mt-4">Subscribe to events</h2>
<div class="w-64">
@if (isDev())
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_test" label="Test" />
@endif
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_status_changes"
label="Container Status Changes" />
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_deployments"
label="Application Deployments" />
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_database_backups"
label="Backup Status" />
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_scheduled_tasks"
label="Scheduled Tasks Status" />
<h2 class="mt-4">Notification Settings</h2>
<p class="mb-4">
Select events for which you would like to receive Discord notifications.
</p>
<div class="flex flex-col gap-4 max-w-2xl">
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="font-medium mb-3">Deployments</h3>
<div class="flex flex-col gap-1.5 pl-1">
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessDiscordNotifications"
label="Deployment Success" />
<x-forms.checkbox instantSave="saveModel" id="deploymentFailureDiscordNotifications"
label="Deployment Failure" />
<x-forms.checkbox instantSave="saveModel"
helper="Send a notification when a container status changes. It will notify for Stopped and Restarted events of a container."
id="statusChangeDiscordNotifications" label="Container Status Changes" />
</div>
</div>
@endif
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="font-medium mb-3">Backups</h3>
<div class="flex flex-col gap-1.5 pl-1">
<x-forms.checkbox instantSave="saveModel" id="backupSuccessDiscordNotifications"
label="Backup Success" />
<x-forms.checkbox instantSave="saveModel" id="backupFailureDiscordNotifications"
label="Backup Failure" />
</div>
</div>
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
<div class="flex flex-col gap-1.5 pl-1">
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessDiscordNotifications"
label="Scheduled Task Success" />
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskFailureDiscordNotifications"
label="Scheduled Task Failure" />
</div>
</div>
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="font-medium mb-3">Server</h3>
<div class="flex flex-col gap-1.5 pl-1">
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessDiscordNotifications"
label="Docker Cleanup Success" />
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupFailureDiscordNotifications"
label="Docker Cleanup Failure" />
<x-forms.checkbox instantSave="saveModel" id="serverDiskUsageDiscordNotifications"
label="Server Disk Usage" />
<x-forms.checkbox instantSave="saveModel" id="serverReachableDiscordNotifications"
label="Server Reachable" />
<x-forms.checkbox instantSave="saveModel" id="serverUnreachableDiscordNotifications"
label="Server Unreachable" />
</div>
</div>
</div>
</div>

View File

@@ -9,113 +9,153 @@
<x-forms.button type="submit">
Save
</x-forms.button>
@if (isInstanceAdmin() && !$team->use_instance_email_settings)
@if (auth()->user()->isAdminFromSession())
@if ($team->isNotificationEnabled('email'))
<x-modal-input buttonTitle="Send Test Email" title="Send Test Email">
<form wire:submit.prevent="sendTestEmail" class="flex flex-col w-full gap-2">
<x-forms.input wire:model="testEmailAddress" placeholder="test@example.com"
id="testEmailAddress" label="Recipients" required />
<x-forms.button type="submit" @click="modalOpen=false">
Send Email
</x-forms.button>
</form>
</x-modal-input>
@else
<x-forms.button disabled class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
Send Test Email
</x-forms.button>
@endif
@endif
</div>
@if (!isCloud())
<div class="w-96">
<x-forms.checkbox instantSave="instantSave()" id="useInstanceEmailSettings"
label="Use system wide (transactional) email settings" />
</div>
@endif
@if (!$useInstanceEmailSettings)
<div class="flex gap-4">
<x-forms.input required id="smtpFromName" helper="Name used in emails." label="From Name" />
<x-forms.input required id="smtpFromAddress" helper="Email address used in emails."
label="From Address" />
</div>
@if (isInstanceAdmin() && !$useInstanceEmailSettings)
<x-forms.button wire:click='copyFromInstanceSettings'>
Copy from Instance Settings
</x-forms.button>
@endif
@if (isEmailEnabled($team) && auth()->user()->isAdminFromSession() && isTestEmailEnabled($team))
<x-modal-input buttonTitle="Send Test Email" title="Send Test Email">
<form wire:submit='submit' class="flex flex-col w-full gap-2">
<x-forms.input placeholder="test@example.com" id="emails" label="Recipients" required />
<x-forms.button wire:click="sendTestNotification" @click="modalOpen=false">
Send Email
</x-forms.button>
</form>
</x-modal-input>
@endif
</div>
@endif
</form>
@if (isCloud())
@if ($this->sharedEmailEnabled)
<div class="w-64 py-4">
<x-forms.checkbox instantSave="instantSaveInstance" id="team.use_instance_email_settings"
label="Use Hosted Email Service" />
</div>
@else
<div class="w-96 pb-4">
<x-forms.checkbox disabled id="team.use_instance_email_settings"
label="Use Hosted Email Service (Pro+ subscription required)" />
</div>
@endif
@else
<div class="w-96">
<x-forms.checkbox instantSave="instantSaveInstance" id="team.use_instance_email_settings"
label="Use system wide (transactional) email settings" />
<div class="w-64 py-4">
<x-forms.checkbox instantSave="instantSave()" id="useInstanceEmailSettings"
label="Use Hosted Email Service" />
</div>
@endif
@if (!$team->use_instance_email_settings)
<form class="flex flex-col items-end gap-2 pt-4 pb-4 xl:flex-row" wire:submit='submitFromFields'>
<x-forms.input required id="team.smtp_from_name" helper="Name used in emails." label="From Name" />
<x-forms.input required id="team.smtp_from_address" helper="Email address used in emails."
label="From Address" />
<x-forms.button type="submit">
Save
</x-forms.button>
</form>
@if (!$useInstanceEmailSettings)
<div class="flex flex-col gap-4">
<div class="p-4 border dark:border-coolgray-300">
<h3>SMTP Server</h3>
<div class="w-32">
<x-forms.checkbox instantSave id="team.smtp_enabled" label="Enabled" />
<form wire:submit='submitSmtp' class="p-4 border dark:border-coolgray-300 flex flex-col gap-2">
<div class="flex items-center gap-2">
<h3>SMTP Server</h3>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<form wire:submit='submit' class="flex flex-col">
<div class="w-32">
<x-forms.checkbox wire:model="smtpEnabled" instantSave="instantSave('SMTP')" id="smtpEnabled"
label="Enabled" />
</div>
<div 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 required id="team.smtp_host" placeholder="smtp.mailgun.org" label="Host" />
<x-forms.input required id="team.smtp_port" placeholder="587" label="Port" />
<x-forms.input id="team.smtp_encryption" helper="If SMTP uses SSL, set it to 'tls'."
placeholder="tls" label="Encryption" />
<x-forms.input required id="smtpHost" placeholder="smtp.mailgun.org" label="Host" />
<x-forms.input required id="smtpPort" placeholder="587" label="Port" />
<x-forms.select id="smtpEncryption" label="Encryption">
<option value="tls">TLS</option>
<option value="ssl">SSL</option>
<option value="none">None</option>
</x-forms.select>
</div>
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input id="team.smtp_username" label="SMTP Username" />
<x-forms.input id="team.smtp_password" type="password" label="SMTP Password" />
<x-forms.input id="team.smtp_timeout" helper="Timeout value for sending emails."
<x-forms.input id="smtpUsername" label="SMTP Username" />
<x-forms.input id="smtpPassword" type="password" label="SMTP Password" />
<x-forms.input id="smtpTimeout" helper="Timeout value for sending emails."
label="Timeout" />
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
</form>
</div>
<div class="p-4 border dark:border-coolgray-300">
<h3>Resend</h3>
<div class="w-32">
<x-forms.checkbox instantSave='instantSaveResend' id="team.resend_enabled" label="Enabled" />
</div>
<form wire:submit='submitResend' class="flex flex-col">
</form>
<form wire:submit='submitResend' class="p-4 border dark:border-coolgray-300 flex flex-col gap-2">
<div class="flex items-center gap-2">
<h3>Resend</h3>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="w-32">
<x-forms.checkbox wire:model="resendEnabled" instantSave="instantSave('Resend')" id="resendEnabled"
label="Enabled" />
</div>
<div 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 required type="password" id="team.resend_api_key" placeholder="API key"
<x-forms.input required type="password" id="resendApiKey" placeholder="API key"
label="API Key" />
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
</form>
</div>
</form>
</div>
@endif
<h2 class="mt-4">Notification Settings</h2>
<p class="mb-4">
Select events for which you would like to receive email notifications.
</p>
<div class="flex flex-col gap-4 max-w-2xl">
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="font-medium mb-3">Deployments</h3>
<div class="flex flex-col gap-1.5 pl-1">
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessEmailNotifications"
label="Deployment Success" />
<x-forms.checkbox instantSave="saveModel" id="deploymentFailureEmailNotifications"
label="Deployment Failure" />
<x-forms.checkbox instantSave="saveModel"
helper="Send an email when a container status changes. It will send and email for Stopped and Restarted events of a container."
id="statusChangeEmailNotifications" label="Container Status Changes" />
</div>
</div>
@endif
@if (isEmailEnabled($team) || data_get($team, 'use_instance_email_settings'))
<h2 class="mt-4">Subscribe to events</h2>
<div class="w-64">
@if (isDev())
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_test" label="Test" />
@endif
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_status_changes"
label="Container Status Changes" />
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_deployments"
label="Application Deployments" />
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_database_backups"
label="Backup Status" />
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_scheduled_tasks"
label="Scheduled Tasks Status" />
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="font-medium mb-3">Backups</h3>
<div class="flex flex-col gap-1.5 pl-1">
<x-forms.checkbox instantSave="saveModel" id="backupSuccessEmailNotifications"
label="Backup Success" />
<x-forms.checkbox instantSave="saveModel" id="backupFailureEmailNotifications"
label="Backup Failure" />
</div>
</div>
@endif
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
<div class="flex flex-col gap-1.5 pl-1">
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessEmailNotifications"
label="Scheduled Task Success" />
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskFailureEmailNotifications"
label="Scheduled Task Failure" />
</div>
</div>
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="font-medium mb-3">Server</h3>
<div class="flex flex-col gap-1.5 pl-1">
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessEmailNotifications"
label="Docker Cleanup Success" />
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupFailureEmailNotifications"
label="Docker Cleanup Failure" />
<x-forms.checkbox instantSave="saveModel" id="serverDiskUsageEmailNotifications"
label="Server Disk Usage" />
<x-forms.checkbox instantSave="saveModel" id="serverReachableEmailNotifications"
label="Server Reachable" />
<x-forms.checkbox instantSave="saveModel" id="serverUnreachableEmailNotifications"
label="Server Unreachable" />
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,79 @@
<div>
<x-slot:title>
Notifications | Coolify
</x-slot>
<x-notification.navbar />
<form wire:submit='submit' class="flex flex-col gap-4 pb-4">
<div class="flex items-center gap-2">
<h2>Slack</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
@if ($slackEnabled)
<x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
wire:click="sendTestNotification">
Send Test Notification
</x-forms.button>
@else
<x-forms.button disabled class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
Send Test Notification
</x-forms.button>
@endif
</div>
<div class="w-32">
<x-forms.checkbox instantSave="instantSaveSlackEnabled" id="slackEnabled" label="Enabled" />
</div>
<x-forms.input type="password"
helper="Generate a webhook in Slack.<br>Example: https://hooks.slack.com/services/...." required
id="slackWebhookUrl" label="Webhook" />
</form>
<h2 class="mt-4">Notification Settings</h2>
<p class="mb-4">
Select events for which you would like to receive Slack notifications.
</p>
<div class="flex flex-col gap-4 max-w-2xl">
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="font-medium mb-3">Deployments</h3>
<div class="flex flex-col gap-1.5 pl-1">
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessSlackNotifications"
label="Deployment Success" />
<x-forms.checkbox instantSave="saveModel" id="deploymentFailureSlackNotifications"
label="Deployment Failure" />
<x-forms.checkbox instantSave="saveModel"
helper="Send a notification when a container status changes. It will notify for Stopped and Restarted events of a container."
id="statusChangeSlackNotifications" label="Container Status Changes" />
</div>
</div>
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="font-medium mb-3">Backups</h3>
<div class="flex flex-col gap-1.5 pl-1">
<x-forms.checkbox instantSave="saveModel" id="backupSuccessSlackNotifications" label="Backup Success" />
<x-forms.checkbox instantSave="saveModel" id="backupFailureSlackNotifications" label="Backup Failure" />
</div>
</div>
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
<div class="flex flex-col gap-1.5 pl-1">
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessSlackNotifications"
label="Scheduled Task Success" />
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskFailureSlackNotifications"
label="Scheduled Task Failure" />
</div>
</div>
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="font-medium mb-3">Server</h3>
<div class="flex flex-col gap-1.5 pl-1">
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessSlackNotifications"
label="Docker Cleanup Success" />
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupFailureSlackNotifications"
label="Docker Cleanup Failure" />
<x-forms.checkbox instantSave="saveModel" id="serverDiskUsageSlackNotifications"
label="Server Disk Usage" />
<x-forms.checkbox instantSave="saveModel" id="serverReachableSlackNotifications"
label="Server Reachable" />
<x-forms.checkbox instantSave="saveModel" id="serverUnreachableSlackNotifications"
label="Server Unreachable" />
</div>
</div>
</div>
</div>

View File

@@ -3,76 +3,164 @@
Notifications | Coolify
</x-slot>
<x-notification.navbar />
<form wire:submit='submit' class="flex flex-col gap-4">
<form wire:submit='submit' class="flex flex-col gap-4 pb-4">
<div class="flex items-center gap-2">
<h2>Telegram</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
@if ($team->telegram_enabled)
@if ($telegramEnabled)
<x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
wire:click="sendTestNotification">
Send Test Notifications
Send Test Notification
</x-forms.button>
@else
<x-forms.button disabled class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
Send Test Notification
</x-forms.button>
@endif
</div>
<div class="w-32">
<x-forms.checkbox instantSave id="team.telegram_enabled" label="Enabled" />
<x-forms.checkbox instantSave="instantSaveTelegramEnabled" id="telegramEnabled" label="Enabled" />
</div>
<div class="flex gap-2">
<x-forms.input type="password"
<x-forms.input type="password" autocomplete="new-password"
helper="Get it from the <a class='inline-block underline dark:text-white' href='https://t.me/botfather' target='_blank'>BotFather Bot</a> on Telegram."
required id="team.telegram_token" label="Token" />
required id="telegramToken" label="Token" />
<x-forms.input helper="Recommended to add your bot to a group chat and add its Chat ID here." required
id="team.telegram_chat_id" label="Chat ID" />
id="telegramChatId" label="Chat ID" />
</div>
@if (data_get($team, 'telegram_enabled'))
<h2 class="mt-4">Subscribe to events</h2>
<div class="flex flex-col gap-4 w-96">
@if (isDev())
<div class="flex flex-col">
<h4>Test Notification</h4>
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_test"
label="Enabled" />
<x-forms.input
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_test_message_thread_id" label="Custom Topic ID" />
</form>
<h2 class="mt-4">Notification Settings</h2>
<p class="mb-4">
Select events for which you would like to receive Telegram notifications.
</p>
<div class="flex flex-col gap-4 ">
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="text-lg font-medium mb-3">Deployments</h3>
<div class="flex flex-col gap-1.5 pl-1">
<div class="pl-1 flex gap-2">
<div class="w-96">
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessTelegramNotifications"
label="Deployment Success" />
</div>
@endif
<div class="flex flex-col">
<h4>Container Status Changes</h4>
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_status_changes"
label="Enabled" />
<x-forms.input
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_status_changes_message_thread_id" label="Custom Topic ID" />
<x-forms.input type="password" placeholder="Custom Telegram Topic ID"
id="telegramNotificationsDeploymentSuccessTopicId" />
</div>
<div class="flex flex-col">
<h4>Application Deployments</h4>
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_deployments"
label="Enabled" />
<x-forms.input
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_deployments_message_thread_id" label="Custom Topic ID" />
<div class="pl-1 flex gap-2">
<div class="w-96">
<x-forms.checkbox instantSave="saveModel" id="deploymentFailureTelegramNotifications"
label="Deployment Failure" />
</div>
<x-forms.input type="password" placeholder="Custom Telegram Topic ID"
id="telegramNotificationsDeploymentFailureTopicId" />
</div>
<div class="flex flex-col">
<h4>Database Backup Status</h4>
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_database_backups"
label="Enabled" />
<x-forms.input
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_database_backups_message_thread_id" label="Custom Topic ID" />
<div class="pl-1 flex gap-2">
<div class="w-96">
<x-forms.checkbox instantSave="saveModel" id="statusChangeTelegramNotifications"
label="Container Status Changes"
helper="Send a notification when a container status changes. It will send a notification for Stopped and Restarted events of a container." />
</div>
<x-forms.input type="password" id="telegramNotificationsStatusChangeTopicId"
placeholder="Custom Telegram Topic ID" />
</div>
<div class="flex flex-col">
<h4>Scheduled Tasks Status</h4>
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_scheduled_tasks"
label="Enabled" />
<x-forms.input
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>
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="text-lg font-medium mb-3">Backups</h3>
<div class="flex flex-col gap-1.5 pl-1">
<div class="pl-1 flex gap-2">
<div class="w-96">
<x-forms.checkbox instantSave="saveModel" id="backupSuccessTelegramNotifications"
label="Backup Success" />
</div>
<x-forms.input type="password" placeholder="Custom Telegram Topic ID"
id="telegramNotificationsBackupSuccessTopicId" />
</div>
<div class="pl-1 flex gap-2">
<div class="w-96">
<x-forms.checkbox instantSave="saveModel" id="backupFailureTelegramNotifications"
label="Backup Failure" />
</div>
<x-forms.input type="password" placeholder="Custom Telegram Topic ID"
id="telegramNotificationsBackupFailureTopicId" />
</div>
</div>
@endif
</form>
</div>
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="text-lg font-medium mb-3">Scheduled Tasks</h3>
<div class="flex flex-col gap-1.5 pl-1">
<div class="pl-1 flex gap-2">
<div class="w-96">
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessTelegramNotifications"
label="Scheduled Task Success" />
</div>
<x-forms.input type="password" placeholder="Custom Telegram Topic ID"
id="telegramNotificationsScheduledTaskSuccessTopicId" />
</div>
<div class="pl-1 flex gap-2">
<div class="w-96">
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskFailureTelegramNotifications"
label="Scheduled Task Failure" />
</div>
<x-forms.input type="password" placeholder="Custom Telegram Topic ID"
id="telegramNotificationsScheduledTaskFailureTopicId" />
</div>
</div>
</div>
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
<h3 class="text-lg font-medium mb-3">Server</h3>
<div class="flex flex-col gap-1.5 pl-1">
<div class="pl-1 flex gap-2">
<div class="w-96">
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessTelegramNotifications"
label="Docker Cleanup Success" />
</div>
<x-forms.input type="password" placeholder="Custom Telegram Topic ID"
id="telegramNotificationsDockerCleanupSuccessTopicId" />
</div>
<div class="pl-1 flex gap-2">
<div class="w-96">
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupFailureTelegramNotifications"
label="Docker Cleanup Failure" />
</div>
<x-forms.input type="password" placeholder="Custom Telegram Topic ID"
id="telegramNotificationsDockerCleanupFailureTopicId" />
</div>
<div class="pl-1 flex gap-2">
<div class="w-96">
<x-forms.checkbox instantSave="saveModel" id="serverDiskUsageTelegramNotifications"
label="Server Disk Usage" />
</div>
<x-forms.input type="password" placeholder="Custom Telegram Topic ID"
id="telegramNotificationsServerDiskUsageTopicId" />
</div>
<div class="pl-1 flex gap-2">
<div class="w-96">
<x-forms.checkbox instantSave="saveModel" id="serverReachableTelegramNotifications"
label="Server Reachable" />
</div>
<x-forms.input type="password" placeholder="Custom Telegram Topic ID"
id="telegramNotificationsServerReachableTopicId" />
</div>
<div class="pl-1 flex gap-2">
<div class="w-96">
<x-forms.checkbox instantSave="saveModel" id="serverUnreachableTelegramNotifications"
label="Server Unreachable" />
</div>
<x-forms.input type="password" placeholder="Custom Telegram Topic ID"
id="telegramNotificationsServerUnreachableTopicId" />
</div>
</div>
</div>
</div>
</div>

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

@@ -1,8 +1,9 @@
<form class="flex flex-col w-full gap-2 rounded" wire:submit='submit'>
<x-forms.input placeholder="Your Cool Project" id="name" label="Name" required />
<x-forms.input placeholder="This is my cool project everyone knows about" id="description" label="Description" />
<div class="subtitle">New project will have a default production environment.</div>
<x-forms.button type="submit" @click="slideOverOpen=false">
<div class="subtitle">New project will have a default <span class="dark:text-warning font-bold">production</span>
environment.</div>
<x-forms.button type="submit">
Continue
</x-forms.button>
</form>

View File

@@ -1,6 +0,0 @@
<form class="flex flex-col w-full gap-2 rounded" wire:submit='submit'>
<x-forms.input placeholder="production" id="name" label="Name" required />
<x-forms.button type="submit" @click="slideOverOpen=false">
Save
</x-forms.button>
</form>

View File

@@ -8,90 +8,88 @@
<h3>General</h3>
@if ($application->git_based())
<x-forms.checkbox helper="Automatically deploy new commits based on Git webhooks." instantSave
id="application.settings.is_auto_deploy_enabled" label="Auto Deploy" />
id="isAutoDeployEnabled" label="Auto Deploy" />
<x-forms.checkbox
helper="Allow to automatically deploy Preview Deployments for all opened PR's.<br><br>Closing a PR will delete Preview Deployments."
instantSave id="application.settings.is_preview_deployments_enabled" label="Preview Deployments" />
instantSave id="isPreviewDeploymentsEnabled" label="Preview Deployments" />
@endif
<x-forms.checkbox helper="Disable Docker build cache on every deployment." instantSave id="disableBuildCache"
label="Disable Build Cache" />
<x-forms.checkbox
helper="Your application will be available only on https if your domain starts with https://..."
instantSave id="is_force_https_enabled" label="Force Https" />
instantSave id="isForceHttpsEnabled" label="Force Https" />
<x-forms.checkbox label="Enable Gzip Compression"
helper="You can disable gzip compression if you want. Some services are compressing data by default. In this case, you do not need this."
instantSave id="is_gzip_enabled" />
instantSave id="isGzipEnabled" />
<x-forms.checkbox helper="Strip Prefix is used to remove prefixes from paths. Like /api/ to /api."
instantSave id="is_stripprefix_enabled" label="Strip Prefixes" />
instantSave id="isStripprefixEnabled" label="Strip Prefixes" />
@if ($application->build_pack === 'dockercompose')
<h3>Docker Compose</h3>
<x-forms.checkbox instantSave id="application.settings.is_raw_compose_deployment_enabled"
label="Raw Compose Deployment"
<x-forms.checkbox instantSave id="isRawComposeDeploymentEnabled" label="Raw Compose Deployment"
helper="WARNING: Advanced use cases only. Your docker compose file will be deployed as-is. Nothing is modified by Coolify. You need to configure the proxy parts. More info in the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/compose#raw-docker-compose-deployment'>documentation.</a>" />
@endif
<h3>Container Names</h3>
<h3 class="pt-4">Container Names</h3>
<x-forms.checkbox
helper="The deployed container will have the same name ({{ $application->uuid }}). <span class='font-bold dark:text-warning'>You will lose the rolling update feature!</span>"
instantSave id="application.settings.is_consistent_container_name_enabled"
label="Consistent Container Names" />
@if (!$application->settings->is_consistent_container_name_enabled)
<form class="flex items-end gap-2 pl-2" wire:submit.prevent='saveCustomName'>
instantSave id="isConsistentContainerNameEnabled" label="Consistent Container Names" />
@if ($isConsistentContainerNameEnabled === false)
<form class="flex items-end gap-2 " wire:submit.prevent='saveCustomName'>
<x-forms.input
helper="You can add a custom name for your container.<br><br>The name will be converted to slug format when you save it. <span class='font-bold dark:text-warning'>You will lose the rolling update feature!</span>"
instantSave id="application.settings.custom_internal_name" label="Custom Container Name" />
instantSave id="customInternalName" label="Custom Container Name" />
<x-forms.button type="submit">Save</x-forms.button>
</form>
@endif
@if ($application->build_pack === 'dockercompose')
<h3>Network</h3>
<x-forms.checkbox instantSave id="application.settings.connect_to_docker_network"
label="Connect To Predefined Network"
<h3 class="pt-4">Network</h3>
<x-forms.checkbox instantSave id="isConnectToDockerNetworkEnabled" label="Connect To Predefined Network"
helper="By default, you do not reach the Coolify defined networks.<br>Starting a docker compose based resource will have an internal network. <br>If you connect to a Coolify defined network, you maybe need to use different internal DNS names to connect to a resource.<br><br>For more information, check <a class='underline dark:text-white' target='_blank' href='https://coolify.io/docs/knowledge-base/docker/compose#connect-to-predefined-networks'>this</a>." />
@endif
@if (!$application->settings->is_raw_compose_deployment_enabled)
<h3>Logs</h3>
@if ($isLogDrainEnabled === false)
<h3 class="pt-4">Logs</h3>
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
instantSave id="application.settings.is_log_drain_enabled" label="Drain Logs" />
instantSave id="isLogDrainEnabled" label="Drain Logs" />
@endif
@if ($application->git_based())
<h3>Git</h3>
<x-forms.checkbox instantSave id="application.settings.is_git_submodules_enabled" label="Submodules"
<x-forms.checkbox instantSave id="isGitSubmodulesEnabled" label="Submodules"
helper="Allow Git Submodules during build process." />
<x-forms.checkbox instantSave id="application.settings.is_git_lfs_enabled" label="LFS"
<x-forms.checkbox instantSave id="isGitLfsEnabled" label="LFS"
helper="Allow Git LFS during build process." />
@endif
{{-- <x-forms.checkbox disabled instantSave id="is_dual_cert" label="Dual Certs?" />
<x-forms.checkbox disabled instantSave id="is_custom_ssl" label="Is Custom SSL?" />
<x-forms.checkbox disabled instantSave id="is_http2" label="Is Http2?" /> --}}
</div>
@if ($application->build_pack !== 'dockercompose')
<h3>GPU</h3>
@endif
<form wire:submit="submit">
@if ($application->build_pack !== 'dockercompose')
<x-forms.checkbox
helper="Enable GPU usage for this application. More info <a href='https://docs.docker.com/compose/gpu-support/' class='underline dark:text-white' target='_blank'>here</a>."
instantSave id="application.settings.is_gpu_enabled" label="Attach GPU" />
@if ($application->settings->is_gpu_enabled)
<h5>GPU Settings</h5>
</div>
<form wire:submit="submit" class="flex flex-col gap-2">
@if ($application->build_pack !== 'dockercompose')
<div class="flex gap-2 items-end pt-4">
<h3>GPU</h3>
@if ($isGpuEnabled)
<x-forms.button type="submit">Save</x-forms.button>
@endif
@endif
@if ($application->settings->is_gpu_enabled)
<div class="flex flex-col w-full gap-2 p-2 xl:flex-row">
<x-forms.input label="GPU Driver" id="application.settings.gpu_driver"> </x-forms.input>
<x-forms.input label="GPU Count" placeholder="empty means use all GPUs"
id="application.settings.gpu_count"> </x-forms.input>
<x-forms.input label="GPU Device Ids" placeholder="0,2"
helper="Comma separated list of device ids. More info <a href='https://docs.docker.com/compose/gpu-support/#access-specific-devices' class='underline dark:text-white' target='_blank'>here</a>."
id="application.settings.gpu_device_ids"> </x-forms.input>
</div>
@endif
@if ($application->build_pack !== 'dockercompose')
<div class="md:w-96 pb-4">
<x-forms.checkbox
helper="Enable GPU usage for this application. More info <a href='https://docs.docker.com/compose/gpu-support/' class='underline dark:text-white' target='_blank'>here</a>."
instantSave id="isGpuEnabled" label="Enable GPU" />
</div>
@endif
@if ($isGpuEnabled)
<div class="flex flex-col w-full gap-2 ">
<div class="flex gap-2 items-end">
<x-forms.input label="GPU Driver" id="gpuDriver"> </x-forms.input>
<x-forms.input label="GPU Count" placeholder="empty means use all GPUs" id="gpuCount">
</x-forms.input>
</div>
</div>
<div class="px-2">
<x-forms.textarea label="GPU Options" id="application.settings.gpu_options">
</x-forms.textarea>
</div>
@endif
</form>
</div>
<x-forms.input label="GPU Device Ids" placeholder="0,2"
helper="Comma separated list of device ids. More info <a href='https://docs.docker.com/compose/gpu-support/#access-specific-devices' class='underline dark:text-white' target='_blank'>here</a>."
id="gpuDeviceIds"> </x-forms.input>
<x-forms.textarea rows="10" label="GPU Options" id="gpuOptions"> </x-forms.textarea>
</div>
@endif
</form>
</div>

View File

@@ -32,7 +32,7 @@
@forelse ($deployments as $deployment)
<div @class([
'dark:bg-coolgray-100 p-2 border-l-2 transition-colors hover:no-underline box-without-bg-without-border bg-white flex-col cursor-pointer dark:hover:text-neutral-400 dark:hover:bg-coolgray-200',
'border-warning border-dashed ' =>
'border-white border-dashed ' =>
data_get($deployment, 'status') === 'in_progress' ||
data_get($deployment, 'status') === 'cancelled-by-user',
'border-error border-dashed ' =>

View File

@@ -19,7 +19,7 @@
},
toggleScroll() {
this.alwaysScroll = !this.alwaysScroll;
if (this.alwaysScroll) {
this.intervalId = setInterval(() => {
const screen = document.getElementById('screen');
@@ -58,30 +58,34 @@
<div @if ($isKeepAliveOn) wire:poll.2000ms="polling" @endif
class="flex flex-col-reverse w-full p-2 px-4 mt-4 overflow-y-auto bg-white dark:text-white dark:bg-coolgray-100 scrollbar dark:border-coolgray-300"
:class="fullscreen ? '' : 'min-h-14 max-h-[40rem] border border-dotted rounded'">
<div :class="fullscreen ? 'fixed' : 'absolute'" class="top-4 right-6">
<div :class="fullscreen ? 'fixed' : 'absolute'" class="top-2 right-3">
<div class="flex justify-end gap-4 fixed -translate-x-full">
<button title="Toggle timestamps" x-on:click="showTimestamps = !showTimestamps">
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none"
stroke="currentColor" stroke-width="2">
<svg class="w-5 h-5 opacity-30 hover:opacity-100" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor"
stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round"
d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</button>
<button title="Go Top" x-show="fullscreen" x-on:click="goTop">
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<svg class="w-5 h-5 opacity-30 hover:opacity-100" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="currentColor" stroke-linecap="round"
stroke-linejoin="round" stroke-width="2" d="M12 5v14m4-10l-4-4M8 9l4-4" />
</svg>
</button>
<button title="Follow Logs" x-show="fullscreen" :class="alwaysScroll ? 'dark:text-warning' : ''"
x-on:click="toggleScroll">
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<svg class="w-5 h-5 opacity-30 hover:opacity-100" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="currentColor" stroke-linecap="round"
stroke-linejoin="round" stroke-width="2" d="M12 5v14m4-4l-4 4m-4-4l4 4" />
</svg>
</button>
<button title="Fullscreen" x-show="!fullscreen" x-on:click="makeFullscreen">
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<svg class="w-5 h-5 opacity-30 hover:opacity-100" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<g fill="none">
<path
d="M24 0v24H0V0h24ZM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035c-.01-.004-.019-.001-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427c-.002-.01-.009-.017-.017-.018Zm.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093c.012.004.023 0 .029-.008l.004-.014l-.034-.614c-.003-.012-.01-.02-.02-.022Zm-.715.002a.023.023 0 0 0-.027.006l-.006.014l-.034.614c0 .012.007.02.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01l-.184-.092Z" />
@@ -91,7 +95,8 @@
</svg>
</button>
<button title="Minimize" x-show="fullscreen" x-on:click="makeFullscreen">
<svg class="icon" viewBox="0 0 24 24"xmlns="http://www.w3.org/2000/svg">
<svg class="w-5 h-5 opacity-30 hover:opacity-100"
viewBox="0 0 24 24"xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="currentColor" stroke-linecap="round"
stroke-linejoin="round" stroke-width="2"
d="M6 14h4m0 0v4m0-4l-6 6m14-10h-4m0 0V6m0 4l6-6" />

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">
@@ -61,8 +61,16 @@
</div>
@endif
@if ($application->settings->is_static || $application->build_pack === 'static')
<x-forms.textarea id="application.custom_nginx_configuration"
placeholder="Empty means default configuration will be used." label="Custom Nginx Configuration"
helper="You can add custom Nginx configuration here." />
<x-forms.button wire:click="generateNginxConfiguration">Generate Default Nginx
Configuration</x-forms.button>
@endif
@if ($application->build_pack !== 'dockercompose')
<div class="flex items-end gap-2">
<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
@@ -70,7 +78,7 @@
</div>
<div class="flex items-end gap-2">
<x-forms.select label="Direction" id="application.redirect" required
helper="You must need to add www and non-www as an A DNS record.">
helper="You must need to add www and non-www as an A DNS record. Make sure the www domain is added under Domains.">
<option value="both">Allow www & non-www.</option>
<option value="www">Redirect to www.</option>
<option value="non-www">Redirect to non-www.</option>
@@ -154,7 +162,7 @@
<div class="pt-1 text-xs">Nixpacks will detect the required configuration
automatically.
<a class="underline"
href="https://coolify.io/docs/resources/applications/index">Framework
href="https://coolify.io/docs/applications">Framework
Specific Docs</a>
</div>
@endif
@@ -238,9 +246,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 +320,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

@@ -2,14 +2,14 @@
<div class="flex items-center gap-2">
<h2>Preview Deployments</h2>
<x-forms.button type="submit">Save</x-forms.button>
<x-forms.button wire:click="resetToDefault">Reset template to default</x-forms.button>
<x-forms.button isHighlighted wire:click="resetToDefault">Reset template to default</x-forms.button>
</div>
<div class="pb-4 ">Preview Deployments based on pull requests are here.</div>
<div class="flex flex-col gap-2 pb-4">
<x-forms.input id="application.preview_url_template" label="Preview URL Template"
<x-forms.input id="previewUrlTemplate" label="Preview URL Template"
helper="Templates:<span class='text-helper'>@@{{ random }}</span> to generate random sub-domain each time a PR is deployed, <span class='text-helper'>@@{{ pr_id }}</span> to use pull request ID as sub-domain or <span class='text-helper'>@@{{ domain }}</span> to replace the domain name with the application's domain name." />
@if ($preview_url_template)
<div class="">Domain Preview: {{ $preview_url_template }}</div>
@if ($previewUrlTemplate)
<div class="">Domain Preview: {{ $previewUrlTemplate }}</div>
@endif
</div>
</form>

View File

@@ -27,25 +27,23 @@
<div class="flex flex-col gap-2">
<div class="flex gap-2">
<x-forms.input placeholder="coollabsio/coolify-example" id="application.git_repository"
label="Repository" />
<x-forms.input placeholder="main" id="application.git_branch" label="Branch" />
<x-forms.input placeholder="coollabsio/coolify-example" id="gitRepository" label="Repository" />
<x-forms.input placeholder="main" id="gitBranch" label="Branch" />
</div>
<div class="flex items-end gap-2">
<x-forms.input placeholder="HEAD" id="application.git_commit_sha" placeholder="HEAD"
label="Commit SHA" />
<x-forms.input placeholder="HEAD" id="gitCommitSha" placeholder="HEAD" label="Commit SHA" />
</div>
</div>
@if (data_get($application, 'private_key_id'))
@if ($privateKeyId)
<h3 class="pt-4">Deploy Key</h3>
<div class="py-2 pt-4">Currently attached Private Key: <span
class="dark:text-warning">{{ data_get($application, 'private_key.name') }}</span>
class="dark:text-warning">{{ $privateKeyName }}</span>
</div>
<h4 class="py-2 ">Select another Private Key</h4>
<div class="flex flex-wrap gap-2">
@foreach ($private_keys as $key)
<x-forms.button wire:click.defer="setPrivateKey('{{ $key->id }}')">{{ $key->name }}
@foreach ($privateKeys as $key)
<x-forms.button wire:click="setPrivateKey('{{ $key->id }}')">{{ $key->name }}
</x-forms.button>
@endforeach
</div>

View File

@@ -6,15 +6,13 @@
Save
</x-forms.button>
</div>
{{-- <div>Advanced Swarm Configuration</div> --}}
<div class="flex flex-col gap-2 py-4">
<div class="flex flex-col items-end gap-2 xl:flex-row">
<x-forms.input id="application.swarm_replicas" label="Replicas" required />
<x-forms.input id="swarmReplicas" label="Replicas" required />
<x-forms.checkbox instantSave helper="If turned off, this resource will start on manager nodes too."
id="application.settings.is_swarm_only_worker_nodes" label="Only Start on Worker nodes" />
id="isSwarmOnlyWorkerNodes" label="Only Start on Worker nodes" />
</div>
<x-forms.textarea id="swarm_placement_constraints" rows="7"
label="Custom Placement Constraints"
<x-forms.textarea id="swarmPlacementConstraints" rows="7" label="Custom Placement Constraints"
placeholder="placement:
constraints:
- 'node.role == worker'" />

View File

@@ -19,12 +19,12 @@
@endif
</div>
<div class="w-48 pb-2">
<x-forms.checkbox instantSave label="Backup Enabled" id="backup.enabled" />
<x-forms.checkbox instantSave label="S3 Enabled" id="backup.save_s3" />
<x-forms.checkbox instantSave label="Backup Enabled" id="backupEnabled" />
<x-forms.checkbox instantSave label="S3 Enabled" id="saveS3" />
</div>
@if ($backup->save_s3)
<div class="pb-6">
<x-forms.select id="backup.s3_storage_id" label="S3 Storage" required>
<x-forms.select id="s3StorageId" label="S3 Storage" required>
<option value="default">Select a S3 storage</option>
@foreach ($s3s as $s3)
<option value="{{ $s3->id }}">{{ $s3->name }}</option>
@@ -35,42 +35,42 @@
<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 />
<x-forms.checkbox label="Backup All Databases" id="dumpAll" instantSave />
</div>
@if (!$backup->dump_all)
<x-forms.input label="Databases To Backup"
helper="Comma separated list of databases to backup. Empty will include the default one."
id="backup.databases_to_backup" />
id="databasesToBackup" />
@endif
@elseif($backup->database_type === 'App\Models\StandaloneMongodb')
<x-forms.input label="Databases To Include"
helper="A list of databases to backup. You can specify which collection(s) per database to exclude from the backup. Empty will include all databases and collections.<br><br>Example:<br><br>database1:collection1,collection2|database2:collection3,collection4<br><br> database1 will include all collections except collection1 and collection2. <br>database2 will include all collections except collection3 and collection4.<br><br>Another Example:<br><br>database1:collection1|database2<br><br> database1 will include all collections except collection1.<br>database2 will include ALL collections."
id="backup.databases_to_backup" />
id="databasesToBackup" />
@elseif($backup->database_type === 'App\Models\StandaloneMysql')
<div class="w-48">
<x-forms.checkbox label="Backup All Databases" id="backup.dump_all" instantSave />
<x-forms.checkbox label="Backup All Databases" id="dumpAll" instantSave />
</div>
@if (!$backup->dump_all)
<x-forms.input label="Databases To Backup"
helper="Comma separated list of databases to backup. Empty will include the default one."
id="backup.databases_to_backup" />
id="databasesToBackup" />
@endif
@elseif($backup->database_type === 'App\Models\StandaloneMariadb')
<div class="w-48">
<x-forms.checkbox label="Backup All Databases" id="backup.dump_all" instantSave />
<x-forms.checkbox label="Backup All Databases" id="dumpAll" instantSave />
</div>
@if (!$backup->dump_all)
<x-forms.input label="Databases To Backup"
helper="Comma separated list of databases to backup. Empty will include the default one."
id="backup.databases_to_backup" />
id="databasesToBackup" />
@endif
@endif
</div>
<div class="flex gap-2">
<x-forms.input label="Frequency" id="backup.frequency" />
<x-forms.input label="Number of backups to keep (locally)" id="backup.number_of_backups_locally" />
<x-forms.input label="Frequency" id="frequency" />
<x-forms.input label="Number of backups to keep (locally)" id="numberOfBackupsLocally" />
</div>
</div>
</form>

View File

@@ -7,68 +7,78 @@
</x-forms.button>
</div>
<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
<x-forms.input label="Name" id="name" />
<x-forms.input label="Description" id="description" />
<x-forms.input label="Image" id="image" required
helper="For all available images, check here:<br><br><a target='_blank' href='https://hub.docker.com/r/clickhouse/clickhouse-server/'>https://hub.docker.com/r/clickhouse/clickhouse-server/</a>" />
</div>
@if ($database->started_at)
<div class="flex gap-2">
<x-forms.input label="Initial Username" id="database.clickhouse_admin_user"
placeholder="If empty: clickhouse" readonly helper="You can only change this in the database." />
<x-forms.input label="Initial Password" id="database.clickhouse_admin_password" type="password" required
<x-forms.input label="Initial Username" id="clickhouseAdminUser" placeholder="If empty: clickhouse"
readonly helper="You can only change this in the database." />
<x-forms.input label="Initial Password" id="clickhouseAdminPassword" type="password" required readonly
helper="You can only change this in the database." />
</div>
@else
<div class=" dark:text-warning">Please verify these values. You can only modify them before the initial
start. After that, you need to modify it in the database.
</div>
<div class="flex gap-2">
<x-forms.input label="Username" id="database.clickhouse_admin_user" required />
<x-forms.input label="Password" id="database.clickhouse_admin_password" type="password" required />
<x-forms.input label="Username" id="clickhouseAdminUser" required />
<x-forms.input label="Password" id="clickhouseAdminPassword" type="password" required />
</div>
@endif
<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>"
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse --security-opt apparmor:unconfined --ulimit nofile=1024:1024 --tmpfs /run:rw,noexec,nosuid,size=65536k"
id="database.custom_docker_run_options" label="Custom Docker Options" />
id="customDockerRunOptions" label="Custom Docker Options" />
<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"
<x-forms.input placeholder="3000:5432" id="portsMappings" 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="Clickhouse 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)
type="password" readonly wire:model="dbUrl" />
@if ($dbUrlPublic)
<x-forms.input label="Clickhouse 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" />
type="password" readonly wire:model="dbUrlPublic" />
@else
<x-forms.input label="Clickhouse URL (public)"
helper="If you change the user/password/port, this could be different. This is with the default values."
readonly value="Starting the database will generate this." />
@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-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 />
</x-slot:content>
<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 class="flex flex-col py-2 w-64">
<div class="flex items-center gap-2 pb-2">
<div class="flex items-center">
<h3>Proxy</h3>
<x-loading wire:loading wire:target="instantSave" />
</div>
@if ($isPublic)
<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 />
</x-slot:content>
<x-forms.button disabled="{{ !$isPublic }}"
@click="slideOverOpen=true">Logs</x-forms.button>
</x-slide-over>
@endif
</div>
<x-forms.checkbox instantSave id="isPublic" label="Make it publicly available" />
</div>
<x-forms.input placeholder="5432" disabled="{{ $isPublic }}" id="publicPort" label="Public Port" />
</div>
</form>
<h3 class="pt-4">Advanced</h3>
<div class="flex flex-col">
<div class="w-64">
<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" />
instantSave="instantSaveAdvanced" id="isLogDrainEnabled" label="Drain Logs" />
</div>
</div>

View File

@@ -2,13 +2,14 @@
<x-forms.input placeholder="0 0 * * * or daily" id="frequency"
helper="You can use every_minute, hourly, daily, weekly, monthly, yearly or a cron expression." label="Frequency"
required />
@if ($s3s->count() === 0)
<h2>S3</h2>
@if ($definedS3s->count() === 0)
<div class="text-red-500">No validated S3 Storages found.</div>
@else
<x-forms.checkbox wire:model.live="save_s3" label="Save to S3" />
@if ($save_s3)
<x-forms.select id="selected_storage_id" label="Select a validated S3 storage">
@foreach ($s3s as $s3)
<x-forms.checkbox wire:model.live="saveToS3" label="Save to S3" />
@if ($saveToS3)
<x-forms.select id="s3StorageId" label="Select a S3 Storage">
@foreach ($definedS3s as $s3)
<option value="{{ $s3->id }}">{{ $s3->name }}</option>
@endforeach
</x-forms.select>

View File

@@ -7,53 +7,75 @@
</x-forms.button>
</div>
<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 />
<x-forms.input label="Name" id="name" />
<x-forms.input label="Description" id="description" />
<x-forms.input label="Image" id="image" required />
</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>"
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse --security-opt apparmor:unconfined --ulimit nofile=1024:1024 --tmpfs /run:rw,noexec,nosuid,size=65536k"
id="database.custom_docker_run_options" label="Custom Docker Options" />
id="customDockerRunOptions" label="Custom Docker Options" />
@if ($database->started_at)
<div class="flex gap-2">
<x-forms.input label="Initial Password" id="dragonflyPassword" type="password" required readonly
helper="You can only change this in the database." />
</div>
@else
<div class=" dark:text-warning">Please verify these values. You can only modify them before the initial
start. After that, you need to modify it in the database.
</div>
<div class="flex gap-2">
<x-forms.input label="Password" id="dragonflyPassword" type="password" required />
</div>
@endif
<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"
<x-forms.input placeholder="3000:5432" id="portsMappings" 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="Dragonfly 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)
type="password" readonly wire:model="dbUrl" />
@if ($dbUrlPublic)
<x-forms.input label="Dragonfly 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" />
type="password" readonly wire:model="dbUrlPublic" />
@else
<x-forms.input label="Dragonfly URL (public)"
helper="If you change the user/password/port, this could be different. This is with the default values."
readonly value="Starting the database will generate this." />
@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-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 />
</x-slot:content>
<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 class="flex flex-col py-2 w-64">
<div class="flex items-center gap-2 pb-2">
<div class="flex items-center">
<h3>Proxy</h3>
<x-loading wire:loading wire:target="instantSave" />
</div>
@if ($isPublic)
<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 />
</x-slot:content>
<x-forms.button disabled="{{ !$isPublic }}"
@click="slideOverOpen=true">Logs</x-forms.button>
</x-slide-over>
@endif
</div>
<x-forms.checkbox instantSave id="isPublic" label="Make it publicly available" />
</div>
</div>
{{-- <x-forms.textarea
helper="<a target='_blank' class='underline dark:text-white' href='https://raw.githubusercontent.com/Snapchat/KeyDB/unstable/keydb.conf'>KeyDB Default Configuration</a>"
label="Custom Dragonfly Configuration" rows="10" id="database.keydb_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.input placeholder="5432" disabled="{{ $isPublic }}" id="publicPort" label="Public Port" />
</div>
</form>
<h3 class="pt-4">Advanced</h3>
<div class="w-64">
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
instantSave="instantSaveAdvanced" id="isLogDrainEnabled" label="Drain Logs" />
</div>
</div>

View File

@@ -7,54 +7,78 @@
</x-forms.button>
</div>
<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
<x-forms.input label="Name" id="name" />
<x-forms.input label="Description" id="description" />
<x-forms.input label="Image" id="image" required
helper="For all available images, check here:<br><br><a target='_blank' href=https://hub.docker.com/r/eqalpha/keydb'>https://hub.docker.com/r/eqalpha/keydb</a>" />
</div>
@if ($database->started_at)
<div class="flex gap-2">
<x-forms.input label="Initial Password" id="keydbPassword" type="password" required readonly
helper="You can only change this in the database." />
</div>
@else
<div class=" dark:text-warning">Please verify these values. You can only modify them before the initial
start. After that, you need to modify it in the database.
</div>
<div class="flex gap-2">
<x-forms.input label="Password" id="keydbPassword" type="password" required />
</div>
@endif
<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>"
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse --security-opt apparmor:unconfined --ulimit nofile=1024:1024 --tmpfs /run:rw,noexec,nosuid,size=65536k"
id="database.custom_docker_run_options" label="Custom Docker Options" />
id="customDockerRunOptions" label="Custom Docker Options" />
<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"
<x-forms.input placeholder="3000:5432" id="portsMappings" 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="KeyDB 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)
type="password" readonly wire:model="dbUrl" />
@if ($dbUrlPublic)
<x-forms.input label="KeyDB 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" />
type="password" readonly wire:model="dbUrlPublic" />
@else
<x-forms.input label="KeyDB URL (public)"
helper="If you change the user/password/port, this could be different. This is with the default values."
readonly value="Starting the database will generate this." />
@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-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 />
</x-slot:content>
<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 class="flex flex-col py-2 w-64">
<div class="flex items-center gap-2 pb-2">
<div class="flex items-center">
<h3>Proxy</h3>
<x-loading wire:loading wire:target="instantSave" />
</div>
@if ($isPublic)
<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 />
</x-slot:content>
<x-forms.button disabled="{{ !$isPublic }}"
@click="slideOverOpen=true">Logs</x-forms.button>
</x-slide-over>
@endif
</div>
<x-forms.checkbox instantSave id="isPublic" label="Make it publicly available" />
</div>
<x-forms.input placeholder="5432" disabled="{{ $isPublic }}" id="publicPort" label="Public Port" />
</div>
<x-forms.textarea
helper="<a target='_blank' class='underline dark:text-white' href='https://raw.githubusercontent.com/Snapchat/KeyDB/unstable/keydb.conf'>KeyDB Default Configuration</a>"
label="Custom KeyDB Configuration" rows="10" id="database.keydb_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" />
</div>
label="Custom KeyDB Configuration" rows="10" id="keydbConf" />
</form>
<h3 class="pt-4">Advanced</h3>
<div class="w-64">
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
instantSave="instantSaveAdvanced" id="isLogDrainEnabled" label="Drain Logs" />
</div>
</div>

View File

@@ -66,21 +66,28 @@
@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-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 />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<div class="flex flex-col py-2 w-64">
<div class="flex items-center gap-2 pb-2">
<div class="flex items-center">
<h3>Proxy</h3>
<x-loading wire:loading wire:target="instantSave" />
</div>
@if (data_get($database, 'is_public'))
<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 />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}"
@click="slideOverOpen=true">Logs</x-forms.button>
</x-slide-over>
@endif
</div>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
</div>
<x-forms.textarea label="Custom MariaDB Configuration" rows="10" id="database.mariadb_conf" />
<h3 class="pt-4">Advanced</h3>

View File

@@ -56,21 +56,28 @@
@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-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 />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<div class="flex flex-col py-2 w-64">
<div class="flex items-center gap-2 pb-2">
<div class="flex items-center">
<h3>Proxy</h3>
<x-loading wire:loading wire:target="instantSave" />
</div>
@if (data_get($database, 'is_public'))
<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 />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}"
@click="slideOverOpen=true">Logs</x-forms.button>
</x-slide-over>
@endif
</div>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
</div>
<x-forms.textarea label="Custom MongoDB Configuration" rows="10" id="database.mongo_conf" />
<h3 class="pt-4">Advanced</h3>

View File

@@ -66,21 +66,28 @@
@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-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 />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<div class="flex flex-col py-2 w-64">
<div class="flex items-center gap-2 pb-2">
<div class="flex items-center">
<h3>Proxy</h3>
<x-loading wire:loading wire:target="instantSave" />
</div>
@if (data_get($database, 'is_public'))
<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 />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}"
@click="slideOverOpen=true">Logs</x-forms.button>
</x-slide-over>
@endif
</div>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
</div>
<x-forms.textarea label="Custom Mysql Configuration" rows="10" id="database.mysql_conf" />
<h3 class="pt-4">Advanced</h3>

View File

@@ -74,21 +74,28 @@
@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-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 />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<div class="flex flex-col py-2 w-64">
<div class="flex items-center gap-2 pb-2">
<div class="flex items-center">
<h3>Proxy</h3>
<x-loading wire:loading wire:target="instantSave" />
</div>
@if (data_get($database, 'is_public'))
<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 />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}"
@click="slideOverOpen=true">Logs</x-forms.button>
</x-slide-over>
@endif
</div>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
</div>
<x-forms.textarea label="Custom PostgreSQL Configuration" rows="10" id="database.postgres_conf" />
</form>
@@ -102,8 +109,7 @@
<h3>Initialization scripts</h3>
<x-modal-input buttonTitle="+ Add" title="New Init Script">
<form class="flex flex-col w-full gap-2 rounded" wire:submit='save_new_init_script'>
<x-forms.input placeholder="create_test_db.sql" id="new_filename" label="Filename"
required />
<x-forms.input placeholder="create_test_db.sql" id="new_filename" label="Filename" required />
<x-forms.textarea rows="20" placeholder="CREATE DATABASE test;" id="new_content"
label="Content" required />
<x-forms.button type="submit">

View File

@@ -12,6 +12,24 @@
<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>"
placeholder="--cap-add SYS_ADMIN --device=/dev/fuse --security-opt apparmor:unconfined --ulimit nofile=1024:1024 --tmpfs /run:rw,noexec,nosuid,size=65536k"
@@ -32,21 +50,28 @@
@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-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 />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
class="w-28">Proxy Logs</x-forms.button>
</x-slide-over>
<div class="flex flex-col py-2 w-64">
<div class="flex items-center gap-2 pb-2">
<div class="flex items-center">
<h3>Proxy</h3>
<x-loading wire:loading wire:target="instantSave" />
</div>
@if (data_get($database, 'is_public'))
<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 />
</x-slot:content>
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}"
@click="slideOverOpen=true">Logs</x-forms.button>
</x-slide-over>
@endif
</div>
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
</div>
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
id="database.public_port" label="Public Port" />
</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>"
@@ -56,5 +81,6 @@
<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

@@ -7,14 +7,13 @@
<h1>Project: {{ data_get($project, 'name') }}</h1>
<div class="flex items-end gap-2">
<x-forms.button type="submit">Save</x-forms.button>
<livewire:project.delete-project :disabled="$project->resource_count() > 0" :project_id="$project->id" />
<livewire:project.delete-project :disabled="!$project->isEmpty()" :project_id="$project->id" />
</div>
</div>
<div class="pt-2 pb-10">Edit project details here.</div>
<div class="flex gap-2">
<x-forms.input label="Name" id="project.name" />
<x-forms.input label="Description" id="project.description" />
<x-forms.input label="Name" id="name" />
<x-forms.input label="Description" id="description" />
</div>
</form>
</div>

View File

@@ -13,7 +13,7 @@
<li class="inline-flex items-center">
<div class="flex items-center">
<a class="text-xs truncate lg:text-sm"
href="{{ route('project.show', ['project_uuid' => data_get($parameters, 'project_uuid')]) }}">
href="{{ route('project.show', ['project_uuid' => $project->uuid]) }}">
{{ $project->name }}</a>
<svg aria-hidden="true" class="w-4 h-4 mx-1 font-bold dark:text-warning" fill="currentColor"
viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
@@ -26,7 +26,9 @@
<li>
<div class="flex items-center">
<a class="text-xs truncate lg:text-sm"
href="{{ route('project.resource.index', ['environment_name' => data_get($parameters, 'environment_name'), 'project_uuid' => data_get($parameters, 'project_uuid')]) }}">{{ data_get($parameters, 'environment_name') }}</a>
href="{{ route('project.resource.index', ['environment_name' => $environment->name, 'project_uuid' => $project->uuid]) }}">
{{ $environment->name }}
</a>
</div>
</li>
<li>
@@ -43,8 +45,8 @@
</ol>
</nav>
<div class="flex gap-2">
<x-forms.input label="Name" id="environment.name" />
<x-forms.input label="Description" id="environment.description" />
<x-forms.input label="Name" id="name" />
<x-forms.input label="Description" id="description" />
</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

@@ -8,7 +8,7 @@
@forelse ($private_keys as $key)
@if ($private_key_id == $key->id)
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-200 box"
wire:click.defer="setPrivateKey('{{ $key->id }}')" wire:key="{{ $key->id }}">
wire:click="setPrivateKey('{{ $key->id }}')" wire:key="{{ $key->id }}">
<div class="flex flex-col mx-6">
<div class="box-title">
{{ $key->name }}
@@ -21,7 +21,7 @@
</div>
@else
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-200 box"
wire:click.defer="setPrivateKey('{{ $key->id }}')" wire:key="{{ $key->id }}">
wire:click="setPrivateKey('{{ $key->id }}')" wire:key="{{ $key->id }}">
<div class="flex flex-col mx-6">
<div class="box-title">
{{ $key->name }}

View File

@@ -1,7 +1,7 @@
<div>
<div class="flex items-end gap-2">
<h1>Create a new Application</h1>
<x-modal-input buttonTitle="+ Add GitHub App" title="New GitHub App">
<x-modal-input buttonTitle="+ Add GitHub App" title="New GitHub App" closeOutside="false">
<livewire:source.github.create />
</x-modal-input>
@if ($repositories->count() > 0)

View File

@@ -12,9 +12,10 @@
<div class="pb-4">Deploy resources, like Applications, Databases, Services...</div>
<div x-data="searchResources()">
@if ($current_step === 'type')
<div class="sticky top-0 z-50 py-2">
<input autocomplete="off" x-ref="searchInput" class="input w-full" x-model="search"
placeholder="Type / to search..." @keydown.window.slash.prevent="$refs.searchInput.focus()">
<div x-init="window.addEventListener('scroll', () => isSticky = window.pageYOffset > 100)" class="sticky top-0 z-50 py-2">
<input autocomplete="off" x-ref="searchInput" class="input-sticky"
:class="{ 'input-sticky-active': isSticky }" x-model="search" placeholder="Type / to search..."
@keydown.window.slash.prevent="$refs.searchInput.focus()">
</div>
<div x-show="loading">Loading...</div>
<div x-show="!loading" class="flex flex-col gap-4 py-4">
@@ -30,8 +31,7 @@
<span x-html="application.description"></span>
</x-slot>
<x-slot:logo>
<img class="w-[4.5rem]
aspect-square h-[4.5rem] p-2 transition-all duration-200 opacity-30 grayscale group-hover:grayscale-0 group-hover:opacity-100 "
<img class="w-[4.5rem] aspect-square h-[4.5rem] p-2 transition-all duration-200 dark:opacity-30 grayscale group-hover:grayscale-0 group-hover:opacity-100 dark:bg-white/10 bg-black/10"
:src="application.logo">
</x-slot:logo>
</x-resource-view>
@@ -47,8 +47,7 @@
<x-slot:title><span x-text="application.name"></span></x-slot>
<x-slot:description><span x-text="application.description"></span></x-slot>
<x-slot:logo> <img
class="w-[4.5rem]
aspect-square h-[4.5rem] p-2 transition-all duration-200 opacity-30 grayscale group-hover:grayscale-0 group-hover:opacity-100 "
class="w-[4.5rem] aspect-square h-[4.5rem] p-2 transition-all duration-200 dark:opacity-30 grayscale group-hover:grayscale-0 group-hover:opacity-100 dark:bg-white/10 bg-black/10 "
:src="application.logo"></x-slot>
</x-resource-view>
</div>
@@ -100,14 +99,18 @@
</x-slot>
<x-slot:logo>
<template x-if="service.logo">
<img class="w-[4.5rem] aspect-square h-[4.5rem] p-2 transition-all duration-200 opacity-30 grayscale group-hover:grayscale-0 group-hover:opacity-100"
:src='service.logo'>
<img class="w-[4.5rem] aspect-square h-[4.5rem] p-2 transition-all duration-200 dark:opacity-30 grayscale group-hover:grayscale-0 group-hover:opacity-100 dark:bg-white/10 bg-black/10"
:src='service.logo'
x-on:error.window="$event.target.src = service.logo_github_url"
onerror="this.onerror=null; this.src=this.getAttribute('data-fallback');"
x-on:error="$event.target.src = '/svgs/coolify.png'"
:data-fallback='service.logo_github_url' />
</template>
</x-slot:logo>
<x-slot:documentation>
<template x-if="service.documentation">
<div class="flex items-center px-2" title="Read the documentation.">
<a class="p-2 rounded hover:bg-coolgray-200 hover:no-underline group-hover:dark:text-white text-neutral-600"
<a class="p-2 rounded hover:bg-gray-100 dark:hover:bg-coolgray-200 hover:no-underline group-hover:dark:text-white text-neutral-600"
onclick="event.stopPropagation()" :href="service.documentation"
target="_blank">
Docs
@@ -134,6 +137,7 @@
return {
search: '',
loading: false,
isSticky: false,
services: [],
gitBasedApplications: [],
dockerBasedApplications: [],
@@ -166,7 +170,8 @@
}
const filtered = Object.values(items).filter(item => {
return (item.name?.toLowerCase().includes(searchLower) ||
item.description?.toLowerCase().includes(searchLower))
item.description?.toLowerCase().includes(searchLower) ||
item.slogan?.toLowerCase().includes(searchLower))
})
return isSort ? filtered.sort(sortFn) : filtered;
},
@@ -205,7 +210,7 @@
}
}
</script>
@endif
@endif
</div>
@if ($current_step === 'servers')
<h2>Select a server</h2>
@@ -270,7 +275,7 @@
16 (default).</div>
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<div class="gap-2 border border-transparent cursor-pointer box-without-bg dark:bg-coolgray-100 bg-white dark:hover:text-neutral-400 dark:hover:bg-coollabs group flex"
<div class="gap-2 border border-transparent cursor-pointer box-without-bg dark:bg-coolgray-100 bg-white dark:hover:text-neutral-400 dark:hover:bg-coollabs group flex "
wire:click="setPostgresqlType('postgres:16-alpine')">
<div class="flex flex-col">
<div class="box-title">PostgreSQL 16 (default)</div>

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,17 +44,108 @@
</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()">
<x-forms.input placeholder="Search for name, fqdn..." x-model="search" id="null" />
<div class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
<template x-if="allFilteredItems.length === 0">
<div>No resource found with the search term "<span x-text="search"></span>".</div>
</template>
<template
x-if="filteredApplications.length === 0 && filteredDatabases.length === 0 && filteredServices.length === 0">
<div>No resource found with the search term "<span x-text="search"></span>".</div>
</template>
<template x-for="item in allFilteredItems" :key="item.uuid">
<template x-if="filteredApplications.length > 0">
<h2 class="pt-4">Applications</h2>
</template>
<div x-show="filteredApplications.length > 0"
class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
<template x-for="item in filteredApplications" :key="item.uuid">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full">
<div class="flex gap-2 px-4">
<div class="pb-2 truncate box-title" x-text="item.name"></div>
<div class="flex-1"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="bg-success badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="bg-warning badge badge-absolute"></div>
</template>
</div>
<div class="max-w-full px-4 truncate box-description" x-text="item.description"></div>
<div class="max-w-full px-4 truncate box-description" x-text="item.fqdn"></div>
<template x-if="item.server_status == false">
<div class="px-4 text-xs font-bold text-error">The underlying server has problems
</div>
</template>
</div>
</a>
<div
class="flex flex-wrap gap-1 pt-1 group-hover:dark:text-white group-hover:text-black group min-h-6">
<template x-for="tag in item.tags">
<div class="tag" @click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="add-tag" @click.prevent="goto(item)">Add tag</div>
</div>
</span>
</template>
</div>
<template x-if="filteredDatabases.length > 0">
<h2 class="pt-4">Databases</h2>
</template>
<div x-show="filteredDatabases.length > 0"
class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
<template x-for="item in filteredDatabases" :key="item.uuid">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full">
<div class="flex gap-2 px-4">
<div class="pb-2 truncate box-title" x-text="item.name"></div>
<div class="flex-1"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="bg-success badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="bg-error badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="bg-warning badge badge-absolute"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="bg-warning badge badge-absolute"></div>
</template>
</div>
<div class="max-w-full px-4 truncate box-description" x-text="item.description"></div>
<div class="max-w-full px-4 truncate box-description" x-text="item.fqdn"></div>
<template x-if="item.server_status == false">
<div class="px-4 text-xs font-bold text-error">The underlying server has problems
</div>
</template>
</div>
</a>
<div
class="flex flex-wrap gap-1 pt-1 group-hover:dark:text-white group-hover:text-black group min-h-6">
<template x-for="tag in item.tags">
<div class="tag" @click.prevent="gotoTag(tag.name)" x-text="tag.name"></div>
</template>
<div class="add-tag" @click.prevent="goto(item)">Add tag</div>
</div>
</span>
</template>
</div>
<template x-if="filteredServices.length > 0">
<h2 class="pt-4">Services</h2>
</template>
<div x-show="filteredServices.length > 0"
class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
<template x-for="item in filteredServices" :key="item.uuid">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col w-full">
@@ -134,9 +225,11 @@
item.tags?.some(tag => tag.name.toLowerCase().includes(searchLower)));
}).sort(sortFn);
},
get allFilteredItems() {
get filteredApplications() {
return this.filterAndSort(this.applications)
},
get filteredDatabases() {
return [
this.applications,
this.postgresqls,
this.redis,
this.mongodbs,
@@ -145,8 +238,10 @@
this.keydbs,
this.dragonflies,
this.clickhouses,
this.services
].flatMap((items) => this.filterAndSort(items))
},
get filteredServices() {
return this.filterAndSort(this.services)
}
};
}

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>
@@ -180,7 +180,7 @@
</div>
<div class="pb-4">Persistent storage to preserve data between deployments.</div>
<div class="pb-4 dark:text-warning text-coollabs">If you would like to add a volume, you must add it to
your compose file (General tab).</div>
your compose file (Service Stack tab).</div>
@foreach ($applications as $application)
<livewire:project.service.storage wire:key="application-{{ $application->id }}" :resource="$application"
lazy />

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

@@ -13,7 +13,7 @@
<x-forms.textarea rows="20" readonly id="service.docker_compose">
</x-forms.textarea>
</div>
<div class="pt-2 w-72">
<div class="pt-2 w-96">
<x-forms.checkbox label="Escape special characters in labels?"
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
id="service.is_container_label_escape_enabled" instantSave></x-forms.checkbox>

View File

@@ -38,7 +38,7 @@
<div class="flex gap-2 ">
<h2 class="pb-4">Scheduled Backups</h2>
<x-modal-input buttonTitle="+ Add" title="New Scheduled Backup">
<livewire:project.database.create-scheduled-backup :database="$serviceDatabase" :s3s="$s3s" />
<livewire:project.database.create-scheduled-backup :database="$serviceDatabase" />
</x-modal-input>
</div>
<livewire:project.database.scheduled-backups :database="$serviceDatabase" />

View File

@@ -15,7 +15,7 @@
volume
name, example: <span class='text-helper'>-pr-1</span>" />
@if ($resource?->build_pack !== 'dockercompose')
<x-modal-input :closeOutside="false" buttonTitle="+ Add" title="New Persistent Storage">
<x-modal-input :closeOutside="false" buttonTitle="+ Add" title="New Persistent Storage" minWidth="64rem">
<livewire:project.shared.storages.add :resource="$resource" />
</x-modal-input>
@endif

View File

@@ -3,7 +3,7 @@
<div class="flex items-center gap-2">
<h2>Environment Variables</h2>
<div class="flex flex-col items-center">
<x-modal-input buttonTitle="+ Add" title="New Environment Variable">
<x-modal-input buttonTitle="+ Add" title="New Environment Variable" :closeOutside="false">
<livewire:project.shared.environment-variable.add />
</x-modal-input>
</div>
@@ -38,7 +38,7 @@
<div>Environment (secrets) variables for Production.</div>
</div>
@php
$requiredEmptyVars = $resource->environment_variables->filter(function($env) {
$requiredEmptyVars = $resource->environment_variables->filter(function ($env) {
return $env->is_required && empty($env->value);
});
$otherVars = $resource->environment_variables->diff($requiredEmptyVars);
@@ -62,11 +62,13 @@
@endif
@else
<form wire:submit.prevent='submit' class="flex flex-col gap-2">
<x-forms.textarea rows="10" class="whitespace-pre-wrap" id="variables" wire:model="variables" label="Production Environment Variables"></x-forms.textarea>
<x-forms.textarea rows="10" class="whitespace-pre-wrap" id="variables" wire:model="variables"
label="Production Environment Variables"></x-forms.textarea>
@if ($showPreview)
<x-forms.textarea rows="10" class="whitespace-pre-wrap" label="Preview Deployments Environment Variables"
id="variablesPreview" wire:model="variablesPreview"></x-forms.textarea>
<x-forms.textarea rows="10" class="whitespace-pre-wrap"
label="Preview Deployments Environment Variables" id="variablesPreview"
wire:model="variablesPreview"></x-forms.textarea>
@endif
<x-forms.button type="submit" class="btn btn-primary">Save All Environment Variables</x-forms.button>

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

@@ -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

@@ -1,11 +1,11 @@
<div>
<div class="flex gap-2">
<h2>Scheduled Tasks</h2>
<x-modal-input buttonTitle="+ Add" title="New Scheduled Task" :closeOutside=false>
<x-modal-input buttonTitle="+ Add" title="New Scheduled Task" :closeOutside="false">
@if ($resource->type() == 'application')
<livewire:project.shared.scheduled-task.add :type="$resource->type()" :containerNames="$containerNames"/>
<livewire:project.shared.scheduled-task.add :type="$resource->type()" :containerNames="$containerNames" />
@elseif ($resource->type() == 'service')
<livewire:project.shared.scheduled-task.add :type="$resource->type()" :containerNames="$containerNames"/>
<livewire:project.shared.scheduled-task.add :type="$resource->type()" :containerNames="$containerNames" />
@endif
</x-modal-input>
</div>

View File

@@ -1,4 +1,17 @@
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-2" x-data="{
init() {
let interval;
$wire.$watch('isPollingActive', value => {
if (value) {
interval = setInterval(() => {
$wire.polling();
}, 1000);
} else {
if (interval) clearInterval(interval);
}
});
}
}">
@forelse($executions as $execution)
<a wire:click="selectTask({{ data_get($execution, 'id') }})" @class([
'flex flex-col border-l-2 transition-colors p-4 cursor-pointer',
@@ -10,6 +23,7 @@
'border-red-500' => data_get($execution, 'status') === 'failed',
'border-yellow-500' => data_get($execution, 'status') === 'running',
])>
@if (data_get($execution, 'status') === 'running')
<div class="absolute top-2 right-2">
<x-loading />
@@ -21,11 +35,34 @@
Started At: {{ $this->formatDateInServerTimezone(data_get($execution, 'created_at', now())) }}
</div>
</a>
@if (strlen($execution->message) > 0)
<x-forms.button wire:click.prevent="downloadLogs({{ data_get($execution, 'id') }})">
Download Logs
</x-forms.button>
@endif
@if (data_get($execution, 'id') == $selectedKey)
<div class="p-4 mb-2 bg-gray-100 dark:bg-coolgray-200 rounded">
@if (data_get($execution, 'message'))
@if (data_get($execution, 'status') === 'running')
<div class="flex items-center gap-2 mb-2">
<span>Task is running...</span>
<x-loading class="w-4 h-4" />
</div>
@endif
@if ($this->logLines->isNotEmpty())
<div>
<pre class="whitespace-pre-wrap">{{ data_get($execution, 'message') }}</pre>
<pre class="whitespace-pre-wrap">
@foreach ($this->logLines as $line)
{{ $line }}
@endforeach
</pre>
<div class="flex gap-2">
@if ($this->hasMoreLogs())
<x-forms.button wire:click.prevent="loadMoreLogs" isHighlighted>
Load More
</x-forms.button>
@endif
</div>
</div>
@else
<div>No output was recorded for this execution.</div>

View File

@@ -16,6 +16,11 @@
<x-forms.button type="submit">
Save
</x-forms.button>
@if ($resource->isRunning())
<x-forms.button type="button" wire:click="executeNow">
Execute Now
</x-forms.button>
@endif
<x-modal-confirmation title="Confirm Scheduled Task Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete({{ $task->id }})" :actions="['The selected scheduled task will be permanently deleted.']" confirmationText="{{ $task->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Scheduled Task Name below"
@@ -24,27 +29,26 @@
</div>
<div class="w-48">
<x-forms.checkbox instantSave id="task.enabled" label="Enabled" />
<x-forms.checkbox instantSave id="isEnabled" label="Enabled" />
</div>
<div class="flex gap-2 w-full">
<x-forms.input placeholder="Name" id="task.name" label="Name" required />
<x-forms.input placeholder="php artisan schedule:run" id="task.command" label="Command" required />
<x-forms.input placeholder="0 0 * * * or daily" id="task.frequency" label="Frequency" required />
<x-forms.input placeholder="Name" id="name" label="Name" required />
<x-forms.input placeholder="php artisan schedule:run" id="command" label="Command" required />
<x-forms.input placeholder="0 0 * * * or daily" id="frequency" label="Frequency" required />
@if ($type === 'application')
<x-forms.input placeholder="php"
helper="You can leave this empty if your resource only has one container." id="task.container"
helper="You can leave this empty if your resource only has one container." id="container"
label="Container name" />
@elseif ($type === 'service')
<x-forms.input placeholder="php"
helper="You can leave this empty if your resource only has one service in your stack. Otherwise use the stack name, without the random generated ID. So if you have a mysql service in your stack, use mysql."
id="task.container" label="Service name" />
id="container" label="Service name" />
@endif
</div>
</form>
<div class="pt-4">
<h3 class="py-4">Recent executions <span class="text-xs text-neutral-500">(click to check output)</span></h3>
<livewire:project.shared.scheduled-task.executions :task="$task" key="{{ $task->id }}" selectedKey=""
:executions="$task->executions->take(20)" />
<livewire:project.shared.scheduled-task.executions :taskId="$task->id" />
</div>
</div>

View File

@@ -1,13 +1,15 @@
<div class="flex flex-col w-full gap-2 rounded max-h-[80vh] overflow-y-auto scrollbar">
<div class="p-4">
You can add Volumes, Files and Directories to your resources here.
<form class="flex flex-col w-full gap-2 rounded" wire:submit='submitPersistentVolume'>
<div class="flex flex-col w-full gap-2 max-h-[80vh] overflow-y-auto scrollbar">
<form class="flex flex-col w-full gap-2 rounded " wire:submit='submitPersistentVolume'>
<div class="flex flex-col">
<h3>Volume Mount</h3>
@if ($isSwarm)
<h5>Swarm Mode detected: You need to set a shared volume (EFS/NFS/etc) on all the worker nodes if you
would
like to use a persistent volumes.</h5>
@endif
<div>Docker Volumes mounted to the container.</div>
</div>
@if ($isSwarm)
<h5>Swarm Mode detected: You need to set a shared volume (EFS/NFS/etc) on all the worker nodes if you
would
like to use a persistent volumes.</h5>
@endif
<div class="flex flex-col gap-2 px-2">
<x-forms.input placeholder="pv-name" id="name" label="Name" required helper="Volume name." />
@if ($isSwarm)
<x-forms.input placeholder="/root" id="host_path" label="Source Path" required
@@ -19,29 +21,39 @@
<x-forms.input placeholder="/tmp/root" id="mount_path" label="Destination Path" required
helper="Directory inside the container." />
<x-forms.button type="submit" @click="modalOpen=false">
Save
Add
</x-forms.button>
</div>
</form>
<form class="flex flex-col w-full gap-2 rounded" wire:submit='submitFileStorage'>
</form>
<form class="flex flex-col w-full gap-2 rounded py-4" wire:submit='submitFileStorage'>
<div class="flex flex-col">
<h3>File Mount</h3>
<div>Actual file mounted from the host system to the container.</div>
</div>
<div class="flex flex-col gap-2 px-2">
<x-forms.input placeholder="/etc/nginx/nginx.conf" id="file_storage_path" label="Destination Path" required
helper="File inside the container" />
helper="File location inside the container" />
<x-forms.textarea label="Content" id="file_storage_content"></x-forms.textarea>
<x-forms.button type="submit" @click="modalOpen=false">
Save
Add
</x-forms.button>
</form>
<form class="flex flex-col w-full gap-2 rounded" wire:submit='submitFileStorageDirectory'>
</div>
</form>
<form class="flex flex-col w-full gap-2 rounded" wire:submit='submitFileStorageDirectory'>
<div class="flex flex-col">
<h3>Directory Mount</h3>
<div>Directory mounted from the host system to the container.</div>
</div>
<div class="flex flex-col gap-2 px-2">
<x-forms.input placeholder="{{ application_configuration_dir() }}/{{ $resource->uuid }}/etc/nginx"
id="file_storage_directory_source" label="Source Directory" required
helper="Directory on the host system." />
<x-forms.input placeholder="/etc/nginx" id="file_storage_directory_destination"
label="Destination Directory" required helper="Directory inside the container." />
<x-forms.button type="submit" @click="modalOpen=false">
Save
Add
</x-forms.button>
</form>
</div>
</div>
</form>
</div>

View File

@@ -26,9 +26,9 @@
<script>
// expose terminal config to the terminal.js file
window.terminalConfig = {
protocol: "{{ env('TERMINAL_PROTOCOL') }}",
host: "{{ env('TERMINAL_HOST') }}",
port: "{{ env('TERMINAL_PORT') }}"
protocol: "{{ config('constants.terminal.protocol') }}",
host: "{{ config('constants.terminal.host') }}",
port: "{{ config('constants.terminal.port') }}"
}
</script>
@endscript

Some files were not shown because too many files have changed in this diff Show More