Merge branch 'next' into improve-cleanup

This commit is contained in:
Andras Bacsai
2024-09-27 16:40:48 +02:00
committed by GitHub
208 changed files with 3593 additions and 2772 deletions

View File

@@ -5,17 +5,13 @@
// app.component("magic-bar", MagicBar);
// app.mount("#vue");
import { Terminal } from '@xterm/xterm';
import '@xterm/xterm/css/xterm.css';
import { FitAddon } from '@xterm/addon-fit';
import { initializeTerminalComponent } from './terminal.js';
if (!window.term) {
window.term = new Terminal({
cols: 80,
rows: 30,
fontFamily: '"Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"',
cursorBlink: true,
['livewire:navigated', 'alpine:init'].forEach((event) => {
document.addEventListener(event, () => {
// tree-shaking
if (document.getElementById('terminal-container')) {
initializeTerminalComponent()
}
});
window.fitAddon = new FitAddon();
window.term.loadAddon(window.fitAddon);
}
});

228
resources/js/terminal.js Normal file
View File

@@ -0,0 +1,228 @@
import { Terminal } from '@xterm/xterm';
import '@xterm/xterm/css/xterm.css';
import { FitAddon } from '@xterm/addon-fit';
export function initializeTerminalComponent() {
function terminalData() {
return {
fullscreen: false,
terminalActive: false,
message: '(connection closed)',
term: null,
fitAddon: null,
socket: null,
commandBuffer: '',
pendingWrites: 0,
paused: false,
MAX_PENDING_WRITES: 5,
keepAliveInterval: null,
init() {
this.setupTerminal();
this.initializeWebSocket();
this.setupTerminalEventListeners();
this.$wire.on('send-back-command', (command) => {
this.socket.send(JSON.stringify({
command: command
}));
});
this.keepAliveInterval = setInterval(this.keepAlive.bind(this), 30000);
this.$watch('terminalActive', (active) => {
if (!active && this.keepAliveInterval) {
clearInterval(this.keepAliveInterval);
}
this.$nextTick(() => {
if (active) {
this.$refs.terminalWrapper.style.display = 'block';
this.resizeTerminal();
} else {
this.$refs.terminalWrapper.style.display = 'none';
}
});
});
['livewire:navigated', 'beforeunload'].forEach((event) => {
document.addEventListener(event, () => {
this.checkIfProcessIsRunningAndKillIt();
clearInterval(this.keepAliveInterval);
}, { once: true });
});
window.onresize = () => {
this.resizeTerminal()
};
},
setupTerminal() {
const terminalElement = document.getElementById('terminal');
if (terminalElement) {
this.term = new Terminal({
cols: 80,
rows: 30,
fontFamily: '"Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"',
cursorBlink: true,
});
this.fitAddon = new FitAddon();
this.term.loadAddon(this.fitAddon);
}
},
initializeWebSocket() {
if (!this.socket || this.socket.readyState === WebSocket.CLOSED) {
const predefined = window.terminalConfig
const connectionString = {
protocol: window.location.protocol === 'https:' ? 'wss' : 'ws',
host: window.location.hostname,
port: ":6002",
path: '/terminal/ws'
}
if (!window.location.port) {
connectionString.port = ''
}
if (predefined.host) {
connectionString.host = predefined.host
}
if (predefined.port) {
connectionString.port = `:${predefined.port}`
}
if (predefined.protocol) {
connectionString.protocol = predefined.protocol
}
const url =
`${connectionString.protocol}://${connectionString.host}${connectionString.port}${connectionString.path}`
this.socket = new WebSocket(url);
this.socket.onmessage = this.handleSocketMessage.bind(this);
this.socket.onerror = (e) => {
console.error('WebSocket error:', e);
};
this.socket.onclose = () => {
console.log('WebSocket connection closed');
};
}
},
handleSocketMessage(event) {
this.message = '(connection closed)';
if (event.data === 'pty-ready') {
if (!this.term._initialized) {
this.term.open(document.getElementById('terminal'));
this.term._initialized = true;
} else {
this.term.reset();
}
this.terminalActive = true;
this.term.focus();
document.querySelector('.xterm-viewport').classList.add('scrollbar', 'rounded');
this.resizeTerminal();
} else if (event.data === 'unprocessable') {
if (this.term) this.term.reset();
this.terminalActive = false;
this.message = '(sorry, something went wrong, please try again)';
} else {
this.pendingWrites++;
this.term.write(event.data, this.flowControlCallback.bind(this));
}
},
flowControlCallback() {
this.pendingWrites--;
if (this.pendingWrites > this.MAX_PENDING_WRITES && !this.paused) {
this.paused = true;
this.socket.send(JSON.stringify({ pause: true }));
} else if (this.pendingWrites <= this.MAX_PENDING_WRITES && this.paused) {
this.paused = false;
this.socket.send(JSON.stringify({ resume: true }));
}
},
setupTerminalEventListeners() {
if (!this.term) return;
this.term.onData((data) => {
this.socket.send(JSON.stringify({ message: data }));
// Handle CTRL + D or exit command
if (data === '\x04' || (data === '\r' && this.stripAnsiCommands(this.commandBuffer).trim().includes('exit'))) {
this.checkIfProcessIsRunningAndKillIt();
setTimeout(() => {
this.terminalActive = false;
this.term.reset();
}, 500);
this.commandBuffer = '';
} else if (data === '\r') {
this.commandBuffer = '';
} else {
this.commandBuffer += data;
}
});
// Copy and paste functionality
this.term.attachCustomKeyEventHandler((arg) => {
if (arg.ctrlKey && arg.code === "KeyV" && arg.type === "keydown") {
navigator.clipboard.readText()
.then(text => {
this.socket.send(JSON.stringify({ message: text }));
});
return false;
}
if (arg.ctrlKey && arg.code === "KeyC" && arg.type === "keydown") {
const selection = this.term.getSelection();
if (selection) {
navigator.clipboard.writeText(selection);
return false;
}
}
return true;
});
},
stripAnsiCommands(input) {
return input.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
},
keepAlive() {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify({ ping: true }));
}
},
checkIfProcessIsRunningAndKillIt() {
if (this.socket && this.socket.readyState == WebSocket.OPEN) {
this.socket.send(JSON.stringify({ checkActive: 'force' }));
}
},
makeFullscreen() {
this.fullscreen = !this.fullscreen;
this.$nextTick(() => {
this.resizeTerminal();
});
},
resizeTerminal() {
if (!this.terminalActive || !this.term || !this.fitAddon) return;
this.fitAddon.fit();
const height = this.$refs.terminalWrapper.clientHeight;
const width = this.$refs.terminalWrapper.clientWidth;
const rows = Math.floor(height / this.term._core._renderService._charSizeService.height) - 1;
const cols = Math.floor(width / this.term._core._renderService._charSizeService.width) - 1;
const termWidth = cols;
const termHeight = rows;
this.term.resize(termWidth, termHeight);
this.socket.send(JSON.stringify({
resize: { cols: termWidth, rows: termHeight }
}));
},
};
}
window.Alpine.data('terminalData', terminalData);
}

View File

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

View File

@@ -3,7 +3,7 @@
'w-full' => !$isMultiline,
])>
@if ($label)
<label class="flex items-center gap-1 mb-1 text-sm font-medium">{{ $label }}
<label class="flex gap-1 items-center mb-1 text-sm font-medium">{{ $label }}
@if ($required)
<x-highlighted text="*" />
@endif
@@ -16,7 +16,7 @@
<div class="relative" x-data="{ type: 'password' }">
@if ($allowToPeak)
<div x-on:click="changePasswordFieldType"
class="absolute inset-y-0 right-0 flex items-center pr-2 cursor-pointer hover:dark:text-white">
class="flex absolute inset-y-0 right-0 items-center pr-2 cursor-pointer hover:dark:text-white">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
@@ -25,7 +25,8 @@
</svg>
</div>
@endif
<input value="{{ $value }}" {{ $attributes->merge(['class' => $defaultClass]) }} @required($required)
<input autocomplete="{{ $autocomplete }}" value="{{ $value }}"
{{ $attributes->merge(['class' => $defaultClass]) }} @required($required)
@if ($id !== 'null') wire:model={{ $id }} @endif
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'
wire:dirty.class="dark:focus:ring-warning dark:ring-warning" wire:loading.attr="disabled"
@@ -35,7 +36,7 @@
</div>
@else
<input @if ($value) value="{{ $value }}" @endif
<input autocomplete="{{ $autocomplete }}" @if ($value) value="{{ $value }}" @endif
{{ $attributes->merge(['class' => $defaultClass]) }} @required($required) @readonly($readonly)
@if ($id !== 'null') wire:model={{ $id }} @endif
wire:dirty.class.remove='dark:focus:ring-coolgray-300 dark:ring-coolgray-300'

View File

