Merge branch 'next' into fix-environement-route
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
|
||||
html,
|
||||
body {
|
||||
@apply h-full bg-neutral-50 text-neutral-800 dark:bg-base dark:text-neutral-400;
|
||||
@apply h-full bg-neutral-50 text-neutral-800 dark:bg-base dark:text-neutral-400 w-full;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -30,6 +30,14 @@ body {
|
||||
@apply text-black dark:bg-coolgray-100 dark:text-white ring-neutral-200 dark:ring-coolgray-300;
|
||||
}
|
||||
|
||||
.input-sticky {
|
||||
@apply text-black dark:bg-coolgray-100 dark:text-white ring-neutral-200 dark:ring-coolgray-300 focus:ring-2 dark:focus:ring-coolgray-300 focus:ring-neutral-400 block w-full py-1.5 rounded border-0 text-sm ring-1 ring-inset;
|
||||
}
|
||||
|
||||
.input-sticky-active {
|
||||
@apply border-2 border-coollabs text-black dark:text-white focus:bg-neutral-200 dark:focus:bg-coolgray-400 focus:border-coollabs;
|
||||
}
|
||||
|
||||
/* Readonly */
|
||||
.input {
|
||||
@apply dark:read-only:text-neutral-500 dark:read-only:ring-0 dark:read-only:bg-coolgray-100/40 placeholder:text-neutral-300 dark:placeholder:text-neutral-700 read-only:text-neutral-500 read-only:bg-neutral-200;
|
||||
@@ -314,4 +322,4 @@ section {
|
||||
|
||||
.dz-button {
|
||||
@apply w-full p-4 py-10 my-4 font-bold bg-white border dark:border-coolgray-400 dark:text-white dark:bg-transparent hover:dark:bg-coolgray-400;
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,22 @@ export function initializeTerminalComponent() {
|
||||
};
|
||||
|
||||
},
|
||||
resetTerminal() {
|
||||
if (this.term) {
|
||||
this.$wire.dispatch('error', 'Terminal websocket connection lost.');
|
||||
this.term.reset();
|
||||
this.term.clear();
|
||||
this.pendingWrites = 0;
|
||||
this.paused = false;
|
||||
this.commandBuffer = '';
|
||||
|
||||
// Force a refresh
|
||||
this.$nextTick(() => {
|
||||
this.resizeTerminal();
|
||||
this.term.focus();
|
||||
});
|
||||
}
|
||||
},
|
||||
setupTerminal() {
|
||||
const terminalElement = document.getElementById('terminal');
|
||||
if (terminalElement) {
|
||||
@@ -69,9 +84,15 @@ export function initializeTerminalComponent() {
|
||||
rows: 30,
|
||||
fontFamily: '"Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"',
|
||||
cursorBlink: true,
|
||||
rendererType: 'canvas',
|
||||
convertEol: true,
|
||||
disableStdin: false
|
||||
});
|
||||
this.fitAddon = new FitAddon();
|
||||
this.term.loadAddon(this.fitAddon);
|
||||
this.$nextTick(() => {
|
||||
this.resizeTerminal();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -101,12 +122,19 @@ export function initializeTerminalComponent() {
|
||||
`${connectionString.protocol}://${connectionString.host}${connectionString.port}${connectionString.path}`
|
||||
this.socket = new WebSocket(url);
|
||||
|
||||
this.socket.onopen = () => {
|
||||
console.log('[Terminal] WebSocket connection established. Cool cool cool cool cool cool.');
|
||||
};
|
||||
|
||||
this.socket.onmessage = this.handleSocketMessage.bind(this);
|
||||
this.socket.onerror = (e) => {
|
||||
console.error('WebSocket error:', e);
|
||||
console.error('[Terminal] WebSocket error.');
|
||||
};
|
||||
this.socket.onclose = () => {
|
||||
console.log('WebSocket connection closed');
|
||||
console.warn('[Terminal] WebSocket connection closed.');
|
||||
this.resetTerminal();
|
||||
this.message = '(connection closed)';
|
||||
this.terminalActive = false;
|
||||
this.reconnect();
|
||||
};
|
||||
}
|
||||
@@ -117,19 +145,18 @@ export function initializeTerminalComponent() {
|
||||
clearInterval(this.reconnectInterval);
|
||||
}
|
||||
this.reconnectInterval = setInterval(() => {
|
||||
console.log('Attempting to reconnect...');
|
||||
console.warn('[Terminal] Attempting to reconnect...');
|
||||
this.initializeWebSocket();
|
||||
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
||||
console.log('Reconnected successfully');
|
||||
console.log('[Terminal] Reconnected successfully');
|
||||
clearInterval(this.reconnectInterval);
|
||||
this.reconnectInterval = null;
|
||||
window.location.reload();
|
||||
|
||||
}
|
||||
}, 2000);
|
||||
},
|
||||
|
||||
handleSocketMessage(event) {
|
||||
this.message = '(connection closed)';
|
||||
if (event.data === 'pty-ready') {
|
||||
if (!this.term._initialized) {
|
||||
this.term.open(document.getElementById('terminal'));
|
||||
@@ -150,8 +177,17 @@ export function initializeTerminalComponent() {
|
||||
this.term.reset();
|
||||
this.commandBuffer = '';
|
||||
} else {
|
||||
this.pendingWrites++;
|
||||
this.term.write(event.data, this.flowControlCallback.bind(this));
|
||||
try {
|
||||
this.pendingWrites++;
|
||||
this.term.write(event.data, (err) => {
|
||||
if (err) {
|
||||
console.error('[Terminal] Write error:', err);
|
||||
}
|
||||
this.flowControlCallback();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Terminal] Write operation failed:', error);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -173,11 +209,15 @@ export function initializeTerminalComponent() {
|
||||
if (!this.term) return;
|
||||
|
||||
this.term.onData((data) => {
|
||||
this.socket.send(JSON.stringify({ message: data }));
|
||||
if (data === '\r') {
|
||||
this.commandBuffer = '';
|
||||
if (this.socket.readyState === WebSocket.OPEN) {
|
||||
this.socket.send(JSON.stringify({ message: data }));
|
||||
if (data === '\r') {
|
||||
this.commandBuffer = '';
|
||||
} else {
|
||||
this.commandBuffer += data;
|
||||
}
|
||||
} else {
|
||||
this.commandBuffer += data;
|
||||
console.warn('[Terminal] WebSocket not ready, data not sent');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div
|
||||
class="w-full bg-white shadow md:mt-0 sm:max-w-md xl:p-0 dark:bg-base ">
|
||||
<div class="p-6 space-y-4 md:space-y-6 sm:p-8">
|
||||
@if (is_transactional_emails_active())
|
||||
@if (is_transactional_emails_enabled())
|
||||
<form action="/forgot-password" method="POST" class="flex flex-col gap-2">
|
||||
@csrf
|
||||
<x-forms.input required type="email" name="email" label="{{ __('input.email') }}" />
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
<div class="p-6 space-y-4 md:space-y-6 sm:p-8">
|
||||
<div class="p-6 space-y-4 md:space-y-3 sm:p-8">
|
||||
<form action="/login" method="POST" class="flex flex-col gap-2">
|
||||
@csrf
|
||||
@env('local')
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
@endisset>
|
||||
|
||||
{{ $slot }}
|
||||
@if ($attributes->whereStartsWith('wire:click')->first())
|
||||
<x-loading-on-button wire:target="{{ $attributes->whereStartsWith('wire:click')->first() }}"
|
||||
wire:loading.delay />
|
||||
@elseif($attributes->whereStartsWith('wire:target')->first())
|
||||
<x-loading-on-button wire:target="{{ $attributes->whereStartsWith('wire:target')->first() }}"
|
||||
wire:loading.delay />
|
||||
@if ($showLoadingIndicator)
|
||||
@if ($attributes->whereStartsWith('wire:click')->first())
|
||||
<x-loading-on-button wire:target="{{ $attributes->whereStartsWith('wire:click')->first() }}"
|
||||
wire:loading.delay />
|
||||
@elseif($attributes->whereStartsWith('wire:target')->first())
|
||||
<x-loading-on-button wire:target="{{ $attributes->whereStartsWith('wire:target')->first() }}"
|
||||
wire:loading.delay />
|
||||
@endif
|
||||
@endif
|
||||
</button>
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
'disabled' => false,
|
||||
'instantSave' => false,
|
||||
'value' => null,
|
||||
'domValue' => null,
|
||||
'checked' => false,
|
||||
'hideLabel' => false,
|
||||
'fullWidth' => false,
|
||||
])
|
||||
|
||||
@@ -14,26 +14,32 @@
|
||||
'flex flex-row items-center gap-4 pr-2 py-1 form-control min-w-fit dark:hover:bg-coolgray-100',
|
||||
'w-full' => $fullWidth,
|
||||
])>
|
||||
@if (!$hideLabel)
|
||||
<label @class([
|
||||
'flex gap-4 items-center px-0 min-w-fit label w-full cursor-pointer',
|
||||
])>
|
||||
<span class="flex flex-grow gap-2">
|
||||
@if ($label)
|
||||
{!! $label !!}
|
||||
@else
|
||||
{{ $id }}
|
||||
@endif
|
||||
@if ($helper)
|
||||
<x-helper :helper="$helper" />
|
||||
@endif
|
||||
</span>
|
||||
@endif
|
||||
<input @disabled($disabled) type="checkbox" {{ $attributes->merge(['class' => $defaultClass]) }}
|
||||
@if ($instantSave) wire:loading.attr="disabled" wire:click='{{ $instantSave === 'instantSave' || $instantSave == '1' ? 'instantSave' : $instantSave }}'
|
||||
@if ($checked) checked @endif
|
||||
wire:model={{ $id }} @else wire:model={{ $value ?? $id }} @endif />
|
||||
@if (!$hideLabel)
|
||||
</label>
|
||||
@endif
|
||||
<label @class([
|
||||
'flex gap-4 items-center px-0 min-w-fit label w-full cursor-pointer',
|
||||
])>
|
||||
<span class="flex flex-grow gap-2">
|
||||
@if ($label)
|
||||
{!! $label !!}
|
||||
@else
|
||||
{{ $id }}
|
||||
@endif
|
||||
@if ($helper)
|
||||
<x-helper :helper="$helper" />
|
||||
@endif
|
||||
</span>
|
||||
@if ($instantSave)
|
||||
<input type="checkbox" @disabled($disabled) {{ $attributes->merge(['class' => $defaultClass]) }}
|
||||
wire:loading.attr="disabled"
|
||||
wire:click='{{ $instantSave === 'instantSave' || $instantSave == '1' ? 'instantSave' : $instantSave }}'
|
||||
wire:model={{ $id }} @if ($checked) checked @endif />
|
||||
@else
|
||||
@if ($domValue)
|
||||
<input type="checkbox" @disabled($disabled) {{ $attributes->merge(['class' => $defaultClass]) }}
|
||||
value={{ $domValue }} @if ($checked) checked @endif />
|
||||
@else
|
||||
<input type="checkbox" @disabled($disabled) {{ $attributes->merge(['class' => $defaultClass]) }}
|
||||
wire:model={{ $value ?? $id }} @if ($checked) checked @endif />
|
||||
@endif
|
||||
@endif
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
monacoLoader: true,
|
||||
monacoFontSize: '15px',
|
||||
monacoId: $id('monaco-editor'),
|
||||
isDarkMode() {
|
||||
return document.documentElement.classList.contains('dark') || localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
},
|
||||
monacoEditor(editor) {
|
||||
editor.onDidChangeModelContent((e) => {
|
||||
this.monacoContent = editor.getValue();
|
||||
@@ -41,357 +44,9 @@
|
||||
let proxy = URL.createObjectURL(new Blob([` self.MonacoEnvironment = { baseUrl: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.39.0/min' }; importScripts('https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.39.0/min/vs/base/worker/workerMain.min.js');`], { type: 'text/javascript' }));
|
||||
window.MonacoEnvironment = { getWorkerUrl: () => proxy };
|
||||
require(['vs/editor/editor.main'], () => {
|
||||
monaco.editor.defineTheme('blackboard', {
|
||||
'base': 'vs-dark',
|
||||
'inherit': true,
|
||||
'rules': [{
|
||||
'background': editorBackground,
|
||||
'token': ''
|
||||
},
|
||||
{
|
||||
'foreground': '959da5',
|
||||
'token': 'comment'
|
||||
},
|
||||
{
|
||||
'foreground': '959da5',
|
||||
'token': 'punctuation.definition.comment'
|
||||
},
|
||||
{
|
||||
'foreground': '959da5',
|
||||
'token': 'string.comment'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'constant'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'entity.name.constant'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'variable.other.constant'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'variable.language'
|
||||
},
|
||||
{
|
||||
'foreground': 'b392f0',
|
||||
'token': 'entity'
|
||||
},
|
||||
{
|
||||
'foreground': 'b392f0',
|
||||
'token': 'entity.name'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'token': 'variable.parameter.function'
|
||||
},
|
||||
{
|
||||
'foreground': '7bcc72',
|
||||
'token': 'entity.name.tag'
|
||||
},
|
||||
{
|
||||
'foreground': 'ea4a5a',
|
||||
'token': 'keyword'
|
||||
},
|
||||
{
|
||||
'foreground': 'ea4a5a',
|
||||
'token': 'storage'
|
||||
},
|
||||
{
|
||||
'foreground': 'ea4a5a',
|
||||
'token': 'storage.type'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'token': 'storage.modifier.package'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'token': 'storage.modifier.import'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'token': 'storage.type.java'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'punctuation.definition.string'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string punctuation.section.embedded source'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'support'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'meta.property-name'
|
||||
},
|
||||
{
|
||||
'foreground': 'fb8532',
|
||||
'token': 'variable'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'token': 'variable.other'
|
||||
},
|
||||
{
|
||||
'foreground': 'd73a49',
|
||||
'fontStyle': 'bold italic underline',
|
||||
'token': 'invalid.broken'
|
||||
},
|
||||
{
|
||||
'foreground': 'd73a49',
|
||||
'fontStyle': 'bold italic underline',
|
||||
'token': 'invalid.deprecated'
|
||||
},
|
||||
{
|
||||
'foreground': 'fafbfc',
|
||||
'background': 'd73a49',
|
||||
'fontStyle': 'italic underline',
|
||||
'token': 'invalid.illegal'
|
||||
},
|
||||
{
|
||||
'foreground': 'fafbfc',
|
||||
'background': 'd73a49',
|
||||
'fontStyle': 'italic underline',
|
||||
'token': 'carriage-return'
|
||||
},
|
||||
{
|
||||
'foreground': 'd73a49',
|
||||
'fontStyle': 'bold italic underline',
|
||||
'token': 'invalid.unimplemented'
|
||||
},
|
||||
{
|
||||
'foreground': 'd73a49',
|
||||
'token': 'message.error'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'token': 'string source'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'string variable'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'source.regexp'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string.regexp'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string.regexp.character-class'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string.regexp constant.character.escape'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string.regexp source.ruby.embedded'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'token': 'string.regexp string.regexp.arbitrary-repitition'
|
||||
},
|
||||
{
|
||||
'foreground': '7bcc72',
|
||||
'fontStyle': 'bold',
|
||||
'token': 'string.regexp constant.character.escape'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'support.constant'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'support.variable'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'meta.module-reference'
|
||||
},
|
||||
{
|
||||
'foreground': 'fb8532',
|
||||
'token': 'markup.list'
|
||||
},
|
||||
{
|
||||
'foreground': '0366d6',
|
||||
'fontStyle': 'bold',
|
||||
'token': 'markup.heading'
|
||||
},
|
||||
{
|
||||
'foreground': '0366d6',
|
||||
'fontStyle': 'bold',
|
||||
'token': 'markup.heading entity.name'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'markup.quote'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'fontStyle': 'italic',
|
||||
'token': 'markup.italic'
|
||||
},
|
||||
{
|
||||
'foreground': 'f6f8fa',
|
||||
'fontStyle': 'bold',
|
||||
'token': 'markup.bold'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'markup.raw'
|
||||
},
|
||||
{
|
||||
'foreground': 'b31d28',
|
||||
'background': 'ffeef0',
|
||||
'token': 'markup.deleted'
|
||||
},
|
||||
{
|
||||
'foreground': 'b31d28',
|
||||
'background': 'ffeef0',
|
||||
'token': 'meta.diff.header.from-file'
|
||||
},
|
||||
{
|
||||
'foreground': 'b31d28',
|
||||
'background': 'ffeef0',
|
||||
'token': 'punctuation.definition.deleted'
|
||||
},
|
||||
{
|
||||
'foreground': '176f2c',
|
||||
'background': 'f0fff4',
|
||||
'token': 'markup.inserted'
|
||||
},
|
||||
{
|
||||
'foreground': '176f2c',
|
||||
'background': 'f0fff4',
|
||||
'token': 'meta.diff.header.to-file'
|
||||
},
|
||||
{
|
||||
'foreground': '176f2c',
|
||||
'background': 'f0fff4',
|
||||
'token': 'punctuation.definition.inserted'
|
||||
},
|
||||
{
|
||||
'foreground': 'b08800',
|
||||
'background': 'fffdef',
|
||||
'token': 'markup.changed'
|
||||
},
|
||||
{
|
||||
'foreground': 'b08800',
|
||||
'background': 'fffdef',
|
||||
'token': 'punctuation.definition.changed'
|
||||
},
|
||||
{
|
||||
'foreground': '2f363d',
|
||||
'background': '959da5',
|
||||
'token': 'markup.ignored'
|
||||
},
|
||||
{
|
||||
'foreground': '2f363d',
|
||||
'background': '959da5',
|
||||
'token': 'markup.untracked'
|
||||
},
|
||||
{
|
||||
'foreground': 'b392f0',
|
||||
'fontStyle': 'bold',
|
||||
'token': 'meta.diff.range'
|
||||
},
|
||||
{
|
||||
'foreground': 'c8e1ff',
|
||||
'token': 'meta.diff.header'
|
||||
},
|
||||
{
|
||||
'foreground': '0366d6',
|
||||
'fontStyle': 'bold',
|
||||
'token': 'meta.separator'
|
||||
},
|
||||
{
|
||||
'foreground': '0366d6',
|
||||
'token': 'meta.output'
|
||||
},
|
||||
{
|
||||
'foreground': 'ffeef0',
|
||||
'token': 'brackethighlighter.tag'
|
||||
},
|
||||
{
|
||||
'foreground': 'ffeef0',
|
||||
'token': 'brackethighlighter.curly'
|
||||
},
|
||||
{
|
||||
'foreground': 'ffeef0',
|
||||
'token': 'brackethighlighter.round'
|
||||
},
|
||||
{
|
||||
'foreground': 'ffeef0',
|
||||
'token': 'brackethighlighter.square'
|
||||
},
|
||||
{
|
||||
'foreground': 'ffeef0',
|
||||
'token': 'brackethighlighter.angle'
|
||||
},
|
||||
{
|
||||
'foreground': 'ffeef0',
|
||||
'token': 'brackethighlighter.quote'
|
||||
},
|
||||
{
|
||||
'foreground': 'd73a49',
|
||||
'token': 'brackethighlighter.unmatched'
|
||||
},
|
||||
{
|
||||
'foreground': 'd73a49',
|
||||
'token': 'sublimelinter.mark.error'
|
||||
},
|
||||
{
|
||||
'foreground': 'fb8532',
|
||||
'token': 'sublimelinter.mark.warning'
|
||||
},
|
||||
{
|
||||
'foreground': '6a737d',
|
||||
'token': 'sublimelinter.gutter-mark'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'fontStyle': 'underline',
|
||||
'token': 'constant.other.reference.link'
|
||||
},
|
||||
{
|
||||
'foreground': '79b8ff',
|
||||
'fontStyle': 'underline',
|
||||
'token': 'string.other.link'
|
||||
}
|
||||
],
|
||||
'colors': {
|
||||
'editor.foreground': '#f6f8fa',
|
||||
'editor.background': editorBackground,
|
||||
'editor.selectionBackground': '#4c2889',
|
||||
'editor.inactiveSelectionBackground': '#444d56',
|
||||
'editor.lineHighlightBackground': '#444d56',
|
||||
'editorCursor.foreground': '#ffffff',
|
||||
'editorWhitespace.foreground': '#6a737d',
|
||||
'editorIndentGuide.background': '#6a737d',
|
||||
'editorIndentGuide.activeBackground': '#f6f8fa',
|
||||
'editor.selectionHighlightBorder': '#444d56'
|
||||
}
|
||||
});
|
||||
|
||||
const editor = monaco.editor.create($refs.monacoEditorElement, {
|
||||
value: monacoContent,
|
||||
theme: editorTheme,
|
||||
theme: document.documentElement.classList.contains('dark') ? 'vs-dark' : 'vs',
|
||||
wordWrap: 'on',
|
||||
readOnly: '{{ $readonly ?? false }}',
|
||||
minimap: { enabled: false },
|
||||
@@ -399,7 +54,20 @@
|
||||
lineNumbersMinChars: 3,
|
||||
automaticLayout: true,
|
||||
language: '{{ $language }}'
|
||||
});
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.attributeName === 'class') {
|
||||
const isDark = document.documentElement.classList.contains('dark');
|
||||
monaco.editor.setTheme(isDark ? 'vs-dark' : 'vs');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class']
|
||||
});
|
||||
|
||||
monacoEditor(editor);
|
||||
@@ -411,7 +79,6 @@
|
||||
|
||||
updatePlaceholder(editor.getValue());
|
||||
|
||||
// Watch for changes in monacoContent from Livewire
|
||||
$watch('monacoContent', value => {
|
||||
if (editor.getValue() !== value) {
|
||||
editor.setValue(value);
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
@if (isset($text))
|
||||
<div>{{ $text }}</div>
|
||||
@endif
|
||||
<svg class="w-4 h-4 mx-1 ml-3 text-coollabs dark:text-warning animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<svg class="w-4 h-4 mx-1 ml-3 text-coollabs dark:text-warning animate-spin" xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
|
||||
|
||||
@@ -258,6 +258,7 @@
|
||||
<input type="text" x-model="confirmationText"
|
||||
class="p-2 pr-10 w-full text-black rounded cursor-text input" readonly>
|
||||
<button @click="copyConfirmationText()"
|
||||
x-show="window.isSecureContext"
|
||||
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">
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
'action' => 'delete',
|
||||
'content' => null,
|
||||
'closeOutside' => true,
|
||||
'minWidth' => '36rem',
|
||||
])
|
||||
<div x-data="{ modalOpen: false }" :class="{ 'z-40': modalOpen }" @keydown.window.escape="modalOpen=false"
|
||||
class="relative w-auto h-auto" wire:ignore>
|
||||
@@ -40,7 +41,7 @@
|
||||
x-transition:leave="ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave-end="opacity-0 -translate-y-2 sm:scale-95"
|
||||
class="relative w-full py-6 border rounded drop-shadow min-w-full lg:min-w-[36rem] max-w-fit bg-white border-neutral-200 dark:bg-base px-6 dark:border-coolgray-300">
|
||||
class="relative w-full py-6 border rounded drop-shadow min-w-full lg:min-w-[{{ $minWidth }}] max-w-fit bg-white border-neutral-200 dark:bg-base px-6 dark:border-coolgray-300">
|
||||
<div class="flex items-center justify-between pb-3">
|
||||
<h3 class="text-2xl font-bold">{{ $title }}</h3>
|
||||
<button @click="modalOpen=false"
|
||||
|
||||
@@ -7,8 +7,13 @@
|
||||
}
|
||||
window.location.reload();
|
||||
},
|
||||
setZoom(zoom) {
|
||||
localStorage.setItem('zoom', zoom);
|
||||
window.location.reload();
|
||||
},
|
||||
init() {
|
||||
this.full = localStorage.getItem('pageWidth');
|
||||
this.zoom = localStorage.getItem('zoom');
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
||||
const userSettings = localStorage.getItem('theme');
|
||||
if (userSettings !== 'system') {
|
||||
@@ -21,6 +26,7 @@
|
||||
}
|
||||
});
|
||||
this.queryTheme();
|
||||
this.checkZoom();
|
||||
},
|
||||
setTheme(type) {
|
||||
this.theme = type;
|
||||
@@ -44,6 +50,30 @@
|
||||
this.theme = 'system';
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
},
|
||||
checkZoom() {
|
||||
if (this.zoom === null) {
|
||||
this.setZoom(100);
|
||||
}
|
||||
if (this.zoom === '90') {
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
html {
|
||||
font-size: 93.75%;
|
||||
}
|
||||
|
||||
:root {
|
||||
--vh: 1vh;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
html {
|
||||
font-size: 87.5%;
|
||||
}
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
}
|
||||
}">
|
||||
<div class="flex pt-6 pb-4 pl-2">
|
||||
@@ -69,7 +99,7 @@
|
||||
</div>
|
||||
</x-slot:title>
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="mb-1 font-bold border-b dark:border-coolgray-500 dark:text-white text-md">Color</div>
|
||||
<div class="font-bold border-b dark:border-coolgray-500 dark:text-white text-md">Color</div>
|
||||
<button @click="setTheme('dark')" class="px-1 dropdown-item-no-padding">Dark</button>
|
||||
<button @click="setTheme('light')" class="px-1 dropdown-item-no-padding">Light</button>
|
||||
<button @click="setTheme('system')" class="px-1 dropdown-item-no-padding">System</button>
|
||||
@@ -78,6 +108,9 @@
|
||||
x-show="full === 'full'">Center</button>
|
||||
<button @click="switchWidth()" class="px-1 dropdown-item-no-padding"
|
||||
x-show="full === 'center'">Full</button>
|
||||
<div class="my-1 font-bold border-b dark:border-coolgray-500 dark:text-white text-md">Zoom</div>
|
||||
<button @click="setZoom(100)" class="px-1 dropdown-item-no-padding">100%</button>
|
||||
<button @click="setZoom(90)" class="px-1 dropdown-item-no-padding">90%</button>
|
||||
</div>
|
||||
</x-dropdown>
|
||||
</div>
|
||||
@@ -163,8 +196,8 @@
|
||||
class="{{ request()->is('storages*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||
href="{{ route('storage.index') }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2">
|
||||
<path d="M4 6a8 3 0 1 0 16 0A8 3 0 1 0 4 6" />
|
||||
<path d="M4 6v6a8 3 0 0 0 16 0V6" />
|
||||
<path d="M4 12v6a8 3 0 0 0 16 0v-6" />
|
||||
@@ -307,17 +340,19 @@
|
||||
</li>
|
||||
@endif
|
||||
|
||||
@if (isCloud() && isInstanceAdmin())
|
||||
<li>
|
||||
<a title="Admin" class="menu-item" href="/admin">
|
||||
<svg class="text-pink-600 icon" viewBox="0 0 256 256"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor"
|
||||
d="M177.62 159.6a52 52 0 0 1-34 34a12.2 12.2 0 0 1-3.6.55a12 12 0 0 1-3.6-23.45a28 28 0 0 0 18.32-18.32a12 12 0 0 1 22.9 7.2ZM220 144a92 92 0 0 1-184 0c0-28.81 11.27-58.18 33.48-87.28a12 12 0 0 1 17.9-1.33l19.69 19.11L127 19.89a12 12 0 0 1 18.94-5.12C168.2 33.25 220 82.85 220 144m-24 0c0-41.71-30.61-78.39-52.52-99.29l-20.21 55.4a12 12 0 0 1-19.63 4.5L80.71 82.36C67 103.38 60 124.06 60 144a68 68 0 0 0 136 0" />
|
||||
</svg>
|
||||
Admin
|
||||
</a>
|
||||
</li>
|
||||
@if (isCloud() || isDev())
|
||||
@if (isInstanceAdmin() || session('impersonating'))
|
||||
<li>
|
||||
<a title="Admin" class="menu-item" href="/admin">
|
||||
<svg class="text-pink-600 icon" viewBox="0 0 256 256"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor"
|
||||
d="M177.62 159.6a52 52 0 0 1-34 34a12.2 12.2 0 0 1-3.6.55a12 12 0 0 1-3.6-23.45a28 28 0 0 0 18.32-18.32a12 12 0 0 1 22.9 7.2ZM220 144a92 92 0 0 1-184 0c0-28.81 11.27-58.18 33.48-87.28a12 12 0 0 1 17.9-1.33l19.69 19.11L127 19.89a12 12 0 0 1 18.94-5.12C168.2 33.25 220 82.85 220 144m-24 0c0-41.71-30.61-78.39-52.52-99.29l-20.21 55.4a12 12 0 0 1-19.63 4.5L80.71 82.36C67 103.38 60 124.06 60 144a68 68 0 0 0 136 0" />
|
||||
</svg>
|
||||
Admin
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
@endif
|
||||
<div class="flex-1"></div>
|
||||
@if (isInstanceAdmin() && !isCloud())
|
||||
|
||||
@@ -7,14 +7,22 @@
|
||||
href="{{ route('notifications.email') }}">
|
||||
<button>Email</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('notifications.telegram') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('notifications.telegram') }}">
|
||||
<button>Telegram</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('notifications.discord') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('notifications.discord') }}">
|
||||
<button>Discord</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('notifications.telegram') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('notifications.telegram') }}">
|
||||
<button>Telegram</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('notifications.slack') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('notifications.slack') }}">
|
||||
<button>Slack</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('notifications.pushover') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('notifications.pushover') }}">
|
||||
<button>Pushover</button>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<li class="inline-flex items-center">
|
||||
<div class="flex items-center">
|
||||
<a class="text-xs truncate lg:text-sm"
|
||||
href="{{ route('project.show', ['project_uuid' => $this->parameters['project_uuid']]) }}">
|
||||
href="{{ route('project.show', ['project_uuid' => data_get($resource, 'environment.project.uuid')]) }}">
|
||||
{{ data_get($resource, 'environment.project.name', 'Undefined Name') }}</a>
|
||||
<svg aria-hidden="true" class="w-4 h-4 mx-1 font-bold dark:text-warning" fill="currentColor"
|
||||
viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
@@ -21,9 +21,7 @@
|
||||
<li>
|
||||
<div class="flex items-center">
|
||||
<a class="text-xs truncate lg:text-sm"
|
||||
href="{{ route('project.resource.index', ['environment_uuid' => $this->parameters['environment_uuid'], 'project_uuid' => $this->parameters['project_uuid']]) }}">
|
||||
{{ data_get($resource, 'environment.name', $this->parameters['environment_uuid']) }}
|
||||
</a>
|
||||
href="{{ route('project.resource.index', ['environment_uuid' => $this->parameters['environment_uuid', 'project_uuid' => data_get($resource, 'environment.project.uuid')]) }}">{{ data_get($resource, 'environment.name') }}</a>
|
||||
<svg aria-hidden="true" class="w-4 h-4 mx-1 font-bold dark:text-warning" fill="currentColor"
|
||||
viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
|
||||
@@ -49,5 +49,6 @@
|
||||
<div class="order-first sm:order-last">
|
||||
<livewire:server.proxy.deploy :server="$server" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,30 +5,42 @@
|
||||
'noLoading' => false,
|
||||
])
|
||||
<div class="flex items-center">
|
||||
@if (!$noLoading)
|
||||
<x-loading wire:loading.delay.longer />
|
||||
@endif
|
||||
<span wire:loading.remove.delay.longer class="flex items-center">
|
||||
<div class="badge badge-success "></div>
|
||||
<div class="pl-2 pr-1 text-xs font-bold tracking-wider text-success" @if($title) title="{{$title}}" @endif>
|
||||
@if ($lastDeploymentLink)
|
||||
<a href="{{ $lastDeploymentLink }}" target="_blank" class="underline cursor-pointer">
|
||||
{{ str($status)->before(':')->headline() }}
|
||||
</a>
|
||||
@else
|
||||
{{ str($status)->before(':')->headline() }}
|
||||
@endif
|
||||
</div>
|
||||
@if (!str($status)->startsWith('Proxy') && !str($status)->contains('('))
|
||||
@if (str($status)->contains('unhealthy'))
|
||||
<x-helper helper="Unhealthy state. <span class='dark:text-warning text-coollabs'>This doesn't mean that the resource is malfunctioning.</span><br><br>- If the resource is accessible, it indicates that no health check is configured - it is not mandatory.<br>- If the resource is not accessible (returning 404 or 503), it may indicate that a health check is needed and has not passed. <span class='dark:text-warning text-coollabs'>Your action is required.</span><br><br>More details in the <a href='https://coolify.io/docs/knowledge-base/traefik/healthcheck/' class='underline dark:text-warning text-coollabs' target='_blank'>documentation</a>." >
|
||||
<div class="flex items-center">
|
||||
<span wire:loading.delay.longer>
|
||||
<div class="badge badge-warning"></div>
|
||||
</span>
|
||||
<span wire:loading.remove.delay.longer>
|
||||
<div class="badge badge-success"></div>
|
||||
</span>
|
||||
<div class="pl-2 pr-1 text-xs font-bold tracking-wider text-success"
|
||||
@if ($title) title="{{ $title }}" @endif>
|
||||
@if ($lastDeploymentLink)
|
||||
<a href="{{ $lastDeploymentLink }}" target="_blank" class="underline cursor-pointer">
|
||||
{{ str($status)->before(':')->headline() }}
|
||||
</a>
|
||||
@else
|
||||
{{ str($status)->before(':')->headline() }}
|
||||
@endif
|
||||
</div>
|
||||
@php
|
||||
$showUnhealthyHelper =
|
||||
!str($status)->startsWith('Proxy') &&
|
||||
!str($status)->contains('(') &&
|
||||
str($status)->contains('unhealthy');
|
||||
@endphp
|
||||
@if ($showUnhealthyHelper)
|
||||
<x-helper
|
||||
helper="Unhealthy state. <span class='dark:text-warning text-coollabs'>This doesn't mean that the resource is malfunctioning.</span><br><br>- If the resource is accessible, it indicates that no health check is configured - it is not mandatory.<br>- If the resource is not accessible (returning 404 or 503), it may indicate that a health check is needed and has not passed. <span class='dark:text-warning text-coollabs'>Your action is required.</span><br><br>More details in the <a href='https://coolify.io/docs/knowledge-base/traefik/healthcheck/' class='underline dark:text-warning text-coollabs' target='_blank'>documentation</a>.">
|
||||
<x-slot:icon>
|
||||
<svg class="hidden w-4 h-4 dark:text-warning lg:block" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor" d="M240.26 186.1L152.81 34.23a28.74 28.74 0 0 0-49.62 0L15.74 186.1a27.45 27.45 0 0 0 0 27.71A28.31 28.31 0 0 0 40.55 228h174.9a28.31 28.31 0 0 0 24.79-14.19a27.45 27.45 0 0 0 .02-27.71m-20.8 15.7a4.46 4.46 0 0 1-4 2.2H40.55a4.46 4.46 0 0 1-4-2.2a3.56 3.56 0 0 1 0-3.73L124 46.2a4.77 4.77 0 0 1 8 0l87.44 151.87a3.56 3.56 0 0 1 .02 3.73M116 136v-32a12 12 0 0 1 24 0v32a12 12 0 0 1-24 0m28 40a16 16 0 1 1-16-16a16 16 0 0 1 16 16"></path>
|
||||
<svg class="hidden w-4 h-4 dark:text-warning lg:block" viewBox="0 0 256 256"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor"
|
||||
d="M240.26 186.1L152.81 34.23a28.74 28.74 0 0 0-49.62 0L15.74 186.1a27.45 27.45 0 0 0 0 27.71A28.31 28.31 0 0 0 40.55 228h174.9a28.31 28.31 0 0 0 24.79-14.19a27.45 27.45 0 0 0 .02-27.71m-20.8 15.7a4.46 4.46 0 0 1-4 2.2H40.55a4.46 4.46 0 0 1-4-2.2a3.56 3.56 0 0 1 0-3.73L124 46.2a4.77 4.77 0 0 1 8 0l87.44 151.87a3.56 3.56 0 0 1 .02 3.73M116 136v-32a12 12 0 0 1 24 0v32a12 12 0 0 1-24 0m28 40a16 16 0 1 1-16-16a16 16 0 0 1 16 16">
|
||||
</path>
|
||||
</svg>
|
||||
</x-slot:icon>
|
||||
</x-helper>
|
||||
@endif
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -11,16 +11,20 @@
|
||||
toast(this.title, { description: this.description, type: this.type, position: this.position, html: html })
|
||||
}
|
||||
}" x-init="window.toast = function(message, options = {}) {
|
||||
let description = '';
|
||||
let type = 'default';
|
||||
let position = 'top-center';
|
||||
let html = '';
|
||||
if (typeof options.description != 'undefined') description = options.description;
|
||||
if (typeof options.type != 'undefined') type = options.type;
|
||||
if (typeof options.position != 'undefined') position = options.position;
|
||||
if (typeof options.html != 'undefined') html = options.html;
|
||||
try {
|
||||
let description = '';
|
||||
let type = 'default';
|
||||
let position = 'top-center';
|
||||
let html = '';
|
||||
if (typeof options.description != 'undefined') description = options.description;
|
||||
if (typeof options.type != 'undefined') type = options.type;
|
||||
if (typeof options.position != 'undefined') position = options.position;
|
||||
if (typeof options.html != 'undefined') html = options.html;
|
||||
|
||||
window.dispatchEvent(new CustomEvent('toast-show', { detail: { type: type, message: message, description: description, position: position, html: html } }));
|
||||
window.dispatchEvent(new CustomEvent('toast-show', { detail: { type: type, message: message, description: description, position: position, html: html } }));
|
||||
} catch (error) {
|
||||
console.error('Error showing toast:', error);
|
||||
}
|
||||
}" class="relative space-y-5">
|
||||
<template x-teleport="body">
|
||||
<ul x-data="{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<a {{ $attributes->merge(['class' => 'text-xs cursor-pointer opacity-90 hover:opacity-100 dark:hover:text-white hover:text-black']) }}
|
||||
href="https://github.com/coollabsio/coolify/releases/tag/v{{ config('version') }}" target="_blank">
|
||||
v{{ config('version') }}
|
||||
</a>
|
||||
href="https://github.com/coollabsio/coolify/releases/tag/v{{ config('constants.coolify.version') }}" target="_blank">
|
||||
v{{ config('constants.coolify.version') }}
|
||||
</a>
|
||||
|
||||
8
resources/views/emails/docker-cleanup-failed.blade.php
Normal file
8
resources/views/emails/docker-cleanup-failed.blade.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<x-emails.layout>
|
||||
Docker Cleanup on {{ $name }} FAILED with the following error:
|
||||
|
||||
<pre>
|
||||
{{ $text }}
|
||||
</pre>
|
||||
|
||||
</x-emails.layout>
|
||||
9
resources/views/emails/docker-cleanup-success.blade.php
Normal file
9
resources/views/emails/docker-cleanup-success.blade.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<x-emails.layout>
|
||||
Docker Cleanup on {{ $name }} succeeded with the following message:
|
||||
|
||||
|
||||
<pre>
|
||||
{{ $text }}
|
||||
</pre>
|
||||
|
||||
</x-emails.layout>
|
||||
9
resources/views/emails/scheduled-task-success.blade.php
Normal file
9
resources/views/emails/scheduled-task-success.blade.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<x-emails.layout>
|
||||
Scheduled task ({{ $task->name }}) completed successfully with the following output:
|
||||
|
||||
<pre>
|
||||
{{ $output }}
|
||||
</pre>
|
||||
|
||||
Click [here]({{ $url }}) to view the task.
|
||||
</x-emails.layout>
|
||||
@@ -1,7 +0,0 @@
|
||||
<x-emails.layout>
|
||||
Someone added this email to the Coolify Cloud's waitlist. [Click here]({{ $confirmation_url }}) to confirm!
|
||||
|
||||
The link will expire in {{ config('constants.waitlist.expiration') }} minutes.
|
||||
|
||||
You have no idea what [Coolify Cloud](https://coolify.io) is or this waitlist? [Click here]({{ $cancel_url }}) to remove you from the waitlist.
|
||||
</x-emails.layout>
|
||||
@@ -1,3 +0,0 @@
|
||||
<x-emails.layout>
|
||||
You have been invited to join the Coolify Cloud: [Get Started]({{ $loginLink }})
|
||||
</x-emails.layout>
|
||||
@@ -34,11 +34,13 @@
|
||||
</style>
|
||||
@if (config('app.name') == 'Coolify Cloud')
|
||||
<script defer data-domain="app.coolify.io" src="https://analytics.coollabs.io/js/plausible.js"></script>
|
||||
<script src="https://js.sentry-cdn.com/0f8593910512b5cdd48c6da78d4093be.min.js" crossorigin="anonymous"></script>
|
||||
@endif
|
||||
@auth
|
||||
<script type="text/javascript" src="{{ URL::asset('js/echo.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ URL::asset('js/pusher.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ URL::asset('js/apexcharts.js') }}"></script>
|
||||
|
||||
@endauth
|
||||
</head>
|
||||
@section('body')
|
||||
@@ -94,6 +96,20 @@
|
||||
enableStats: false,
|
||||
enableLogging: true,
|
||||
enabledTransports: ['ws', 'wss'],
|
||||
disableStats: true,
|
||||
// Add auto reconnection settings
|
||||
enabledTransports: ['ws', 'wss'],
|
||||
disabledTransports: ['sockjs', 'xhr_streaming', 'xhr_polling'],
|
||||
// Attempt to reconnect on connection lost
|
||||
autoReconnect: true,
|
||||
// Wait 1 second before first reconnect attempt
|
||||
reconnectionDelay: 1000,
|
||||
// Maximum delay between reconnection attempts
|
||||
maxReconnectionDelay: 1000,
|
||||
// Multiply delay by this number for each reconnection attempt
|
||||
reconnectionDelayGrowth: 1,
|
||||
// Maximum number of reconnection attempts
|
||||
maxAttempts: 15
|
||||
});
|
||||
@endauth
|
||||
let checkHealthInterval = null;
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<div>
|
||||
<h1>Admin Dashboard</h1>
|
||||
<h3 class="pt-4">Who am I now?</h3>
|
||||
<div class="pb-4">{{ auth()->user()->name }}</div>
|
||||
<div class="flex gap-2 pt-4">
|
||||
<h3>Who am I now?</h3>
|
||||
@if (session('impersonating'))
|
||||
<x-forms.button wire:click="back">Go back to root</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
<div class="pb-4">{{ auth()->user()->name }} ({{ auth()->user()->email }})</div>
|
||||
<form wire:submit="submitSearch" class="flex flex-col gap-2 lg:flex-row">
|
||||
<x-forms.input wire:model="search" placeholder="Search for a user" />
|
||||
<x-forms.button type="submit">Search</x-forms.button>
|
||||
|
||||
@@ -11,16 +11,22 @@
|
||||
|
||||
let checkNumber = 1;
|
||||
let checkPusherInterval = null;
|
||||
let checkReconnectInterval = null;
|
||||
|
||||
if (!this.popups.realtime) {
|
||||
checkPusherInterval = setInterval(() => {
|
||||
if (window.Echo && window.Echo.connector.pusher.connection.state !== 'connected') {
|
||||
checkNumber++;
|
||||
if (checkNumber > 5) {
|
||||
this.popups.realtime = true;
|
||||
console.error(
|
||||
'Coolify could not connect to its real-time service. This will cause unusual problems on the UI if not fixed! Please check the related documentation (https://coolify.io/docs/knowledge-base/cloudflare/tunnels) or get help on Discord (https://coollabs.io/discord).)'
|
||||
);
|
||||
clearInterval(checkPusherInterval);
|
||||
if (window.Echo) {
|
||||
if (window.Echo.connector.pusher.connection.state === 'connected') {
|
||||
this.popups.realtime = false;
|
||||
} else {
|
||||
checkNumber++;
|
||||
if (checkNumber > 5) {
|
||||
this.popups.realtime = true;
|
||||
console.error(
|
||||
'Coolify could not connect to its real-time service. This will cause unusual problems on the UI if not fixed! Please check the related documentation (https://coolify.io/docs/knowledge-base/cloudflare/tunnels) or get help on Discord (https://coollabs.io/discord).)'
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Notifications | Coolify
|
||||
</x-slot>
|
||||
<x-notification.navbar />
|
||||
<form wire:submit='submit' class="flex flex-col gap-4">
|
||||
<form wire:submit='submit' class="flex flex-col gap-4 pb-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Discord</h2>
|
||||
<x-forms.button type="submit">
|
||||
@@ -12,7 +12,11 @@
|
||||
@if ($discordEnabled)
|
||||
<x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
|
||||
wire:click="sendTestNotification">
|
||||
Send Test Notifications
|
||||
Send Test Notification
|
||||
</x-forms.button>
|
||||
@else
|
||||
<x-forms.button disabled class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
|
||||
Send Test Notification
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
@@ -20,24 +24,58 @@
|
||||
<x-forms.checkbox instantSave="instantSaveDiscordEnabled" id="discordEnabled" label="Enabled" />
|
||||
</div>
|
||||
<x-forms.input type="password"
|
||||
helper="Generate a webhook in Discord.<br>Example: https://discord.com/api/webhooks/...." required
|
||||
helper="Create a Discord Server and generate a Webhook URL. <br><a class='inline-block underline dark:text-white' href='https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks' target='_blank'>Webhook Documentation</a>" required
|
||||
id="discordWebhookUrl" label="Webhook" />
|
||||
</form>
|
||||
@if ($discordEnabled)
|
||||
<h2 class="mt-4">Subscribe to events</h2>
|
||||
<div class="w-64">
|
||||
@if (isDev())
|
||||
<x-forms.checkbox instantSave="saveModel" id="discordNotificationsTest" label="Test" />
|
||||
@endif
|
||||
<x-forms.checkbox instantSave="saveModel" id="discordNotificationsStatusChanges"
|
||||
label="Container Status Changes" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="discordNotificationsDeployments"
|
||||
label="Application Deployments" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="discordNotificationsDatabaseBackups" label="Backup Status" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="discordNotificationsScheduledTasks"
|
||||
label="Scheduled Tasks Status" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="discordNotificationsServerDiskUsage"
|
||||
label="Server Disk Usage" />
|
||||
<h2 class="mt-4">Notification Settings</h2>
|
||||
<p class="mb-4">
|
||||
Select events for which you would like to receive Discord notifications.
|
||||
</p>
|
||||
<div class="flex flex-col gap-4 max-w-2xl">
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Deployments</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessDiscordNotifications"
|
||||
label="Deployment Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="deploymentFailureDiscordNotifications"
|
||||
label="Deployment Failure" />
|
||||
<x-forms.checkbox instantSave="saveModel"
|
||||
helper="Send a notification when a container status changes. It will notify for Stopped and Restarted events of a container."
|
||||
id="statusChangeDiscordNotifications" label="Container Status Changes" />
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Backups</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="backupSuccessDiscordNotifications"
|
||||
label="Backup Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="backupFailureDiscordNotifications"
|
||||
label="Backup Failure" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessDiscordNotifications"
|
||||
label="Scheduled Task Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskFailureDiscordNotifications"
|
||||
label="Scheduled Task Failure" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Server</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessDiscordNotifications"
|
||||
label="Docker Cleanup Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupFailureDiscordNotifications"
|
||||
label="Docker Cleanup Failure" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverDiskUsageDiscordNotifications"
|
||||
label="Server Disk Usage" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverReachableDiscordNotifications"
|
||||
label="Server Reachable" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverUnreachableDiscordNotifications"
|
||||
label="Server Unreachable" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,25 +9,27 @@
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
@if (isInstanceAdmin() && !$useInstanceEmailSettings)
|
||||
<x-forms.button wire:click='copyFromInstanceSettings'>
|
||||
Copy from Instance Settings
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@if (isEmailEnabled($team) && auth()->user()->isAdminFromSession() && isTestEmailEnabled($team))
|
||||
<x-modal-input buttonTitle="Send Test Email" title="Send Test Email">
|
||||
<form wire:submit.prevent="sendTestEmail" class="flex flex-col w-full gap-2">
|
||||
<x-forms.input wire:model="testEmailAddress" placeholder="test@example.com" id="testEmailAddress" label="Recipients" required />
|
||||
<x-forms.button type="submit" @click="modalOpen=false">
|
||||
Send Email
|
||||
</x-forms.button>
|
||||
</form>
|
||||
</x-modal-input>
|
||||
@if (auth()->user()->isAdminFromSession())
|
||||
@if ($team->isNotificationEnabled('email'))
|
||||
<x-modal-input buttonTitle="Send Test Email" title="Send Test Email">
|
||||
<form wire:submit.prevent="sendTestEmail" class="flex flex-col w-full gap-2">
|
||||
<x-forms.input wire:model="testEmailAddress" placeholder="test@example.com"
|
||||
id="testEmailAddress" label="Recipients" required />
|
||||
<x-forms.button type="submit" @click="modalOpen=false">
|
||||
Send Email
|
||||
</x-forms.button>
|
||||
</form>
|
||||
</x-modal-input>
|
||||
@else
|
||||
<x-forms.button disabled class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
|
||||
Send Test Email
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
@if (!isCloud())
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave="instantSaveInstance" id="useInstanceEmailSettings"
|
||||
<x-forms.checkbox instantSave="instantSave()" id="useInstanceEmailSettings"
|
||||
label="Use system wide (transactional) email settings" />
|
||||
</div>
|
||||
@endif
|
||||
@@ -37,17 +39,22 @@
|
||||
<x-forms.input required id="smtpFromAddress" helper="Email address used in emails."
|
||||
label="From Address" />
|
||||
</div>
|
||||
@if (isInstanceAdmin() && !$useInstanceEmailSettings)
|
||||
<x-forms.button wire:click='copyFromInstanceSettings'>
|
||||
Copy from Instance Settings
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@endif
|
||||
</form>
|
||||
@if (isCloud())
|
||||
<div class="w-64 py-4">
|
||||
<x-forms.checkbox instantSave="instantSaveInstance" id="useInstanceEmailSettings"
|
||||
<x-forms.checkbox instantSave="instantSave()" id="useInstanceEmailSettings"
|
||||
label="Use Hosted Email Service" />
|
||||
</div>
|
||||
@endif
|
||||
@if (!$useInstanceEmailSettings)
|
||||
<div class="flex flex-col gap-4">
|
||||
<form wire:submit='submit' class="p-4 border dark:border-coolgray-300 flex flex-col gap-2">
|
||||
<form wire:submit='submitSmtp' class="p-4 border dark:border-coolgray-300 flex flex-col gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<h3>SMTP Server</h3>
|
||||
<x-forms.button type="submit">
|
||||
@@ -55,15 +62,19 @@
|
||||
</x-forms.button>
|
||||
</div>
|
||||
<div class="w-32">
|
||||
<x-forms.checkbox instantSave="instantSaveSmtpEnabled" id="smtpEnabled" label="Enabled" />
|
||||
<x-forms.checkbox wire:model="smtpEnabled" instantSave="instantSave('SMTP')" id="smtpEnabled"
|
||||
label="Enabled" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||
<x-forms.input required id="smtpHost" placeholder="smtp.mailgun.org" label="Host" />
|
||||
<x-forms.input required id="smtpPort" placeholder="587" label="Port" />
|
||||
<x-forms.input id="smtpEncryption" helper="If SMTP uses SSL, set it to 'tls'."
|
||||
placeholder="tls" label="Encryption" />
|
||||
<x-forms.select id="smtpEncryption" label="Encryption">
|
||||
<option value="tls">TLS</option>
|
||||
<option value="ssl">SSL</option>
|
||||
<option value="none">None</option>
|
||||
</x-forms.select>
|
||||
</div>
|
||||
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||
<x-forms.input id="smtpUsername" label="SMTP Username" />
|
||||
@@ -74,7 +85,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form wire:submit='submit' class="p-4 border dark:border-coolgray-300 flex flex-col gap-2">
|
||||
<form wire:submit='submitResend' class="p-4 border dark:border-coolgray-300 flex flex-col gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<h3>Resend</h3>
|
||||
<x-forms.button type="submit">
|
||||
@@ -82,7 +93,8 @@
|
||||
</x-forms.button>
|
||||
</div>
|
||||
<div class="w-32">
|
||||
<x-forms.checkbox instantSave='instantSaveResend' id="resendEnabled" label="Enabled" />
|
||||
<x-forms.checkbox wire:model="resendEnabled" instantSave="instantSave('Resend')" id="resendEnabled"
|
||||
label="Enabled" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col gap-4">
|
||||
@@ -95,20 +107,55 @@
|
||||
</form>
|
||||
</div>
|
||||
@endif
|
||||
@if (isEmailEnabled($team) || $useInstanceEmailSettings)
|
||||
<h2 class="mt-4">Subscribe to events</h2>
|
||||
<div class="w-64">
|
||||
@if (isDev())
|
||||
<x-forms.checkbox instantSave="saveModel" id="smtpNotificationsTest" label="Test" />
|
||||
@endif
|
||||
<x-forms.checkbox instantSave="saveModel" id="smtpNotificationsStatusChanges"
|
||||
label="Container Status Changes" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="smtpNotificationsDeployments"
|
||||
label="Application Deployments" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="smtpNotificationsDatabaseBackups" label="Backup Status" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="smtpNotificationsScheduledTasks"
|
||||
label="Scheduled Tasks Status" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="smtpNotificationsServerDiskUsage" label="Server Disk Usage" />
|
||||
<h2 class="mt-4">Notification Settings</h2>
|
||||
<p class="mb-4">
|
||||
Select events for which you would like to receive email notifications.
|
||||
</p>
|
||||
<div class="flex flex-col gap-4 max-w-2xl">
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Deployments</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessEmailNotifications"
|
||||
label="Deployment Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="deploymentFailureEmailNotifications"
|
||||
label="Deployment Failure" />
|
||||
<x-forms.checkbox instantSave="saveModel"
|
||||
helper="Send an email when a container status changes. It will send and email for Stopped and Restarted events of a container."
|
||||
id="statusChangeEmailNotifications" label="Container Status Changes" />
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Backups</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="backupSuccessEmailNotifications"
|
||||
label="Backup Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="backupFailureEmailNotifications"
|
||||
label="Backup Failure" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessEmailNotifications"
|
||||
label="Scheduled Task Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskFailureEmailNotifications"
|
||||
label="Scheduled Task Failure" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Server</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessEmailNotifications"
|
||||
label="Docker Cleanup Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupFailureEmailNotifications"
|
||||
label="Docker Cleanup Failure" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverDiskUsageEmailNotifications"
|
||||
label="Server Disk Usage" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverReachableEmailNotifications"
|
||||
label="Server Reachable" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverUnreachableEmailNotifications"
|
||||
label="Server Unreachable" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
86
resources/views/livewire/notifications/pushover.blade.php
Normal file
86
resources/views/livewire/notifications/pushover.blade.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<div>
|
||||
<x-slot:title>
|
||||
Notifications | Coolify
|
||||
</x-slot>
|
||||
<x-notification.navbar />
|
||||
<form wire:submit='submit' class="flex flex-col gap-4 pb-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Pushover</h2>
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
@if ($pushoverEnabled)
|
||||
<x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
|
||||
wire:click="sendTestNotification">
|
||||
Send Test Notification
|
||||
</x-forms.button>
|
||||
@else
|
||||
<x-forms.button disabled class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
|
||||
Send Test Notification
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
<div class="w-32">
|
||||
<x-forms.checkbox instantSave="instantSavePushoverEnabled" id="pushoverEnabled" label="Enabled" />
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input type="password"
|
||||
helper="Get your User Key in Pushover. You need to be logged in to Pushover to see your user key in the top right corner. <br><a class='inline-block underline dark:text-white' href='https://pushover.net/' target='_blank'>Pushover Dashboard</a>"
|
||||
required id="pushoverUserKey" label="User Key" />
|
||||
<x-forms.input type="password"
|
||||
helper="Generate an API Token/Key in Pushover by creating a new application. <br><a class='inline-block underline dark:text-white' href='https://pushover.net/apps/build' target='_blank'>Create Pushover Application</a>"
|
||||
required id="pushoverApiToken" label="API Token" />
|
||||
</div>
|
||||
</form>
|
||||
<h2 class="mt-4">Notification Settings</h2>
|
||||
<p class="mb-4">
|
||||
Select events for which you would like to receive Pushover notifications.
|
||||
</p>
|
||||
<div class="flex flex-col gap-4 max-w-2xl">
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Deployments</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessPushoverNotifications"
|
||||
label="Deployment Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="deploymentFailurePushoverNotifications"
|
||||
label="Deployment Failure" />
|
||||
<x-forms.checkbox instantSave="saveModel"
|
||||
helper="Send a notification when a container status changes. It will notify for Stopped and Restarted events of a container."
|
||||
id="statusChangePushoverNotifications" label="Container Status Changes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Backups</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="backupSuccessPushoverNotifications"
|
||||
label="Backup Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="backupFailurePushoverNotifications"
|
||||
label="Backup Failure" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessPushoverNotifications"
|
||||
label="Scheduled Task Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskFailurePushoverNotifications"
|
||||
label="Scheduled Task Failure" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Server</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessPushoverNotifications"
|
||||
label="Docker Cleanup Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupFailurePushoverNotifications"
|
||||
label="Docker Cleanup Failure" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverDiskUsagePushoverNotifications"
|
||||
label="Server Disk Usage" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverReachablePushoverNotifications"
|
||||
label="Server Reachable" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverUnreachablePushoverNotifications"
|
||||
label="Server Unreachable" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
79
resources/views/livewire/notifications/slack.blade.php
Normal file
79
resources/views/livewire/notifications/slack.blade.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<div>
|
||||
<x-slot:title>
|
||||
Notifications | Coolify
|
||||
</x-slot>
|
||||
<x-notification.navbar />
|
||||
<form wire:submit='submit' class="flex flex-col gap-4 pb-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Slack</h2>
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
@if ($slackEnabled)
|
||||
<x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
|
||||
wire:click="sendTestNotification">
|
||||
Send Test Notification
|
||||
</x-forms.button>
|
||||
@else
|
||||
<x-forms.button disabled class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
|
||||
Send Test Notification
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
<div class="w-32">
|
||||
<x-forms.checkbox instantSave="instantSaveSlackEnabled" id="slackEnabled" label="Enabled" />
|
||||
</div>
|
||||
<x-forms.input type="password"
|
||||
helper="Create a Slack APP and generate a Incoming Webhook URL. <br><a class='inline-block underline dark:text-white' href='https://api.slack.com/apps' target='_blank'>Create Slack APP</a>" required
|
||||
id="slackWebhookUrl" label="Webhook" />
|
||||
</form>
|
||||
<h2 class="mt-4">Notification Settings</h2>
|
||||
<p class="mb-4">
|
||||
Select events for which you would like to receive Slack notifications.
|
||||
</p>
|
||||
<div class="flex flex-col gap-4 max-w-2xl">
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Deployments</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessSlackNotifications"
|
||||
label="Deployment Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="deploymentFailureSlackNotifications"
|
||||
label="Deployment Failure" />
|
||||
<x-forms.checkbox instantSave="saveModel"
|
||||
helper="Send a notification when a container status changes. It will notify for Stopped and Restarted events of a container."
|
||||
id="statusChangeSlackNotifications" label="Container Status Changes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Backups</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="backupSuccessSlackNotifications" label="Backup Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="backupFailureSlackNotifications" label="Backup Failure" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Scheduled Tasks</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessSlackNotifications"
|
||||
label="Scheduled Task Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskFailureSlackNotifications"
|
||||
label="Scheduled Task Failure" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="font-medium mb-3">Server</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessSlackNotifications"
|
||||
label="Docker Cleanup Success" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupFailureSlackNotifications"
|
||||
label="Docker Cleanup Failure" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverDiskUsageSlackNotifications"
|
||||
label="Server Disk Usage" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverReachableSlackNotifications"
|
||||
label="Server Reachable" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverUnreachableSlackNotifications"
|
||||
label="Server Unreachable" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3,7 +3,7 @@
|
||||
Notifications | Coolify
|
||||
</x-slot>
|
||||
<x-notification.navbar />
|
||||
<form wire:submit='submit' class="flex flex-col gap-4">
|
||||
<form wire:submit='submit' class="flex flex-col gap-4 pb-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Telegram</h2>
|
||||
<x-forms.button type="submit">
|
||||
@@ -12,7 +12,11 @@
|
||||
@if ($telegramEnabled)
|
||||
<x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
|
||||
wire:click="sendTestNotification">
|
||||
Send Test Notifications
|
||||
Send Test Notification
|
||||
</x-forms.button>
|
||||
@else
|
||||
<x-forms.button disabled class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
|
||||
Send Test Notification
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
@@ -20,61 +24,143 @@
|
||||
<x-forms.checkbox instantSave="instantSaveTelegramEnabled" id="telegramEnabled" label="Enabled" />
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
|
||||
<x-forms.input type="password" autocomplete="new-password"
|
||||
helper="Get it from the <a class='inline-block underline dark:text-white' href='https://t.me/botfather' target='_blank'>BotFather Bot</a> on Telegram."
|
||||
required id="telegramToken" label="Token" />
|
||||
<x-forms.input helper="Recommended to add your bot to a group chat and add its Chat ID here." required
|
||||
required id="telegramToken" label="Bot API Token" />
|
||||
<x-forms.input type="password" autocomplete="new-password" helper="Add your bot to a group chat and add its Chat ID here." required
|
||||
id="telegramChatId" label="Chat ID" />
|
||||
</div>
|
||||
@if ($telegramEnabled)
|
||||
<h2 class="mt-4">Subscribe to events</h2>
|
||||
<div class="flex flex-col gap-4 w-96">
|
||||
@if (isDev())
|
||||
<div class="flex flex-col">
|
||||
<h4>Test Notification</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="telegramNotificationsTest" label="Enabled" />
|
||||
<x-forms.input
|
||||
helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
|
||||
id="telegramNotificationsTestMessageThreadId" label="Custom Topic ID" />
|
||||
</form>
|
||||
<h2 class="mt-4">Notification Settings</h2>
|
||||
<p class="mb-4">
|
||||
Select events for which you would like to receive Telegram notifications.
|
||||
</p>
|
||||
<div class="flex flex-col gap-4 ">
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="text-lg font-medium mb-3">Deployments</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<div class="pl-1 flex gap-2">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave="saveModel" id="deploymentSuccessTelegramNotifications"
|
||||
label="Deployment Success" />
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex flex-col">
|
||||
<h4>Container Status Changes</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="telegramNotificationsStatusChanges" label="Enabled" />
|
||||
<x-forms.input
|
||||
helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
|
||||
id="telegramNotificationsStatusChangesMessageThreadId" label="Custom Topic ID" />
|
||||
<x-forms.input type="password" placeholder="Custom Telegram Thread ID"
|
||||
id="telegramNotificationsDeploymentSuccessThreadId" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<h4>Application Deployments</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="telegramNotificationsDeployments" label="Enabled" />
|
||||
<x-forms.input
|
||||
helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
|
||||
id="telegramNotificationsDeploymentsMessageThreadId" label="Custom Topic ID" />
|
||||
<div class="pl-1 flex gap-2">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave="saveModel" id="deploymentFailureTelegramNotifications"
|
||||
label="Deployment Failure" />
|
||||
</div>
|
||||
<x-forms.input type="password" placeholder="Custom Telegram Thread ID"
|
||||
id="telegramNotificationsDeploymentFailureThreadId" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<h4>Database Backup Status</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="telegramNotificationsDatabaseBackups"
|
||||
label="Enabled" />
|
||||
<x-forms.input
|
||||
helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
|
||||
id="telegramNotificationsDatabaseBackupsMessageThreadId" label="Custom Topic ID" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<h4>Scheduled Tasks Status</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="telegramNotificationsScheduledTasks"
|
||||
label="Enabled" />
|
||||
<x-forms.input
|
||||
helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
|
||||
id="telegramNotificationsScheduledTasksMessageThreadId" label="Custom Topic ID" />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<h4>Server Disk Usage</h4>
|
||||
<x-forms.checkbox instantSave="saveModel" id="telegramNotificationsServerDiskUsage"
|
||||
label="Enabled" />
|
||||
<div class="pl-1 flex gap-2">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave="saveModel" id="statusChangeTelegramNotifications"
|
||||
label="Container Status Changes"
|
||||
helper="Send a notification when a container status changes. It will send a notification for Stopped and Restarted events of a container." />
|
||||
</div>
|
||||
<x-forms.input type="password" id="telegramNotificationsStatusChangeThreadId"
|
||||
placeholder="Custom Telegram Thread ID" />
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</form>
|
||||
</div>
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="text-lg font-medium mb-3">Backups</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<div class="pl-1 flex gap-2">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave="saveModel" id="backupSuccessTelegramNotifications"
|
||||
label="Backup Success" />
|
||||
</div>
|
||||
<x-forms.input type="password" placeholder="Custom Telegram Thread ID"
|
||||
id="telegramNotificationsBackupSuccessThreadId" />
|
||||
</div>
|
||||
|
||||
<div class="pl-1 flex gap-2">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave="saveModel" id="backupFailureTelegramNotifications"
|
||||
label="Backup Failure" />
|
||||
</div>
|
||||
<x-forms.input type="password" placeholder="Custom Telegram Thread ID"
|
||||
id="telegramNotificationsBackupFailureThreadId" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="text-lg font-medium mb-3">Scheduled Tasks</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<div class="pl-1 flex gap-2">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessTelegramNotifications"
|
||||
label="Scheduled Task Success" />
|
||||
</div>
|
||||
<x-forms.input type="password" placeholder="Custom Telegram Thread ID"
|
||||
id="telegramNotificationsScheduledTaskSuccessThreadId" />
|
||||
</div>
|
||||
|
||||
<div class="pl-1 flex gap-2">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave="saveModel" id="scheduledTaskFailureTelegramNotifications"
|
||||
label="Scheduled Task Failure" />
|
||||
</div>
|
||||
<x-forms.input type="password" placeholder="Custom Telegram Thread ID"
|
||||
id="telegramNotificationsScheduledTaskFailureThreadId" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border dark:border-coolgray-300 p-4 rounded-lg">
|
||||
<h3 class="text-lg font-medium mb-3">Server</h3>
|
||||
<div class="flex flex-col gap-1.5 pl-1">
|
||||
<div class="pl-1 flex gap-2">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessTelegramNotifications"
|
||||
label="Docker Cleanup Success" />
|
||||
</div>
|
||||
<x-forms.input type="password" placeholder="Custom Telegram Thread ID"
|
||||
id="telegramNotificationsDockerCleanupSuccessThreadId" />
|
||||
</div>
|
||||
|
||||
<div class="pl-1 flex gap-2">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave="saveModel" id="dockerCleanupFailureTelegramNotifications"
|
||||
label="Docker Cleanup Failure" />
|
||||
</div>
|
||||
<x-forms.input type="password" placeholder="Custom Telegram Thread ID"
|
||||
id="telegramNotificationsDockerCleanupFailureThreadId" />
|
||||
</div>
|
||||
|
||||
<div class="pl-1 flex gap-2">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverDiskUsageTelegramNotifications"
|
||||
label="Server Disk Usage" />
|
||||
</div>
|
||||
<x-forms.input type="password" placeholder="Custom Telegram Thread ID"
|
||||
id="telegramNotificationsServerDiskUsageThreadId" />
|
||||
</div>
|
||||
|
||||
<div class="pl-1 flex gap-2">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverReachableTelegramNotifications"
|
||||
label="Server Reachable" />
|
||||
</div>
|
||||
<x-forms.input type="password" placeholder="Custom Telegram Thread ID"
|
||||
id="telegramNotificationsServerReachableThreadId" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="pl-1 flex gap-2">
|
||||
<div class="w-96">
|
||||
<x-forms.checkbox instantSave="saveModel" id="serverUnreachableTelegramNotifications"
|
||||
label="Server Unreachable" />
|
||||
</div>
|
||||
<x-forms.input type="password" placeholder="Custom Telegram Thread ID"
|
||||
id="telegramNotificationsServerUnreachableThreadId" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
class="font-mono pr-10"
|
||||
/>
|
||||
<button
|
||||
x-show="window.isSecureContext"
|
||||
@click="navigator.clipboard.writeText(secretKey); copiedSecretKey = true; setTimeout(() => copiedSecretKey = false, 2000)"
|
||||
class="absolute right-2 bottom-1 p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
>
|
||||
@@ -78,6 +79,7 @@
|
||||
class="font-mono pr-10"
|
||||
/>
|
||||
<button
|
||||
x-show="window.isSecureContext"
|
||||
@click="navigator.clipboard.writeText(otpUrl); copiedOtpUrl = true; setTimeout(() => copiedOtpUrl = false, 2000)"
|
||||
class="absolute right-2 bottom-1 p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
>
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
helper="Allow to automatically deploy Preview Deployments for all opened PR's.<br><br>Closing a PR will delete Preview Deployments."
|
||||
instantSave id="isPreviewDeploymentsEnabled" label="Preview Deployments" />
|
||||
@endif
|
||||
<x-forms.checkbox helper="Disable Docker build cache on every deployment." instantSave id="disableBuildCache"
|
||||
label="Disable Build Cache" />
|
||||
<x-forms.checkbox
|
||||
helper="Your application will be available only on https if your domain starts with https://..."
|
||||
instantSave id="isForceHttpsEnabled" label="Force Https" />
|
||||
|
||||
@@ -5,34 +5,36 @@
|
||||
<h1>Configuration</h1>
|
||||
<livewire:project.shared.configuration-checker :resource="$application" />
|
||||
<livewire:project.application.heading :application="$application" />
|
||||
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
|
||||
|
||||
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
|
||||
<div class="flex flex-col items-start gap-2 min-w-fit">
|
||||
<a class="menu-item" :class="activeTab === 'general' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'general'; window.location.hash = 'general'" href="#">General</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.configuration', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>General</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.advanced', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Advanced</a>
|
||||
@if ($application->destination->server->isSwarm())
|
||||
<a class="menu-item" :class="activeTab === 'swarm' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'swarm'; window.location.hash = 'swarm'" href="#">Swarm
|
||||
<a class="menu-item"
|
||||
wire:current.exact="menu-item-active
|
||||
href="{{ route('project.application.swarm', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Swarm
|
||||
Configuration</a>
|
||||
@endif
|
||||
<a class="menu-item" :class="activeTab === 'advanced' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'advanced'; window.location.hash = 'advanced'" href="#">Advanced</a>
|
||||
@if ($application->build_pack !== 'static')
|
||||
<a class="menu-item" :class="activeTab === 'environment-variables' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'"
|
||||
href="#">Environment
|
||||
Variables</a>
|
||||
@endif
|
||||
@if ($application->build_pack !== 'static')
|
||||
<a class="menu-item" :class="activeTab === 'storages' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'storages'; window.location.hash = 'storages'" href="#">Storages
|
||||
</a>
|
||||
@endif
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.environment-variables', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Environment Variables</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.persistent-storage', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Persistent Storage</a>
|
||||
@if ($application->git_based())
|
||||
<a class="menu-item" :class="activeTab === 'source' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'source'; window.location.hash = 'source'" href="#">Source</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.source', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Git Source</a>
|
||||
@endif
|
||||
<a class="menu-item" :class="activeTab === 'servers' && 'menu-item-active'" class="flex items-center gap-2"
|
||||
@click.prevent="activeTab = 'servers'; window.location.hash = 'servers'" href="#">Servers
|
||||
<a class="menu-item flex items-center gap-2" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.servers', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Servers
|
||||
@if (str($application->status)->contains('degraded'))
|
||||
<span title="Some servers are unavailable">
|
||||
<svg class="w-4 h-4 text-error" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
||||
@@ -50,102 +52,74 @@
|
||||
</span>
|
||||
@endif
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'scheduled-tasks' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'scheduled-tasks'; window.location.hash = 'scheduled-tasks'"
|
||||
href="#">Scheduled Tasks
|
||||
</a>
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.scheduled-tasks.show', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Scheduled Tasks</a>
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.webhooks', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Webhooks</a>
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.preview-deployments', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Preview Deployments</a>
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.healthcheck', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Healthcheck</a>
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.rollback', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Rollback</a>
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.resource-limits', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Resource Limits</a>
|
||||
|
||||
<a class="menu-item" :class="activeTab === 'webhooks' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'webhooks'; window.location.hash = 'webhooks'" href="#">Webhooks
|
||||
</a>
|
||||
@if ($application->git_based())
|
||||
<a class="menu-item" :class="activeTab === 'previews' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'previews'; window.location.hash = 'previews'" href="#">Preview
|
||||
Deployments
|
||||
</a>
|
||||
@endif
|
||||
@if ($application->build_pack !== 'static' && $application->build_pack !== 'dockercompose')
|
||||
<a class="menu-item" :class="activeTab === 'health' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'health'; window.location.hash = 'health'" href="#">Healthchecks
|
||||
</a>
|
||||
@endif
|
||||
<a class="menu-item" :class="activeTab === 'rollback' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'rollback'; window.location.hash = 'rollback'" href="#">Rollback
|
||||
</a>
|
||||
@if ($application->build_pack !== 'dockercompose')
|
||||
<a class="menu-item" :class="activeTab === 'resource-limits' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'resource-limits'; window.location.hash = 'resource-limits'"
|
||||
href="#">Resource Limits
|
||||
</a>
|
||||
@endif
|
||||
<a class="menu-item" :class="activeTab === 'resource-operations' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'resource-operations'; window.location.hash = 'resource-operations'"
|
||||
href="#">Resource Operations
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'metrics' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'metrics'; window.location.hash = 'metrics'" href="#">Metrics
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'tags' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'tags'; window.location.hash = 'tags'" href="#">Tags
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'danger' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'danger'; window.location.hash = 'danger'" href="#">Danger Zone
|
||||
</a>
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.resource-operations', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Resource Operations</a>
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.metrics', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Metrics</a>
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.tags', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Tags</a>
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.danger', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Danger Zone</a>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<div x-cloak x-show="activeTab === 'general'" class="h-full">
|
||||
@if (request()->route()->getName() === 'project.application.configuration')
|
||||
<livewire:project.application.general :application="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'swarm'" class="h-full">
|
||||
@elseif (request()->route()->getName() === 'project.application.swarm' && $application->destination->server->isSwarm())
|
||||
<livewire:project.application.swarm :application="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'advanced'" class="h-full">
|
||||
@elseif (request()->route()->getName() === 'project.application.advanced')
|
||||
<livewire:project.application.advanced :application="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'environment-variables'">
|
||||
@elseif (request()->route()->getName() === 'project.application.environment-variables')
|
||||
<livewire:project.shared.environment-variable.all :resource="$application" />
|
||||
</div>
|
||||
@if ($application->git_based())
|
||||
<div x-cloak x-show="activeTab === 'source'">
|
||||
<livewire:project.application.source :application="$application" />
|
||||
</div>
|
||||
@endif
|
||||
<div x-cloak x-show="activeTab === 'servers'">
|
||||
<livewire:project.shared.destination :resource="$application" :servers="$servers" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'storages'">
|
||||
<livewire:project.service.storage :resource="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'webhooks'">
|
||||
<livewire:project.shared.webhooks :resource="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'previews'">
|
||||
<livewire:project.application.previews :application="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'health'">
|
||||
<livewire:project.shared.health-checks :resource="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'rollback'">
|
||||
<livewire:project.application.rollback :application="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'resource-limits'">
|
||||
<livewire:project.shared.resource-limits :resource="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'scheduled-tasks'">
|
||||
@elseif (request()->route()->getName() === 'project.application.persistent-storage')
|
||||
<livewire:project.service.storage :resource="$application" />
|
||||
@elseif (request()->route()->getName() === 'project.application.source' && $application->git_based())
|
||||
<livewire:project.application.source :application="$application" />
|
||||
@elseif (request()->route()->getName() === 'project.application.servers')
|
||||
<livewire:project.shared.destination :resource="$application" />
|
||||
@elseif (request()->route()->getName() === 'project.application.scheduled-tasks.show')
|
||||
<livewire:project.shared.scheduled-task.all :resource="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'resource-operations'">
|
||||
@elseif (request()->route()->getName() === 'project.application.webhooks')
|
||||
<livewire:project.shared.webhooks :resource="$application" />
|
||||
@elseif (request()->route()->getName() === 'project.application.preview-deployments')
|
||||
<livewire:project.application.previews :application="$application" />
|
||||
@elseif (request()->route()->getName() === 'project.application.healthcheck')
|
||||
<livewire:project.shared.health-checks :resource="$application" />
|
||||
@elseif (request()->route()->getName() === 'project.application.rollback')
|
||||
<livewire:project.application.rollback :application="$application" />
|
||||
@elseif (request()->route()->getName() === 'project.application.resource-limits')
|
||||
<livewire:project.shared.resource-limits :resource="$application" />
|
||||
@elseif (request()->route()->getName() === 'project.application.resource-operations')
|
||||
<livewire:project.shared.resource-operations :resource="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'metrics'">
|
||||
@elseif (request()->route()->getName() === 'project.application.metrics')
|
||||
<livewire:project.shared.metrics :resource="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'tags'">
|
||||
<livewire:project.shared.tags :resource="$application" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'danger'">
|
||||
@elseif (request()->route()->getName() === 'project.application.tags')
|
||||
<livewire:project.shared.tags :resource="$application" />
|
||||
@elseif (request()->route()->getName() === 'project.application.danger')
|
||||
<livewire:project.shared.danger :resource="$application" />
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
@forelse ($deployments as $deployment)
|
||||
<div @class([
|
||||
'dark:bg-coolgray-100 p-2 border-l-2 transition-colors hover:no-underline box-without-bg-without-border bg-white flex-col cursor-pointer dark:hover:text-neutral-400 dark:hover:bg-coolgray-200',
|
||||
'border-warning border-dashed ' =>
|
||||
'border-white border-dashed ' =>
|
||||
data_get($deployment, 'status') === 'in_progress' ||
|
||||
data_get($deployment, 'status') === 'cancelled-by-user',
|
||||
'border-error border-dashed ' =>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
toggleScroll() {
|
||||
this.alwaysScroll = !this.alwaysScroll;
|
||||
|
||||
|
||||
if (this.alwaysScroll) {
|
||||
this.intervalId = setInterval(() => {
|
||||
const screen = document.getElementById('screen');
|
||||
@@ -58,30 +58,34 @@
|
||||
<div @if ($isKeepAliveOn) wire:poll.2000ms="polling" @endif
|
||||
class="flex flex-col-reverse w-full p-2 px-4 mt-4 overflow-y-auto bg-white dark:text-white dark:bg-coolgray-100 scrollbar dark:border-coolgray-300"
|
||||
:class="fullscreen ? '' : 'min-h-14 max-h-[40rem] border border-dotted rounded'">
|
||||
<div :class="fullscreen ? 'fixed' : 'absolute'" class="top-4 right-6">
|
||||
<div :class="fullscreen ? 'fixed' : 'absolute'" class="top-2 right-3">
|
||||
<div class="flex justify-end gap-4 fixed -translate-x-full">
|
||||
<button title="Toggle timestamps" x-on:click="showTimestamps = !showTimestamps">
|
||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<svg class="w-5 h-5 opacity-30 hover:opacity-100" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor"
|
||||
stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button title="Go Top" x-show="fullscreen" x-on:click="goTop">
|
||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg class="w-5 h-5 opacity-30 hover:opacity-100" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2" d="M12 5v14m4-10l-4-4M8 9l4-4" />
|
||||
</svg>
|
||||
</button>
|
||||
<button title="Follow Logs" x-show="fullscreen" :class="alwaysScroll ? 'dark:text-warning' : ''"
|
||||
x-on:click="toggleScroll">
|
||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg class="w-5 h-5 opacity-30 hover:opacity-100" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2" d="M12 5v14m4-4l-4 4m-4-4l4 4" />
|
||||
</svg>
|
||||
</button>
|
||||
<button title="Fullscreen" x-show="!fullscreen" x-on:click="makeFullscreen">
|
||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg class="w-5 h-5 opacity-30 hover:opacity-100" viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none">
|
||||
<path
|
||||
d="M24 0v24H0V0h24ZM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035c-.01-.004-.019-.001-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427c-.002-.01-.009-.017-.017-.018Zm.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093c.012.004.023 0 .029-.008l.004-.014l-.034-.614c-.003-.012-.01-.02-.02-.022Zm-.715.002a.023.023 0 0 0-.027.006l-.006.014l-.034.614c0 .012.007.02.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01l-.184-.092Z" />
|
||||
@@ -91,7 +95,8 @@
|
||||
</svg>
|
||||
</button>
|
||||
<button title="Minimize" x-show="fullscreen" x-on:click="makeFullscreen">
|
||||
<svg class="icon" viewBox="0 0 24 24"xmlns="http://www.w3.org/2000/svg">
|
||||
<svg class="w-5 h-5 opacity-30 hover:opacity-100"
|
||||
viewBox="0 0 24 24"xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="none" stroke="currentColor" stroke-linecap="round"
|
||||
stroke-linejoin="round" stroke-width="2"
|
||||
d="M6 14h4m0 0v4m0-4l-6 6m14-10h-4m0 0V6m0 4l6-6" />
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
<option value="non-www">Redirect to non-www.</option>
|
||||
</x-forms.select>
|
||||
<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 . '/' }}"
|
||||
submitAction="setRedirect" :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>
|
||||
@@ -161,8 +161,7 @@
|
||||
</div>
|
||||
<div class="pt-1 text-xs">Nixpacks will detect the required configuration
|
||||
automatically.
|
||||
<a class="underline"
|
||||
href="https://coolify.io/docs/resources/applications/index">Framework
|
||||
<a class="underline" href="https://coolify.io/docs/applications">Framework
|
||||
Specific Docs</a>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -2,17 +2,21 @@
|
||||
<x-resources.breadcrumbs :resource="$application" :parameters="$parameters" :title="$lastDeploymentInfo" :lastDeploymentLink="$lastDeploymentLink" />
|
||||
<div class="navbar-main">
|
||||
<nav class="flex flex-shrink-0 gap-6 items-center whitespace-nowrap scrollbar min-h-10">
|
||||
<a href="{{ route('project.application.configuration', $parameters) }}">
|
||||
<a class="{{ request()->routeIs('project.application.configuration') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('project.application.configuration', $parameters) }}">
|
||||
Configuration
|
||||
</a>
|
||||
<a href="{{ route('project.application.deployment.index', $parameters) }}">
|
||||
<a class="{{ request()->routeIs('project.application.deployment.index') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('project.application.deployment.index', $parameters) }}">
|
||||
<button>Deployments</button>
|
||||
</a>
|
||||
<a href="{{ route('project.application.logs', $parameters) }}">
|
||||
<a class="{{ request()->routeIs('project.application.logs') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('project.application.logs', $parameters) }}">
|
||||
<button>Logs</button>
|
||||
</a>
|
||||
@if (!$application->destination->server->isSwarm())
|
||||
<a href="{{ route('project.application.command', $parameters) }}">
|
||||
<a class="{{ request()->routeIs('project.application.command') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('project.application.command', $parameters) }}">
|
||||
<button>Terminal</button>
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<h4 class="py-2 ">Select another Private Key</h4>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@foreach ($privateKeys as $key)
|
||||
<x-forms.button wire:click.defer="setPrivateKey('{{ $key->id }}')">{{ $key->name }}
|
||||
<x-forms.button wire:click="setPrivateKey('{{ $key->id }}')">{{ $key->name }}
|
||||
</x-forms.button>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@@ -5,57 +5,45 @@
|
||||
<h1>Configuration</h1>
|
||||
<livewire:project.shared.configuration-checker :resource="$database" />
|
||||
<livewire:project.database.heading :database="$database" />
|
||||
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
|
||||
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
|
||||
<div class="flex flex-col items-start gap-2 min-w-fit">
|
||||
<a class="menu-item" :class="activeTab === 'general' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'general';
|
||||
window.location.hash = 'general'"
|
||||
href="#">General</a>
|
||||
<a class="menu-item" :class="activeTab === 'environment-variables' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'"
|
||||
href="#">Environment
|
||||
Variables</a>
|
||||
<a class="menu-item" :class="activeTab === 'servers' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'servers';
|
||||
window.location.hash = 'servers'"
|
||||
href="#">Servers
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'storages' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'storages';
|
||||
window.location.hash = 'storages'"
|
||||
href="#">Storages
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'import' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'import';
|
||||
window.location.hash = 'import'" href="#">Import
|
||||
Backup
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'webhooks' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'webhooks'; window.location.hash = 'webhooks'" href="#">Webhooks
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'resource-limits' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'resource-limits';
|
||||
window.location.hash = 'resource-limits'"
|
||||
href="#">Resource Limits
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'resource-operations' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'resource-operations'; window.location.hash = 'resource-operations'"
|
||||
href="#">Resource Operations
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'metrics' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'metrics'; window.location.hash = 'metrics'" href="#">Metrics
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'tags' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'tags'; window.location.hash = 'tags'" href="#">Tags
|
||||
</a>
|
||||
<a class="menu-item" :class="activeTab === 'danger' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'danger';
|
||||
window.location.hash = 'danger'"
|
||||
href="#">Danger Zone
|
||||
</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.database.configuration', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>General</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.database.environment-variables', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>Environment Variables</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.database.servers', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>Servers</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.database.persistent-storage', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>Persistent Storage</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.database.import-backups', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>Import Backups</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.database.webhooks', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>Webhooks</a>
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.database.resource-limits', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>Resource Limits</a>
|
||||
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.database.resource-operations', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>Resource Operations</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.database.metrics', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>Metrics</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.database.tags', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>Tags</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.database.danger', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>Danger Zone</a>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<div x-cloak x-show="activeTab === 'general'" class="h-full">
|
||||
@if (request()->route()->getName() === 'project.database.configuration')
|
||||
@if ($database->type() === 'standalone-postgresql')
|
||||
<livewire:project.database.postgresql.general :database="$database" />
|
||||
@elseif ($database->type() === 'standalone-redis')
|
||||
@@ -73,37 +61,27 @@
|
||||
@elseif ($database->type() === 'standalone-clickhouse')
|
||||
<livewire:project.database.clickhouse.general :database="$database" />
|
||||
@endif
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'environment-variables'">
|
||||
@elseif (request()->route()->getName() === 'project.database.environment-variables')
|
||||
<livewire:project.shared.environment-variable.all :resource="$database" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'servers'">
|
||||
@elseif (request()->route()->getName() === 'project.database.servers')
|
||||
<livewire:project.shared.destination :resource="$database" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'storages'">
|
||||
@elseif (request()->route()->getName() === 'project.database.persistent-storage')
|
||||
<livewire:project.service.storage :resource="$database" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'webhooks'">
|
||||
<livewire:project.shared.webhooks :resource="$database" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'resource-limits'">
|
||||
<livewire:project.shared.resource-limits :resource="$database" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'import'">
|
||||
@elseif (request()->route()->getName() === 'project.database.import-backups')
|
||||
<livewire:project.database.import :resource="$database" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'resource-operations'">
|
||||
@elseif (request()->route()->getName() === 'project.database.webhooks')
|
||||
<livewire:project.shared.webhooks :resource="$database" />
|
||||
@elseif (request()->route()->getName() === 'project.database.resource-limits')
|
||||
<livewire:project.shared.resource-limits :resource="$database" />
|
||||
@elseif (request()->route()->getName() === 'project.database.resource-operations')
|
||||
<livewire:project.shared.resource-operations :resource="$database" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'metrics'">
|
||||
@elseif (request()->route()->getName() === 'project.database.metrics')
|
||||
<livewire:project.shared.metrics :resource="$database" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'tags'">
|
||||
<livewire:project.shared.tags :resource="$database" lazy />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'danger'">
|
||||
@elseif (request()->route()->getName() === 'project.database.tags')
|
||||
<livewire:project.shared.tags :resource="$database" />
|
||||
@elseif (request()->route()->getName() === 'project.database.danger')
|
||||
<livewire:project.shared.danger :resource="$database" />
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<x-slide-over @startdatabase.window="slideOverOpen = true" closeWithX fullScreen>
|
||||
<x-slot:title>Database Startup</x-slot:title>
|
||||
<x-slot:content>
|
||||
<livewire:activity-monitor header="Logs" showWaiting />
|
||||
<livewire:activity-monitor header="Logs" showWaiting fullHeight />
|
||||
</x-slot:content>
|
||||
</x-slide-over>
|
||||
<div class="navbar-main">
|
||||
|
||||
@@ -12,9 +12,10 @@
|
||||
<div class="pb-4">Deploy resources, like Applications, Databases, Services...</div>
|
||||
<div x-data="searchResources()">
|
||||
@if ($current_step === 'type')
|
||||
<div class="sticky top-0 z-50 py-2">
|
||||
<input autocomplete="off" x-ref="searchInput" class="input w-full" x-model="search"
|
||||
placeholder="Type / to search..." @keydown.window.slash.prevent="$refs.searchInput.focus()">
|
||||
<div x-init="window.addEventListener('scroll', () => isSticky = window.pageYOffset > 100)" class="sticky top-0 z-50 py-2">
|
||||
<input autocomplete="off" x-ref="searchInput" class="input-sticky"
|
||||
:class="{ 'input-sticky-active': isSticky }" x-model="search" placeholder="Type / to search..."
|
||||
@keydown.window.slash.prevent="$refs.searchInput.focus()">
|
||||
</div>
|
||||
<div x-show="loading">Loading...</div>
|
||||
<div x-show="!loading" class="flex flex-col gap-4 py-4">
|
||||
@@ -30,8 +31,7 @@
|
||||
<span x-html="application.description"></span>
|
||||
</x-slot>
|
||||
<x-slot:logo>
|
||||
<img class="w-[4.5rem]
|
||||
aspect-square h-[4.5rem] p-2 transition-all duration-200 opacity-30 grayscale group-hover:grayscale-0 group-hover:opacity-100 "
|
||||
<img class="w-[4.5rem] aspect-square h-[4.5rem] p-2 transition-all duration-200 dark:opacity-30 grayscale group-hover:grayscale-0 group-hover:opacity-100 dark:bg-white/10 bg-black/10"
|
||||
:src="application.logo">
|
||||
</x-slot:logo>
|
||||
</x-resource-view>
|
||||
@@ -47,8 +47,7 @@
|
||||
<x-slot:title><span x-text="application.name"></span></x-slot>
|
||||
<x-slot:description><span x-text="application.description"></span></x-slot>
|
||||
<x-slot:logo> <img
|
||||
class="w-[4.5rem]
|
||||
aspect-square h-[4.5rem] p-2 transition-all duration-200 opacity-30 grayscale group-hover:grayscale-0 group-hover:opacity-100 "
|
||||
class="w-[4.5rem] aspect-square h-[4.5rem] p-2 transition-all duration-200 dark:opacity-30 grayscale group-hover:grayscale-0 group-hover:opacity-100 dark:bg-white/10 bg-black/10 "
|
||||
:src="application.logo"></x-slot>
|
||||
</x-resource-view>
|
||||
</div>
|
||||
@@ -100,7 +99,7 @@
|
||||
</x-slot>
|
||||
<x-slot:logo>
|
||||
<template x-if="service.logo">
|
||||
<img class="w-[4.5rem] aspect-square h-[4.5rem] p-2 transition-all duration-200 opacity-30 grayscale group-hover:grayscale-0 group-hover:opacity-100"
|
||||
<img class="w-[4.5rem] aspect-square h-[4.5rem] p-2 transition-all duration-200 dark:opacity-30 grayscale group-hover:grayscale-0 group-hover:opacity-100 dark:bg-white/10 bg-black/10"
|
||||
:src='service.logo'
|
||||
x-on:error.window="$event.target.src = service.logo_github_url"
|
||||
onerror="this.onerror=null; this.src=this.getAttribute('data-fallback');"
|
||||
@@ -111,7 +110,7 @@
|
||||
<x-slot:documentation>
|
||||
<template x-if="service.documentation">
|
||||
<div class="flex items-center px-2" title="Read the documentation.">
|
||||
<a class="p-2 rounded hover:bg-coolgray-200 hover:no-underline group-hover:dark:text-white text-neutral-600"
|
||||
<a class="p-2 rounded hover:bg-gray-100 dark:hover:bg-coolgray-200 hover:no-underline group-hover:dark:text-white text-neutral-600"
|
||||
onclick="event.stopPropagation()" :href="service.documentation"
|
||||
target="_blank">
|
||||
Docs
|
||||
@@ -138,6 +137,7 @@
|
||||
return {
|
||||
search: '',
|
||||
loading: false,
|
||||
isSticky: false,
|
||||
services: [],
|
||||
gitBasedApplications: [],
|
||||
dockerBasedApplications: [],
|
||||
@@ -170,7 +170,8 @@
|
||||
}
|
||||
const filtered = Object.values(items).filter(item => {
|
||||
return (item.name?.toLowerCase().includes(searchLower) ||
|
||||
item.description?.toLowerCase().includes(searchLower))
|
||||
item.description?.toLowerCase().includes(searchLower) ||
|
||||
item.slogan?.toLowerCase().includes(searchLower))
|
||||
})
|
||||
return isSort ? filtered.sort(sortFn) : filtered;
|
||||
},
|
||||
@@ -272,77 +273,74 @@
|
||||
<h2>Select a Postgresql type</h2>
|
||||
<div>If you need extra extensions, you can select Supabase PostgreSQL (or others), otherwise select PostgreSQL
|
||||
16 (default).</div>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="gap-2 border border-transparent cursor-pointer box-without-bg dark:bg-coolgray-100 bg-white dark:hover:text-neutral-400 dark:hover:bg-coollabs group flex"
|
||||
wire:click="setPostgresqlType('postgres:16-alpine')">
|
||||
<div class="flex flex-col">
|
||||
<div class="box-title">PostgreSQL 16 (default)</div>
|
||||
<div class="box-description">
|
||||
PostgreSQL is a powerful, open-source object-relational database system (no extensions).
|
||||
</div>
|
||||
<div class="flex flex-col gap-6 pt-8">
|
||||
<div class="gap-2 border border-transparent cursor-pointer box-without-bg dark:bg-coolgray-100 bg-white dark:hover:text-neutral-400 dark:hover:bg-coollabs group flex "
|
||||
wire:click="setPostgresqlType('postgres:16-alpine')">
|
||||
<div class="flex flex-col">
|
||||
<div class="box-title">PostgreSQL 16 (default)</div>
|
||||
<div class="box-description">
|
||||
PostgreSQL is a powerful, open-source object-relational database system (no extensions).
|
||||
</div>
|
||||
<div class="flex-1"></div>
|
||||
</div>
|
||||
<div class="flex-1"></div>
|
||||
|
||||
<div class="flex items-center px-2" title="Read the documentation.">
|
||||
<a class="p-2 hover:underline group-hover:dark:text-white dark:text-white text-neutral-6000"
|
||||
onclick="event.stopPropagation()" href="https://hub.docker.com/_/postgres/"
|
||||
target="_blank">
|
||||
Documentation
|
||||
</a>
|
||||
<div class="flex items-center px-2" title="Read the documentation.">
|
||||
<a class="p-2 hover:underline group-hover:dark:text-white dark:text-white text-neutral-6000"
|
||||
onclick="event.stopPropagation()" href="https://hub.docker.com/_/postgres/" target="_blank">
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gap-2 border border-transparent cursor-pointer box-without-bg dark:bg-coolgray-100 bg-white dark:hover:text-neutral-400 dark:hover:bg-coollabs group flex"
|
||||
wire:click="setPostgresqlType('supabase/postgres:15.6.1.113')">
|
||||
<div class="flex flex-col">
|
||||
<div class="box-title">Supabase PostgreSQL (with extensions)</div>
|
||||
<div class="box-description">
|
||||
Supabase is a modern, open-source alternative to PostgreSQL with lots of extensions.
|
||||
</div>
|
||||
</div>
|
||||
<div class="gap-2 border border-transparent cursor-pointer box-without-bg dark:bg-coolgray-100 bg-white dark:hover:text-neutral-400 dark:hover:bg-coollabs group flex"
|
||||
wire:click="setPostgresqlType('supabase/postgres:15.6.1.113')">
|
||||
<div class="flex flex-col">
|
||||
<div class="box-title">Supabase PostgreSQL (with extensions)</div>
|
||||
<div class="box-description">
|
||||
Supabase is a modern, open-source alternative to PostgreSQL with lots of extensions.
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1"></div>
|
||||
<div class="flex items-center px-2" title="Read the documentation.">
|
||||
<a class="p-2 hover:underline group-hover:dark:text-white dark:text-white text-neutral-600"
|
||||
onclick="event.stopPropagation()" href="https://github.com/supabase/postgres"
|
||||
target="_blank">
|
||||
Documentation
|
||||
</a>
|
||||
<div class="flex-1"></div>
|
||||
<div class="flex items-center px-2" title="Read the documentation.">
|
||||
<a class="p-2 hover:underline group-hover:dark:text-white dark:text-white text-neutral-600"
|
||||
onclick="event.stopPropagation()" href="https://github.com/supabase/postgres"
|
||||
target="_blank">
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gap-2 border border-transparent cursor-pointer box-without-bg dark:bg-coolgray-100 bg-white dark:hover:text-neutral-400 dark:hover:bg-coollabs group flex"
|
||||
wire:click="setPostgresqlType('postgis/postgis')">
|
||||
<div class="flex flex-col">
|
||||
<div class="box-title">PostGIS</div>
|
||||
<div class="box-description">
|
||||
PostGIS is a PostgreSQL extension for geographic objects.
|
||||
</div>
|
||||
</div>
|
||||
<div class="gap-2 border border-transparent cursor-pointer box-without-bg dark:bg-coolgray-100 bg-white dark:hover:text-neutral-400 dark:hover:bg-coollabs group flex"
|
||||
wire:click="setPostgresqlType('postgis/postgis')">
|
||||
<div class="flex flex-col">
|
||||
<div class="box-title">PostGIS</div>
|
||||
<div class="box-description">
|
||||
PostGIS is a PostgreSQL extension for geographic objects.
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1"></div>
|
||||
<div class="flex items-center px-2" title="Read the documentation.">
|
||||
<a class="p-2 hover:underline group-hover:dark:text-white dark:text-white text-neutral-600"
|
||||
onclick="event.stopPropagation()" href="https://github.com/postgis/docker-postgis"
|
||||
target="_blank">
|
||||
Documentation
|
||||
</a>
|
||||
<div class="flex-1"></div>
|
||||
<div class="flex items-center px-2" title="Read the documentation.">
|
||||
<a class="p-2 hover:underline group-hover:dark:text-white dark:text-white text-neutral-600"
|
||||
onclick="event.stopPropagation()" href="https://github.com/postgis/docker-postgis"
|
||||
target="_blank">
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gap-2 border border-transparent cursor-pointer box-without-bg dark:bg-coolgray-100 bg-white dark:hover:text-neutral-400 dark:hover:bg-coollabs group flex"
|
||||
wire:click="setPostgresqlType('pgvector/pgvector:pg16')">
|
||||
<div class="flex flex-col">
|
||||
<div class="box-title">PGVector (16)</div>
|
||||
<div class="box-description">
|
||||
PGVector is a PostgreSQL extension for vector data types.
|
||||
</div>
|
||||
</div>
|
||||
<div class="gap-2 border border-transparent cursor-pointer box-without-bg dark:bg-coolgray-100 bg-white dark:hover:text-neutral-400 dark:hover:bg-coollabs group flex"
|
||||
wire:click="setPostgresqlType('pgvector/pgvector:pg16')">
|
||||
<div class="flex flex-col">
|
||||
<div class="box-title">PGVector (16)</div>
|
||||
<div class="box-description">
|
||||
PGVector is a PostgreSQL extension for vector data types.
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1"></div>
|
||||
<div class="flex-1"></div>
|
||||
|
||||
<div class="flex items-center px-2" title="Read the documentation.">
|
||||
<a class="p-2 hover:underline group-hover:dark:text-white dark:text-white text-neutral-600"
|
||||
onclick="event.stopPropagation()" href="https://github.com/pgvector/pgvector"
|
||||
target="_blank">
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex items-center px-2" title="Read the documentation.">
|
||||
<a class="p-2 hover:underline group-hover:dark:text-white dark:text-white text-neutral-600"
|
||||
onclick="event.stopPropagation()" href="https://github.com/pgvector/pgvector"
|
||||
target="_blank">
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,50 +1,42 @@
|
||||
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'service-stack' }">
|
||||
<div>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($service, 'name')->limit(10) }} > Configuration | Coolify
|
||||
</x-slot>
|
||||
<livewire:project.service.navbar :service="$service" :parameters="$parameters" :query="$query" />
|
||||
<div class="flex flex-col gap-8 pt-6 h-full sm:flex-row">
|
||||
<div class="flex flex-col gap-2 items-start min-w-fit">
|
||||
|
||||
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
|
||||
<div class="flex flex-col items-start gap-2 min-w-fit">
|
||||
<a class="menu-item sm:min-w-fit" target="_blank" href="{{ $service->documentation() }}">Documentation
|
||||
<x-external-link /></a>
|
||||
<a class="menu-item sm:min-w-fit" :class="activeTab === 'service-stack' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'service-stack';
|
||||
window.location.hash = 'service-stack'"
|
||||
href="#">Service Stack</a>
|
||||
<a class="menu-item sm:min-w-fit" :class="activeTab === 'environment-variables' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'"
|
||||
href="#">Environment
|
||||
Variables</a>
|
||||
<a class="menu-item sm:min-w-fit" :class="activeTab === 'storages' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'storages';
|
||||
window.location.hash = 'storages'"
|
||||
href="#">Storages</a>
|
||||
<a class="menu-item" :class="activeTab === 'scheduled-tasks' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'scheduled-tasks'; window.location.hash = 'scheduled-tasks'"
|
||||
href="#">Scheduled Tasks
|
||||
</a>
|
||||
<a class="menu-item sm:min-w-fit" :class="activeTab === 'logs' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'logs';
|
||||
window.location.hash = 'logs'"
|
||||
href="#">Logs</a>
|
||||
<a class="menu-item sm:min-w-fit" :class="activeTab === 'webhooks' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'webhooks'; window.location.hash = 'webhooks'" href="#">Webhooks
|
||||
</a>
|
||||
<a class="menu-item sm:min-w-fit" :class="activeTab === 'resource-operations' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'resource-operations'; window.location.hash = 'resource-operations'"
|
||||
href="#">Resource Operations
|
||||
</a>
|
||||
<a class="menu-item sm:min-w-fit" :class="activeTab === 'tags' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'tags'; window.location.hash = 'tags'" href="#">Tags
|
||||
</a>
|
||||
<a class="menu-item sm:min-w-fit" :class="activeTab === 'danger' && 'menu-item-active'"
|
||||
@click.prevent="activeTab = 'danger';
|
||||
window.location.hash = 'danger'"
|
||||
href="#">Danger Zone
|
||||
</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.service.configuration', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'service_uuid' => $service->uuid]) }}"
|
||||
wire:navigate>General</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.service.environment-variables', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'service_uuid' => $service->uuid]) }}"
|
||||
wire:navigate>Environment Variables</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.service.storages', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'service_uuid' => $service->uuid]) }}"
|
||||
wire:navigate>Persistent Storages</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.service.scheduled-tasks.show', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'service_uuid' => $service->uuid]) }}"
|
||||
wire:navigate>Scheduled Tasks</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.service.webhooks', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'service_uuid' => $service->uuid]) }}"
|
||||
wire:navigate>Webhooks</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.service.resource-operations', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'service_uuid' => $service->uuid]) }}"
|
||||
wire:navigate>Resource Operations</a>
|
||||
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.service.tags', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'service_uuid' => $service->uuid]) }}"
|
||||
wire:navigate>Tags</a>
|
||||
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.service.danger', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'service_uuid' => $service->uuid]) }}"
|
||||
wire:navigate>Danger Zone</a>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<div x-cloak x-show="activeTab === 'service-stack'">
|
||||
@if (request()->route()->getName() === 'project.service.configuration')
|
||||
<livewire:project.service.stack-form :service="$service" />
|
||||
<h3>Services</h3>
|
||||
<div class="grid grid-cols-1 gap-2 pt-4 xl:grid-cols-1">
|
||||
@@ -103,7 +95,7 @@
|
||||
</div>
|
||||
<div class="flex items-center px-4">
|
||||
<a class="mx-4 text-xs font-bold hover:underline"
|
||||
href="{{ route('project.service.index', [...$parameters, 'stack_service_uuid' => $application->uuid]) }}">
|
||||
href="{{ route('project.service.index', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'service_uuid' => $service->uuid, 'stack_service_uuid' => $application->uuid]) }}">
|
||||
Settings
|
||||
</a>
|
||||
@if (str($application->status)->contains('running'))
|
||||
@@ -151,12 +143,12 @@
|
||||
<div class="flex items-center px-4">
|
||||
@if ($database->isBackupSolutionAvailable())
|
||||
<a class="mx-4 text-xs font-bold hover:underline"
|
||||
href="{{ route('project.service.index', [...$parameters, 'stack_service_uuid' => $database->uuid]) }}#backups">
|
||||
href="{{ route('project.service.index', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'service_uuid' => $service->uuid, 'stack_service_uuid' => $database->uuid]) }}#backups">
|
||||
Backups
|
||||
</a>
|
||||
@endif
|
||||
<a class="mx-4 text-xs font-bold hover:underline"
|
||||
href="{{ route('project.service.index', [...$parameters, 'stack_service_uuid' => $database->uuid]) }}">
|
||||
href="{{ route('project.service.index', ['project_uuid' => $project->uuid, 'environment_name' => $environment->name, 'service_uuid' => $service->uuid, 'stack_service_uuid' => $database->uuid]) }}">
|
||||
Settings
|
||||
</a>
|
||||
@if (str($database->status)->contains('running'))
|
||||
@@ -173,8 +165,9 @@
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'storages'">
|
||||
@elseif (request()->route()->getName() === 'project.service.environment-variables')
|
||||
<livewire:project.shared.environment-variable.all :resource="$service" />
|
||||
@elseif (request()->route()->getName() === 'project.service.storages')
|
||||
<div class="flex gap-2 items-center">
|
||||
<h2>Storages</h2>
|
||||
</div>
|
||||
@@ -182,35 +175,23 @@
|
||||
<div class="pb-4 dark:text-warning text-coollabs">If you would like to add a volume, you must add it to
|
||||
your compose file (Service Stack tab).</div>
|
||||
@foreach ($applications as $application)
|
||||
<livewire:project.service.storage wire:key="application-{{ $application->id }}" :resource="$application"
|
||||
lazy />
|
||||
<livewire:project.service.storage wire:key="application-{{ $application->id }}"
|
||||
:resource="$application" />
|
||||
@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" />
|
||||
@endforeach
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'scheduled-tasks'">
|
||||
@elseif (request()->route()->getName() === 'project.service.scheduled-tasks.show')
|
||||
<livewire:project.shared.scheduled-task.all :resource="$service" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'webhooks'">
|
||||
@elseif (request()->route()->getName() === 'project.service.webhooks')
|
||||
<livewire:project.shared.webhooks :resource="$service" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'logs'">
|
||||
<livewire:project.shared.logs :resource="$service" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'environment-variables'">
|
||||
<livewire:project.shared.environment-variable.all :resource="$service" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'resource-operations'">
|
||||
@elseif (request()->route()->getName() === 'project.service.resource-operations')
|
||||
<livewire:project.shared.resource-operations :resource="$service" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'tags'">
|
||||
@elseif (request()->route()->getName() === 'project.service.tags')
|
||||
<livewire:project.shared.tags :resource="$service" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'danger'">
|
||||
@elseif (request()->route()->getName() === 'project.service.danger')
|
||||
<livewire:project.shared.danger :resource="$service" />
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<x-forms.textarea rows="20" readonly id="service.docker_compose">
|
||||
</x-forms.textarea>
|
||||
</div>
|
||||
<div class="pt-2 w-72">
|
||||
<div class="pt-2 w-96">
|
||||
<x-forms.checkbox label="Escape special characters in labels?"
|
||||
helper="By default, $ (and other chars) is escaped. So if you write $ in the labels, it will be saved as $$.<br><br>If you want to use env variables inside the labels, turn this off."
|
||||
id="service.is_container_label_escape_enabled" instantSave></x-forms.checkbox>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div wire:poll.10000ms="check_status_without_notification">
|
||||
<div>
|
||||
<livewire:project.shared.configuration-checker :resource="$service" />
|
||||
<x-slide-over @startservice.window="slideOverOpen = true" closeWithX fullScreen>
|
||||
<x-slot:title>Service Startup</x-slot:title>
|
||||
@@ -14,6 +14,10 @@
|
||||
href="{{ route('project.service.configuration', $parameters) }}">
|
||||
<button>Configuration</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('project.service.logs') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('project.service.logs', $parameters) }}">
|
||||
<button>Logs</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('project.service.command') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('project.service.command', $parameters) }}">
|
||||
<button>Terminal</button>
|
||||
@@ -22,7 +26,7 @@
|
||||
</nav>
|
||||
@if ($service->isDeployable)
|
||||
<div class="flex flex-wrap order-first gap-2 items-center sm:order-last">
|
||||
@if (str($service->status())->contains('running'))
|
||||
@if (str($service->status)->contains('running'))
|
||||
<x-dropdown>
|
||||
<x-slot:title>
|
||||
Advanced
|
||||
@@ -70,7 +74,7 @@
|
||||
Stop
|
||||
</x-slot:button-title>
|
||||
</x-modal-confirmation>
|
||||
@elseif (str($service->status())->contains('degraded'))
|
||||
@elseif (str($service->status)->contains('degraded'))
|
||||
<button @click="$wire.dispatch('startEvent')" class="gap-2 button">
|
||||
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
@@ -99,7 +103,7 @@
|
||||
Stop
|
||||
</x-slot:button-title>
|
||||
</x-modal-confirmation>
|
||||
@elseif (str($service->status())->contains('exited'))
|
||||
@elseif (str($service->status)->contains('exited'))
|
||||
<button wire:click='stop(true)' class="gap-2 button">
|
||||
<svg class="w-5 h-5" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="red" d="M26 20h-6v-2h6zm4 8h-6v-2h6zm-2-4h-6v-2h6z" />
|
||||
@@ -150,8 +154,7 @@
|
||||
@else
|
||||
<div class="flex flex-wrap order-first gap-2 items-center sm:order-last">
|
||||
<div class="text-error">
|
||||
Unable to deploy. <a
|
||||
class="underline font-bold cursor-pointer"
|
||||
Unable to deploy. <a class="underline font-bold cursor-pointer"
|
||||
@click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'">
|
||||
Required environment variables missing.</a>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
volume
|
||||
name, example: <span class='text-helper'>-pr-1</span>" />
|
||||
@if ($resource?->build_pack !== 'dockercompose')
|
||||
<x-modal-input :closeOutside="false" buttonTitle="+ Add" title="New Persistent Storage">
|
||||
<x-modal-input :closeOutside="false" buttonTitle="+ Add" title="New Persistent Storage" minWidth="64rem">
|
||||
<livewire:project.shared.storages.add :resource="$resource" />
|
||||
</x-modal-input>
|
||||
@endif
|
||||
|
||||
@@ -62,17 +62,13 @@
|
||||
<x-forms.button isError
|
||||
wire:click="stop('{{ data_get($destination, 'server.id') }}')">Stop</x-forms.button>
|
||||
@endif
|
||||
<x-modal-confirmation
|
||||
title="Confirm server removal?"
|
||||
isErrorButton
|
||||
buttonTitle="Remove Server"
|
||||
<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') }}"
|
||||
: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"
|
||||
/>
|
||||
shortConfirmationLabel="Server Name" step3ButtonText="Permanently Remove Server" />
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Environment Variables</h2>
|
||||
<div class="flex flex-col items-center">
|
||||
<x-modal-input buttonTitle="+ Add" title="New Environment Variable">
|
||||
<x-modal-input buttonTitle="+ Add" title="New Environment Variable" :closeOutside="false">
|
||||
<livewire:project.shared.environment-variable.add />
|
||||
</x-modal-input>
|
||||
</div>
|
||||
@@ -13,7 +13,7 @@
|
||||
<div>Environment variables (secrets) for this resource. </div>
|
||||
@if ($this->resourceClass === 'App\Models\Application' && data_get($this->resource, 'build_pack') !== 'dockercompose')
|
||||
<div class="w-64 pt-2">
|
||||
<x-forms.checkbox id="resource.settings.is_env_sorting_enabled" label="Sort alphabetically"
|
||||
<x-forms.checkbox id="is_env_sorting_enabled" label="Sort alphabetically"
|
||||
helper="Turn this off if one environment is dependent on an other. It will be sorted by creation order (like you pasted them or in the order you created them)."
|
||||
instantSave></x-forms.checkbox>
|
||||
</div>
|
||||
@@ -38,7 +38,7 @@
|
||||
<div>Environment (secrets) variables for Production.</div>
|
||||
</div>
|
||||
@php
|
||||
$requiredEmptyVars = $resource->environment_variables->filter(function($env) {
|
||||
$requiredEmptyVars = $resource->environment_variables->filter(function ($env) {
|
||||
return $env->is_required && empty($env->value);
|
||||
});
|
||||
$otherVars = $resource->environment_variables->diff($requiredEmptyVars);
|
||||
@@ -55,18 +55,20 @@
|
||||
<h3>Preview Deployments Environment Variables</h3>
|
||||
<div>Environment (secrets) variables for Preview Deployments.</div>
|
||||
</div>
|
||||
@foreach ($resource->environment_variables_preview as $env)
|
||||
{{-- @foreach ($resource->environment_variables_preview as $env)
|
||||
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
|
||||
:env="$env" :type="$resource->type()" />
|
||||
@endforeach
|
||||
@endforeach --}}
|
||||
@endif
|
||||
@else
|
||||
<form wire:submit.prevent='submit' class="flex flex-col gap-2">
|
||||
<x-forms.textarea rows="10" class="whitespace-pre-wrap" id="variables" wire:model="variables" label="Production Environment Variables"></x-forms.textarea>
|
||||
<x-forms.textarea rows="10" class="whitespace-pre-wrap" id="variables" wire:model="variables"
|
||||
label="Production Environment Variables"></x-forms.textarea>
|
||||
|
||||
@if ($showPreview)
|
||||
<x-forms.textarea rows="10" class="whitespace-pre-wrap" label="Preview Deployments Environment Variables"
|
||||
id="variablesPreview" wire:model="variablesPreview"></x-forms.textarea>
|
||||
<x-forms.textarea rows="10" class="whitespace-pre-wrap"
|
||||
label="Preview Deployments Environment Variables" id="variablesPreview"
|
||||
wire:model="variablesPreview"></x-forms.textarea>
|
||||
@endif
|
||||
|
||||
<x-forms.button type="submit" class="btn btn-primary">Save All Environment Variables</x-forms.button>
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
<div>
|
||||
<form wire:submit='submit'
|
||||
@class([
|
||||
'flex flex-col items-center gap-4 p-4 bg-white border lg:items-start dark:bg-base',
|
||||
'border-error' => $env->is_really_required,
|
||||
'dark:border-coolgray-300' => !$env->is_really_required,
|
||||
])
|
||||
>
|
||||
<form wire:submit='submit' @class([
|
||||
'flex flex-col items-center gap-4 p-4 bg-white border lg:items-start dark:bg-base',
|
||||
'border-error' => $is_really_required,
|
||||
'dark:border-coolgray-300' => !$is_really_required,
|
||||
])>
|
||||
@if ($isLocked)
|
||||
<div class="flex flex-1 w-full gap-2">
|
||||
<x-forms.input disabled id="env.key" />
|
||||
<x-forms.input disabled id="key" />
|
||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||
<path d="M5 13a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-6z" />
|
||||
<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>
|
||||
@@ -25,49 +22,49 @@
|
||||
@else
|
||||
@if ($isDisabled)
|
||||
<div class="flex flex-col w-full gap-2 lg:flex-row">
|
||||
<x-forms.input disabled id="env.key" />
|
||||
<x-forms.input disabled type="password" id="env.value" />
|
||||
@if ($env->is_shared)
|
||||
<x-forms.input disabled type="password" id="env.real_value" />
|
||||
<x-forms.input disabled id="key" />
|
||||
<x-forms.input disabled type="password" id="value" />
|
||||
@if ($is_shared)
|
||||
<x-forms.input disabled type="password" id="real_value" />
|
||||
@endif
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col w-full gap-2 lg:flex-row">
|
||||
@if ($env->is_multiline)
|
||||
<x-forms.input isMultiline="{{ $env->is_multiline }}" id="env.key" />
|
||||
<x-forms.textarea type="password" id="env.value" />
|
||||
@if ($is_multiline)
|
||||
<x-forms.input isMultiline="{{ $is_multiline }}" id="key" />
|
||||
<x-forms.textarea type="password" id="value" />
|
||||
@else
|
||||
<x-forms.input id="env.key" />
|
||||
<x-forms.input type="password" id="env.value" />
|
||||
<x-forms.input id="key" />
|
||||
<x-forms.input type="password" id="value" />
|
||||
@endif
|
||||
@if ($env->is_shared)
|
||||
<x-forms.input disabled type="password" id="env.real_value" />
|
||||
@if ($is_shared)
|
||||
<x-forms.input disabled type="password" id="real_value" />
|
||||
@endif
|
||||
</div>
|
||||
@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"
|
||||
<x-forms.checkbox instantSave 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?" />
|
||||
@else
|
||||
@if ($env->is_shared)
|
||||
<x-forms.checkbox instantSave id="env.is_build_time"
|
||||
@if ($is_shared)
|
||||
<x-forms.checkbox instantSave 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?" />
|
||||
<x-forms.checkbox instantSave id="env.is_literal"
|
||||
<x-forms.checkbox instantSave id="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?" />
|
||||
@else
|
||||
@if ($isSharedVariable)
|
||||
<x-forms.checkbox instantSave id="env.is_multiline" label="Is Multiline?" />
|
||||
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
|
||||
@else
|
||||
<x-forms.checkbox instantSave id="env.is_build_time"
|
||||
<x-forms.checkbox instantSave 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 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"
|
||||
<x-forms.checkbox instantSave id="is_multiline" label="Is Multiline?" />
|
||||
@if ($is_multiline === false)
|
||||
<x-forms.checkbox instantSave id="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?" />
|
||||
@endif
|
||||
@@ -84,7 +81,7 @@
|
||||
</x-forms.button>
|
||||
<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 }}"
|
||||
confirmationText="{{ $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" />
|
||||
@@ -97,7 +94,7 @@
|
||||
</x-forms.button>
|
||||
<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 }}"
|
||||
confirmationText="{{ $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" />
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
@else
|
||||
@if (count($containers) > 0)
|
||||
@if (count($containers) === 1)
|
||||
<form class="w-full pt-4"
|
||||
wire:submit="$dispatchSelf('connectToContainer')" wire:init="$dispatchSelf('connectToContainer')">
|
||||
<form class="w-full pt-4" wire:submit="$dispatchSelf('connectToContainer')"
|
||||
wire:init="$dispatchSelf('connectToContainer')">
|
||||
<x-forms.button class="w-full" type="submit">Reconnect</x-forms.button>
|
||||
</form>
|
||||
@else
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
<h1>Logs</h1>
|
||||
<livewire:project.application.heading :application="$resource" />
|
||||
<div class="pt-4">
|
||||
<h2>Logs</h2>
|
||||
<div class="subtitle">Here you can see the logs of the application.</div>
|
||||
<div class="pt-2" wire:loading wire:target="loadContainers">
|
||||
Loading containers...
|
||||
@@ -31,10 +30,8 @@
|
||||
<h1>Logs</h1>
|
||||
<livewire:project.database.heading :database="$resource" />
|
||||
<div class="pt-4">
|
||||
<div class="subtitle">Here you can see the logs of the database.</div>
|
||||
@forelse ($containers as $container)
|
||||
@if ($loop->first)
|
||||
<h2 class="pb-4">Logs</h2>
|
||||
@endif
|
||||
@if (data_get($servers, '0'))
|
||||
<livewire:project.shared.get-logs :server="data_get($servers, '0')" :resource="$resource" :container="$container" />
|
||||
@else
|
||||
@@ -45,11 +42,10 @@
|
||||
@endforelse
|
||||
</div>
|
||||
@elseif ($type === 'service')
|
||||
<div>
|
||||
<livewire:project.service.navbar :service="$resource" :parameters="$parameters" :query="$query" title="Logs" />
|
||||
<div class="pt-4">
|
||||
<div class="subtitle">Here you can see the logs of the service.</div>
|
||||
@forelse ($containers as $container)
|
||||
@if ($loop->first)
|
||||
<h2 class="pb-4">Logs</h2>
|
||||
@endif
|
||||
@if (data_get($servers, '0'))
|
||||
<livewire:project.shared.get-logs :server="data_get($servers, '0')" :resource="$resource" :container="$container" />
|
||||
@else
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-2" x-data="{
|
||||
init() {
|
||||
let interval;
|
||||
$wire.$watch('isPollingActive', value => {
|
||||
if (value) {
|
||||
interval = setInterval(() => {
|
||||
$wire.polling();
|
||||
}, 1000);
|
||||
} else {
|
||||
if (interval) clearInterval(interval);
|
||||
}
|
||||
});
|
||||
}
|
||||
}">
|
||||
@forelse($executions as $execution)
|
||||
<a wire:click="selectTask({{ data_get($execution, 'id') }})" @class([
|
||||
'flex flex-col border-l-2 transition-colors p-4 cursor-pointer',
|
||||
@@ -10,6 +23,7 @@
|
||||
'border-red-500' => data_get($execution, 'status') === 'failed',
|
||||
'border-yellow-500' => data_get($execution, 'status') === 'running',
|
||||
])>
|
||||
|
||||
@if (data_get($execution, 'status') === 'running')
|
||||
<div class="absolute top-2 right-2">
|
||||
<x-loading />
|
||||
@@ -21,11 +35,34 @@
|
||||
Started At: {{ $this->formatDateInServerTimezone(data_get($execution, 'created_at', now())) }}
|
||||
</div>
|
||||
</a>
|
||||
@if (strlen($execution->message) > 0)
|
||||
<x-forms.button wire:click.prevent="downloadLogs({{ data_get($execution, 'id') }})">
|
||||
Download Logs
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@if (data_get($execution, 'id') == $selectedKey)
|
||||
<div class="p-4 mb-2 bg-gray-100 dark:bg-coolgray-200 rounded">
|
||||
@if (data_get($execution, 'message'))
|
||||
@if (data_get($execution, 'status') === 'running')
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span>Task is running...</span>
|
||||
<x-loading class="w-4 h-4" />
|
||||
</div>
|
||||
@endif
|
||||
@if ($this->logLines->isNotEmpty())
|
||||
<div>
|
||||
<pre class="whitespace-pre-wrap">{{ data_get($execution, 'message') }}</pre>
|
||||
<pre class="whitespace-pre-wrap">
|
||||
@foreach ($this->logLines as $line)
|
||||
{{ $line }}
|
||||
@endforeach
|
||||
</pre>
|
||||
<div class="flex gap-2">
|
||||
@if ($this->hasMoreLogs())
|
||||
<x-forms.button wire:click.prevent="loadMoreLogs" isHighlighted>
|
||||
Load More
|
||||
</x-forms.button>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div>No output was recorded for this execution.</div>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
<div class="flex flex-col w-full gap-2 rounded max-h-[80vh] overflow-y-auto scrollbar">
|
||||
<div class="p-4">
|
||||
You can add Volumes, Files and Directories to your resources here.
|
||||
<form class="flex flex-col w-full gap-2 rounded" wire:submit='submitPersistentVolume'>
|
||||
<div class="flex flex-col w-full gap-2 max-h-[80vh] overflow-y-auto scrollbar">
|
||||
<form class="flex flex-col w-full gap-2 rounded " wire:submit='submitPersistentVolume'>
|
||||
<div class="flex flex-col">
|
||||
<h3>Volume Mount</h3>
|
||||
@if ($isSwarm)
|
||||
<h5>Swarm Mode detected: You need to set a shared volume (EFS/NFS/etc) on all the worker nodes if you
|
||||
would
|
||||
like to use a persistent volumes.</h5>
|
||||
@endif
|
||||
<div>Docker Volumes mounted to the container.</div>
|
||||
</div>
|
||||
@if ($isSwarm)
|
||||
<h5>Swarm Mode detected: You need to set a shared volume (EFS/NFS/etc) on all the worker nodes if you
|
||||
would
|
||||
like to use a persistent volumes.</h5>
|
||||
@endif
|
||||
<div class="flex flex-col gap-2 px-2">
|
||||
<x-forms.input placeholder="pv-name" id="name" label="Name" required helper="Volume name." />
|
||||
@if ($isSwarm)
|
||||
<x-forms.input placeholder="/root" id="host_path" label="Source Path" required
|
||||
@@ -19,29 +21,39 @@
|
||||
<x-forms.input placeholder="/tmp/root" id="mount_path" label="Destination Path" required
|
||||
helper="Directory inside the container." />
|
||||
<x-forms.button type="submit" @click="modalOpen=false">
|
||||
Save
|
||||
Add
|
||||
</x-forms.button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
<form class="flex flex-col w-full gap-2 rounded" wire:submit='submitFileStorage'>
|
||||
</form>
|
||||
<form class="flex flex-col w-full gap-2 rounded py-4" wire:submit='submitFileStorage'>
|
||||
<div class="flex flex-col">
|
||||
<h3>File Mount</h3>
|
||||
<div>Actual file mounted from the host system to the container.</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 px-2">
|
||||
<x-forms.input placeholder="/etc/nginx/nginx.conf" id="file_storage_path" label="Destination Path" required
|
||||
helper="File inside the container" />
|
||||
helper="File location inside the container" />
|
||||
<x-forms.textarea label="Content" id="file_storage_content"></x-forms.textarea>
|
||||
<x-forms.button type="submit" @click="modalOpen=false">
|
||||
Save
|
||||
Add
|
||||
</x-forms.button>
|
||||
</form>
|
||||
<form class="flex flex-col w-full gap-2 rounded" wire:submit='submitFileStorageDirectory'>
|
||||
</div>
|
||||
</form>
|
||||
<form class="flex flex-col w-full gap-2 rounded" wire:submit='submitFileStorageDirectory'>
|
||||
<div class="flex flex-col">
|
||||
<h3>Directory Mount</h3>
|
||||
<div>Directory mounted from the host system to the container.</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 px-2">
|
||||
<x-forms.input placeholder="{{ application_configuration_dir() }}/{{ $resource->uuid }}/etc/nginx"
|
||||
id="file_storage_directory_source" label="Source Directory" required
|
||||
helper="Directory on the host system." />
|
||||
<x-forms.input placeholder="/etc/nginx" id="file_storage_directory_destination"
|
||||
label="Destination Directory" required helper="Directory inside the container." />
|
||||
<x-forms.button type="submit" @click="modalOpen=false">
|
||||
Save
|
||||
Add
|
||||
</x-forms.button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -25,21 +25,31 @@
|
||||
<div class="flex gap-1 font-bold dark:text-white">
|
||||
@if ($permissions)
|
||||
@foreach ($permissions as $permission)
|
||||
@if ($permission === '*')
|
||||
<div>Root access, be careful!</div>
|
||||
@else
|
||||
<div>{{ $permission }}</div>
|
||||
@endif
|
||||
<div>{{ $permission }}</div>
|
||||
@endforeach
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4>Token Permissions</h4>
|
||||
<div class="w-64">
|
||||
<x-forms.checkbox label="Root Access" wire:model.live="rootAccess"></x-forms.checkbox>
|
||||
<x-forms.checkbox label="Read-only" wire:model.live="readOnly"></x-forms.checkbox>
|
||||
<x-forms.checkbox label="View Sensitive Data" wire:model.live="viewSensitiveData"></x-forms.checkbox>
|
||||
<x-forms.checkbox label="root" wire:model.live="permissions" domValue="root"
|
||||
helper="Root access, be careful!" :checked="in_array('root', $permissions)"></x-forms.checkbox>
|
||||
@if (!in_array('root', $permissions))
|
||||
<x-forms.checkbox label="write" wire:model.live="permissions" domValue="write"
|
||||
helper="Write access to all resources" :checked="in_array('write', $permissions)"></x-forms.checkbox>
|
||||
<x-forms.checkbox label="deploy" wire:model.live="permissions" domValue="deploy"
|
||||
helper="Can trigger deploy webhooks" :checked="in_array('deploy', $permissions)"></x-forms.checkbox>
|
||||
<x-forms.checkbox label="read" domValue="read" wire:model.live="permissions" domValue="read"
|
||||
:checked="in_array('read', $permissions)"></x-forms.checkbox>
|
||||
<x-forms.checkbox label="read:sensitive" wire:model.live="permissions" domValue="read:sensitive"
|
||||
helper="Responses will include secrets, logs, passwords, and compose file contents"
|
||||
:checked="in_array('read:sensitive', $permissions)"></x-forms.checkbox>
|
||||
@endif
|
||||
</div>
|
||||
@if (in_array('root', $permissions))
|
||||
<div class="font-bold text-warning">Root access, be careful!</div>
|
||||
@endif
|
||||
</form>
|
||||
@if (session()->has('token'))
|
||||
<div class="py-4 font-bold dark:text-warning">Please copy this token now. For your security, it won't be shown
|
||||
@@ -50,12 +60,13 @@
|
||||
<h3 class="py-4">Issued Tokens</h3>
|
||||
<div class="grid gap-2 lg:grid-cols-1">
|
||||
@forelse ($tokens as $token)
|
||||
<div class="flex flex-col gap-1 p-2 border dark:border-coolgray-200 hover:no-underline">
|
||||
<div wire:key="token-{{ $token->id }}"
|
||||
class="flex flex-col gap-1 p-2 border dark:border-coolgray-200 hover:no-underline">
|
||||
<div>Description: {{ $token->name }}</div>
|
||||
<div>Last used: {{ $token->last_used_at ? $token->last_used_at->diffForHumans() : 'Never' }}</div>
|
||||
<div class="flex gap-1">
|
||||
@if ($token->abilities)
|
||||
Abilities:
|
||||
Permissions:
|
||||
@foreach ($token->abilities as $ability)
|
||||
<div class="font-bold dark:text-white">{{ $ability }}</div>
|
||||
@endforeach
|
||||
|
||||
@@ -27,6 +27,13 @@
|
||||
helper="If set, all resources will only have docker container labels for {{ str($server->proxyType())->title() }}.<br>For applications, labels needs to be regenerated manually. <br>Resources needs to be restarted."
|
||||
id="server.settings.generate_exact_labels"
|
||||
label="Generate labels only for {{ str($server->proxyType())->title() }}" instantSave />
|
||||
<x-forms.checkbox instantSave="instantSaveRedirect" id="redirect_enabled"
|
||||
label="Override default request handler"
|
||||
helper="Requests to unknown hosts or stopped services will recieve a 503 response or be redirected to the URL you set below (need to enable this first)." />
|
||||
@if ($redirect_enabled)
|
||||
<x-forms.input placeholder="https://app.coolify.io" id="redirect_url"
|
||||
label="Redirect to (optional)" />
|
||||
@endif
|
||||
</div>
|
||||
@if ($server->proxyType() === ProxyTypes::TRAEFIK->value)
|
||||
<h4>Traefik</h4>
|
||||
@@ -40,8 +47,6 @@
|
||||
configurations.
|
||||
</div>
|
||||
@endif
|
||||
<x-forms.input placeholder="https://app.coolify.io" id="redirect_url" label="Default Redirect 404"
|
||||
helper="All urls that has no service available will be redirected to this domain." />
|
||||
<div wire:loading wire:target="loadProxyConfiguration" class="pt-4">
|
||||
<x-loading text="Loading proxy configuration..." />
|
||||
</div>
|
||||
|
||||
@@ -20,17 +20,12 @@
|
||||
</a>
|
||||
</button>
|
||||
@endif
|
||||
<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-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"
|
||||
@@ -42,17 +37,12 @@
|
||||
Restart Proxy
|
||||
</x-slot:button-title>
|
||||
</x-modal-confirmation>
|
||||
<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-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"
|
||||
@@ -82,7 +72,6 @@
|
||||
@script
|
||||
<script>
|
||||
$wire.$on('checkProxyEvent', () => {
|
||||
$wire.$dispatch('info', 'Starting proxy.');
|
||||
$wire.$call('checkProxy');
|
||||
});
|
||||
$wire.$on('restartEvent', () => {
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
@if (str_replace('|', '.', $fileName) === 'coolify.yaml' ||
|
||||
str_replace('|', '.', $fileName) === 'Caddyfile' ||
|
||||
str_replace('|', '.', $fileName) === 'coolify.caddy' ||
|
||||
str_replace('|', '.', $fileName) === 'default_redirect_404.caddy')
|
||||
str_replace('|', '.', $fileName) === 'default_redirect_503.yaml' ||
|
||||
str_replace('|', '.', $fileName) === 'default_redirect_503.caddy')
|
||||
<div>
|
||||
<h3 class="dark:text-white">File: {{ str_replace('|', '.', $fileName) }}</h3>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<div x-init="$wire.checkProxy()" class="flex gap-2">
|
||||
<x-forms.button wire:click='checkProxy(true)' :showLoadingIndicator="false">Refresh</x-forms.button>
|
||||
@if (data_get($server, 'proxy.status') === 'running')
|
||||
<x-status.running status="Proxy Running" noLoading />
|
||||
<x-status.running status="Proxy Running" />
|
||||
@elseif (data_get($server, 'proxy.status') === 'restarting')
|
||||
<x-status.restarting status="Proxy Restarting" noLoading />
|
||||
<x-status.restarting status="Proxy Restarting" />
|
||||
@elseif (data_get($server, 'proxy.force_stop'))
|
||||
<x-status.stopped status="Proxy Stopped" noLoading />
|
||||
<x-status.stopped status="Proxy Stopped" />
|
||||
@elseif (data_get($server, 'proxy.status') === 'exited')
|
||||
<x-status.stopped status="Proxy Exited" noLoading />
|
||||
<x-status.stopped status="Proxy Exited" />
|
||||
@else
|
||||
<x-status.stopped status="Proxy Not Running" noLoading />
|
||||
<x-status.stopped status="Proxy Not Running" />
|
||||
@endif
|
||||
<x-forms.button wire:click='checkProxy(true)'>Refresh</x-forms.button>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
<div class="w-full" x-data="{
|
||||
open: false,
|
||||
search: '{{ $serverTimezone ?: '' }}',
|
||||
timezones: @js($timezones),
|
||||
timezones: @js($this->timezones),
|
||||
placeholder: '{{ $serverTimezone ? 'Search timezone...' : 'Select Server Timezone' }}',
|
||||
init() {
|
||||
this.$watch('search', value => {
|
||||
|
||||
@@ -9,17 +9,27 @@
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
@if (is_transactional_emails_enabled() && auth()->user()->isAdminFromSession())
|
||||
<x-modal-input buttonTitle="Send Test Email" title="Send Test Email">
|
||||
<form wire:submit.prevent="sendTestEmail" class="flex flex-col w-full gap-2">
|
||||
<x-forms.input wire:model="testEmailAddress" placeholder="test@example.com" id="testEmailAddress"
|
||||
label="Recipients" required />
|
||||
<x-forms.button type="submit" @click="modalOpen=false">
|
||||
Send Email
|
||||
</x-forms.button>
|
||||
</form>
|
||||
</x-modal-input>
|
||||
@endif
|
||||
</div>
|
||||
<div class="pb-4 ">Email settings for password resets, invitations, etc.</div>
|
||||
<div class="flex gap-4">
|
||||
<div class="pb-4">Instance wide email settings for password resets, invitations, etc.</div>
|
||||
<div class="flex gap-4">
|
||||
<x-forms.input required id="smtpFromName" helper="Name used in emails." label="From Name" />
|
||||
<x-forms.input required id="smtpFromAddress" helper="Email address used in emails." label="From Address" />
|
||||
</div>
|
||||
|
||||
</form>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="p-4 border dark:border-coolgray-300">
|
||||
<form wire:submit='submit' class="flex flex-col">
|
||||
<form wire:submit.prevent="submitSmtp" class="flex flex-col">
|
||||
<div class="flex gap-2">
|
||||
<h3>SMTP Server</h3>
|
||||
<x-forms.button type="submit">
|
||||
@@ -33,8 +43,11 @@
|
||||
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||
<x-forms.input required id="smtpHost" placeholder="smtp.mailgun.org" label="Host" />
|
||||
<x-forms.input required id="smtpPort" placeholder="587" label="Port" />
|
||||
<x-forms.input id="smtpEncryption" helper="If SMTP uses SSL, set it to 'tls'." placeholder="tls"
|
||||
label="Encryption" />
|
||||
<x-forms.select id="smtpEncryption" label="Encryption">
|
||||
<option value="tls">TLS</option>
|
||||
<option value="ssl">SSL</option>
|
||||
<option value="none">None</option>
|
||||
</x-forms.select>
|
||||
</div>
|
||||
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||
<x-forms.input id="smtpUsername" label="SMTP Username" />
|
||||
@@ -46,7 +59,7 @@
|
||||
</form>
|
||||
</div>
|
||||
<div class="p-4 border dark:border-coolgray-300">
|
||||
<form wire:submit='submit' class="flex flex-col">
|
||||
<form wire:submit.prevent="submitResend" class="flex flex-col">
|
||||
<div class="flex gap-2">
|
||||
<h3>Resend</h3>
|
||||
<x-forms.button type="submit">
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
<div class="flex flex-col gap-2 pt-4">
|
||||
@foreach ($oauth_settings_map as $oauth_setting)
|
||||
<div class="p-4 border dark:border-coolgray-300">
|
||||
<h3>{{ ucfirst($oauth_setting->provider) }} Oauth</h3>
|
||||
<h3>{{ ucfirst($oauth_setting->provider) }}</h3>
|
||||
<div class="w-32">
|
||||
<x-forms.checkbox instantSave id="oauth_settings_map.{{ $oauth_setting->provider }}.enabled"
|
||||
label="Enabled" />
|
||||
<x-forms.checkbox instantSave="instantSave('{{ $oauth_setting->provider }}')"
|
||||
id="oauth_settings_map.{{ $oauth_setting->provider }}.enabled" label="Enabled" />
|
||||
</div>
|
||||
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.client_id"
|
||||
@@ -32,6 +32,10 @@
|
||||
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.tenant"
|
||||
label="Tenant" />
|
||||
@endif
|
||||
@if ($oauth_setting->provider == 'authentik')
|
||||
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.base_url"
|
||||
label="Base URL" />
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<div class="w-full" x-data="{
|
||||
open: false,
|
||||
search: '{{ $settings->instance_timezone ?: '' }}',
|
||||
timezones: @js($timezones),
|
||||
timezones: @js($this->timezones),
|
||||
placeholder: '{{ $settings->instance_timezone ? 'Search timezone...' : 'Select Server Timezone' }}',
|
||||
init() {
|
||||
this.$watch('search', value => {
|
||||
@@ -154,7 +154,7 @@
|
||||
<x-modal-confirmation title="Disable Two Step Confirmation?"
|
||||
buttonTitle="Disable Two Step Confirmation" isErrorButton
|
||||
submitAction="toggleTwoStepConfirmation" :actions="[
|
||||
'Tow Step confimation will be disabled globally.',
|
||||
'Two Step confimation will be disabled globally.',
|
||||
'Disabling two step confirmation reduces security (as anyone can easily delete anything).',
|
||||
'The risk of accidental actions will increase.',
|
||||
]"
|
||||
|
||||
@@ -58,7 +58,18 @@
|
||||
@else
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input id="github_app.name" label="App Name" disabled />
|
||||
<div class="flex items-end gap-2 w-full">
|
||||
<x-forms.input id="github_app.name" label="App Name" disabled />
|
||||
<x-forms.button wire:click.prevent="updateGithubAppName" class="bg-coollabs">
|
||||
Sync Name
|
||||
</x-forms.button>
|
||||
<a href="{{ $this->getGithubAppNameUpdatePath() }}">
|
||||
<x-forms.button>
|
||||
Rename
|
||||
<x-external-link />
|
||||
</x-forms.button>
|
||||
</a>
|
||||
</div>
|
||||
<x-forms.input id="github_app.organization" label="Organization" disabled
|
||||
placeholder="If empty, personal user will be used" />
|
||||
</div>
|
||||
|
||||
@@ -2,42 +2,38 @@
|
||||
<x-slot:title>
|
||||
Subscribe | Coolify
|
||||
</x-slot>
|
||||
@if ($settings->is_resale_license_active)
|
||||
@if (auth()->user()->isAdminFromSession())
|
||||
<div>
|
||||
<div class="flex gap-2">
|
||||
<h1>Subscriptions</h1>
|
||||
@if (subscriptionProvider() === 'stripe' && $alreadySubscribed)
|
||||
<x-forms.button wire:click='stripeCustomerPortal'>Manage My Subscription</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
@if (request()->query->get('cancelled'))
|
||||
<div class="mb-6 rounded alert-error">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span>Something went wrong with your subscription. Please try again or contact
|
||||
support.</span>
|
||||
</div>
|
||||
@if (auth()->user()->isAdminFromSession())
|
||||
<div>
|
||||
<div class="flex gap-2">
|
||||
<h1>Subscriptions</h1>
|
||||
@if (subscriptionProvider() === 'stripe' && $alreadySubscribed)
|
||||
<x-forms.button wire:click='stripeCustomerPortal'>Manage My Subscription</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
@if (request()->query->get('cancelled'))
|
||||
<div class="mb-6 rounded alert-error">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span>Something went wrong with your subscription. Please try again or contact
|
||||
support.</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (config('subscription.provider') === 'stripe')
|
||||
<livewire:subscription.pricing-plans />
|
||||
@endif
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col justify-center mx-10">
|
||||
<div class="flex gap-2">
|
||||
<h1>Subscription</h1>
|
||||
</div>
|
||||
<div>You are not an admin or have been removed from this team. If this does not make sense, please <span
|
||||
class="underline cursor-pointer dark:text-white" wire:click="help">contact
|
||||
us</span>.</div>
|
||||
</div>
|
||||
@endif
|
||||
@if (config('subscription.provider') === 'stripe')
|
||||
<livewire:subscription.pricing-plans />
|
||||
@endif
|
||||
</div>
|
||||
@else
|
||||
<div class="px-10">Resale license is not active. Please contact your instance admin.</div>
|
||||
<div class="flex flex-col justify-center mx-10">
|
||||
<div class="flex gap-2">
|
||||
<h1>Subscription</h1>
|
||||
</div>
|
||||
<div>You are not an admin or have been removed from this team. If this does not make sense, please <span
|
||||
class="underline cursor-pointer dark:text-white" wire:click="help">contact
|
||||
us</span>.</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<x-forms.select wire:model.live="selectedTeamId" label="Current Team">
|
||||
<option value="default" disabled selected>Switch team</option>
|
||||
@foreach (auth()->user()->teams as $team)
|
||||
<option value="{{ $team->id }}">{{ $team->name }}</option>
|
||||
<option value="{{ $team->id }}">{{ $team->name }}</option>
|
||||
@endforeach
|
||||
</x-forms.select>
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
<h3 class="pt-4">Users</h3>
|
||||
<div class="flex flex-col gap-2 ">
|
||||
@forelse ($users as $user)
|
||||
<div class="flex items-center justify-center gap-2 bg-white box-without-bg dark:bg-coolgray-100">
|
||||
<div wire:key="user-{{ $user->id }}"
|
||||
class="flex items-center justify-center gap-2 bg-white box-without-bg dark:bg-coolgray-100">
|
||||
<div>{{ $user->name }}</div>
|
||||
<div>{{ $user->email }}</div>
|
||||
<div class="flex-1"></div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.button type="submit">Generate Invitation Link</x-forms.button>
|
||||
@if (is_transactional_emails_active())
|
||||
@if (is_transactional_emails_enabled())
|
||||
<x-forms.button wire:click.prevent='viaEmail'>Send Invitation via Email</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</div>
|
||||
@if (auth()->user()->isAdminFromSession())
|
||||
<div class="py-4">
|
||||
@if (is_transactional_emails_active())
|
||||
@if (is_transactional_emails_enabled())
|
||||
<h2 class="pb-4">Invite New Member</h2>
|
||||
@else
|
||||
<h2>Invite New Member</h2>
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<div class="min-h-screen hero">
|
||||
<div class="w-96 min-w-fit">
|
||||
<div class="flex flex-col items-center pb-8">
|
||||
<a href="{{ route('dashboard') }}">
|
||||
<div class="text-5xl font-bold tracking-tight text-center dark:text-white">Coolify</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex items-center justify-center pb-4 text-center">
|
||||
<h2>Self-hosting in the cloud
|
||||
<svg class="inline-block w-8 h-8 dark:text-warning width="512" height="512" viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="currentColor" fill-rule="evenodd" clip-rule="evenodd">
|
||||
<path
|
||||
d="M13 4h-1a4.002 4.002 0 0 0-3.874 3H8a4 4 0 1 0 0 8h8a4 4 0 0 0 .899-7.899A4.002 4.002 0 0 0 13 4Z"
|
||||
opacity=".2" />
|
||||
<path
|
||||
d="M11 3h-1a4.002 4.002 0 0 0-3.874 3H6a4 4 0 1 0 0 8h8a4 4 0 0 0 .899-7.899A4.002 4.002 0 0 0 11 3ZM6.901 7l.193-.75A3.002 3.002 0 0 1 10 4h1c1.405 0 2.614.975 2.924 2.325l.14.61l.61.141A3.001 3.001 0 0 1 14 13H6a3 3 0 1 1 0-6h.901Z" />
|
||||
</g>
|
||||
</svg>
|
||||
</h2>
|
||||
</div>
|
||||
<form class="flex items-end gap-2" wire:submit='submit'>
|
||||
<x-forms.input id="email" type="email" label="Email" placeholder="youareawesome@protonmail.com" />
|
||||
<x-forms.button type="submit">Join Waitlist</x-forms.button>
|
||||
</form>
|
||||
<div>People waiting in the line: <span class="font-bold dark:text-warning">{{ $waitingInLine }}</div>
|
||||
<div>Already using Coolify Cloud: <span class="font-bold dark:text-warning">{{ $users }}</div>
|
||||
<div class="pt-8">
|
||||
This is a paid & hosted version of Coolify.<br> See the pricing <a href="https://coolify.io/pricing"
|
||||
class="dark:text-warning">here</a>.
|
||||
</div>
|
||||
<div class="pt-4">
|
||||
If you are looking for the self-hosted version go <a href="https://coolify.io"
|
||||
class="dark:text-warning">here</a>.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user