Merge branch 'next' into feat/deployment-token

This commit is contained in:
Andras Bacsai
2024-12-09 09:16:59 +01:00
449 changed files with 21219 additions and 5891 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;
@@ -172,10 +178,6 @@ section {
@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;
}
@@ -197,7 +199,7 @@ section {
}
.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 {
@@ -320,4 +322,4 @@ section {
.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

@@ -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,18 +5,20 @@
'disabled' => false,
'instantSave' => false,
'value' => null,
'domValue' => null,
'checked' => false,
'hideLabel' => 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', 'opacity-40' => $disabled])>
<span class="flex gap-2">
<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
@@ -26,12 +28,12 @@
<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
@if ($domValue) value="{{ $domValue }}" @endif
/>
@if ($checked) checked @endif
wire:model={{ $id }} @else wire:model={{ $value ?? $id }} @endif />
@if (!$hideLabel)
</label>
@endif
</div>

View File

@@ -28,7 +28,7 @@
$disableTwoStepConfirmation = data_get(InstanceSettings::get(), 'disable_two_step_confirmation');
@endphp
<div x-data="{
<div wire:ignore x-data="{
modalOpen: false,
step: {{ empty($checkboxes) ? 2 : 1 }},
initialStep: {{ empty($checkboxes) ? 2 : 1 }},
@@ -106,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">
@@ -302,7 +302,8 @@
</x-forms.button>
@endif
<x-forms.button
x-bind:disabled="!disableTwoStepConfirmation && confirmWithText && userConfirmationText !== confirmationText"
x-bind:disabled="!disableTwoStepConfirmation && confirmWithText && userConfirmationText !==
confirmationText"
class="w-auto" isError
@click="
if (dispatchEvent) {
@@ -337,11 +338,14 @@
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">
<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>
<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

View File

@@ -7,9 +7,10 @@
'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 }}
@@ -27,7 +28,7 @@
@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"
@@ -40,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

@@ -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,6 +50,30 @@
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-2">
@@ -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.index') }}" wire:navigate>
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"

View File

@@ -1,29 +1,31 @@
<div class="flex flex-col items-start gap-2 min-w-fit">
<a class="menu-item {{ $activeMenu === 'general' ? 'menu-item-active' : '' }}"
href="{{ route('server.show', ['server_uuid' => $server->uuid]) }}" wire:navigate>General</a>
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]) }}" wire:navigate>Advanced
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]) }}" wire:navigate>Private Key
href="{{ route('server.private-key', ['server_uuid' => $server->uuid]) }}">Private Key
</a>
@if ($server->isFunctional())
@if (!$server->isLocalhost())
<a class="menu-item {{ $activeMenu === 'cloudflare-tunnels' ? 'menu-item-active' : '' }}"
href="{{ route('server.cloudflare-tunnels', ['server_uuid' => $server->uuid]) }}" wire:navigate>Cloudflare
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]) }}" wire:navigate>Destinations
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]) }}" wire:navigate>Log
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]) }}" wire:navigate>Danger</a>
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

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

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

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

@@ -12,7 +12,7 @@
@if ($foundUsers->count() > 0)
<div class="flex flex-wrap gap-2 pt-4">
@foreach ($foundUsers as $user)
<div class="box w-64 group">
<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>

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

@@ -16,8 +16,7 @@
@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')]) }}"
wire:navigate>
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>
@@ -26,8 +25,7 @@
@endif
@if ($destination->getMorphClass() === 'App\Models\SwarmDocker')
<a class="box group"
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}"
wire:navigate>
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>

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

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

@@ -17,7 +17,7 @@
@endif
</div>
<div class="w-32">
<x-forms.checkbox instantSave id="discordEnabled" 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

View File

@@ -9,115 +9,110 @@
<x-forms.button type="submit">
Save
</x-forms.button>
@if (isInstanceAdmin() && !$team->use_instance_email_settings)
@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">
<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>
@endif
</div>
</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)" />
@if (!isCloud())
<div class="w-96">
<x-forms.checkbox instantSave="instantSaveInstance" id="useInstanceEmailSettings"
label="Use system wide (transactional) email settings" />
</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" />
@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>
@endif
</form>
@if (isCloud())
<div class="w-64 py-4">
<x-forms.checkbox instantSave="instantSaveInstance" 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='submit' 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 instantSave="instantSaveSmtpEnabled" 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='submit' 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 instantSave='instantSaveResend' 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>
</div>
</form>
</div>
@endif
@if (isEmailEnabled($team) || data_get($team, 'use_instance_email_settings'))
@if (isEmailEnabled($team) || $useInstanceEmailSettings)
<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" />
<x-forms.checkbox instantSave="saveModel" id="smtpNotificationsTest" label="Test" />
@endif
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_status_changes"
<x-forms.checkbox instantSave="saveModel" id="smtpNotificationsStatusChanges"
label="Container Status Changes" />
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_deployments"
<x-forms.checkbox instantSave="saveModel" id="smtpNotificationsDeployments"
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"
<x-forms.checkbox instantSave="saveModel" id="smtpNotificationsDatabaseBackups" label="Backup Status" />
<x-forms.checkbox instantSave="saveModel" id="smtpNotificationsScheduledTasks"
label="Scheduled Tasks Status" />
<x-forms.checkbox instantSave="saveModel" id="team.smtp_notifications_server_disk_usage"
label="Server Disk Usage" />
<x-forms.checkbox instantSave="saveModel" id="smtpNotificationsServerDiskUsage" label="Server Disk Usage" />
</div>
@endif
</div>

View File

@@ -0,0 +1,43 @@
<div>
<x-slot:title>
Notifications | Coolify
</x-slot>
<x-notification.navbar />
<form wire:submit='submit' class="flex flex-col gap-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 Notifications
</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>
@if ($slackEnabled)
<h2 class="mt-4">Subscribe to events</h2>
<div class="w-64">
@if (isDev())
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsTest" label="Test" />
@endif
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsStatusChanges"
label="Container Status Changes" />
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsDeployments"
label="Application Deployments" />
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsDatabaseBackups" label="Backup Status" />
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsScheduledTasks"
label="Scheduled Tasks Status" />
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsServerDiskUsage"
label="Server Disk Usage" />
</div>
@endif
</div>

View File