@@ -1,14 +1,105 @@
@props([
'title' => 'Are you sure?',
'isErrorButton' => false,
'buttonTitle' => 'REWRITE THIS BUTTON TITLE PLEASSSSEEEE',
'buttonTitle' => 'Confirm Action',
'buttonFullWidth' => false,
'customButton' => null,
'disabled' => false,
'action' => 'delete',
'submitAction' => 'delete',
'content' => null,
'checkboxes' => [],
'actions' => [],
'confirmWithText' => true,
'confirmationText' => 'Confirm Deletion',
'confirmationLabel' => 'Please confirm the execution of the actions by entering the Name below',
'shortConfirmationLabel' => 'Name',
'confirmWithPassword' => true,
'step1ButtonText' => 'Continue',
'step2ButtonText' => 'Continue',
'step3ButtonText' => 'Confirm',
'dispatchEvent' => false,
'dispatchEventType' => 'success',
'dispatchEventMessage' => '',
])
<div x-data="{ modalOpen: false }" @keydown.escape.window="modalOpen = false" :class="{ 'z-40': modalOpen }"
<div x-data="{
modalOpen: false,
step: {{ empty($checkboxes) ? 2 : 1 }},
initialStep: {{ empty($checkboxes) ? 2 : 1 }},
finalStep: {{ $confirmWithPassword ? 3 : 2 }},
deleteText: '',
password: '',
actions: @js($actions),
confirmationText: @js($confirmationText),
userConfirmationText: '',
confirmWithText: @js($confirmWithText),
confirmWithPassword: @js($confirmWithPassword),
copied: false,
submitAction: @js($submitAction),
passwordError: '',
selectedActions: @js(collect($checkboxes)->pluck('id')->filter(fn($id) => $this->$id)->values()->all()),
dispatchEvent: @js($dispatchEvent),
dispatchEventType: @js($dispatchEventType),
dispatchEventMessage: @js($dispatchEventMessage),
resetModal() {
this.step = this.initialStep;
this.deleteText = '';
this.password = '';
this.userConfirmationText = '';
this.selectedActions = @js(collect($checkboxes)->pluck('id')->filter(fn($id) => $this->$id)->values()->all());
$wire.$refresh();
},
step1ButtonText: @js($step1ButtonText),
step2ButtonText: @js($step2ButtonText),
step3ButtonText: @js($step3ButtonText),
validatePassword() {
if (this.confirmWithPassword && !this.password) {
return 'Password is required.';
}
return '';
},
submitForm() {
if (this.confirmWithPassword) {
this.passwordError = this.validatePassword();
if (this.passwordError) {
return Promise.resolve(this.passwordError);
}
}
const methodName = this.submitAction.split('(')[0];
const paramsMatch = this.submitAction.match(/\((.*?)\)/);
const params = paramsMatch ? paramsMatch[1].split(',').map(param => param.trim()) : [];
if (this.confirmWithPassword) {
params.push(this.password);
}
params.push(this.selectedActions);
return $wire[methodName](...params)
.then(result => {
if (result === true) {
return true;
} else if (typeof result === 'string') {
return result;
}
});
},
copyConfirmationText() {
navigator.clipboard.writeText(this.confirmationText);
this.copied = true;
setTimeout(() => {
this.copied = false;
}, 2000);
},
toggleAction(id) {
const index = this.selectedActions.indexOf(id);
if (index > -1) {
this.selectedActions.splice(index, 1);
} else {
this.selectedActions.push(id);
}
}
}" @keydown.escape.window="modalOpen = false; resetModal()" :class="{ 'z-40': modalOpen }"
class="relative w-auto h-auto">
@if ($customButton)
@if ($buttonFullWidth)
@@ -48,7 +139,7 @@
@endif
@else
@if ($buttonFullWidth)
<x-forms.button @click="modalOpen=true" class="flex w-full gap-2" wire:target>
<x-forms.button @click="modalOpen=true" class="flex gap-2 w-full" wire:target>
{{ $buttonTitle }}
</x-forms.button>
@else
@@ -60,11 +151,9 @@
@endif
@endif
<template x-teleport="body">
<div x-show="modalOpen"
<div x-show="modalOpen" @click.away="modalOpen = false; resetModal()"
class="fixed top-0 lg:pt-10 left-0 z-[99] flex items-start justify-center w-screen h-screen" x-cloak>
<div x-show="modalOpen" 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" @click="modalOpen=false"
<div x-show="modalOpen" @click="modalOpen = false; resetModal()"
class="absolute inset-0 w-full h-full bg-black bg-opacity-20 backdrop-blur-sm"></div>
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100"
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
@@ -72,56 +161,182 @@
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 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-2xl font-bold">{{ $title }}</h3>
{{-- <button @click="modalOpen=false"
class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 rounded-full dark:text-white hover:bg-coolgray-300">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
class="relative w-full py-6 border rounded min-w-full lg:min-w-[36rem] max-w-[48rem] bg-neutral-100 border-neutral-400 dark:bg-base px-7 dark:border-coolgray-300">
<div class="flex justify-between items-center pb-3">
<h3 class="pr-8 text-2xl font-bold">{{ $title }}</h3>
<button @click="modalOpen = false; resetModal()"
class="flex absolute top-2 right-2 justify-center items-center w-8 h-8 rounded-full dark:text-white hover:bg-coolgray-300">
<svg class="w-6 h-6" 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> --}}
</button>
</div>
<div class="relative w-auto pb-8">
{{ $slot }}
</div>
<div class="flex flex-row justify-end space-x-2">
<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>
@if ($attributes->whereStartsWith('wire:click')->first())
@if ($isErrorButton)
<x-forms.button @click="modalOpen=false" class="w-24" isError type="button"
wire:click.prevent="{{ $attributes->get('wire:click') }}">Continue
</x-forms.button>
@else
<x-forms.button @click="modalOpen=false" class="w-24" isHighlighted type="button"
wire:click.prevent="{{ $attributes->get('wire:click') }}">Continue
</x-forms.button>
@endif
@elseif ($attributes->whereStartsWith('@click')->first())
@if ($isErrorButton)
<x-forms.button class="w-24" isError type="button"
@click="modalOpen=false;{{ $attributes->get('@click') }}">Continue
</x-forms.button>
@else
<x-forms.button class="w-24" isHighlighted type="button"
@click="modalOpen=false;{{ $attributes->get('@click') }}">Continue
</x-forms.button>
@endif
@elseif ($action)
@if ($isErrorButton)
<x-forms.button @click="modalOpen=false" class="w-24" isError type="button"
wire:click.prevent="{{ $action }}">Continue
</x-forms.button>
@else
<x-forms.button @click="modalOpen=false" class="w-24" isHighlighted type="button"
wire:click.prevent="{{ $action }}">Continue
</x-forms.button>
@endif
<div class="relative pb-8 w-auto">
@if (!empty($checkboxes))
<!-- Step 1: Select actions -->
<div x-show="step === 1">
<div class="flex justify-between items-center">
<h4>Actions</h4>
</div>
@foreach ($checkboxes as $index => $checkbox)
<div class="flex justify-between items-center mb-2">
<x-forms.checkbox fullWidth :label="$checkbox['label']" :id="$checkbox['id']"
:wire:model="$checkbox['id']"
x-on:change="toggleAction('{{ $checkbox['id'] }}')" :checked="$this->{$checkbox['id']}"
x-bind:checked="selectedActions.includes('{{ $checkbox['id'] }}')" />
</div>
@endforeach
</div>
@endif
<!-- Step 2: Confirm deletion -->
<div x-show="step === 2">
<div class="p-4 mb-4 text-white border-l-4 border-red-500 bg-error" role="alert">
<p class="font-bold">Warning</p>
<p>This operation is permanent and cannot be undone. Please think again before proceeding!
</p>
</div>
<div class="mb-4">The following actions will be performed:</div>
<ul class="mb-4 space-y-2">
@foreach ($actions as $action)
<li class="flex items-center text-red-500">
<svg class="flex-shrink-0 mr-2 w-5 h-5" fill="none" stroke="currentColor"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12"></path>
</svg>
<span>{{ $action }}</span>
</li>
@endforeach
@foreach ($checkboxes as $checkbox)
<template x-if="selectedActions.includes('{{ $checkbox['id'] }}')">
<li class="flex items-center text-red-500">
<svg class="flex-shrink-0 mr-2 w-5 h-5" fill="none" stroke="currentColor"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12"></path>
</svg>
<span>{{ $checkbox['label'] }}</span>
</li>
</template>
@endforeach
</ul>
@if ($confirmWithText)
<div class="mb-4">
<h4 class="mb-2 text-lg font-semibold">Confirm Actions</h4>
<p class="mb-2 text-sm">{{ $confirmationLabel }}</p>
<div class="relative mb-2">
<input type="text" x-model="confirmationText"
class="p-2 pr-10 w-full text-black rounded cursor-text input" readonly>
<button @click="copyConfirmationText()"
class="absolute right-2 top-1/2 text-gray-500 transform -translate-y-1/2 hover:text-gray-700"
title="Copy confirmation text" x-ref="copyButton">
<template x-if="!copied">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 20 20"
fill="currentColor">
<path d="M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z" />
<path
d="M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z" />
</svg>
</template>
<template x-if="copied">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-green-500"
viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd" />
</svg>
</template>
</button>
</div>
<label for="userConfirmationText"
class="block mt-4 text-sm font-medium text-gray-700 dark:text-gray-300">
{{ $shortConfirmationLabel }}
</label>
<input type="text" x-model="userConfirmationText"
class="p-2 mt-1 w-full text-black rounded input">
</div>
@endif
</div>
<!-- Step 3: Password confirmation -->
<div x-show="step === 3 && confirmWithPassword">
<div class="p-4 mb-4 text-white border-l-4 border-red-500 bg-error" role="alert">
<p class="font-bold">Final Confirmation</p>
<p>Please enter your password to confirm this destructive action.</p>
</div>
<div class="flex flex-col gap-2 mb-4">
<label for="password-confirm"
class="block text-sm font-medium text-gray-700 dark:text-gray-300">
Your Password
</label>
<input type="password" id="password-confirm" x-model="password" class="w-full input"
placeholder="Enter your password">
<p x-show="passwordError" x-text="passwordError" class="mt-1 text-sm text-red-500"></p>
@error('password')
<p class="mt-1 text-sm text-red-500">{{ $message }}</p>
@enderror
</div>
</div>
</div>
<!-- Navigation buttons -->
<div class="flex flex-wrap gap-2 justify-between mt-4">
<template x-if="step > initialStep">
<x-forms.button @click="step--" class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">
Back
</x-forms.button>
</template>
<template x-if="step === initialStep">
<x-forms.button @click="modalOpen = false; resetModal()"
class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">
Cancel
</x-forms.button>
</template>
<template x-if="step === 1">
<x-forms.button @click="step++" class="w-auto" isError>
<span x-text="step1ButtonText"></span>
</x-forms.button>
</template>
<template x-if="step === 2">
<x-forms.button x-bind:disabled="confirmWithText && userConfirmationText !== confirmationText"
class="w-auto" isError
@click="
if (dispatchEvent) {
$wire.dispatch(dispatchEventType, dispatchEventMessage);
}
if (confirmWithPassword) {
step++;
} else {
modalOpen = false;
resetModal();
submitForm();
}">
<span x-text="step2ButtonText"></span>
</x-forms.button>
</template>
<template x-if="step === 3 && confirmWithPassword">
<x-forms.button x-bind:disabled="!password" class="w-auto" isError
@click="
if (dispatchEvent) {
$wire.dispatch(dispatchEventType, dispatchEventMessage);
}
submitForm().then((result) => {
if (result === true) {
modalOpen = false;
resetModal();
} else {
passwordError = result;
}
});
">
<span x-text="step3ButtonText"></span>
</x-forms.button>
</template>
</div>
</div>
</div>

View File

