Merge branch 'next' into logs-for-tasks

This commit is contained in:
Andras Bacsai
2024-12-09 11:22:00 +01:00
88 changed files with 1350 additions and 440 deletions

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,43 @@
<div>
<x-slot:title>
Notifications | Coolify
</x-slot>
<x-notification.navbar />
<form wire:submit='submit' class="flex flex-col gap-4">
<div class="flex items-center gap-2">
<h2>Slack</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
@if ($slackEnabled)
<x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
wire:click="sendTestNotification">
Send Test Notifications
</x-forms.button>
@endif
</div>
<div class="w-32">
<x-forms.checkbox instantSave="instantSaveSlackEnabled" id="slackEnabled" label="Enabled" />
</div>
<x-forms.input type="password"
helper="Generate a webhook in Slack.<br>Example: https://hooks.slack.com/services/...." required
id="slackWebhookUrl" label="Webhook" />
</form>
@if ($slackEnabled)
<h2 class="mt-4">Subscribe to events</h2>
<div class="w-64">
@if (isDev())
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsTest" label="Test" />
@endif
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsStatusChanges"
label="Container Status Changes" />
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsDeployments"
label="Application Deployments" />
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsDatabaseBackups" label="Backup Status" />
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsScheduledTasks"
label="Scheduled Tasks Status" />
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsServerDiskUsage"
label="Server Disk Usage" />
</div>
@endif
</div>

View File

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

View File

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

View File

@@ -12,7 +12,7 @@
<div class="pb-4">Deploy resources, like Applications, Databases, Services...</div>
<div x-data="searchResources()">
@if ($current_step === 'type')
<div x-data="{ isSticky: false }" x-init="window.addEventListener('scroll', () => isSticky = window.pageYOffset > 100)" class="sticky top-0 z-50 py-2">
<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()">
@@ -137,6 +137,7 @@
return {
search: '',
loading: false,
isSticky: false,
services: [],
gitBasedApplications: [],
dockerBasedApplications: [],

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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