@@ -9,7 +9,7 @@
<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
@@ -17,64 +17,61 @@
@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" 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" />
<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="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'))
@if ($telegramEnabled)
<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.checkbox instantSave="saveModel" id="telegramNotificationsTest" 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" />
id="telegramNotificationsTestMessageThreadId" label="Custom Topic ID" />
</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.checkbox instantSave="saveModel" id="telegramNotificationsStatusChanges" 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" />
id="telegramNotificationsStatusChangesMessageThreadId" label="Custom Topic ID" />
</div>
<div class="flex flex-col">
<h4>Application Deployments</h4>
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_deployments"
label="Enabled" />
<x-forms.checkbox instantSave="saveModel" id="telegramNotificationsDeployments" 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" />
id="telegramNotificationsDeploymentsMessageThreadId" label="Custom Topic ID" />
</div>
<div class="flex flex-col">
<h4>Database Backup Status</h4>
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_database_backups"
<x-forms.checkbox instantSave="saveModel" id="telegramNotificationsDatabaseBackups"
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" />
id="telegramNotificationsDatabaseBackupsMessageThreadId" label="Custom 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.checkbox instantSave="saveModel" id="telegramNotificationsScheduledTasks"
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" />
id="telegramNotificationsScheduledTasksMessageThreadId" label="Custom Topic ID" />
</div>
<div class="flex flex-col">
<h4>Server Disk Usage</h4>
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_server_disk_usage"
<x-forms.checkbox instantSave="saveModel" id="telegramNotificationsServerDiskUsage"
label="Enabled" />
</div>
</div>

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

@@ -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 need to add both the www and non-www A DNS records pointing to your server.">
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

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

@@ -43,7 +43,7 @@
<h4 class="py-2 ">Select another Private Key</h4>
<div class="flex flex-wrap gap-2">
@foreach ($privateKeys as $key)
<x-forms.button wire:click.defer="setPrivateKey('{{ $key->id }}')">{{ $key->name }}
<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>
@@ -37,40 +37,40 @@
<div class="flex gap-2 flex-col ">
@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

@@ -9,7 +9,8 @@
<div class="flex gap-2">
<x-forms.input label="Name" id="database.name" />
<x-forms.input label="Description" id="database.description" />
<x-forms.input label="Image" id="database.image" required helper="For all available images, check here:<br><br><a target='_blank' href='https://hub.docker.com/_/redis'>https://hub.docker.com/_/redis</a>" />
<x-forms.input label="Image" id="database.image" required
helper="For all available images, check here:<br><br><a target='_blank' href='https://hub.docker.com/_/redis'>https://hub.docker.com/_/redis</a>" />
</div>
<div class="flex flex-col gap-2">
@if (version_compare($redis_version, '6.0', '>='))
@@ -36,31 +37,49 @@
<div class="flex flex-col gap-2">
<h3 class="py-2">Network</h3>
<div class="flex items-end gap-2">
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings" helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings"
helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
</div>
<x-forms.input label="Redis URL (internal)" helper="If you change the user/password/port, this could be different. This is with the default values." type="password" readonly wire:model="db_url" />
<x-forms.input label="Redis URL (internal)"
helper="If you change the user/password/port, this could be different. This is with the default values."
type="password" readonly wire:model="db_url" />
@if ($db_url_public)
<x-forms.input label="Redis URL (public)" helper="If you change the user/password/port, this could be different. This is with the default values." type="password" readonly wire:model="db_url_public" />
<x-forms.input label="Redis URL (public)"
helper="If you change the user/password/port, this could be different. This is with the default values."
type="password" readonly wire:model="db_url_public" />
@endif
</div>
<div>
<h3 class="py-2">Proxy</h3>
<div class="flex items-end gap-2">
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}" id="database.public_port" label="Public Port" />
<x-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>" label="Custom Redis Configuration" rows="10" id="database.redis_conf" />
<x-forms.textarea
helper="<a target='_blank' class='underline dark:text-white' href='https://raw.githubusercontent.com/redis/redis/7.2/redis.conf'>Redis Default Configuration</a>"
label="Custom Redis Configuration" rows="10" id="database.redis_conf" />
<h3 class="pt-4">Advanced</h3>
<div class="flex flex-col">
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings." instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" />
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" />
</div>
</form>

View File

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

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-data="{ isSticky: false }" 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
@@ -166,7 +169,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 +209,7 @@
}
}
</script>
@endif
@endif
</div>
@if ($current_step === 'servers')
<h2>Select a server</h2>
@@ -270,7 +274,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

@@ -44,17 +44,108 @@
</nav>
</div>
@if ($environment->isEmpty())
<a href="{{ route('project.resource.create', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_name' => data_get($parameters, '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

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

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

@@ -26,8 +26,8 @@
@else
@if (count($containers) > 0)
@if (count($containers) === 1)
<form class="w-full pt-4"
wire:submit="$dispatchSelf('connectToContainer')" wire:init="$dispatchSelf('connectToContainer')">
<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

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

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

View File

@@ -12,7 +12,7 @@
</x-forms.button>
</form>
</x-modal-input>
<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 class="text-xs truncate subtitle lg:text-sm">{{ $project->name }}.</div>
<div class="grid gap-2 lg:grid-cols-2">

View File

@@ -35,10 +35,15 @@
@endif
<h4>Token Permissions</h4>
<div class="w-64">
<x-forms.checkbox label="read" wire:model.live="permissions" domValue="read" :checked="in_array('read', $permissions)"></x-forms.checkbox>
<x-forms.checkbox label="read:sensitive" wire:model.live="permissions" domValue="read:sensitive" helper="Responses will include secrets, logs, passwords, and compose file contents" :checked="in_array('read:sensitive', $permissions)"></x-forms.checkbox>
<x-forms.checkbox label="write" wire:model.live="permissions" domValue="write" helper="Root access, be careful!" :checked="in_array('write', $permissions)"></x-forms.checkbox>
<x-forms.checkbox label="deploy" wire:model.live="permissions" domValue="deploy" helper="Can trigger deploy webhooks" :checked="in_array('deploy', $permissions)"></x-forms.checkbox>
<x-forms.checkbox label="read" wire:model.live="permissions" value="read"
:checked="in_array('read', $permissions)"></x-forms.checkbox>
<x-forms.checkbox label="read:sensitive" wire:model.live="permissions" value="read:sensitive"
helper="Responses will include secrets, logs, passwords, and compose file contents"
:checked="in_array('read:sensitive', $permissions)"></x-forms.checkbox>
<x-forms.checkbox label="write" wire:model.live="permissions" value="write"
helper="Root access, be careful!" :checked="in_array('write', $permissions)"></x-forms.checkbox>
<x-forms.checkbox label="deploy" wire:model.live="permissions" value="deploy"
helper="Can trigger deploy webhooks" :checked="in_array('deploy', $permissions)"></x-forms.checkbox>
</div>
</form>
@if (session()->has('token'))

View File