@@ -2,7 +2,9 @@
<livewire:server.proxy.modal :server="$server" />
<div class="flex items-center gap-2">
<h1>Server</h1>
<livewire:server.proxy.status :server="$server" />
@if ($server->proxySet())
<livewire:server.proxy.status :server="$server" />
@endif
</div>
<div class="subtitle">{{ data_get($server, 'name') }}.</div>
<div class="navbar-main">

View File

@@ -1,20 +1,18 @@
<div class="flex h-full pr-4">
<div class="flex flex-col w-48 gap-4 min-w-fit">
<a class="{{ request()->routeIs('server.proxy') ? 'dark:text-white' : '' }}"
href="{{ route('server.proxy', $parameters) }}">
<button>Configuration</button>
</a>
@if ($server->proxyType() !== 'NONE')
{{-- @if ($server->proxyType() === 'TRAEFIK') --}}
@if ($server->proxySet())
<div class="flex h-full pr-4">
<div class="flex flex-col w-48 gap-4 min-w-fit">
<a class="{{ request()->routeIs('server.proxy') ? 'dark:text-white' : '' }}"
href="{{ route('server.proxy', $parameters) }}">
<button>Configuration</button>
</a>
<a class="{{ request()->routeIs('server.proxy.dynamic-confs') ? 'dark:text-white' : '' }}"
href="{{ route('server.proxy.dynamic-confs', $parameters) }}">
<button>Dynamic Configurations</button>
</a>
{{-- @endif --}}
<a class="{{ request()->routeIs('server.proxy.logs') ? 'dark:text-white' : '' }}"
href="{{ route('server.proxy.logs', $parameters) }}">
<button>Logs</button>
</a>
@endif
</div>
</div>
</div>
@endif

View File

@@ -1,5 +1,5 @@
<x-emails.layout>
Your server ({{ $name }}) disabled because it is not paid! All automations and integrations are stopped.
Your server ({{ $name }}) disabled because it is not paid! All automations and integrations are stopped.
Please update your subscription to enable the server again [here](https://app.coolify.io/subsciprtions).
Please update your subscription to enable the server again [here](https://app.coolify.io/subscriptions).
</x-emails.layout>

View File

@@ -273,27 +273,23 @@
Please let me know your server details.
</x-slot:question>
<x-slot:actions>
<form wire:submit='saveServer' class="flex flex-col w-full gap-4 lg:pr-10">
<div class="flex flex-col gap-2 lg:flex-row">
<x-forms.input required placeholder="Choose a name for your Server. Could be anything."
label="Name" id="remoteServerName" wire:model="remoteServerName" />
<x-forms.input placeholder="Description, so others will know more about this."
label="Description" id="remoteServerDescription"
wire:model="remoteServerDescription" />
</div>
<div class="flex flex-col gap-2 lg:flex-row ">
<x-forms.input required placeholder="127.0.0.1" label="IP Address" id="remoteServerHost"
wire:model="remoteServerHost" />
</div>
<form wire:submit='saveServer' class="flex flex-col w-full gap-4 lg:w-96">
<x-forms.input required placeholder="Choose a name for your Server. Could be anything."
label="Name" id="remoteServerName" wire:model="remoteServerName" />
<x-forms.input placeholder="Description, so others will know more about this."
label="Description" id="remoteServerDescription"
wire:model="remoteServerDescription" />
<x-forms.input required placeholder="127.0.0.1" label="IP Address" id="remoteServerHost"
wire:model="remoteServerHost" />
<div x-data="{ showAdvanced: false }" class="flex flex-col gap-2">
<button @click="showAdvanced = !showAdvanced" type="button"
class="text-left text-sm text-gray-600 dark:text-gray-300 hover:underline">
Advanced Settings
</button>
<div x-show="showAdvanced" class="flex flex-col gap-2 lg:flex-row">
<div x-show="showAdvanced" class="flex flex-col gap-2">
<x-forms.input placeholder="Port number of your server. Default is 22." label="Port"
id="remoteServerPort" wire:model="remoteServerPort" />
<div class="w-full">
<div>
<x-forms.input placeholder="Default is root." label="User"
id="remoteServerUser" wire:model="remoteServerUser" />
<div class="text-xs text-gray-600 dark:text-gray-300">Non-root user is
@@ -303,11 +299,6 @@
</div>
</div>
</div>
<div class="lg:w-64">
<x-forms.checkbox
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.<br><span class='dark:text-warning'>Coolify does not install/setup Cloudflare (cloudflared) on your server.</span>"
id="isCloudflareTunnel" label="Cloudflare Tunnel" wire:model="isCloudflareTunnel" />
</div>
<x-forms.button type="submit">Continue</x-forms.button>
</form>
</x-slot:actions>

View File

@@ -122,9 +122,19 @@
@if (count($deployments_per_server) > 0)
<x-loading />
@endif
<x-modal-confirmation isErrorButton action="cleanup_queue" buttonTitle="Cleanup Queues">
This will clean up the deployment queue. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation
title="Confirm Cleanup Queues?"
buttonTitle="Cleanup Queues"
isErrorButton
submitAction="cleanup_queue"
:actions="['All running Deployment Queues will be cleaned up.']"
:confirmWithText="false"
:confirmWithPassword="false"
step2ButtonText="Permanently Cleanup Deployment Queues"
:dispatchEvent="true"
dispatchEventType="success"
dispatchEventMessage="Deployment Queues cleanup started."
/>
</div>
<div wire:poll.3000ms="get_deployments" class="grid grid-cols-1">
@forelse ($deployments_per_server as $server_name => $deployments)
@@ -170,4 +180,6 @@
}
</script>
{{-- <x-forms.button wire:click='getIptables'>Get IPTABLES</x-forms.button> --}}
</div>

View File

@@ -6,9 +6,10 @@
Save
</x-forms.button>
@if ($destination->network !== 'coolify')
<x-modal-confirmation isErrorButton buttonTitle="Delete Destination">
This destination will be deleted. It is not reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Destination Deletion?" buttonTitle="Delete Destination" isErrorButton
submitAction="delete" :actions="['This will delete the selected destination/network.']" confirmationText="{{ $destination->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Destination Name below"
shortConfirmationLabel="Destination Name" :confirmWithPassword="false" step2ButtonText="Permanently Delete" />
@endif
</div>

View File

@@ -1,5 +1,13 @@
<div>
<x-modal-confirmation buttonFullWidth isErrorButton buttonTitle="Delete Team">
This team be deleted. It is not reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation
title="Confirm Team Deletion?"
buttonTitle="Delete Team"
isErrorButton
submitAction="delete"
:actions="['The current Team will be permanently deleted.']"
confirmationText="{{ $team }}"
confirmationLabel="Please confirm the execution of the actions by entering the Team Name below"
shortConfirmationLabel="Team Name"
step3ButtonText="Permanently Delete"
/>
</div>

View File

@@ -68,11 +68,13 @@
<option value="www">Redirect to www.</option>
<option value="non-www">Redirect to non-www.</option>
</x-forms.select>
<x-modal-confirmation action="set_redirect">
<x-modal-confirmation title="Confirm Redirection Setting?" buttonTitle="Set Direction"
submitAction="set_redirect" :actions="['All traffic will be redirected to the selected direction.']" confirmationText="{{ $application->fqdn . '/' }}"
confirmationLabel="Please confirm the execution of the action by entering the Application URL below"
shortConfirmationLabel="Application URL" :confirmWithPassword="false" step2ButtonText="Set Direction">
<x-slot:customButton>
<div class="w-[7.2rem]">Set Direction</div>
</x-slot:customButton>
This will reset the container labels. Are you sure?
</x-modal-confirmation>
</div>
@endif
@@ -302,12 +304,15 @@
helper="If you know what are you doing, you can enable this to edit the labels directly. Coolify won't update labels automatically. <br><br>Be careful, it could break the proxy configuration after you restart the container."
id="application.settings.is_container_label_readonly_enabled" instantSave></x-forms.checkbox>
</div>
<x-modal-confirmation buttonFullWidth action="resetDefaultLabels"
buttonTitle="Reset to Coolify Generated Labels">
Are you sure you want to reset the labels to Coolify generated labels? <br>It could break the proxy
configuration after you restart the container.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Labels Reset to Coolify Defaults?"
buttonTitle="Reset Labels to Coolify Defaults" buttonFullWidth submitAction="resetDefaultLabels"
:actions="[
'All your custom proxy labels will be lost.',
'Proxy labels (traefik, caddy, etc) will be reset to the coolify defaults.',
]" confirmationText="{{ $application->fqdn . '/' }}"
confirmationLabel="Please confirm the execution of the actions by entering the Application URL below"
shortConfirmationLabel="Application URL" :confirmWithPassword="false"
step2ButtonText="Permanently Reset Labels" />
@endif
<h3 class="pt-8">Pre/Post Deployment Commands</h3>

View File

@@ -72,7 +72,13 @@
</x-forms.button>
@endif
@endif
<x-modal-confirmation @click="$wire.dispatch('stopEvent')">
<x-modal-confirmation title="Confirm Application Stopping?" buttonTitle="Stop"
submitAction="stop" :checkboxes="$checkboxes" :actions="[
'This application will be stopped.',
'All non-persistent data of this application will be deleted.',
]" :confirmWithText="false" :confirmWithPassword="false"
step1ButtonText="Continue" step2ButtonText="Confirm" :dispatchEvent="true"
dispatchEventType="stopEvent">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
@@ -87,7 +93,6 @@
</svg>
Stop
</x-slot:button-title>
This application will be stopped. <br>Please think again.
</x-modal-confirmation>
@else
<x-forms.button wire:click='deploy'>

View File

@@ -152,8 +152,15 @@
@endif
</x-forms.button>
@if (data_get($preview, 'status') !== 'exited')
<x-modal-confirmation isErrorButton
action="stop({{ data_get($preview, 'pull_request_id') }})">
<x-modal-confirmation
title="Confirm Preview Deployment Stopping?"
buttonTitle="Stop"
submitAction="stop({{ data_get($preview, 'pull_request_id') }})"
:actions="['This preview deployment will be stopped.', 'If the preview deployment is currently in use data could be lost.', 'All non-persistent data of this preview deployment (containers, networks, unused images) will be deleted (don\'t worry, no data is lost and you can start the preview deployment again).']"
:confirmWithText="false"
:confirmWithPassword="false"
step2ButtonText="Stop Preview Deployment"
>
<x-slot:customButton>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
@@ -168,14 +175,19 @@
</svg>
Stop
</x-slot:customButton>
This will stop the preview deployment. <br>Please think again.
</x-modal-confirmation>
@endif
<x-modal-confirmation isErrorButton
action="delete({{ data_get($preview, 'pull_request_id') }})" buttonTitle="Delete">
This will delete the preview deployment. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation
title="Confirm Preview Deployment Deletion?"
buttonTitle="Delete"
isErrorButton
submitAction="delete({{ data_get($preview, 'pull_request_id') }})"
:actions="['All containers of this preview deployment will be stopped and permanently deleted.']"
confirmationText="{{ data_get($preview, 'fqdn'). '/' }}"
confirmationLabel="Please confirm the execution of the actions by entering the Preview Deployment name below"
shortConfirmationLabel="Preview Deployment Name"
:confirmWithPassword="false"
/>
</div>
</div>
@endforeach

View File

@@ -8,12 +8,17 @@
<livewire:project.database.backup-now :backup="$backup" />
@endif
@if ($backup->database_id !== 0)
<x-modal-confirmation isErrorButton>
<x-slot:button-title>
Delete
</x-slot:button-title>
This will stop the scheduled backup for this database.<br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation
title="Confirm Backup Schedule Deletion?"
buttonTitle="Delete Backups and Schedule"
isErrorButton
submitAction="delete"
:checkboxes="$checkboxes"
:actions="['The selected backup schedule will be deleted.', 'Scheduled backups for this database will be stopped (if this is the only backup schedule for this database).']"
confirmationText="{{ $backup->database->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Database Name of the scheduled backups below"
shortConfirmationLabel="Database Name"
/>
@endif
</div>
<div class="w-48 pb-2">

View File

@@ -45,12 +45,20 @@
<x-forms.button class="dark:hover:bg-coolgray-400"
x-on:click="download_file('{{ data_get($execution, 'id') }}')">Download</x-forms.button>
@endif
<x-modal-confirmation isErrorButton action="deleteBackup({{ data_get($execution, 'id') }})">
<x-slot:button-title>
Delete
</x-slot:button-title>
This will delete this backup. It is not reversible.<br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation
title="Confirm Backup Deletion?"
buttonTitle="Delete"
isErrorButton
submitAction="deleteBackup({{ data_get($execution, 'id') }})"
{{-- :checkboxes="$checkboxes" --}}
:actions="[
'This backup will be permanently deleted from local storage.'
]"
confirmationText="{{ data_get($execution, 'filename') }}"
confirmationLabel="Please confirm the execution of the actions by entering the Backup Filename below"
shortConfirmationLabel="Backup Filename"
step3ButtonText="Permanently Delete"
/>
</div>
</div>
@empty

View File

@@ -35,7 +35,12 @@
</nav>
<div class="flex flex-wrap gap-2 items-center">
@if (!str($database->status)->startsWith('exited'))
<x-modal-confirmation @click="$wire.dispatch('restartEvent')">
<x-modal-confirmation title="Confirm Database Restart?" buttonTitle="Restart" submitAction="restart"
:actions="[
'This database will be unavailable during the restart.',
'If the database is currently in use data could be lost.',
]" :confirmWithText="false" :confirmWithPassword="false" step2ButtonText="Restart Database"
:dispatchEvent="true" dispatchEventType="restartEvent">
<x-slot:button-title>
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
@@ -46,9 +51,14 @@
</svg>
Restart
</x-slot:button-title>
This database will be restarted. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation @click="$wire.dispatch('stopEvent')">
<x-modal-confirmation title="Confirm Database Stopping?" buttonTitle="Stop" submitAction="stop"
:checkboxes="$checkboxes" :actions="[
'This database will be stopped.',
'If the database is currently in use data could be lost.',
'All non-persistent data of this database (containers, networks, unused images) will be deleted (don\'t worry, no data is lost and you can start the database again).',
]" :confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue"
step2ButtonText="Stop Database" :dispatchEvent="true" dispatchEventType="stopEvent">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
@@ -61,7 +71,6 @@
</svg>
Stop
</x-slot:button-title>
This database will be stopped. <br>Please think again.
</x-modal-confirmation>
@else
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">

View File

@@ -2,9 +2,13 @@
<div class="flex items-end gap-2">
<x-forms.input id="filename" label="Filename" />
<x-forms.button type="submit">Save</x-forms.button>
<x-modal-confirmation isErrorButton buttonTitle="Delete">
This script will be deleted. It is not reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm init-script deletion?" buttonTitle="Delete" isErrorButton
submitAction="delete" :actions="[
'The init-script of this database will be permanently deleted.',
'If you are actively using this init-script, it could cause errors on redeployment.',
]" confirmationText="{{ $filename }}"
confirmationLabel="Please confirm the execution of the actions by entering the init-script name below"
shortConfirmationLabel="Init-script Name" :confirmWithPassword=false step2ButtonText="Permanently Delete" />
</div>
<x-forms.textarea id="content" label="Content" />
</form>

View File

@@ -1,3 +1,5 @@
<x-modal-confirmation isErrorButton buttonTitle="Delete Environment" disabled="{{ $disabled }}">
This environment will be deleted. It is not reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Environment Deletion?" buttonTitle="Delete Environment" isErrorButton
submitAction="delete" :actions="['This will delete the selected environment.']"
confirmationLabel="Please confirm the execution of the actions by entering the Environment Name below"
shortConfirmationLabel="Environment Name" confirmationText="{{ $environmentName }}" :confirmWithPassword="false"
step2ButtonText="Permanently Delete" />

View File

@@ -1,3 +1,7 @@
<x-modal-confirmation isErrorButton buttonTitle="Delete Project" disabled="{{ $disabled }}">
This project will be deleted. It is not reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Project Deletion?" buttonTitle="Delete Project" isErrorButton submitAction="delete"
:actions="[
'This will delete the selected project',
'All Environments inside the project will be deleted as well.',
]" confirmationLabel="Please confirm the execution of the actions by entering the Project Name below"
shortConfirmationLabel="Project Name" confirmationText="{{ $projectName }}" :confirmWithPassword="false"
step2ButtonText="Permanently Delete" />

View File

@@ -491,7 +491,9 @@
</div>
<div class="pb-4 text-xs">Trademarks Policy: The respective trademarks mentioned here are owned by the
respective
companies, and use of them does not imply any affiliation or endorsement.</div>
companies, and use of them does not imply any affiliation or endorsement.<br>Find more services <a
class="dark:text-white underline" target="_blank"
href="https://coolify.io/docs/services">here</a>.</div>
<input class="input" autofocus wire:model.live.debounce.200ms="search" autofocus
placeholder="Search...">
@if ($loadingServices)

View File

@@ -76,7 +76,7 @@
@endif
@if ($application->fqdn)
<span class="flex gap-1 text-xs">{{ Str::limit($application->fqdn, 60) }}
<x-modal-input title="Edit Domains">
<x-modal-input title="Edit Domains" :closeOutside="false">
<x-slot:content>
<span class="cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg"
@@ -107,11 +107,14 @@
Settings
</a>
@if (str($application->status)->contains('running'))
<x-modal-confirmation action="restartApplication({{ $application->id }})"
isErrorButton buttonTitle="Restart">
This application will be unavailable during the restart. <br>Please think
again.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Service Application Restart?"
buttonTitle="Restart"
submitAction="restartApplication({{ $application->id }})" :actions="[
'The selected service application will be unavailable during the restart.',
'If the service application is currently in use data could be lost.',
]"
:confirmWithText="false" :confirmWithPassword="false"
step2ButtonText="Restart Service Container" />
@endif
</div>
</div>
@@ -151,11 +154,13 @@
Settings
</a>
@if (str($database->status)->contains('running'))
<x-modal-confirmation action="restartDatabase({{ $database->id }})"
isErrorButton buttonTitle="Restart">
This database will be unavailable during the restart. <br>Please think
again.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Service Database Restart?"
buttonTitle="Restart" submitAction="restartDatabase({{ $database->id }})"
:actions="[
'This service database will be unavailable during the restart.',
'If the service database is currently in use data could be lost.',
]" :confirmWithText="false" :confirmWithPassword="false"
step2ButtonText="Restart Database" />
@endif
</div>
</div>
@@ -175,7 +180,8 @@
lazy />
@endforeach
@foreach ($databases as $database)
<livewire:project.service.storage wire:key="database-{{ $database->id }}" :resource="$database" lazy />
<livewire:project.service.storage wire:key="database-{{ $database->id }}" :resource="$database"
lazy />
@endforeach
</div>
<div x-cloak x-show="activeTab === 'scheduled-tasks'">

View File

@@ -15,18 +15,32 @@
<form wire:submit='submit' class="flex flex-col gap-2">
<div class="flex gap-2">
@if ($fileStorage->is_directory)
<x-modal-confirmation action="convertToFile" buttonTitle="Convert to file">
<div>This will delete all files in this directory. It is not reversible. <strong
class="text-error">Please think
again.</strong><br><br></div>
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Directory Conversion to File?" buttonTitle="Convert to file"
submitAction="convertToFile" :actions="[
'All files in this directory will be permanently deleted and an empty file will be created in its place.',
]" confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" :confirmWithPassword="false" step2ButtonText="Convert to file" />
@else
<x-modal-confirmation action="convertToDirectory" buttonTitle="Convert to directory">
<div>This will delete the file and make a directory instead. It is not reversible.
<strong class="text-error">Please think
again.</strong><br><br>
</div>
</x-modal-confirmation>
<x-modal-confirmation title="Confirm File Conversion to Directory?" buttonTitle="Convert to directory"
submitAction="convertToDirectory" :actions="[
'The selected file will be permanently deleted and an empty directory will be created in its place.',
]" confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" :confirmWithPassword="false" step2ButtonText="Convert to directory" />
@endif
@if ($fileStorage->is_directory)
<x-modal-confirmation title="Confirm Directory Deletion?" buttonTitle="Delete Directory" isErrorButton
submitAction="delete" :checkboxes="$directoryDeletionCheckboxes" :actions="[
'The selected directory and all its contents will be permanently deleted from the container.',
]" confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
@else
<x-modal-confirmation title="Confirm File Deletion?" buttonTitle="Delete File" isErrorButton
submitAction="delete" :checkboxes="$fileDeletionCheckboxes" :actions="['The selected file will be permanently deleted from the container.']" confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
@endif
@if (!$fileStorage->is_based_on_git)

View File