@@ -12,8 +12,7 @@
<x-helper class="inline-flex"
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all SSH requests to your server through Cloudflare.<br> You then can close your server's SSH port in the firewall of your hosting provider.<br><span class='dark:text-warning'>If you choose manual configuration, Coolify does not install or set up Cloudflare (cloudflared) on your server.</span>" />
</div>
<div>Secure your servers with Cloudflare Tunnels</div>
<div>Secure your servers with Cloudflare Tunnels.</div>
</div>
<div class="flex flex-col gap-2 pt-6">
@if ($isCloudflareTunnelsEnabled)

View File

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

View File

@@ -11,7 +11,7 @@
<h2>Log Drains</h2>
<x-loading wire:target="instantSave" wire:loading.delay />
</div>
<div class="">Sends service logs to 3rd party tools.</div>
<div>Sends service logs to 3rd party tools.</div>
<div class="flex flex-col gap-4 pt-4">
<div class="p-4 border dark:border-coolgray-300">
<form wire:submit='submit("newrelic")' class="flex flex-col">

View File

@@ -27,6 +27,13 @@
helper="If set, all resources will only have docker container labels for {{ str($server->proxyType())->title() }}.<br>For applications, labels needs to be regenerated manually. <br>Resources needs to be restarted."
id="server.settings.generate_exact_labels"
label="Generate labels only for {{ str($server->proxyType())->title() }}" instantSave />
<x-forms.checkbox instantSave="instantSaveRedirect" id="redirect_enabled"
label="Override default request handler"
helper="Requests to unknown hosts or stopped services will recieve a 503 response or be redirected to the URL you set below (need to enable this first)." />
@if ($redirect_enabled)
<x-forms.input placeholder="https://app.coolify.io" id="redirect_url"
label="Redirect to (optional)" />
@endif
</div>
@if ($server->proxyType() === ProxyTypes::TRAEFIK->value)
<h4>Traefik</h4>
@@ -40,8 +47,6 @@
configurations.
</div>
@endif
<x-forms.input placeholder="https://app.coolify.io" id="redirect_url" label="Default Redirect 404"
helper="All urls that has no service available will be redirected to this domain." />
<div wire:loading wire:target="loadProxyConfiguration" class="pt-4">
<x-loading text="Loading proxy configuration..." />
</div>

View File

@@ -29,7 +29,8 @@
@if (str_replace('|', '.', $fileName) === 'coolify.yaml' ||
str_replace('|', '.', $fileName) === 'Caddyfile' ||
str_replace('|', '.', $fileName) === 'coolify.caddy' ||
str_replace('|', '.', $fileName) === 'default_redirect_404.caddy')
str_replace('|', '.', $fileName) === 'default_redirect_503.yaml' ||
str_replace('|', '.', $fileName) === 'default_redirect_503.caddy')
<div>
<h3 class="dark:text-white">File: {{ str_replace('|', '.', $fileName) }}</h3>
</div>

View File