@@ -32,7 +32,9 @@
</svg>
Pull Latest Images & Restart
</button>
<x-modal-confirmation @click="$wire.dispatch('stopEvent')">
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" submitAction="stop"
:checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]" :confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue"
step2ButtonText="Stop Service" :dispatchEvent="true" dispatchEventType="stopEvent">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
@@ -45,7 +47,6 @@
</svg>
Stop
</x-slot:button-title>
This service will be stopped. <br>Please think again.
</x-modal-confirmation>
@elseif (str($service->status())->contains('degraded'))
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
@@ -58,7 +59,10 @@
</svg>
Restart Degraded Services
</button>
<x-modal-confirmation @click="$wire.dispatch('stopEvent')">
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" submitAction="stop"
:checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]" :confirmWithText="false" :confirmWithPassword="false"
step1ButtonText="Continue" step2ButtonText="Stop Service" :dispatchEvent="true"
dispatchEventType="stopEvent">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
@@ -71,7 +75,6 @@
</svg>
Stop
</x-slot:button-title>
This service will be stopped. <br>Please think again.
</x-modal-confirmation>
@elseif (str($service->status())->contains('exited'))
<button wire:click='stop(true)' class="gap-2 button">
@@ -92,7 +95,10 @@
Deploy
</button>
@else
<x-modal-confirmation @click="$wire.dispatch('stopEvent')">
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" submitAction="stop"
:checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]" :confirmWithText="false" :confirmWithPassword="false"
step1ButtonText="Continue" step2ButtonText="Stop Service" :dispatchEvent="true"
dispatchEventType="stopEvent">
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
@@ -105,7 +111,6 @@
</svg>
Stop
</x-slot:button-title>
This service will be stopped. <br>Please think again.
</x-modal-confirmation>
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24"

View File

@@ -7,12 +7,11 @@
<h2>{{ Str::headline($application->name) }}</h2>
@endif
<x-forms.button type="submit">Save</x-forms.button>
<x-modal-confirmation isErrorButton>
<x-slot:button-title>
Delete
</x-slot:button-title>
This will delete this service application. It is not reversible.<br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Service Application Deletion?" buttonTitle="Delete" isErrorButton
submitAction="delete" {{-- :checkboxes="$checkboxes" --}} :actions="['The selected service application container will be stopped and permanently deleted.']"
confirmationText="{{ Str::headline($application->name) }}"
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
shortConfirmationLabel="Service Application Name" step3ButtonText="Permanently Delete" />
</div>
<div class="flex flex-col gap-2">
<div class="flex gap-2">

View File

@@ -2,15 +2,10 @@
<h2>Danger Zone</h2>
<div class="">Woah. I hope you know what are you doing.</div>
<h4 class="pt-4">Delete Resource</h4>
<div class="pb-4">This will stop your containers, delete all related data, etc. Beware! There is no coming
back!
<div class="pb-4">This will stop your containers, delete all related data, etc. Beware! There is no coming back!
</div>
<x-modal-confirmation isErrorButton buttonTitle="Delete" confirm={{ $confirm }}>
<div class="px-2">This resource will be deleted. It is not reversible. <strong class="text-error">Please think
again.</strong><br><br></div>
<h4>Actions</h4>
<x-forms.checkbox id="delete_configurations"
label="Permanently delete configuration files from the server?"></x-forms.checkbox>
<x-forms.checkbox id="delete_volumes" label="Permanently delete associated volumes?"></x-forms.checkbox>
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Resource Deletion?" buttonTitle="Delete" isErrorButton submitAction="delete"
buttonTitle="Delete" :checkboxes="$checkboxes" :actions="['Permanently delete all containers of this resource.']" confirmationText="{{ $resourceName }}"
confirmationLabel="Please confirm the execution of the actions by entering the Resource Name below"
shortConfirmationLabel="Resource Name" step3ButtonText="Permanently Delete" />
</div>

View File

@@ -63,11 +63,16 @@
wire:click="stop('{{ data_get($destination, 'server.id') }}')">Stop</x-forms.button>
@endif
<x-modal-confirmation
action="removeServer({{ data_get($destination, 'id') }},{{ data_get($destination, 'server.id') }})"
isErrorButton buttonTitle="Remove Server">
This will stop the running application in this server and remove it as a deployment
destination.<br><br>Please think again.
</x-modal-confirmation>
title="Confirm server removal?"
isErrorButton
buttonTitle="Remove Server"
submitAction="removeServer({{ data_get($destination, 'id') }},{{ data_get($destination, 'server.id') }})"
:actions="['This will stop the all running applications on this server and remove it as a deployment destination.']"
confirmationText="{{ data_get($destination, 'server.name') }}"
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
shortConfirmationLabel="Server Name"
step3ButtonText="Permanently Remove Server"
/>
</div>
</div>
@endforeach

View File

@@ -4,7 +4,9 @@
<x-forms.input x-show="$wire.is_multiline === false" x-cloak placeholder="production" id="value"
x-bind:label="$wire.is_multiline === false && 'Value'" required />
@if (data_get($parameters, 'application_uuid'))
<x-forms.checkbox id="is_build_time" label="Build Variable?" />
<x-forms.checkbox id="is_build_time"
helper="If you are using Docker, remember to modify the file to be ready to receive the build time args. Ex.: for docker file, add `ARG name_of_the_variable`, or dockercompose add `- 'name_of_the_variable=${name_of_the_variable}'`"
label="Build Variable?" />
@endif
<x-forms.checkbox id="is_multiline" label="Is Multiline?" />
@if (!$shared)

View File

@@ -11,10 +11,11 @@
<path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0-2 0m-3-5V7a4 4 0 1 1 8 0v4" />
</g>
</svg>
<x-modal-confirmation isErrorButton buttonTitle="Delete">
You will delete environment variable <span
class="font-bold dark:text-warning text-coollabs">{{ $env->key }}</span>.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Environment Variable Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="['The selected environment variable will be permanently deleted.']" confirmationText="{{ $env->key }}"
confirmationLabel="Please confirm the execution of the actions by entering the Environment Variable Name below"
shortConfirmationLabel="Environment Variable Name" :confirmWithPassword="false"
step2ButtonText="Permanently Delete" />
</div>
@else
@if ($isDisabled)
@@ -41,10 +42,14 @@
@endif
<div class="flex flex-col w-full gap-2 lg:flex-row">
@if ($type === 'service')
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
<x-forms.checkbox instantSave id="env.is_build_time"
helper="If you are using Docker, remember to modify the file to be ready to receive the build time args. Ex.: for docker file, add `ARG name_of_the_variable`, or dockercompose add `- 'name_of_the_variable=${name_of_the_variable}'`"
label="Build Variable?" />
@else
@if ($env->is_shared)
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
<x-forms.checkbox instantSave id="env.is_build_time"
helper="If you are using Docker, remember to modify the file to be ready to receive the build time args. Ex.: for docker file, add `ARG name_of_the_variable`, or dockercompose add `- 'name_of_the_variable=${name_of_the_variable}'`"
label="Build Variable?" />
<x-forms.checkbox instantSave id="env.is_literal"
helper="This means that when you use $VARIABLES in a value, it should be interpreted as the actual characters '$VARIABLES' and not as the value of a variable named VARIABLE.<br><br>Useful if you have $ sign in your value and there are some characters after it, but you would not like to interpolate it from another value. In this case, you should set this to true."
label="Is Literal?" />
@@ -52,7 +57,9 @@
@if ($isSharedVariable)
<x-forms.checkbox instantSave id="env.is_multiline" label="Is Multiline?" />
@else
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
<x-forms.checkbox instantSave id="env.is_build_time"
helper="If you are using Docker, remember to modify the file to be ready to receive the build time args. Ex.: for dockerfile, add `ARG name_of_the_variable`, or dockercompose add `- 'name_of_the_variable=${name_of_the_variable}'`"
label="Build Variable?" />
<x-forms.checkbox instantSave id="env.is_multiline" label="Is Multiline?" />
@if (!data_get($env, 'is_multiline'))
<x-forms.checkbox instantSave id="env.is_literal"
@@ -70,10 +77,12 @@
<x-forms.button wire:click='lock'>
Lock
</x-forms.button>
<x-modal-confirmation isErrorButton buttonTitle="Delete">
You will delete environment variable <span
class="font-bold dark:text-warning">{{ $env->key }}</span>.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Environment Variable Deletion?" isErrorButton
buttonTitle="Delete" submitAction="delete" :actions="['The selected environment variable will be permanently deleted.']"
confirmationText="{{ $env->key }}"
confirmationLabel="Please confirm the execution of the actions by entering the Environment Variable Name below"
shortConfirmationLabel="Environment Variable Name" :confirmWithPassword="false"
step2ButtonText="Permanently Delete" />
@else
<x-forms.button type="submit">
Update
@@ -81,10 +90,12 @@
<x-forms.button wire:click='lock'>
Lock
</x-forms.button>
<x-modal-confirmation buttonFullWidth isErrorButton buttonTitle="Delete">
You will delete environment variable <span
class="font-bold dark:text-warning">{{ $env->key }}</span>.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Environment Variable Deletion?" isErrorButton
buttonTitle="Delete" submitAction="delete" :actions="['The selected environment variable will be permanently deleted.']"
confirmationText="{{ $env->key }}"
confirmationLabel="Please confirm the execution of the actions by entering the Environment Variable Name below"
shortConfirmationLabel="Environment Variable Name" :confirmWithPassword="false"
step2ButtonText="Permanently Delete" />
@endif
</div>
@endif

View File

@@ -32,7 +32,7 @@
<x-forms.button type="submit">Connect</x-forms.button>
</form>
@else
<div class="pt-4">No containers are not running.</div>
<div class="pt-4">No containers are running.</div>
@endif
</div>
</div>

View File

@@ -8,7 +8,20 @@
@foreach ($servers->sortBy('id') as $server)
<h5>Server: <span class="font-bold text-dark dark:text-white">{{ $server->name }}</span></h5>
@foreach ($server->destinations() as $destination)
<x-modal-confirmation action="cloneTo({{ data_get($destination, 'id') }})">
<x-modal-confirmation
title="Clone Resource?"
buttonTitle="Clone Resource"
submitAction="cloneTo({{ data_get($destination, 'id') }})"
:actions="[
'All containers of this resource will be duplicated and cloned to the selected destination.'
]"
:confirmWithText="false"
:confirmWithPassword="false"
step2ButtonText="Clone Resource"
dispatchEvent="true"
dispatchEventType="success"
dispatchEventMessage="Resource cloned to {{ $destination->name }} destination."
>
<x:slot name="content">
<div class="box group">
<div class="flex flex-col">
@@ -17,7 +30,6 @@
</div>
</div>
</x:slot>
<div>You are about to clone this resource.</div>
</x-modal-confirmation>
@endforeach
@endforeach
@@ -36,8 +48,21 @@
<h5>Project: <span class="font-bold text-dark dark:text-white">{{ $project->name }}</span></h5>
@foreach ($project->environments as $environment)
<x-modal-confirmation action="moveTo({{ data_get($environment, 'id') }})">
<x:slot name="content">
<x-modal-confirmation
title="Move Resource?"
buttonTitle="Move Resource"
submitAction="moveTo({{ data_get($environment, 'id') }})"
:actions="[
'All containers of this resource will be moved to the selected environment.'
]"
:confirmWithText="false"
:confirmWithPassword="false"
step2ButtonText="Move Resource"
dispatchEvent="true"
dispatchEventType="success"
dispatchEventMessage="Resource moved to {{ $environment->name }} environment."
>
<x:slot:content>
<div class="box group">
<div class="flex flex-col">
<div class="box-title">Environment</div>
@@ -45,7 +70,6 @@
</div>
</div>
</x:slot>
<div>You are about to move this resource.</div>
</x-modal-confirmation>
@endforeach
@empty

View File

@@ -1,47 +1,50 @@
<div>
<x-slot:title>
{{ data_get_str($resource, 'name')->limit(10) }} > Scheduled Tasks | Coolify
</x-slot>
@if ($type === 'application')
</x-slot>
@if ($type === 'application')
<h1>Scheduled Task</h1>
<livewire:project.application.heading :application="$resource" />
@elseif ($type === 'service')
@elseif ($type === 'service')
<livewire:project.service.navbar :service="$resource" :parameters="$parameters" />
@endif
@endif
<form wire:submit="submit" class="w-full">
<div class="flex flex-col gap-2 pb-2">
<div class="flex gap-2 items-end pt-4">
<h2>Scheduled Task</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
<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"
shortConfirmationLabel="Scheduled Task Name" :confirmWithPassword="false"
step2ButtonText="Permanently Delete" />
<form wire:submit="submit" class="w-full">
<div class="flex flex-col gap-2 pb-2">
<div class="flex items-end gap-2 pt-4">
<h2>Scheduled Task</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
<x-modal-confirmation isErrorButton buttonTitle="Delete Scheduled Task">
You will delete scheduled task <span class="font-bold dark:text-warning">{{ $task->name }}</span>.
</x-modal-confirmation>
</div>
<div class="w-48">
<x-forms.checkbox instantSave id="task.enabled" label="Enabled" />
</div>
</div>
<div class="flex w-full gap-2">
<div class="w-48">
<x-forms.checkbox instantSave id="task.enabled" 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 />
@if ($type === 'application')
<x-forms.input placeholder="php"
helper="You can leave this empty if your resource only has one container." id="task.container"
label="Container name" />
<x-forms.input placeholder="php"
helper="You can leave this empty if your resource only has one container." id="task.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" />
<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" />
@endif
</div>
</form>
</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)" />
</div>
<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)" />
</div>
</div>

View File