@@ -71,8 +71,7 @@
<x-forms.input id="name" label="Name" required />
<x-forms.input id="description" label="Description" />
@if (!$isSwarmWorker && !$isBuildServer)
<x-forms.input placeholder="https://example.com" id="wildcard_domain"
label="Wildcard Domain"
<x-forms.input placeholder="https://example.com" id="wildcardDomain" label="Wildcard Domain"
helper='A wildcard domain allows you to receive a randomly generated domain for your new applications. <br><br>For instance, if you set "https://example.com" as your wildcard domain, your applications will receive domains like "https://randomId.example.com".' />
@endif
@@ -89,7 +88,7 @@
<div class="w-full" x-data="{
open: false,
search: '{{ $serverTimezone ?: '' }}',
timezones: @js($timezones),
timezones: @js($this->timezones),
placeholder: '{{ $serverTimezone ? 'Search timezone...' : 'Select Server Timezone' }}',
init() {
this.$watch('search', value => {
@@ -111,8 +110,7 @@
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" x-model="search"
@focus="open = true" @click.away="open = false" @input="open = true"
class="w-full input" :placeholder="placeholder"
wire:model.debounce.300ms="serverTimezone">
class="w-full input" :placeholder="placeholder" wire:model="serverTimezone">
<svg class="absolute right-0 mr-2 w-4 h-4" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
@click="open = true">
@@ -125,7 +123,7 @@
<template
x-for="timezone in timezones.filter(tz => tz.toLowerCase().includes(search.toLowerCase()))"
:key="timezone">
<div @click="search = timezone; open = false; $wire.set('server.settings.server_timezone', timezone)"
<div @click="search = timezone; open = false; $wire.set('serverTimezone', timezone); $wire.submit()"
class="px-4 py-2 text-gray-800 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 dark:text-gray-200"
x-text="timezone"></div>
</template>
@@ -221,12 +219,12 @@
<div class="flex flex-wrap gap-2 sm:flex-nowrap">
<x-forms.input id="sentinelMetricsRefreshRateSeconds"
label="Metrics rate (seconds)" required
helper="The interval for gathering metrics. Lower means more disk space will be used." />
helper="Interval used for gathering metrics. Lower values result in more disk space usage." />
<x-forms.input id="sentinelMetricsHistoryDays" label="Metrics history (days)"
required helper="How many days should the metrics data should be reserved." />
required helper="Number of days to retain metrics data for." />
<x-forms.input id="sentinelPushIntervalSeconds" label="Push interval (seconds)"
required
helper="How many seconds should the metrics data should be pushed to the collector." />
helper="Interval at which metrics data is sent to the collector." />
</div>
</div>
@endif

View File

@@ -86,16 +86,24 @@
</g>
</svg></div>
@isset($docker_version)
@if($docker_version)
<div class="flex w-64 gap-2">Minimum Docker version: <svg class="w-5 h-5 text-success"
viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<g fill="currentColor">
<path
d="m237.66 85.26l-128.4 128.4a8 8 0 0 1-11.32 0l-71.6-72a8 8 0 0 1 0-11.31l24-24a8 8 0 0 1 11.32 0l36.68 35.32a8 8 0 0 0 11.32 0l92.68-91.32a8 8 0 0 1 11.32 0l24 23.6a8 8 0 0 1 0 11.31"
opacity=".2" />
<path
d="m243.28 68.24l-24-23.56a16 16 0 0 0-22.58 0L104 136l-.11-.11l-36.64-35.27a16 16 0 0 0-22.57.06l-24 24a16 16 0 0 0 0 22.61l71.62 72a16 16 0 0 0 22.63 0l128.4-128.38a16 16 0 0 0-.05-22.67M103.62 208L32 136l24-24l.11.11l36.64 35.27a16 16 0 0 0 22.52 0L208.06 56L232 79.6Z" />
</g>
</svg></div>
@else
<div class="flex w-64 gap-2">Minimum Docker version: <svg class="w-5 h-5 text-error"
viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<g fill="currentColor">
<path
d="m237.66 85.26l-128.4 128.4a8 8 0 0 1-11.32 0l-71.6-72a8 8 0 0 1 0-11.31l24-24a8 8 0 0 1 11.32 0l36.68 35.32a8 8 0 0 0 11.32 0l92.68-91.32a8 8 0 0 1 11.32 0l24 23.6a8 8 0 0 1 0 11.31"
opacity=".2" />
<path
d="m243.28 68.24l-24-23.56a16 16 0 0 0-22.58 0L104 136l-.11-.11l-36.64-35.27a16 16 0 0 0-22.57.06l-24 24a16 16 0 0 0 0 22.61l71.62 72a16 16 0 0 0 22.63 0l128.4-128.38a16 16 0 0 0-.05-22.67M103.62 208L32 136l24-24l.11.11l36.64 35.27a16 16 0 0 0 22.52 0L208.06 56L232 79.6Z" />
</g>
<path fill="currentColor"
d="M208.49 191.51a12 12 0 0 1-17 17L128 145l-63.51 63.49a12 12 0 0 1-17-17L111 128L47.51 64.49a12 12 0 0 1 17-17L128 111l63.51-63.52a12 12 0 0 1 17 17L145 128Z" />
</svg></div>
@endif
@else
<div class="w-64"><x-loading text="Minimum Docker version:" /></div>
@endisset

View File

@@ -33,8 +33,11 @@
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input required id="smtpHost" placeholder="smtp.mailgun.org" label="Host" />
<x-forms.input required id="smtpPort" placeholder="587" label="Port" />
<x-forms.input id="smtpEncryption" helper="If SMTP uses SSL, set it to 'tls'." placeholder="tls"
label="Encryption" />
<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="smtpUsername" label="SMTP Username" />

View File

@@ -23,7 +23,7 @@
<div class="w-full" x-data="{
open: false,
search: '{{ $settings->instance_timezone ?: '' }}',
timezones: @js($timezones),
timezones: @js($this->timezones),
placeholder: '{{ $settings->instance_timezone ? 'Search timezone...' : 'Select Server Timezone' }}',
init() {
this.$watch('search', value => {
@@ -40,14 +40,13 @@
helper="Timezone for the Coolify instance. This is used for the update check and automatic update frequency." />
</div>
<div class="relative">
<div class="inline-flex items-center relative w-full">
<div class="inline-flex relative items-center w-full">
<input autocomplete="off"
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" x-model="search"
@focus="open = true" @click.away="open = false" @input="open = true"
class="w-full input " :placeholder="placeholder"
wire:model.debounce.300ms="instance_timezone">
<svg class="absolute right-0 w-4 h-4 mr-2" xmlns="http://www.w3.org/2000/svg"
class="w-full input" :placeholder="placeholder" wire:model="instance_timezone">
<svg class="absolute right-0 mr-2 w-4 h-4" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
@click="open = true">
<path stroke-linecap="round" stroke-linejoin="round"
@@ -55,26 +54,25 @@
</svg>
</div>
<div x-show="open"
class="absolute z-50 w-full mt-1 bg-white dark:bg-coolgray-100 border dark:border-coolgray-200 rounded-md shadow-lg max-h-60 overflow-auto scrollbar overflow-x-hidden">
class="overflow-auto overflow-x-hidden absolute z-50 mt-1 w-full max-h-60 bg-white rounded-md border shadow-lg dark:bg-coolgray-100 dark:border-coolgray-200 scrollbar">
<template
x-for="timezone in timezones.filter(tz => tz.toLowerCase().includes(search.toLowerCase()))"
:key="timezone">
<div @click="search = timezone; open = false; $wire.set('instance_timezone', timezone)"
class="px-4 py-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 text-gray-800 dark:text-gray-200"
<div @click="search = timezone; open = false; $wire.set('instance_timezone', timezone); $wire.submit()"
class="px-4 py-2 text-gray-800 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 dark:text-gray-200"
x-text="timezone"></div>
</template>
</div>
</div>
</div>
</div>
<div class="flex gap-2">
<div class="flex gap-2 md:flex-row flex-col w-full">
<x-forms.input id="public_ipv4" type="password" label="Instance's IPv4"
helper="Enter the IPv4 address of the instance.<br><br>It is useful if you have several IPv4 addresses and Coolify could not detect the correct one."
placeholder="1.2.3.4" />
placeholder="1.2.3.4" autocomplete="new-password" />
<x-forms.input id="public_ipv6" type="password" label="Instance's IPv6"
helper="Enter the IPv6 address of the instance.<br><br>It is useful if you have several IPv6 addresses and Coolify could not detect the correct one."
placeholder="2001:db8::1" />
placeholder="2001:db8::1" autocomplete="new-password" />
</div>
<h4 class="w-full pt-6">DNS Validation</h4>
<div class="md:w-96">
@@ -92,24 +90,20 @@
</div>
<h4 class="pt-6">API</h4>
<div class="pb-4">For API documentation, please visit <a class="dark:text-warning underline"
href="/docs/api">/docs/api</a></div>
<div class="md:w-96 pb-2">
<x-forms.checkbox instantSave id="is_api_enabled" label="Enabled" />
</div>
<x-forms.input id="allowed_ips" label="Allowed IPs"
helper="Allowed IP lists for the API. A comma separated list of IPs. Empty means you allow from everywhere."
placeholder="1.1.1.1,8.8.8.8" />
<h4 class="pt-6">Advanced</h4>
<div class="text-right md:w-96">
<x-forms.checkbox instantSave id="is_registration_enabled" label="Registration Allowed" />
<x-forms.checkbox instantSave id="do_not_track" label="Do Not Track" />
</div>
<h4 class="pt-6">Update</h4>
<div class="text-right md:w-96">
@if (!is_null(env('AUTOUPDATE', null)))
@if (!is_null(config('constants.coolify.autoupdate', null)))
<div class="text-right md:w-96">
<x-forms.checkbox instantSave helper="AUTOUPDATE is set in .env file, you need to modify it there."
disabled id="is_auto_update_enabled" label="Enabled" />
disabled checked="{{ config('constants.coolify.autoupdate') }}" label="Auto Update Enabled" />
</div>
@else
<x-forms.checkbox instantSave id="is_auto_update_enabled" label="Auto Update Enabled" />
@@ -123,7 +117,7 @@
<x-forms.button wire:click='checkManually'>Check Manually</x-forms.button>
</div>
@if (is_null(env('AUTOUPDATE', null)) && $is_auto_update_enabled)
@if (is_null(config('constants.coolify.autoupdate', null)) && $is_auto_update_enabled)
<x-forms.input required id="auto_update_frequency" label="Auto Update Frequency" placeholder="0 0 * * *"
helper="Cron expression for auto update frequency (automatically update coolify).<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every day at 00:00" />
@endif
@@ -135,30 +129,48 @@
<x-forms.checkbox instantSave id="do_not_track" label="Do Not Track" />
</div>
<h5 class="py-4 font-bold text-white">Confirmation Settings</h5>
@if ($disable_two_step_confirmation)
<div class="md:w-96 pb-4">
<x-forms.checkbox instantSave id="disable_two_step_confirmation" label="Disable Two Step Confirmation"
helper="When disabled, you will not need to confirm actions with a text and user password. This significantly reduces security and may lead to accidental deletions or unwanted changes. Use with extreme caution, especially on production servers." />
<h4 class="py-4">Confirmation Settings</h4>
<div x-data="{ open: false }" class="mb-32 md:w-[40rem]">
<button type="button" @click.prevent="open = !open"
class="flex items-center justify-between w-full p-4 rounded-md
dark:bg-coolgray-100 dark:hover:bg-coolgray-200
bg-gray-100 hover:bg-gray-200">
<span class="font-medium">Two-Step Confirmation Settings</span>
<svg class="w-5 h-5 transition-transform" :class="{ 'rotate-180': open }" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
<div x-show="open" x-transition class="mt-4">
@if ($disable_two_step_confirmation)
<div class="md:w-96 pb-4">
<x-forms.checkbox instantSave id="disable_two_step_confirmation"
label="Disable Two Step Confirmation"
helper="When disabled, you will not need to confirm actions with a text and user password. This significantly reduces security and may lead to accidental deletions or unwanted changes. Use with extreme caution, especially on production servers." />
</div>
@else
<div class="md:w-96 pb-4">
<x-modal-confirmation title="Disable Two Step Confirmation?"
buttonTitle="Disable Two Step Confirmation" isErrorButton
submitAction="toggleTwoStepConfirmation" :actions="[
'Tow Step confimation will be disabled globally.',
'Disabling two step confirmation reduces security (as anyone can easily delete anything).',
'The risk of accidental actions will increase.',
]"
confirmationText="DISABLE TWO STEP CONFIRMATION"
confirmationLabel="Please type the confirmation text to disable two step confirmation."
shortConfirmationLabel="Confirmation text"
step3ButtonText="Disable Two Step Confirmation" />
</div>
<div class="w-full px-4 py-2 mb-4 text-white rounded-sm border-l-4 border-red-500 bg-error">
<p class="font-bold">Warning!</p>
<p>Disabling two step confirmation reduces security (as anyone can easily delete anything) and
increases
the risk of accidental actions. This is not recommended for production servers.</p>
</div>
@endif
</div>
@else
<div class="md:w-96 pb-4">
<x-modal-confirmation title="Disable Two Step Confirmation?"
buttonTitle="Disable Two Step Confirmation" isErrorButton submitAction="toggleTwoStepConfirmation"
:actions="[
'Tow Step confimation will be disabled globally.',
'Disabling two step confirmation reduces security (as anyone can easily delete anything).',
'The risk of accidental actions will increase.',
]" confirmationText="DISABLE TWO STEP CONFIRMATION"
confirmationLabel="Please type the confirmation text to disable two step confirmation."
shortConfirmationLabel="Confirmation text" step3ButtonText="Disable Two Step Confirmation" />
</div>
<div class="p-4 mb-4 text-white border-l-4 border-red-500 bg-error md:w-[40rem] w-full mb-32">
<p class="font-bold">Warning!</p>
<p>Disabling two step confirmation reduces security (as anyone can easily delete anything) and increases
the
risk of accidental actions. This is not recommended for production servers.</p>
</div>
@endif
</div>
</form>
</div>

View File

@@ -1,28 +0,0 @@
<div>
<x-settings.navbar />
<h2>Resale License</h2>
<form wire:submit='submit' class="flex flex-col gap-2">
<div>
@if ($settings->is_resale_license_active)
<div class="text-success">License is active</div>
@else
<div class="text-error">License is not active</div>
@endif
</div>
<div class="flex gap-2">
<x-forms.input type="password" id="settings.resale_license"
placeholder="eg: BE558E91-0CC5-4AA2-B1C0-B6403C2988DD" label="License Key" />
<x-forms.input type="password" id="instance_id" label="Instance Id (do not change this)" disabled />
</div>
<div class="flex gap-2">
<x-forms.button type="submit">
Check License
</x-forms.button>
</div>
@if (session()->has('error'))
<div class="text-error">
{!! session('error') !!}
</div>
@endif
</form>
</div>

View File

@@ -58,7 +58,18 @@
@else
<div class="flex flex-col gap-2">
<div class="flex gap-2">
<x-forms.input id="github_app.name" label="App Name" disabled />
<div class="flex items-end gap-2 w-full">
<x-forms.input id="github_app.name" label="App Name" disabled />
<x-forms.button wire:click.prevent="updateGithubAppName" class="bg-coollabs">
Sync Name
</x-forms.button>
<a href="{{ $this->getGithubAppNameUpdatePath() }}">
<x-forms.button>
Rename
<x-external-link />
</x-forms.button>
</a>
</div>
<x-forms.input id="github_app.organization" label="Organization" disabled
placeholder="If empty, personal user will be used" />
</div>
@@ -254,7 +265,7 @@
const name = @js($name);
const isDev = @js(config('app.env')) ===
'local';
const devWebhook = @js(config('coolify.dev_webhook'));
const devWebhook = @js(config('constants.webhooks.dev_webhook'));
if (isDev && devWebhook) {
baseUrl = devWebhook;
}

View File

@@ -1,4 +1,6 @@
<div class="w-full">
<div class="mb-4">For more details, please visit the <a class="underline dark:text-warning"
href="https://coolify.io/docs/knowledge-base/s3" target="_blank">Coolify Docs</a>.</div>
<form class="flex flex-col gap-2" wire:submit='submit'>
<div class="flex gap-2">
<x-forms.input required label="Name" id="name" />
@@ -7,14 +9,15 @@
<x-forms.input required type="url" label="Endpoint" wire:model.blur="endpoint" />
<div class="flex gap-2">
<x-forms.input required label="Bucket" id="bucket" />
<x-forms.input required label="Region" id="region" />
<x-forms.input required helper="Region only required for AWS. Leave it as-is for other providers."
label="Region" id="region" />
</div>
<div class="flex gap-2">
<x-forms.input required type="password" label="Access Key" id="key" />
<x-forms.input required type="password" label="Secret Key" id="secret" />
</div>
<x-forms.button type="submit">
<x-forms.button class="mt-4" type="submit">
Validate Connection & Continue
</x-forms.button>
</form>

View File

@@ -21,7 +21,7 @@
</div>
@if (!$storage->is_usable)
<div class="text-red-500">Not Usable</div>
@endif
@endif
</div>
</div>
@empty

View File

@@ -45,7 +45,7 @@
</div>
</div>
<div class="pt-4">
If you have any problems, please <a class="underline dark:text-white" href="{{ config('coolify.contact') }}"
If you have any problems, please <a class="underline dark:text-white" href="{{ config('constants.urls.contact') }}"
target="_blank">contact us.</a>
</div>
@endif

View File

@@ -1,6 +1,6 @@
<x-forms.select wire:model.live="selectedTeamId" label="Current Team">
<option value="default" disabled selected>Switch team</option>
@foreach (auth()->user()->teams as $team)
<option value="{{ $team->id }}">{{ $team->name }}</option>
<option value="{{ $team->id }}">{{ $team->name }}</option>
@endforeach
</x-forms.select>

View File

@@ -1,64 +0,0 @@
<div>
<h1>Tags</h1>
<div class="flex flex-col pb-6">
<div class="subtitle">Tags help you to perform actions on multiple resources.</div>
<div class="">
@if ($tags->count() === 0)
<div>No tags yet defined yet. Go to a resource and add a tag there.</div>
@else
<x-forms.datalist wire:model="tag" onUpdate='tagUpdated'>
@foreach ($tags as $oneTag)
<option value="{{ $oneTag->name }}">{{ $oneTag->name }}</option>
@endforeach
</x-forms.datalist>
@if ($tag)
<div class="pt-5">
<div class="flex items-end gap-2 ">
<div class="w-[500px]">
<x-forms.input readonly label="Deploy Webhook URL" id="webhook" />
</div>
<x-modal-confirmation title="Redeploy all resources with this tag?" isHighlighted
buttonTitle="Redeploy All" submitAction="redeployAll" :actions="[
'All resources with this tag will be redeployed.',
'During redeploy resources will be temporarily unavailable.',
]"
confirmationText="{{ $tag }}"
confirmationLabel="Please confirm the execution of the actions by entering the Tag Name below"
shortConfirmationLabel="Tag Name" :confirmWithPassword="false" step2ButtonText="Redeploy All" />
</div>
<div class="grid grid-cols-1 gap-2 pt-4 lg:grid-cols-2 xl:grid-cols-3">
@foreach ($applications as $application)
<a href="{{ $application->link() }}"class="box group">
<div class="flex flex-col">
<div class="box-title">{{ $application->name }}</div>
<div class="box-description">
{{ $application->project()->name }}/{{ $application->environment->name }}
</div>
<div class="box-description">{{ $application->description }}</div>
</div>
</a>
@endforeach
@foreach ($services as $service)
<a href="{{ $service->link() }}" class="box group">
<div class="flex flex-col ">
<div class="box-title">{{ $service->name }}</div>
<div class="box-description">
{{ $service->project()->name }}/{{ $service->environment->name }}</div>
<div class="box-description">{{ $service->description }}</div>
</div>
</a>
@endforeach
</div>
<div class="flex items-center gap-2">
<h3 class="py-4">Deployments</h3>
@if (count($deploymentsPerTagPerServer) > 0)
<x-loading />
@endif
</div>
<livewire:tags.deployments :deploymentsPerTagPerServer="$deploymentsPerTagPerServer" :resourceIds="$applications->pluck('id')" />
</div>
@endif
@endif
</div>
</div>
</div>

View File

@@ -1,87 +1,96 @@
<div>
<div class="flex items-start gap-2">
<div class="flex items-start gap-2 pb-10">
<div>
<h1>Tags</h1>
<div>Tags help you to perform actions on multiple resources.</div>
</div>
</div>
<div class="flex flex-col gap-2 pb-6 ">
<div>Available tags</div>
<div class="flex flex-wrap gap-2 ">
@forelse ($tags as $oneTag)
<a :class="{{ $tag->id == $oneTag->id }} && 'bg-coollabs hover:bg-coollabs-100'" class="w-64 box"
href="{{ route('tags.show', ['tag_name' => $oneTag->name]) }}">{{ $oneTag->name }}</a>
@empty
<div>No tags yet defined yet. Go to a resource and add a tag there.</div>
@endforelse
</div>
<div class="flex flex-wrap gap-2 ">
@forelse ($tags as $oneTag)
<a :class="{{ $tag?->id == $oneTag->id }} && 'dark:bg-coollabs hover:bg-coollabs-100'"
class="w-64 box-without-bg dark:bg-coolgray-100 dark:text-white font-bold"
href="{{ route('tags.show', ['tagName' => $oneTag->name]) }}">{{ $oneTag->name }}</a>
@empty
<div>No tags yet defined yet. Go to a resource and add a tag there.</div>
@endforelse
</div>
<div>
<h3 class="py-4">Details</h3>
<div class="flex items-end gap-2 ">
<div class="w-[500px]">
<x-forms.input readonly label="Deploy Webhook URL" id="webhook" />
@if (isset($tag))
<div>
<h3 class="py-4">Details</h3>
<div class="flex items-end gap-2 ">
<div class="w-[500px]">
<x-forms.input readonly label="Deploy Webhook URL" id="webhook" />
</div>
<x-modal-confirmation title="Redeploy all resources with this tag?" isHighlighted
buttonTitle="Redeploy All" submitAction="redeployAll" :actions="[
'All resources with this tag will be redeployed.',
'During redeploy resources will be temporarily unavailable.',
]"
confirmationText="{{ $tag->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Tag Name below"
shortConfirmationLabel="Tag Name" :confirmWithPassword="false" step2ButtonText="Redeploy All" />
</div>
<x-modal-confirmation title="Redeploy all resources with this tag?" isHighlighted buttonTitle="Redeploy All"
submitAction="redeploy_all" :actions="[
'All resources with this tag will be redeployed.',
'During redeploy resources will be temporarily unavailable.',
]" confirmationText="{{ $tag }}"
confirmationLabel="Please confirm the execution of the actions by entering the Tag Name below"
shortConfirmationLabel="Tag Name" :confirmWithPassword="false" step2ButtonText="Redeploy All" />
</div>
<div class="grid grid-cols-1 gap-2 pt-4 lg:grid-cols-2 xl:grid-cols-3">
@foreach ($applications as $application)
<a href="{{ $application->link() }}" class="box group">
<div class="flex flex-col">
<div class="box-title">{{ $application->project()->name }}/{{ $application->environment->name }}
</div>
<div class="box-description">{{ $application->name }}</div>
<div class="box-description">{{ $application->description }}</div>
</div>
</a>
@endforeach
@foreach ($services as $service)
<a href="{{ $service->link() }}" class="flex flex-col box group">
<div class="flex flex-col">
<div class="box-title">{{ $service->project()->name }}/{{ $service->environment->name }}
</div>
<div class="box-description">{{ $service->name }}</div>
<div class="box-description">{{ $service->description }}</div>
</div>
</a>
@endforeach
</div>
<div class="flex items-center gap-2">
<h3 class="py-4">Deployments</h3>
@if (count($deployments_per_tag_per_server) > 0)
<x-loading />
@endif
</div>
<div wire:poll.1000ms="get_deployments" class="grid grid-cols-1">
@forelse ($deployments_per_tag_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',
])>
<div class="flex flex-col mx-6">
<div class="font-bold dark:text-white">
{{ data_get($deployment, 'application_name') }}
</div>
<div class="description">
{{ str(data_get($deployment, 'status'))->headline() }}
<div class="grid grid-cols-1 gap-2 pt-4 lg:grid-cols-2 xl:grid-cols-3">
@if (isset($applications) && count($applications) > 0)
@foreach ($applications as $application)
<a href="{{ $application->link() }}" class="box group">
<div class="flex flex-col">
<div class="box-title">
{{ $application->project()->name }}/{{ $application->environment->name }}
</div>
<div class="box-description">{{ $application->name }}</div>
<div class="box-description">{{ $application->description }}</div>
</div>
<div class="flex-1"></div>
</a>
@endforeach
</div>
@empty
<div>No deployments running.</div>
@endforelse
@endif
@if (isset($services) && count($services) > 0)
@foreach ($services as $service)
<a href="{{ $service->link() }}" class="flex flex-col box group">
<div class="flex flex-col">
<div class="box-title">
{{ $service->project()->name }}/{{ $service->environment->name }}
</div>
<div class="box-description">{{ $service->name }}</div>
<div class="box-description">{{ $service->description }}</div>
</div>
</a>
@endforeach
@endif
</div>
<div class="flex items-center gap-2">
<h3 class="py-4">Deployments</h3>
@if (count($deploymentsPerTagPerServer) > 0)
<x-loading />
@endif
</div>
<div wire:poll="getDeployments" class="grid grid-cols-1">
@forelse ($deploymentsPerTagPerServer as $serverName => $deployments)
<h4 class="py-4">{{ $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 mx-6">
<div class="font-bold dark:text-white">
{{ data_get($deployment, 'application_name') }}
</div>
<div class="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>
</div>
</div>
@endif
</div>

View File

@@ -1,6 +1,6 @@
<tr @class([
'dark:text-white text-black dark:bg-coolblack dark:hover:bg-coolgray-100',
'dark:bg-coolgray-100 bg-neutral-200' => $member->id == auth()->user()->id,
'dark:bg-coolgray-100 bg-neutral-200' => $member->id == Auth::id(),
])>
<td class="px-5 py-4 text-sm whitespace-nowrap">
{{ $member->name }}
@@ -12,9 +12,9 @@
{{ data_get($member, 'pivot.role') }}
</td>
<td class="flex gap-2 px-5 py-4 text-sm whitespace-nowrap">
@if (auth()->user()->isAdminFromSession())
@if ($member->id !== auth()->user()->id)
@if (auth()->user()->isOwner())
@if (Auth::user()->isAdminFromSession())
@if ($member->id !== Auth::id())
@if (Auth::user()->isOwner())
@if (data_get($member, 'pivot.role') === 'owner')
<x-forms.button wire:click="makeAdmin">To Admin</x-forms.button>
<x-forms.button wire:click="makeReadonly">To Member</x-forms.button>
@@ -30,7 +30,7 @@
<x-forms.button wire:click="makeAdmin">To Admin</x-forms.button>
<x-forms.button isError wire:click="remove">Remove</x-forms.button>
@endif
@elseif (auth()->user()->isAdmin())
@elseif (Auth::user()->isAdmin())
@if (data_get($member, 'pivot.role') === 'admin')
<x-forms.button wire:click="makeReadonly">To Member</x-forms.button>
<x-forms.button isError wire:click="remove">Remove</x-forms.button>

View File

@@ -8,27 +8,32 @@
<x-helper
helper="If you're having trouble connecting to your server, make sure that the port is open.<br><br><a class='underline' href='https://coolify.io/docs/knowledge-base/server/firewall/#terminal' target='_blank'>Documentation</a>"></x-helper>
</div>
<div>
<form class="flex flex-col gap-2 justify-center xl:items-end xl:flex-row"
wire:submit="$dispatchSelf('connectToContainer')">
<x-forms.select id="server" required wire:model.live="selected_uuid">
@foreach ($servers as $server)
@if ($loop->first)
<option disabled value="default">Select a server or container</option>
@endif
<option value="{{ $server->uuid }}">{{ $server->name }}</option>
@foreach ($containers as $container)
@if ($container['server_uuid'] == $server->uuid)
<option value="{{ $container['uuid'] }}">
{{ $server->name }} -> {{ $container['name'] }}
</option>
<div x-init="$wire.loadContainers()">
@if ($isLoadingContainers)
<div class="pt-1">
<x-loading text="Loading servers and containers..." />
</div>
@else
<form class="flex flex-col gap-2 justify-center xl:items-end xl:flex-row"
wire:submit="$dispatchSelf('connectToContainer')">
<x-forms.select id="server" required wire:model.live="selected_uuid">
@foreach ($servers as $server)
@if ($loop->first)
<option disabled value="default">Select a server or container</option>
@endif
<option value="{{ $server->uuid }}">{{ $server->name }}</option>
@foreach ($containers as $container)
@if ($container['server_uuid'] == $server->uuid)
<option value="{{ $container['uuid'] }}">
{{ $server->name }} -> {{ $container['name'] }}
</option>
@endif
@endforeach
@endforeach
@endforeach
</x-forms.select>
<x-forms.button type="submit">Connect</x-forms.button>
</form>
</x-forms.select>
<x-forms.button type="submit">Connect</x-forms.button>
</form>
@endif
<livewire:project.shared.terminal />
</div>
</div>

View File

@@ -2,31 +2,29 @@
x-init="$wire.checkUpdate" x-data="upgradeModal">
@if ($isUpgradeAvailable)
<div :class="{ 'z-40': modalOpen }" class="relative w-auto h-auto">
<button class="menu-item" @click="modalOpen=true">
@if ($showProgress)
<svg xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6 text-pink-500 transition-colors hover:text-pink-300 lds-heart" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M19.5 13.572l-7.5 7.428l-7.5 -7.428m0 0a5 5 0 1 1 7.5 -6.566a5 5 0 1 1 7.5 6.572" />
</svg>
In progress
@else
<svg xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6 text-pink-500 transition-colors hover:text-pink-300" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M9 12h-3.586a1 1 0 0 1 -.707 -1.707l6.586 -6.586a1 1 0 0 1 1.414 0l6.586 6.586a1 1 0 0 1 -.707 1.707h-3.586v3h-6v-3z" />
<path d="M9 21h6" />
<path d="M9 18h6" />
</svg>
Upgrade
@endif
<button class="menu-item" @click="modalOpen=true" x-show="showProgress">
<svg xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6 text-pink-500 transition-colors hover:text-pink-300 lds-heart" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M19.5 13.572l-7.5 7.428l-7.5 -7.428m0 0a5 5 0 1 1 7.5 -6.566a5 5 0 1 1 7.5 6.572" />
</svg>
In progress
</button>
<button class="menu-item" @click="modalOpen=true" x-show="!showProgress">
<svg xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6 text-pink-500 transition-colors hover:text-pink-300" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M9 12h-3.586a1 1 0 0 1 -.707 -1.707l6.586 -6.586a1 1 0 0 1 1.414 0l6.586 6.586a1 1 0 0 1 -.707 1.707h-3.586v3h-6v-3z" />
<path d="M9 21h6" />
<path d="M9 18h6" />
</svg>
Upgrade
</button>
<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"
@@ -45,19 +43,21 @@
class="relative w-full py-6 border rounded min-w-full lg:min-w-[36rem] max-w-fit bg-neutral-100 border-neutral-400 dark:bg-base px-7 dark:border-coolgray-300">
<div class="flex items-center justify-between pb-3">
<h3 class="text-lg font-semibold">Upgrade confirmation</h3>
@if (!$showProgress)
<button @click="modalOpen=false"
class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 text-gray-600 rounded-full hover:text-gray-800 hover:bg-gray-50">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
@endif
<button x-show="!showProgress" @click="modalOpen=false"
class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 text-gray-600 rounded-full hover:text-gray-800 hover:bg-gray-50">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="relative w-auto pb-8">
<p>Are you sure you would like to upgrade your instance to {{ $latestVersion }}?</p>
<br />
<div class="p-4 mb-4 text-yellow-800 border border-yellow-300 rounded-lg bg-yellow-50 dark:bg-yellow-900/30 dark:text-yellow-300 dark:border-yellow-800">
<p class="font-medium">Warning: Any deployments running during the update process will fail. Please ensure no deployments are in progress on any server before continuing.</p>
</div>
<p>You can review the changelogs <a class="font-bold underline dark:text-white"
href="https://github.com/coollabsio/coolify/releases" target="_blank">here</a>.</p>
<br />
@@ -65,22 +65,18 @@
<a class="font-bold underline dark:text-white" href="https://coolify.io/docs/upgrade"
target="_blank">guide</a> on what to do.
</p>
@if ($showProgress)
<div class="flex flex-col pt-4">
<h2>Progress <x-loading /></h2>
<div x-html="currentStatus"></div>
</div>
@endif
<div class="flex flex-col pt-4" x-show="showProgress">
<h2>Progress <x-loading /></h2>
<div x-html="currentStatus"></div>
</div>
</div>
<div class="flex gap-4">
@if (!$showProgress)
<x-forms.button @click="modalOpen=false"
class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel
</x-forms.button>
<div class="flex-1"></div>
<x-forms.button @click="confirmed" class="w-24" isHighlighted type="button">Continue
</x-forms.button>
@endif
<div class="flex gap-4" x-show="!showProgress">
<x-forms.button @click="modalOpen=false"
class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel
</x-forms.button>
<div class="flex-1"></div>
<x-forms.button @click="confirmed" class="w-24" isHighlighted type="button">Continue
</x-forms.button>
</div>
</div>
</div>
@@ -93,12 +89,12 @@
document.addEventListener('alpine:init', () => {
Alpine.data('upgradeModal', () => ({
modalOpen: false,
showProgress: @js($showProgress),
showProgress: false,
currentStatus: '',
confirmed() {
this.showProgress = true;
this.$wire.$call('upgrade')
this.upgrade();
this.$wire.showProgress = true;
window.addEventListener('beforeunload', (event) => {
event.preventDefault();
event.returnValue = '';

View File

@@ -4,7 +4,7 @@
</x-slot>
<div class="flex items-start gap-2">
<h1>Sources</h1>
<x-modal-input buttonTitle="+ Add" title="New GitHub App">
<x-modal-input buttonTitle="+ Add" title="New GitHub App" :closeOutside="false">
<livewire:source.github.create />
</x-modal-input>
</div>

View File

@@ -1,4 +0,0 @@
<x-layout>
<h1>New GitHub Source</h1>
</x-layout>

View File

@@ -1,22 +0,0 @@
<x-layout>
<h1>New Source</h1>
<div class="subtitle ">Add source providers for your applications.</div>
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'github' }">
{{-- <div class="flex justify-center h-full gap-2 pb-6">
<a class="flex items-center justify-center w-1/2 p-2 transition-colors rounded-none min-h-12 bg-coolgray-200 hover:bg-coollabs-100 hover:dark:text-white hover:no-underline"
:class="activeTab === 'github' && 'bg-coollabs dark:text-white'"
@click.prevent="activeTab = 'github'; window.location.hash = 'github'" href="#">GitHub
</a>
<a class="flex items-center justify-center w-1/2 p-2 transition-colors rounded-none min-h-12 bg-coolgray-200 hover:bg-coollabs-100 hover:dark:text-white hover:no-underline"
:class="activeTab === 'gitlab' && 'bg-coollabs dark:text-white'"
@click.prevent="activeTab = 'gitlab'; window.location.hash = 'gitlab'" href="#">GitLab
</a>
</div> --}}
<div x-cloak x-show="activeTab === 'github'" class="h-full">
<livewire:source.github.create />
</div>
{{-- <div x-cloak x-show="activeTab === 'gitlab'" class="h-full">
WIP
</div> --}}
</div>
</x-layout>