@@ -46,13 +46,13 @@
<x-forms.button type="submit">
Update
</x-forms.button>
<x-modal-confirmation isErrorButton buttonTitle="Delete">
This storage will be deleted <span class="font-bold dark:text-warning">{{ $storage->name }}</span>.
It
is
not
reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm persistent storage deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="[
'The selected persistent storage/volume will be permanently deleted.',
'If the persistent storage/volume is actvily used by a resource data will be lost.',
]" confirmationText="{{ $storage->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Storage Name below"
shortConfirmationLabel="Storage Name" step3ButtonText="Permanently Delete" />
</div>
@endif
</form>

View File

@@ -1,4 +1,4 @@
<div x-data="data()">
<div id="terminal-container" x-data="terminalData()">
{{-- <div x-show="!terminalActive" class="flex items-center justify-center w-full py-4 mx-auto h-[510px]">
<div class="p-1 w-full h-full rounded border dark:bg-coolgray-100 dark:border-coolgray-300">
<span class="font-mono text-sm text-gray-500" x-text="message"></span>
@@ -22,219 +22,14 @@
</g>
</svg></button>
</div>
@script
<script>
const MAX_PENDING_WRITES = 5;
let pendingWrites = 0;
let paused = false;
let socket;
let commandBuffer = '';
function keepAlive() {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({
ping: true
}));
}
}
const keepAliveInterval = setInterval(keepAlive, 30000);
// Clear the interval when the component is destroyed
document.addEventListener('livewire:navigating', () => {
clearInterval(keepAliveInterval);
});
function initializeWebSocket() {
if (!socket || socket.readyState === WebSocket.CLOSED) {
const predefined = {
protocol: "{{ env('TERMINAL_PROTOCOL') }}",
host: "{{ env('TERMINAL_HOST') }}",
port: "{{ env('TERMINAL_PORT') }}"
}
const connectionString = {
protocol: window.location.protocol === 'https:' ? 'wss' : 'ws',
host: window.location.hostname,
port: ":6002",
path: '/terminal/ws'
}
if (!window.location.port) {
connectionString.port = ''
}
if (predefined.host) {
connectionString.host = predefined.host
}
if (predefined.port) {
connectionString.port = `:${predefined.port}`
}
if (predefined.protocol) {
connectionString.protocol = predefined.protocol
}
const url =
`${connectionString.protocol}://${connectionString.host}${connectionString.port}${connectionString.path}`
socket = new WebSocket(url);
socket.onmessage = handleSocketMessage;
socket.onerror = (e) => {
console.error('WebSocket error:', e);
};
socket.onclose = () => {
console.log('WebSocket connection closed');
};
}
}
function handleSocketMessage(event) {
$data.message = '(connection closed)';
// Initialize Terminal
if (event.data === 'pty-ready') {
term.open(document.getElementById('terminal'));
$data.terminalActive = true;
term.reset();
term.focus();
document.querySelector('.xterm-viewport').classList.add('scrollbar', 'rounded')
$data.resizeTerminal()
} else if (event.data === 'unprocessable') {
term.reset();
$data.terminalActive = false;
$data.message = '(sorry, something went wrong, please try again)';
} else {
pendingWrites++;
term.write(event.data, flowControlCallback);
}
}
function flowControlCallback() {
pendingWrites--;
if (pendingWrites > MAX_PENDING_WRITES && !paused) {
paused = true;
socket.send(JSON.stringify({
pause: true
}));
return;
}
if (pendingWrites <= MAX_PENDING_WRITES && paused) {
paused = false;
socket.send(JSON.stringify({
resume: true
}));
return;
}
}
term.onData((data) => {
socket.send(JSON.stringify({
message: data
}));
// Type CTRL + D or exit in the terminal
if (data === '\x04' || (data === '\r' && stripAnsiCommands(commandBuffer).trim().includes('exit'))) {
checkIfProcessIsRunningAndKillIt();
setTimeout(() => {
$data.terminalActive = false;
term.reset();
}, 500);
commandBuffer = '';
} else if (data === '\r') {
commandBuffer = '';
} else {
commandBuffer += data;
}
});
function stripAnsiCommands(input) {
return input.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
}
// Copy and paste
// Enables ctrl + c and ctrl + v
// defaults otherwise to ctrl + insert, shift + insert
term.attachCustomKeyEventHandler((arg) => {
if (arg.ctrlKey && arg.code === "KeyV" && arg.type === "keydown") {
navigator.clipboard.readText()
.then(text => {
socket.send(JSON.stringify({
message: text
}));
})
};
if (arg.ctrlKey && arg.code === "KeyC" && arg.type === "keydown") {
const selection = term.getSelection();
if (selection) {
navigator.clipboard.writeText(selection);
return false;
}
}
return true;
});
$wire.on('send-back-command', function(command) {
socket.send(JSON.stringify({
command: command
}));
});
window.addEventListener('beforeunload', function(e) {
checkIfProcessIsRunningAndKillIt();
});
function checkIfProcessIsRunningAndKillIt() {
socket.send(JSON.stringify({
checkActive: 'force'
}));
}
window.onresize = function() {
$data.resizeTerminal()
};
Alpine.data('data', () => ({
fullscreen: false,
terminalActive: false,
message: '(connection closed)',
init() {
this.$watch('terminalActive', (value) => {
this.$nextTick(() => {
if (value) {
$refs.terminalWrapper.style.display = 'block';
this.resizeTerminal();
} else {
$refs.terminalWrapper.style.display = 'none';
}
});
});
},
makeFullscreen() {
this.fullscreen = !this.fullscreen;
$nextTick(() => {
this.resizeTerminal()
})
},
resizeTerminal() {
if (!this.terminalActive) return;
fitAddon.fit();
const height = $refs.terminalWrapper.clientHeight;
const rows = height / term._core._renderService._charSizeService.height - 1;
var termWidth = term.cols;
var termHeight = parseInt(rows.toString(), 10);
term.resize(termWidth, termHeight);
socket.send(JSON.stringify({
resize: {
cols: termWidth,
rows: termHeight
}
}));
}
}));
initializeWebSocket();
</script>
<script>
// expose terminal config to the terminal.js file
window.terminalConfig = {
protocol: "{{ env('TERMINAL_PROTOCOL') }}",
host: "{{ env('TERMINAL_HOST') }}",
port: "{{ env('TERMINAL_PORT') }}"
}
</script>
@endscript
</div>

View File

@@ -56,12 +56,18 @@
@endif
</div>
<x-modal-confirmation isErrorButton action="revoke({{ data_get($token, 'id') }})">
<x-slot:button-title>
Revoke token
</x-slot:button-title>
This API Token will be deleted and anything using it will fail. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation
title="Confirm API Token Revocation?"
isErrorButton
buttonTitle="Revoke token"
submitAction="revoke({{ data_get($token, 'id') }})"
:actions="['This API Token will be revoked and permanently deleted.', 'Any API call made with this token will fail.']"
confirmationText="{{ $token->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the API Token Description below"
shortConfirmationLabel="API Token Description"
:confirmWithPassword="false"
step2ButtonText="Revoke API Token"
/>
</div>
@empty
<div>

View File

@@ -4,8 +4,8 @@
<div class="font-bold">You should not use passphrase protected keys.</div>
</div>
<div class="flex gap-2 mb-4">
<x-forms.button wire:click="generateNewRSAKey">Generate new RSA SSH Key</x-forms.button>
<x-forms.button wire:click="generateNewEDKey">Generate new ED25519 SSH Key</x-forms.button>
<x-forms.button wire:click="generateNewEDKey">Generate new ED25519 SSH Key (Recommended, fastest and most secure)</x-forms.button>
<x-forms.button wire:click="generateNewRSAKey">Generate new RSA SSH Key</x-forms.button>
</div>
<form class="flex flex-col gap-2" wire:submit='createPrivateKey'>
<div class="flex gap-2">

View File

@@ -1,13 +1,20 @@
<x-layout>
<x-slot:title>
Private Keys | Coolify
</x-slot>
<div>
<x-security.navbar />
<div class="flex gap-2">
<h2 class="pb-4">Private Keys</h2>
<x-modal-input buttonTitle="+ Add" title="New Private Key">
<livewire:security.private-key.create />
</x-modal-input>
<x-modal-confirmation
title="Confirm unused SSH Key Deletion?"
buttonTitle="Delete unused SSH Keys"
isErrorButton
submitAction="cleanupUnusedKeys"
:actions="['All unused SSH keys (marked with unused) are permanently deleted.']"
:confirmWithText="false"
:confirmWithPassword="false"
/>
</div>
<div class="grid gap-2 lg:grid-cols-2">
@forelse ($privateKeys as $key)
@@ -19,11 +26,15 @@
</div>
<div class="box-description">
{{ $key->description }}
@if (!$key->isInUse())
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-400 text-black">Unused</span>
@endif
</div>
</div>
</a>
@empty
<div>No private keys found.</div>
@endforelse
</div>
</x-layout>
</div>

View File

@@ -11,9 +11,18 @@
Save
</x-forms.button>
@if (data_get($private_key, 'id') > 0)
<x-modal-confirmation isErrorButton buttonTitle="Delete">
This private key will be deleted. It is not reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation
title="Confirm Private Key Deletion?"
isErrorButton
buttonTitle="Delete"
submitAction="delete({{ $private_key->id }})"
:actions="['This private key will be permanently deleted.', 'All servers connected to this private key will stop working.', 'Any git app using this private key will stop working.']"
confirmationText="{{ $private_key->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Private Key Name below"
shortConfirmationLabel="Private Key Name"
:confirmWithPassword="false"
step2ButtonText="Delete Private Key"
/>
@endif
</div>
<x-forms.input id="private_key.name" label="Name" required />

View File

@@ -1,8 +1,6 @@
<form wire:submit.prevent='submit' class="flex flex-col w-full gap-2">
<x-forms.input id="cloudflare_token" required label="Cloudflare Token" />
<form wire:submit.prevent='submit' class="flex flex-col gap-2 w-full">
<x-forms.input id="cloudflare_token" required label="Cloudflare Token" type="password" />
<x-forms.input id="ssh_domain" label="Configured SSH Domain" required
helper="The SSH Domain you configured in Cloudflare" />
<x-forms.button type="submit" isHighlighted @click="modalOpen=false">Automated Configuration (experimental)</x-forms.button>
<h3 class="text-center">Or</h3>
<x-forms.button wire:click.prevent='alreadyConfigured' @click="modalOpen=false">I have already set up the tunnel manually on the server.</x-forms.button>
helper="The SSH domain you configured in Cloudflare. Make sure there is no protocol like http(s):// so you provide a FQDN not a URL. <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/cloudflare/tunnels/#automated' target='_blank'>Documentation</a>" />
<x-forms.button type="submit" isHighlighted @click="modalOpen=false">Continue</x-forms.button>
</form>

View File

@@ -8,13 +8,15 @@
</div>
@if ($server->definedResources()->count() > 0)
<div class="pb-2 text-red-500">You need to delete all resources before deleting this server.</div>
<x-modal-confirmation disabled isErrorButton buttonTitle="Delete">
This server will be deleted. It is not reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Server Deletion?" isErrorButton buttonTitle="Delete" submitAction="delete"
:actions="['This server will be permanently deleted.']" confirmationText="{{ $server->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
shortConfirmationLabel="Server Name" step3ButtonText="Permanently Delete" />
@else
<x-modal-confirmation isErrorButton buttonTitle="Delete">
This server will be deleted. It is not reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Server Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="['This server will be permanently deleted.']" confirmationText="{{ $server->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
shortConfirmationLabel="Server Name" step2ButtonText="Continue" step3ButtonText="Permanently Delete" />
@endif
@endif
</div>

View File

@@ -3,11 +3,10 @@
<div class="flex gap-2">
<h2>General</h2>
@if ($server->id === 0)
<x-modal-confirmation buttonTitle="Save" title="Change Localhost" action="submit">
You could lose a lot of functionalities if you change the server details of the server where Coolify
is
running on.<br>Please think again.
</x-modal-confirmation>
<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())
@@ -35,7 +34,7 @@
<livewire:server.validate-and-install :server="$server" />
</x-slot:content>
<x-forms.button @click="slideOverOpen=true"
class="w-full mt-8 mb-4 font-bold box-without-bg bg-coollabs hover:bg-coollabs-100"
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>
@@ -58,7 +57,7 @@
number of servers for which you have paid.</div>
@endif
<div class="flex flex-col gap-2 pt-4">
<div class="flex flex-col w-full gap-2 lg:flex-row">
<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)
@@ -67,9 +66,9 @@
@endif
</div>
<div class="flex flex-col w-full gap-2 lg:flex-row">
<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)." required />
helper="An IP Address (127.0.0.1) or domain (example.com). Make sure there is no protocol like http(s):// so you provide a FQDN not a URL." required />
<div class="flex gap-2">
<x-forms.input id="server.user" label="User" required />
<x-forms.input type="number" id="server.port" label="Port" required />
@@ -94,88 +93,100 @@
<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 items-center relative w-64">
<div class="inline-flex relative items-center w-64">
<input 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 "
@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 w-4 h-4 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none"
<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="absolute z-50 w-64 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-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 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 text-gray-800 dark:text-gray-200"
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-64">
@if ($server->isFunctional())
@if (!$server->isLocalhost())
<x-forms.checkbox instantSave id="server.settings.is_build_server"
label="Use it as a build server?" />
<div class="flex items-center gap-1 pt-6">
<h3 class="">Cloudflare Tunnels
</h3>
<div class="{{ $server->isFunctional() ? 'w-96' : 'w-full' }}">
@if (!$server->isLocalhost())
<x-forms.checkbox instantSave id="server.settings.is_build_server"
label="Use it as a build server?" />
<div class="flex flex-col gap-2 pt-6">
<div class="flex gap-1 items-center">
<h3 class="text-lg font-semibold">Cloudflare Tunnels</h3>
<x-helper class="inline-flex"
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all SSH requests to your server through Cloudflare.<br><span class='dark:text-warning'>Coolify does not install or set up Cloudflare (cloudflared) on your server.</span>" />
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all SSH requests to your server through Cloudflare.<br> You then can close your server's SSH port in the firewall of your hosting provider.<br><span class='dark:text-warning'>If you choose manual configuration, Coolify does not install or set up Cloudflare (cloudflared) on your server.</span>" />
</div>
@if ($server->settings->is_cloudflare_tunnel)
<x-forms.checkbox instantSave id="server.settings.is_cloudflare_tunnel" label="Enabled" />
@else
<x-modal-input buttonTitle="Configure" title="Cloudflare Tunnels">
<div class="w-64">
<x-forms.checkbox instantSave id="server.settings.is_cloudflare_tunnel" label="Enabled" />
</div>
@elseif (!$server->isFunctional())
<div class="p-4 mb-4 w-full text-sm text-yellow-800 bg-yellow-100 rounded dark:bg-yellow-900 dark:text-yellow-300">
<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>
To <span class="font-semibold">automatically</span> configure Cloudflare Tunnels, please click
<span @click="slideOverOpen=true"
wire:click.prevent='validateServer' class="underline cursor-pointer">
here.</span> You will need a Cloudflare token and domain.
</x-slide-over>
<br/>
To <span class="font-semibold">manually</span> configure Cloudflare Tunnels, please click <span wire:click="manualCloudflareConfig" class="underline cursor-pointer">here</span>, then you should validate the server.
<br/><br/>
For more information, please read our <a href="https://coolify.io/docs/knowledge-base/cloudflare/tunnels/" target="_blank" class="font-medium underline hover:text-yellow-600 dark:hover:text-yellow-200">documentation</a>.
</div>
@endif
@if (!$server->settings->is_cloudflare_tunnel && $server->isFunctional())
<x-modal-input buttonTitle="Automated Configuration" title="Cloudflare Tunnels" class="w-full">
<livewire:server.configure-cloudflare-tunnels :server_id="$server->id" />
</x-modal-input>
@endif
@if (!$server->isBuildServer())
<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>.
@if ($server->isFunctional() &&!$server->settings->is_cloudflare_tunnel)
<div wire:click="manualCloudflareConfig" class="w-full underline cursor-pointer">
I have configured Cloudflare Tunnels manually
</div>
@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
@endif
@endif
@else
<div class="flex items-center gap-1 pt-6">
<h3 class="">Cloudflare Tunnels
</h3>
<x-helper class="inline-flex"
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all SSH requests to your server through Cloudflare.<br><span class='dark:text-warning'>Coolify does not install or set up Cloudflare (cloudflared) on your server.</span>" />
</div>
@if ($server->settings->is_cloudflare_tunnel)
<x-forms.checkbox instantSave id="server.settings.is_cloudflare_tunnel" label="Enabled" />
@else
<x-modal-input buttonTitle="Configure" title="Cloudflare Tunnels">
<livewire:server.configure-cloudflare-tunnels :server_id="$server->id" />
</x-modal-input>
@if (!$server->isBuildServer() && !$server->settings->is_cloudflare_tunnel)
<h3 class="pt-6">Swarm <span class="text-xs text-neutral-500">(experimental)</span></h3>
<div class="pb-4">Read the docs <a class='underline dark:text-white'
href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>.
</div>
@if ($server->settings->is_swarm_worker)
<x-forms.checkbox disabled instantSave type="checkbox"
id="server.settings.is_swarm_manager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@else
<x-forms.checkbox instantSave type="checkbox" id="server.settings.is_swarm_manager"
helper="For more information, please read the documentation <a class='dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/swarm' target='_blank'>here</a>."
label="Is it a Swarm Manager?" />
@endif
@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
@endif
@endif
</div>
@@ -261,7 +272,7 @@
helper="You can define the maximum duration for a deployment to run before timing it out." />
</div>
</div>
<div class="flex items-center gap-2 pt-4 pb-2">
<div class="flex gap-2 items-center pt-4 pb-2">
<h3>Sentinel</h3>
{{-- @if ($server->isSentinelEnabled()) --}}
{{-- <x-forms.button wire:click='restartSentinel'>Restart</x-forms.button> --}}

View File

@@ -83,9 +83,9 @@
<x-forms.button class="box" wire:click="selectProxy('CADDY')">
Caddy
</x-forms.button>
<x-forms.button disabled class="box">
{{-- <x-forms.button disabled class="box">
Nginx
</x-forms.button>
</x-forms.button> --}}
</div>
</div>
@endif

View File

@@ -1,10 +1,6 @@
@php use App\Enums\ProxyTypes; @endphp
<div>
@if (
$server->proxyType() !== 'NONE' &&
$server->isFunctional() &&
!$server->isSwarmWorker() &&
!$server->settings->is_build_server)
@if ($server->proxySet())
<x-slide-over closeWithX fullScreen @startproxy.window="slideOverOpen = true">
<x-slot:title>Proxy Status</x-slot:title>
<x-slot:content>
@@ -24,7 +20,17 @@
</a>
</button>
@endif
<x-modal-confirmation @click="$wire.dispatch('restartEvent')">
<x-modal-confirmation
title="Confirm Proxy Restart?"
buttonTitle="Restart Proxy"
submitAction="restart"
:actions="['This proxy will be stopped and started again.', 'All resources hosted on coolify will be unavailable during the restart.']"
:confirmWithText="false"
:confirmWithPassword="false"
step2ButtonText="Restart Proxy"
:dispatchEvent="true"
dispatchEventType="restartEvent"
>
<x-slot:button-title>
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
@@ -35,10 +41,18 @@
</svg>
Restart Proxy
</x-slot:button-title>
This proxy will be stopped and started. It is not reversible. <br>All resources will be unavailable
during the restart. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation @click="$wire.dispatch('stopEvent')">
<x-modal-confirmation
title="Confirm Proxy Stopping?"
buttonTitle="Stop Proxy"
submitAction="stop(true)"
:actions="['The coolify proxy will be stopped.', 'All resources hosted on coolify will be unavailable.']"
:confirmWithText="false"
:confirmWithPassword="false"
step2ButtonText="Stop Proxy"
:dispatchEvent="true"
dispatchEventType="stopEvent"
>
<x-slot:button-title>
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round"
@@ -51,8 +65,6 @@
</svg>
Stop Proxy
</x-slot:button-title>
This proxy will be stopped. It is not reversible. <br>All resources will be unavailable.
<br>Please think again.
</x-modal-confirmation>
</div>
@else

View File

@@ -1,20 +1,14 @@
<div x-init="$wire.checkProxy()">
@if (
$server->proxyType() !== 'NONE' &&
$server->isFunctional() &&
!$server->isSwarmWorker() &&
!$server->settings->is_build_server)
<div class="flex gap-2">
@if (data_get($server, 'proxy.status') === 'running')
<x-status.running status="Proxy Running" />
@elseif (data_get($server, 'proxy.status') === 'restarting')
<x-status.restarting status="Proxy Restarting" />
@else
<x-status.stopped status="Proxy Stopped" />
@endif
@if (data_get($server, 'proxy.status') === 'running')
<x-forms.button wire:click='checkProxy(true)'>Refresh</x-forms.button>
@endif
</div>
<div x-init="$wire.checkProxy()" class="flex gap-2">
@if (data_get($server, 'proxy.status') === 'running')
<x-status.running status="Proxy Running" />
@elseif (data_get($server, 'proxy.status') === 'restarting')
<x-status.restarting status="Proxy Restarting" />
@elseif (data_get($server, 'proxy.force_stop'))
<x-status.stopped status="Proxy Stopped" />
@elseif (data_get($server, 'proxy.status') === 'exited')
<x-status.stopped status="Proxy Exited" />
@else
<x-status.stopped status="Proxy Not Running" />
@endif
<x-forms.button wire:click='checkProxy(true)'>Refresh</x-forms.button>
</div>

View File

@@ -26,7 +26,8 @@
<h3 class="pb-4">Choose another Key</h3>
<div class="grid grid-cols-3 gap-2">
@forelse ($privateKeys as $private_key)
<div class="box group" wire:click='setPrivateKey({{ $private_key->id }})'>
<div class="box group cursor-pointer"
wire:click='setPrivateKey({{ $private_key->id }})'>
<div class="flex flex-col ">
<div class="box-title">{{ $private_key->name }}</div>
<div class="box-description">{{ $private_key->description }}</div>

View File

@@ -14,13 +14,31 @@
</a>
@endif
@if ($applications->count() > 0)
<x-modal-confirmation disabled isErrorButton buttonTitle="Delete">
This source will be deleted. It is not reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation
title="Confirm GitHub App Deletion?"
isErrorButton
buttonTitle="Delete"
submitAction="delete"
:actions="['The selected GitHub App will be permanently deleted.']"
confirmationText="{{ data_get($github_app, 'name') }}"
confirmationLabel="Please confirm the execution of the actions by entering the GitHub App Name below"
shortConfirmationLabel="GitHub App Name"
:confirmWithPassword="false"
step2ButtonText="Permanently Delete"
/>
@else
<x-modal-confirmation isErrorButton buttonTitle="Delete">
This source will be deleted. It is not reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation
title="Confirm GitHub App Deletion?"
isErrorButton
buttonTitle="Delete"
submitAction="delete"
:actions="['The selected GitHub App will be permanently deleted.']"
confirmationLabel="Please confirm the execution of the actions by entering the GitHub App Name below"
shortConfirmationLabel="GitHub App Name"
confirmationText="{{ data_get($github_app, 'name') }}"
:confirmWithPassword="false"
step2ButtonText="Permanently Delete"
/>
@endif
</div>
</div>
@@ -160,9 +178,18 @@
<div class="flex items-center gap-2 pb-4">
<h1>GitHub App</h1>
<div class="flex gap-2">
<x-modal-confirmation isErrorButton buttonTitle="Delete">
This source will be deleted. It is not reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation
title="Confirm GitHub App Deletion?"
isErrorButton
buttonTitle="Delete"
submitAction="delete"
:actions="['The selected GitHub App will be permanently deleted.']"
confirmationText="{{ data_get($github_app, 'name') }}"
confirmationLabel="Please confirm the execution of the actions by entering the GitHub App Name below"
shortConfirmationLabel="GitHub App Name"
:confirmWithPassword="false"
step2ButtonText="Permanently Delete"
/>
</div>
</div>
<div class="mb-10 rounded alert-error">

View File

@@ -16,9 +16,13 @@
<x-forms.button wire:click="test_s3_connection">
Validate Connection
</x-forms.button>
<x-modal-confirmation isErrorButton buttonTitle="Delete">
This storage will be deleted. It is not reversible. Your data won't be touched!<br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Storage Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete({{ $storage->id }})" :actions="[
'The selected storage location will be permanently deleted from Coolify.',
'If the storage location is in use by any backup jobs those backup jobs will only store the backup locally on the server.',
]" confirmationText="{{ $storage->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Storage Name below"
shortConfirmationLabel="Storage Name" :confirmWithPassword="false" step2ButtonText="Permanently Delet" />
</div>
<div class="flex gap-2">
<x-forms.input label="Name" id="storage.name" />

View File

@@ -20,9 +20,18 @@
<div class="w-[500px]">
<x-forms.input readonly label="Deploy Webhook URL" id="webhook" />
</div>
<x-modal-confirmation isHighlighted buttonTitle="Redeploy All" action="redeploy_all">
All resources will be redeployed.
</x-modal-confirmation>
<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="{{ $oneTag->name }}"
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)

View File

@@ -24,9 +24,18 @@
<div class="w-[500px]">
<x-forms.input readonly label="Deploy Webhook URL" id="webhook" />
</div>
<x-modal-confirmation isHighlighted buttonTitle="Redeploy All" action="redeploy_all">
All resources will be redeployed.
</x-modal-confirmation>
<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="{{ $oneTag->name }}"
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)

View File

@@ -15,14 +15,14 @@
<div>{{ $user->email }}</div>
<div class="flex-1"></div>
<div class="flex items-center justify-center gap-2 mx-4 text-xs font-bold ">
<x-modal-confirmation isErrorButton action="delete({{ $user->id }})" buttonTitle="Delete">
This will delete all resources (application, databases, services, configurations, servers,
private keys, tags, etc.) from Coolify and <span
class="font-bold text-red-500 dark:text-warning">from the server (if it's reachable)</span>.
<br> <br>
It is not reversible. <br><br>
<div class="font-bold text-red-500 dark:text-white">Think twice!</div>
</x-modal-confirmation>
<x-modal-confirmation title="Confirm User Deletion?" buttonTitle="Delete" isErrorButton
submitAction="delete({{ $user->id }})" :actions="[
'The selected user will be permanently deleted from Coolify and the database.',
'All resources (application, databases, services, configurations, servers, private keys, tags, etc.) related to this user will be deleted from Coolify and from the server (if the server is reachable).',
]"
confirmationText="{{ $user->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the User Name below"
shortConfirmationLabel="User Name" step3ButtonText="Permanently Delete" />
</div>
</div>
@empty

View File

@@ -31,9 +31,11 @@
@else
@if (currentTeam()->isEmpty())
<div class="pb-4">This will delete your team. Beware! There is no coming back!</div>
<x-modal-confirmation isErrorButton buttonTitle="Delete">
This team be deleted. It is not reversible. <br>Please think again.
</x-modal-confirmation>
<x-modal-confirmation title="Confirm Team Deletion?" buttonTitle="Delete" isErrorButton
submitAction="delete({{ currentTeam()->id }})" :actions="['The current team will be permanently deleted from Coolify and the database.']"
confirmationText="{{ currentTeam()->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Team Name below"
shortConfirmationLabel="Team Name" :confirmWithPassword="false" step2ButtonText="Permanently Delete" />
@else
<div>
<div class="pb-4">You need to delete the following resources to be able to delete the team:</div>