refactor(ui): separate views for instance settings to separate paths to make it cleaner

This commit is contained in:
Andras Bacsai
2025-06-30 08:50:17 +02:00
parent d37369dcaf
commit 90817914d9
36 changed files with 647 additions and 402 deletions

View File

@@ -0,0 +1,119 @@
<?php
namespace App\Livewire\Settings;
use App\Models\InstanceSettings;
use App\Models\Server;
use Auth;
use Hash;
use Livewire\Attributes\Validate;
use Livewire\Component;
class Advanced extends Component
{
#[Validate('required')]
public Server $server;
public InstanceSettings $settings;
#[Validate('boolean')]
public bool $is_registration_enabled;
#[Validate('boolean')]
public bool $do_not_track;
#[Validate('boolean')]
public bool $is_dns_validation_enabled;
#[Validate('nullable|string')]
public ?string $custom_dns_servers = null;
#[Validate('boolean')]
public bool $is_api_enabled;
#[Validate('nullable|string')]
public ?string $allowed_ips = null;
#[Validate('boolean')]
public bool $is_sponsorship_popup_enabled;
#[Validate('boolean')]
public bool $disable_two_step_confirmation;
public function mount()
{
if (! isInstanceAdmin()) {
return redirect()->route('dashboard');
} else {
$this->server = Server::findOrFail(0);
$this->settings = instanceSettings();
$this->custom_dns_servers = $this->settings->custom_dns_servers;
$this->allowed_ips = $this->settings->allowed_ips;
$this->do_not_track = $this->settings->do_not_track;
$this->is_registration_enabled = $this->settings->is_registration_enabled;
$this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
$this->is_api_enabled = $this->settings->is_api_enabled;
$this->disable_two_step_confirmation = $this->settings->disable_two_step_confirmation;
$this->is_sponsorship_popup_enabled = $this->settings->is_sponsorship_popup_enabled;
}
}
public function submit()
{
try {
$this->validate();
$this->custom_dns_servers = str($this->custom_dns_servers)->replaceEnd(',', '')->trim();
$this->custom_dns_servers = str($this->custom_dns_servers)->trim()->explode(',')->map(function ($dns) {
return str($dns)->trim()->lower();
})->unique()->implode(',');
$this->allowed_ips = str($this->allowed_ips)->replaceEnd(',', '')->trim();
$this->allowed_ips = str($this->allowed_ips)->trim()->explode(',')->map(function ($ip) {
return str($ip)->trim();
})->unique()->implode(',');
$this->instantSave();
} catch (\Exception $e) {
return handleError($e, $this);
}
}
public function instantSave()
{
try {
$this->settings->is_registration_enabled = $this->is_registration_enabled;
$this->settings->do_not_track = $this->do_not_track;
$this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
$this->settings->custom_dns_servers = $this->custom_dns_servers;
$this->settings->is_api_enabled = $this->is_api_enabled;
$this->settings->allowed_ips = $this->allowed_ips;
$this->settings->is_sponsorship_popup_enabled = $this->is_sponsorship_popup_enabled;
$this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation;
$this->settings->save();
$this->dispatch('success', 'Settings updated!');
} catch (\Exception $e) {
return handleError($e, $this);
}
}
public function toggleTwoStepConfirmation($password): bool
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return false;
}
$this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation = true;
$this->settings->save();
$this->dispatch('success', 'Two step confirmation has been disabled.');
return true;
}
public function render()
{
return view('livewire.settings.advanced');
}
}

View File

@@ -2,11 +2,8 @@
namespace App\Livewire\Settings;
use App\Jobs\CheckForUpdatesJob;
use App\Models\InstanceSettings;
use App\Models\Server;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Validate;
use Livewire\Component;
@@ -15,10 +12,7 @@ class Index extends Component
{
public InstanceSettings $settings;
protected Server $server;
#[Validate('boolean')]
public bool $is_auto_update_enabled;
public Server $server;
#[Validate('nullable|string|max:255')]
public ?string $fqdn = null;
@@ -29,48 +23,18 @@ class Index extends Component
#[Validate('required|integer|min:1025|max:65535')]
public int $public_port_max;
#[Validate('nullable|string')]
public ?string $custom_dns_servers = null;
#[Validate('nullable|string|max:255')]
public ?string $instance_name = null;
#[Validate('nullable|string')]
public ?string $allowed_ips = null;
#[Validate('nullable|string')]
public ?string $public_ipv4 = null;
#[Validate('nullable|string')]
public ?string $public_ipv6 = null;
#[Validate('string')]
public string $auto_update_frequency;
#[Validate('string|required')]
public string $update_check_frequency;
#[Validate('required|string|timezone')]
public string $instance_timezone;
#[Validate('boolean')]
public bool $do_not_track;
#[Validate('boolean')]
public bool $is_registration_enabled;
#[Validate('boolean')]
public bool $is_dns_validation_enabled;
#[Validate('boolean')]
public bool $is_api_enabled;
#[Validate('boolean')]
public bool $disable_two_step_confirmation;
#[Validate('boolean')]
public bool $is_sponsorship_popup_enabled;
public function render()
{
return view('livewire.settings.index');
@@ -82,24 +46,14 @@ class Index extends Component
return redirect()->route('dashboard');
} else {
$this->settings = instanceSettings();
$this->server = Server::findOrFail(0);
$this->fqdn = $this->settings->fqdn;
$this->public_port_min = $this->settings->public_port_min;
$this->public_port_max = $this->settings->public_port_max;
$this->custom_dns_servers = $this->settings->custom_dns_servers;
$this->instance_name = $this->settings->instance_name;
$this->allowed_ips = $this->settings->allowed_ips;
$this->public_ipv4 = $this->settings->public_ipv4;
$this->public_ipv6 = $this->settings->public_ipv6;
$this->do_not_track = $this->settings->do_not_track;
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
$this->is_registration_enabled = $this->settings->is_registration_enabled;
$this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
$this->is_api_enabled = $this->settings->is_api_enabled;
$this->auto_update_frequency = $this->settings->auto_update_frequency;
$this->update_check_frequency = $this->settings->update_check_frequency;
$this->instance_timezone = $this->settings->instance_timezone;
$this->disable_two_step_confirmation = $this->settings->disable_two_step_confirmation;
$this->is_sponsorship_popup_enabled = $this->settings->is_sponsorship_popup_enabled;
}
}
@@ -115,30 +69,13 @@ class Index extends Component
public function instantSave($isSave = true)
{
$this->validate();
if ($this->settings->is_auto_update_enabled === true) {
$this->validate([
'auto_update_frequency' => ['required', 'string'],
]);
}
$this->settings->fqdn = $this->fqdn;
$this->settings->public_port_min = $this->public_port_min;
$this->settings->public_port_max = $this->public_port_max;
$this->settings->custom_dns_servers = $this->custom_dns_servers;
$this->settings->instance_name = $this->instance_name;
$this->settings->allowed_ips = $this->allowed_ips;
$this->settings->public_ipv4 = $this->public_ipv4;
$this->settings->public_ipv6 = $this->public_ipv6;
$this->settings->do_not_track = $this->do_not_track;
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
$this->settings->is_registration_enabled = $this->is_registration_enabled;
$this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
$this->settings->is_api_enabled = $this->is_api_enabled;
$this->settings->auto_update_frequency = $this->auto_update_frequency;
$this->settings->update_check_frequency = $this->update_check_frequency;
$this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation;
$this->settings->instance_timezone = $this->instance_timezone;
$this->settings->is_sponsorship_popup_enabled = $this->is_sponsorship_popup_enabled;
if ($isSave) {
$this->settings->save();
$this->dispatch('success', 'Settings updated!');
@@ -149,7 +86,6 @@ class Index extends Component
{
try {
$error_show = false;
$this->server = Server::findOrFail(0);
$this->resetErrorBag();
if (! validate_timezone($this->instance_timezone)) {
@@ -166,46 +102,15 @@ class Index extends Component
}
$this->validate();
if ($this->is_auto_update_enabled && ! validate_cron_expression($this->auto_update_frequency)) {
$this->dispatch('error', 'Invalid Cron / Human expression for Auto Update Frequency.');
if (empty($this->auto_update_frequency)) {
$this->auto_update_frequency = '0 0 * * *';
}
return;
}
if (! validate_cron_expression($this->update_check_frequency)) {
$this->dispatch('error', 'Invalid Cron / Human expression for Update Check Frequency.');
if (empty($this->update_check_frequency)) {
$this->update_check_frequency = '0 * * * *';
}
return;
}
if ($this->settings->is_dns_validation_enabled && $this->settings->fqdn) {
if (! validate_dns_entry($this->settings->fqdn, $this->server)) {
$this->dispatch('error', "Validating DNS failed.<br><br>Make sure you have added the DNS records correctly.<br><br>{$this->settings->fqdn}->{$this->server->ip}<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
if ($this->settings->is_dns_validation_enabled && $this->fqdn) {
if (! validate_dns_entry($this->fqdn, $this->server)) {
$this->dispatch('error', "Validating DNS failed.<br><br>Make sure you have added the DNS records correctly.<br><br>{$this->fqdn}->{$this->server->ip}<br><br>Check this <a target='_blank' class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/dns-configuration'>documentation</a> for further help.");
$error_show = true;
}
}
if ($this->settings->fqdn) {
check_domain_usage(domain: $this->settings->fqdn);
if ($this->fqdn) {
check_domain_usage(domain: $this->fqdn);
}
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->replaceEnd(',', '')->trim();
$this->settings->custom_dns_servers = str($this->settings->custom_dns_servers)->trim()->explode(',')->map(function ($dns) {
return str($dns)->trim()->lower();
});
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->unique();
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->implode(',');
$this->settings->allowed_ips = str($this->settings->allowed_ips)->replaceEnd(',', '')->trim();
$this->settings->allowed_ips = str($this->settings->allowed_ips)->trim()->explode(',')->map(function ($ip) {
return str($ip)->trim();
});
$this->settings->allowed_ips = $this->settings->allowed_ips->unique();
$this->settings->allowed_ips = $this->settings->allowed_ips->implode(',');
$this->instantSave(isSave: false);
@@ -218,31 +123,4 @@ class Index extends Component
return handleError($e, $this);
}
}
public function checkManually()
{
CheckForUpdatesJob::dispatchSync();
$this->dispatch('updateAvailable');
$settings = instanceSettings();
if ($settings->new_version_available) {
$this->dispatch('success', 'New version available!');
} else {
$this->dispatch('success', 'No new version available.');
}
}
public function toggleTwoStepConfirmation($password): bool
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return false;
}
$this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation = true;
$this->settings->save();
$this->dispatch('success', 'Two step confirmation has been disabled.');
return true;
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace App\Livewire\Settings;
use App\Jobs\CheckForUpdatesJob;
use App\Models\InstanceSettings;
use App\Models\Server;
use Livewire\Attributes\Validate;
use Livewire\Component;
class Updates extends Component
{
public InstanceSettings $settings;
public Server $server;
#[Validate('string')]
public string $auto_update_frequency;
#[Validate('string|required')]
public string $update_check_frequency;
#[Validate('boolean')]
public bool $is_auto_update_enabled;
public function mount()
{
$this->server = Server::findOrFail(0);
$this->settings = instanceSettings();
$this->auto_update_frequency = $this->settings->auto_update_frequency;
$this->update_check_frequency = $this->settings->update_check_frequency;
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
}
public function instantSave()
{
try {
if ($this->settings->is_auto_update_enabled === true) {
$this->validate([
'auto_update_frequency' => ['required', 'string'],
]);
}
$this->settings->auto_update_frequency = $this->auto_update_frequency;
$this->settings->update_check_frequency = $this->update_check_frequency;
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
$this->settings->save();
$this->dispatch('success', 'Settings updated!');
} catch (\Exception $e) {
return handleError($e, $this);
}
}
public function submit()
{
try {
$this->resetErrorBag();
$this->validate();
if ($this->is_auto_update_enabled && ! validate_cron_expression($this->auto_update_frequency)) {
$this->dispatch('error', 'Invalid Cron / Human expression for Auto Update Frequency.');
if (empty($this->auto_update_frequency)) {
$this->auto_update_frequency = '0 0 * * *';
}
return;
}
if (! validate_cron_expression($this->update_check_frequency)) {
$this->dispatch('error', 'Invalid Cron / Human expression for Update Check Frequency.');
if (empty($this->update_check_frequency)) {
$this->update_check_frequency = '0 * * * *';
}
return;
}
$this->instantSave();
$this->server->setupDynamicProxyConfiguration();
} catch (\Exception $e) {
return handleError($e, $this);
}
}
public function checkManually()
{
CheckForUpdatesJob::dispatchSync();
$this->dispatch('updateAvailable');
$settings = instanceSettings();
if ($settings->new_version_available) {
$this->dispatch('success', 'New version available!');
} else {
$this->dispatch('success', 'No new version available.');
}
}
public function render()
{
return view('livewire.settings.updates');
}
}

View File

@@ -31,7 +31,7 @@ class Form extends Component
'storage.endpoint' => 'Endpoint',
];
public function test_s3_connection()
public function testConnection()
{
try {
$this->storage->testConnection(shouldSave: true);
@@ -45,6 +45,8 @@ class Form extends Component
public function delete()
{
try {
$this->authorize('delete', $this->storage);
$this->storage->delete();
return redirect()->route('storage.index');
@@ -57,7 +59,7 @@ class Form extends Component
{
$this->validate();
try {
$this->test_s3_connection();
$this->testConnection();
} catch (\Throwable $e) {
return handleError($e, $this);
}

View File

@@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\S3Storage;
use App\Models\Server;
use App\Models\User;
class S3StoragePolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return true;
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, S3Storage $storage): bool
{
return $user->teams()->get()->firstWhere('id', $storage->team_id) !== null;
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return true;
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Server $server): bool
{
return $user->teams()->get()->firstWhere('id', $server->team_id) !== null;
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, S3Storage $storage): bool
{
return $user->teams()->get()->firstWhere('id', $storage->team_id) !== null;
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, S3Storage $storage): bool
{
return false;
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, S3Storage $storage): bool
{
return false;
}
}

View File

@@ -42,7 +42,7 @@
}
@utility button {
@apply flex gap-2 justify-center items-center px-2 py-1 text-sm text-black normal-case rounded-sm border outline-0 cursor-pointer bg-neutral-200/50 border-neutral-300 hover:bg-neutral-300 dark:bg-coolgray-200 dark:text-white dark:hover:text-white dark:hover:bg-coolgray-500 dark:border-coolgray-300 hover:text-black disabled:cursor-not-allowed min-w-fit dark:disabled:text-neutral-600 disabled:border-none disabled:hover:bg-transparent disabled:bg-transparent disabled:text-neutral-300;
@apply flex gap-2 justify-center items-center px-2 py-1 text-sm text-black normal-case rounded-sm border outline-0 cursor-pointer bg-neutral-200/50 border-neutral-300 hover:bg-neutral-300 dark:bg-coolgray-200 dark:text-white dark:hover:text-white dark:hover:bg-coolgray-500 dark:border-coolgray-300 hover:text-black disabled:cursor-not-allowed min-w-fit dark:disabled:text-neutral-600 disabled:border-transparent disabled:hover:bg-transparent disabled:bg-transparent disabled:text-neutral-300;
}
@utility alert-success {

View File

@@ -5,19 +5,19 @@
<nav class="flex items-center gap-6 min-h-10 whitespace-nowrap">
<a class="{{ request()->routeIs('settings.index') ? 'dark:text-white' : '' }}"
href="{{ route('settings.index') }}">
<button>Configuration</button>
Configuration
</a>
<a class="{{ request()->routeIs('settings.backup') ? 'dark:text-white' : '' }}"
href="{{ route('settings.backup') }}">
<button>Backup</button>
Backup
</a>
<a class="{{ request()->routeIs('settings.email') ? 'dark:text-white' : '' }}"
href="{{ route('settings.email') }}">
<button>Transactional Email</button>
Transactional Email
</a>
<a class="{{ request()->routeIs('settings.oauth') ? 'dark:text-white' : '' }}"
href="{{ route('settings.oauth') }}">
<button>OAuth</button>
OAuth
</a>
<div class="flex-1"></div>
</nav>

View File

@@ -0,0 +1,8 @@
<div class="flex flex-col items-start gap-2 min-w-fit">
<a class="menu-item {{ $activeMenu === 'general' ? 'menu-item-active' : '' }}"
href="{{ route('settings.index') }}">General</a>
<a class="menu-item {{ $activeMenu === 'advanced' ? 'menu-item-active' : '' }}"
href="{{ route('settings.advanced') }}">Advanced</a>
<a class="menu-item {{ $activeMenu === 'updates' ? 'menu-item-active' : '' }}"
href="{{ route('settings.updates') }}">Updates</a>
</div>

View File

@@ -9,16 +9,16 @@
<div class="navbar-main">
<nav class="flex items-center gap-6 min-h-10">
<a class="{{ request()->routeIs('team.index') ? 'dark:text-white' : '' }}" href="{{ route('team.index') }}">
<button>General</button>
General
</a>
<a class="{{ request()->routeIs('team.member.index') ? 'dark:text-white' : '' }}"
href="{{ route('team.member.index') }}">
<button>Members</button>
Members
</a>
@if (isInstanceAdmin())
<a class="{{ request()->routeIs('team.admin-view') ? 'dark:text-white' : '' }}"
href="{{ route('team.admin-view') }}">
<button>Admin View</button>
Admin View
</a>
@endif
<div class="flex-1"></div>

View File

@@ -63,12 +63,13 @@
<section>
<h3 class="pb-2">Servers</h3>
@if ($servers->count() > 0)
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
<div class="grid grid-cols-1 gap-4 xl:grid-cols-2">
@foreach ($servers as $server)
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
@class([
'gap-2 border cursor-pointer box group',
'border-red-500' => !$server->settings->is_reachable,
'border-red-500' =>
!$server->settings->is_reachable || $server->settings->force_disabled,
])>
<div class="flex flex-col justify-center mx-6">
<div class="box-title">

View File

@@ -11,13 +11,13 @@
@endif
</div>
<div class="subtitle">Network endpoints to deploy your resources.</div>
<div class="grid gap-2 lg:grid-cols-1">
<div class="grid gap-4 lg:grid-cols-2">
@forelse ($servers as $server)
@forelse ($server->destinations() as $destination)
@if ($destination->getMorphClass() === 'App\Models\StandaloneDocker')
<a class="box group"
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}">
<div class="flex flex-col mx-6">
<div class="flex flex-col justify-center mx-6">
<div class="box-title">{{ $destination->name }}</div>
<div class="box-description">Server: {{ $destination->server->name }}</div>
</div>

View File

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

View File

@@ -112,7 +112,7 @@
:checkboxes="$checkboxes"
:actions="['This backup will be permanently deleted from local storage.']" confirmationText="{{ data_get($execution, 'filename') }}"
confirmationLabel="Please confirm the execution of the actions by entering the Backup Filename below"
shortConfirmationLabel="Backup Filename" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Backup Filename" step3ButtonText="Confirm" />
</div>
</div>
@empty

View File

@@ -11,11 +11,11 @@
buttonTitle="Convert to Application" submitAction="convertToApplication" :actions="['The selected resource will be converted to an application.']"
confirmationText="{{ Str::headline($database->name) }}"
confirmationLabel="Please confirm the execution of the actions by entering the Service Database Name below"
shortConfirmationLabel="Service Database Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Service Database Name" step3ButtonText="Confirm" />
<x-modal-confirmation title="Confirm Service Database Deletion?" buttonTitle="Delete" isErrorButton
submitAction="delete" :actions="['The selected service database container will be stopped and permanently deleted.']" confirmationText="{{ Str::headline($database->name) }}"
confirmationLabel="Please confirm the execution of the actions by entering the Service Database Name below"
shortConfirmationLabel="Service Database Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Service Database Name" step3ButtonText="Confirm" />
</div>
<div class="flex flex-col gap-2">
<div class="flex gap-2">

View File

@@ -21,7 +21,7 @@
]"
confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Filepath" step3ButtonText="Confirm" />
@else
@if (!$fileStorage->is_binary)
<x-modal-confirmation :ignoreWire="false" title="Confirm File Conversion to Directory?"
@@ -37,7 +37,7 @@
isErrorButton submitAction="delete" :checkboxes="$fileDeletionCheckboxes" :actions="['The selected file will be permanently deleted from the container.']"
confirmationText="{{ $fs_path }}"
confirmationLabel="Please confirm the execution of the actions by entering the Filepath below"
shortConfirmationLabel="Filepath" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Filepath" step3ButtonText="Confirm" />
@endif
</div>
@if (!$fileStorage->is_directory)

View File

@@ -11,11 +11,11 @@
buttonTitle="Convert to Database" submitAction="convertToDatabase" :actions="['The selected resource will be converted to a service database.']"
confirmationText="{{ Str::headline($application->name) }}"
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
shortConfirmationLabel="Service Application Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Service Application Name" step3ButtonText="Confirm" />
<x-modal-confirmation title="Confirm Service Application Deletion?" buttonTitle="Delete" isErrorButton
submitAction="delete" :actions="['The selected service application container will be stopped and permanently deleted.']" confirmationText="{{ Str::headline($application->name) }}"
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
shortConfirmationLabel="Service Application Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Service Application Name" step3ButtonText="Confirm" />
</div>
<div class="flex flex-col gap-2">
<div class="flex gap-2">

View File

@@ -7,5 +7,5 @@
<x-modal-confirmation title="Confirm Resource Deletion?" buttonTitle="Delete" isErrorButton submitAction="delete"
buttonTitle="Delete" :checkboxes="$checkboxes" :actions="['Permanently delete all containers of this resource.']" confirmationText="{{ $resourceName }}"
confirmationLabel="Please confirm the execution of the actions by entering the Resource Name below"
shortConfirmationLabel="Resource Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Resource Name" step3ButtonText="Confirm" />
</div>

View File

@@ -70,7 +70,7 @@
'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="Remove application from server" />
shortConfirmationLabel="Server Name" step3ButtonText="Confirm" />
</div>
</div>
@endforeach

View File

@@ -60,7 +60,7 @@
'If the persistent storage/volume is actvily used by a resource data will be lost.',
]" confirmationText="{{ $storage->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Storage Name below"
shortConfirmationLabel="Storage Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Storage Name" step3ButtonText="Confirm" />
</div>
@endif
</form>

View File

@@ -1,6 +1,5 @@
<div>
<x-security.navbar />
<div class="flex gap-2">
<h2 class="pb-4">Private Keys</h2>
<x-modal-input buttonTitle="+ Add" title="New Private Key">
@@ -9,18 +8,19 @@
<x-modal-confirmation title="Confirm unused SSH Key Deletion?" buttonTitle="Delete unused SSH Keys" isErrorButton
submitAction="cleanupUnusedKeys" :actions="['All unused SSH keys (marked with unused) are permanently deleted.']" :confirmWithText="false" :confirmWithPassword="false" />
</div>
<div class="grid gap-2 lg:grid-cols-2">
<div class="grid gap-4 lg:grid-cols-2">
@forelse ($privateKeys as $key)
<a class="box group"
href="{{ route('security.private-key.show', ['private_key_uuid' => data_get($key, 'uuid')]) }}">
<div class="flex flex-col mx-6">
<div class="flex flex-col justify-center mx-6">
<div class="box-title">
{{ data_get($key, 'name') }}
</div>
<div class="box-description">
{{ $key->description }}
@if (!$key->isInUse())
<span class="inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-400 text-black">Unused</span>
<span
class="inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-yellow-400 text-black">Unused</span>
@endif
</div>

View File

@@ -4,29 +4,30 @@
</x-slot>
<x-security.navbar />
<div x-data="{ showPrivateKey: false }">
<form class="flex flex-col gap-2" wire:submit='changePrivateKey'>
<div class="flex items-end gap-2">
<h2>Private Key</h2>
<form class="flex flex-col" wire:submit='changePrivateKey'>
<div class="flex items-start gap-2">
<h2 class="pb-4">Private Key</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
@if (data_get($private_key, 'id') > 0)
<x-modal-confirmation
title="Confirm Private Key Deletion?"
isErrorButton
buttonTitle="Delete"
submitAction="delete({{ $private_key->id }})"
:actions="['This private key will be permanently deleted.', 'All servers connected to this private key will stop working.', 'Any git app using this private key will stop working.']"
<x-modal-confirmation title="Confirm Private Key Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete({{ $private_key->id }})" :actions="[
'This private key will be permanently deleted.',
'All servers connected to this private key will stop working.',
'Any git app using this private key will stop working.',
]"
confirmationText="{{ $private_key->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Private Key Name below"
shortConfirmationLabel="Private Key Name"
:confirmWithPassword="false"
step2ButtonText="Delete Private Key"
/>
shortConfirmationLabel="Private Key Name" :confirmWithPassword="false"
step2ButtonText="Delete Private Key" />
@endif
</div>
<div class="flex flex-col gap-2">
<div class="flex gap-2">
<x-forms.input id="private_key.name" label="Name" required />
<x-forms.input id="private_key.description" label="Description" />
</div>
<div>
<div class="flex items-end gap-2 py-2 ">
<div class="pl-1">Public Key</div>
@@ -56,6 +57,7 @@
<x-forms.textarea rows="10" id="private_key.private_key" required />
</div>
</div>
</div>
</form>
</div>
</div>

View File

@@ -42,8 +42,7 @@
]"
confirmationText="DISABLE CLOUDFLARE TUNNEL"
confirmationLabel="Please type the confirmation text to disable Cloudflare Tunnel."
shortConfirmationLabel="Confirmation text"
step3ButtonText="Disable Cloudflare Tunnel" />
shortConfirmationLabel="Confirmation text" step3ButtonText="Confirm" />
@else
<x-modal-confirmation title="Disable Cloudflare Tunnel?"
buttonTitle="Disable Cloudflare Tunnel" isErrorButton
@@ -55,8 +54,7 @@
]"
confirmationText="DISABLE CLOUDFLARE TUNNEL"
confirmationLabel="Please type the confirmation text to disable Cloudflare Tunnel."
shortConfirmationLabel="Confirmation text"
step3ButtonText="Disable Cloudflare Tunnel" />
shortConfirmationLabel="Confirmation text" step3ButtonText="Confirm" />
@endif
</div>

View File

@@ -18,12 +18,12 @@
<x-modal-confirmation title="Confirm Server Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="['This server will be permanently deleted.']" confirmationText="{{ $server->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
shortConfirmationLabel="Server Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Server Name" step3ButtonText="Confirm" />
@else
<x-modal-confirmation title="Confirm Server Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete" :actions="['This server will be permanently deleted.']" confirmationText="{{ $server->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
shortConfirmationLabel="Server Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="Server Name" step3ButtonText="Confirm" />
@endif
@endif
</div>

View File

@@ -9,15 +9,11 @@
</x-modal-input>
</div>
<div class="subtitle">All your servers are here.</div>
<div class="grid gap-2 lg:grid-cols-2">
<div class="grid gap-4 lg:grid-cols-2">
@forelse ($servers as $server)
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
@class([
'gap-2 border cursor-pointer box group',
'border-transparent' =>
$server->settings->is_reachable &&
$server->settings->is_usable &&
!$server->settings->force_disabled,
'border-red-500' =>
!$server->settings->is_reachable || $server->settings->force_disabled,
])>

View File

@@ -0,0 +1,79 @@
<div>
<x-slot:title>
Advanced Settings | Coolify
</x-slot>
<x-settings.navbar />
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 sm:flex-row">
<x-settings.sidebar activeMenu="advanced" />
<form wire:submit='submit' class="flex flex-col">
<div class="flex items-center gap-2">
<h2>Advanced</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="pb-4">Advanced settings for your Coolify instance.</div>
<div class="flex flex-col gap-1 md:w-96">
<x-forms.checkbox instantSave id="is_registration_enabled"
helper="If enabled, users can register themselves. If disabled, only administrators can create new users."
label="Registration Allowed" />
<x-forms.checkbox instantSave id="do_not_track"
helper="If enabled, Coolify will not track any data. This is useful if you are concerned about privacy."
label="Do Not Track" />
<h4 class="pt-4">DNS Settings</h4>
<x-forms.checkbox instantSave id="is_dns_validation_enabled"
helper="If you set a custom domain, Coolify will validate the domain in your DNS provider."
label="DNS Validation" />
<x-forms.input id="custom_dns_servers" label="Custom DNS Servers"
helper="DNS servers to validate domains against. A comma separated list of DNS servers."
placeholder="1.1.1.1,8.8.8.8" />
<h4 class="pt-4">API Settings</h4>
<x-forms.checkbox instantSave id="is_api_enabled" label="API Access"
helper="If enabled, the API will be enabled. If disabled, the API will be disabled." />
<x-forms.input id="allowed_ips" label="Allowed IPs for API Access"
helper="Allowed IP lists for the API. A comma separated list of IPs. Empty means you allow from everywhere."
placeholder="1.1.1.1,8.8.8.8" />
<h4 class="pt-4">Confirmation Settings</h4>
<div class="md:w-96 pb-1">
<x-forms.checkbox instantSave id="is_sponsorship_popup_enabled" label="Show Sponsorship Popup"
helper="When enabled, sponsorship popups will be shown monthly to users. When disabled, the sponsorship popup will be permanently hidden for all users." />
</div>
</div>
<div class="flex flex-col gap-1">
@if ($disable_two_step_confirmation)
<div class="md:w-96 pb-4" wire:key="two-step-confirmation-enabled">
<x-forms.checkbox instantSave id="disable_two_step_confirmation"
label="Disable Two Step Confirmation"
helper="When disabled, you will not need to confirm actions with a text and user password. This significantly reduces security and may lead to accidental deletions or unwanted changes. Use with extreme caution, especially on production servers." />
</div>
@else
<div class="md:w-96 pb-4 flex items-center justify-between gap-2"
wire:key="two-step-confirmation-disabled">
<label class="flex items-center gap-2">
Disable Two Step Confirmation
<x-helper
helper="When disabled, you will not need to confirm actions with a text and user password. This significantly reduces security and may lead to accidental deletions or unwanted changes. Use with extreme caution, especially on production servers.">
</x-helper>
</label>
<x-modal-confirmation title="Disable Two Step Confirmation?" buttonTitle="Disable" isErrorButton
submitAction="toggleTwoStepConfirmation" :actions="[
'Two Step confirmation will be disabled globally.',
'Disabling two step confirmation reduces security (as anyone can easily delete anything).',
'The risk of accidental actions will increase.',
]"
confirmationText="DISABLE TWO STEP CONFIRMATION"
confirmationLabel="Please type the confirmation text to disable two step confirmation."
shortConfirmationLabel="Confirmation text" step3ButtonText="Confirm" />
</div>
<div class="w-full px-4 py-2 mb-4 text-white rounded-xs border-l-4 border-red-500 bg-error">
<p class="font-bold">Warning!</p>
<p>Disabling two step confirmation reduces security (as anyone can easily delete anything) and
increases
the risk of accidental actions. This is not recommended for production servers.</p>
</div>
@endif
</div>
</form>
</div>
</div>

View File

@@ -3,23 +3,25 @@
Settings | Coolify
</x-slot>
<x-settings.navbar />
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 sm:flex-row">
<x-settings.sidebar activeMenu="general" />
<form wire:submit='submit' class="flex flex-col">
<div class="flex items-center gap-2 pb-2">
<h2>Configuration</h2>
<div class="flex items-center gap-2">
<h2>General</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div>General configuration for your Coolify instance.</div>
<div class="pb-4">General configuration for your Coolify instance.</div>
<div class="flex flex-col gap-2">
<h4 class="pt-6">Instance Settings</h4>
<div class="flex flex-wrap items-end gap-2">
<div class="flex gap-2 md:flex-row flex-col w-full">
<x-forms.input id="fqdn" label="Instance's Domain"
<x-forms.input id="fqdn" label="Domain"
helper="Enter the full domain name (FQDN) of the instance, including 'https://' if you want to secure the dashboard with HTTPS. Setting this will make the dashboard accessible via this domain, secured by HTTPS, instead of just the IP address."
placeholder="https://coolify.yourdomain.com" />
<x-forms.input id="instance_name" label="Instance's Name" placeholder="Coolify" />
<x-forms.input id="instance_name" label="Name" placeholder="Coolify"
helper="Custom name for your Coolify instance, shown in the URL." />
<div class="w-full" x-data="{
open: false,
search: '{{ $settings->instance_timezone ?: '' }}',
@@ -67,95 +69,15 @@
</div>
</div>
<div class="flex gap-2 md:flex-row flex-col w-full">
<x-forms.input id="public_ipv4" type="password" label="Instance's IPv4"
<x-forms.input id="public_ipv4" type="password" label="Instance's Public IPv4"
helper="Enter the IPv4 address of the instance.<br><br>It is useful if you have several IPv4 addresses and Coolify could not detect the correct one."
placeholder="1.2.3.4" autocomplete="new-password" />
<x-forms.input id="public_ipv6" type="password" label="Instance's IPv6"
<x-forms.input id="public_ipv6" type="password" label="Instance's Public IPv6"
helper="Enter the IPv6 address of the instance.<br><br>It is useful if you have several IPv6 addresses and Coolify could not detect the correct one."
placeholder="2001:db8::1" autocomplete="new-password" />
</div>
<h4 class="w-full pt-6">DNS Validation</h4>
<div class="md:w-96">
<x-forms.checkbox instantSave id="is_dns_validation_enabled" label="Enabled" />
</div>
<x-forms.input id="custom_dns_servers" label="DNS Servers"
helper="DNS servers to validate FQDNs against. A comma separated list of DNS servers."
placeholder="1.1.1.1,8.8.8.8" />
</div>
{{-- <div class="flex gap-2 ">
<x-forms.input type="number" id="public_port_min" label="Public Port Min" />
<x-forms.input type="number" id="public_port_max" label="Public Port Max" />
</div> --}}
</div>
<h4 class="pt-6">API</h4>
<div class="md:w-96 pb-2">
<x-forms.checkbox instantSave id="is_api_enabled" label="Enabled" />
</div>
<x-forms.input id="allowed_ips" label="Allowed IPs"
helper="Allowed IP lists for the API. A comma separated list of IPs. Empty means you allow from everywhere."
placeholder="1.1.1.1,8.8.8.8" />
<h4 class="pt-6">Update</h4>
<div class="text-right md:w-96 pb-4">
@if (!is_null(config('constants.coolify.autoupdate', null)))
<div class="text-right md:w-96">
<x-forms.checkbox instantSave helper="AUTOUPDATE is set in .env file, you need to modify it there."
disabled checked="{{ config('constants.coolify.autoupdate') }}" label="Auto Update Enabled" />
</div>
@else
<x-forms.checkbox instantSave id="is_auto_update_enabled" label="Auto Update Enabled" />
@endif
</div>
<div class="flex flex-col gap-2">
<div class="flex items-end gap-2">
<x-forms.input required id="update_check_frequency" label="Update Check Frequency"
placeholder="0 * * * *"
helper="Cron expression for update check frequency (check for new Coolify versions and pull new Service Templates from CDN).<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every hour." />
<x-forms.button wire:click='checkManually'>Check Manually</x-forms.button>
</div>
@if (is_null(config('constants.coolify.autoupdate', null)) && $is_auto_update_enabled)
<x-forms.input required id="auto_update_frequency" label="Auto Update Frequency" placeholder="0 0 * * *"
helper="Cron expression for auto update frequency (automatically update coolify).<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every day at 00:00" />
@endif
</div>
<h4 class="pt-6">Advanced</h4>
<div class="text-right md:w-96">
<x-forms.checkbox instantSave id="is_registration_enabled" label="Registration Allowed" />
<x-forms.checkbox instantSave id="do_not_track" label="Do Not Track" />
</div>
<h4 class="py-4">Confirmation Settings</h4>
<div class="md:w-96 ">
<x-forms.checkbox instantSave id="is_sponsorship_popup_enabled" label="Show Sponsorship Popup"
helper="When enabled, sponsorship popups will be shown monthly to users. When disabled, the sponsorship popup will be permanently hidden for all users." />
</div>
@if ($disable_two_step_confirmation)
<div class="md:w-96 pb-4" wire:key="two-step-confirmation-enabled">
<x-forms.checkbox instantSave id="disable_two_step_confirmation" label="Disable Two Step Confirmation"
helper="When disabled, you will not need to confirm actions with a text and user password. This significantly reduces security and may lead to accidental deletions or unwanted changes. Use with extreme caution, especially on production servers." />
</div>
@else
<div class="md:w-96 pb-4" wire:key="two-step-confirmation-disabled">
<x-modal-confirmation title="Disable Two Step Confirmation?"
buttonTitle="Disable Two Step Confirmation" isErrorButton submitAction="toggleTwoStepConfirmation"
:actions="[
'Two Step confirmation will be disabled globally.',
'Disabling two step confirmation reduces security (as anyone can easily delete anything).',
'The risk of accidental actions will increase.',
]" confirmationText="DISABLE TWO STEP CONFIRMATION"
confirmationLabel="Please type the confirmation text to disable two step confirmation."
shortConfirmationLabel="Confirmation text" step3ButtonText="Disable Two Step Confirmation" />
</div>
<div class="w-full px-4 py-2 mb-4 text-white rounded-xs border-l-4 border-red-500 bg-error">
<p class="font-bold">Warning!</p>
<p>Disabling two step confirmation reduces security (as anyone can easily delete anything) and
increases
the risk of accidental actions. This is not recommended for production servers.</p>
</div>
@endif
</form>
</div>
</div>

View File

@@ -0,0 +1,51 @@
<div>
<x-slot:title>
Auto Update | Coolify
</x-slot>
<x-settings.navbar />
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 sm:flex-row">
<x-settings.sidebar activeMenu="updates" />
<form wire:submit='submit' class="flex flex-col w-full">
<div class="flex items-center gap-2">
<h2>Updates</h2>
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="pb-4">Your instance's update settings.</div>
<div class="flex flex-col gap-2">
<div class="flex items-end gap-2">
<x-forms.input required id="update_check_frequency" label="Update Check Frequency"
placeholder="0 * * * *"
helper="Frequency (cron expression) to check for new Coolify versions and pull new Service Templates from CDN.<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every hour." />
<x-forms.button wire:click='checkManually'>Check Manually</x-forms.button>
</div>
<h4 class="pt-4">Auto Update</h4>
<div class="text-right md:w-64">
@if (!is_null(config('constants.coolify.autoupdate', null)))
<div class="text-right">
<x-forms.checkbox instantSave
helper="AUTOUPDATE is set in .env file, you need to modify it there." disabled
checked="{{ config('constants.coolify.autoupdate') }}" label="Enabled" />
</div>
@else
<x-forms.checkbox instantSave id="is_auto_update_enabled" label="Enabled" />
@endif
</div>
@if (is_null(config('constants.coolify.autoupdate', null)) && $is_auto_update_enabled)
<x-forms.input required id="auto_update_frequency" label="Frequency (cron expression)"
placeholder="0 0 * * *"
helper="Frequency (cron expression) (automatically update coolify).<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every day at 00:00" />
@else
<x-forms.input required label="Frequency (cron expression)" disabled placeholder="disabled"
helper="Frequency (cron expression) (automatically update coolify).<br>You can use every_minute, hourly, daily, weekly, monthly, yearly.<br><br>Default is every day at 00:00" />
@endif
</div>
</form>
</div>
</div>

View File

@@ -1,21 +1,28 @@
<div>
<form class="flex flex-col gap-2 pb-6" wire:submit='submit'>
<div class="flex items-start gap-2">
<div class="pb-4">
<div class="">
<h1>Storage Details</h1>
<div class="subtitle">{{ $storage->name }}</div>
<div class="flex items-center gap-2 pb-4">
<div>Current Status:</div>
@if ($storage->is_usable)
<div>Usable</div>
<span
class="px-2 py-1 text-xs font-semibold text-green-800 bg-green-100 rounded dark:text-green-100 dark:bg-green-800">
Usable
</span>
@else
<div class="text-red-500">Not Usable</div>
<span
class="px-2 py-1 text-xs font-semibold text-red-800 bg-red-100 rounded dark:text-red-100 dark:bg-red-800">
Not Usable
</span>
@endif
</div>
</div>
<x-forms.button type="submit">
Save
</x-forms.button>
<x-forms.button wire:click="test_s3_connection">
Validate Connection
</x-forms.button>
<x-modal-confirmation title="Confirm Storage Deletion?" isErrorButton buttonTitle="Delete"
submitAction="delete({{ $storage->id }})" :actions="[
'The selected storage location will be permanently deleted from Coolify.',
@@ -37,5 +44,8 @@
<x-forms.input required type="password" label="Access Key" id="storage.key" />
<x-forms.input required type="password" label="Secret Key" id="storage.secret" />
</div>
<x-forms.button class="mt-4" isHighlighted wire:click="testConnection">
Validate Connection
</x-forms.button>
</form>
</div>

View File

@@ -9,10 +9,10 @@
</x-modal-input>
</div>
<div class="subtitle">S3 storages for backups.</div>
<div class="grid gap-2 lg:grid-cols-2">
<div class="grid gap-4 lg:grid-cols-2">
@forelse ($s3 as $storage)
<a href="/storages/{{ $storage->uuid }}" @class(['gap-2 border cursor-pointer box group border-transparent'])>
<div class="flex flex-col mx-6">
<a href="/storages/{{ $storage->uuid }}" @class(['gap-2 border cursor-pointer box group'])>
<div class="flex flex-col justify-center mx-6">
<div class="box-title">
{{ $storage->name }}
</div>
@@ -20,7 +20,10 @@
{{ $storage->description }}
</div>
@if (!$storage->is_usable)
<div class="text-red-500">Not Usable</div>
<span
class="px-2 py-1 text-xs font-semibold text-red-800 bg-red-100 rounded dark:text-red-100 dark:bg-red-800">
Not Usable
</span>
@endif
</div>
</a>

View File

@@ -7,8 +7,8 @@
</div>
<div class="flex flex-wrap gap-2 ">
@forelse ($tags as $oneTag)
<a :class="{{ $tag?->id == $oneTag->id }} && 'dark:bg-coollabs hover:bg-coollabs-100'"
class="w-64 box-without-bg dark:bg-coolgray-100 dark:text-white font-bold"
<a :class="{{ $tag?->id == $oneTag->id }} && 'dark:bg-coollabs'"
class="w-32 box-without-bg dark:bg-coolgray-100 dark:text-white font-bold dark:hover:bg-coollabs-100 flex justify-center items-center"
href="{{ route('tags.show', ['tagName' => $oneTag->name]) }}">{{ $oneTag->name }}</a>
@empty
<div>No tags yet defined yet. Go to a resource and add a tag there.</div>
@@ -16,7 +16,7 @@
</div>
@if (isset($tag))
<div>
<h3 class="py-4">Details</h3>
<h3 class="py-4">Tag Details</h3>
<div class="flex items-end gap-2 ">
<div class="w-[500px]">
<x-forms.input readonly label="Deploy Webhook URL" id="webhook" />
@@ -35,7 +35,7 @@
@if (isset($applications) && count($applications) > 0)
@foreach ($applications as $application)
<a href="{{ $application->link() }}" class="box group">
<div class="flex flex-col">
<div class="flex flex-col justify-center">
<div class="box-title">
{{ $application->project()->name }}/{{ $application->environment->name }}
</div>

View File

@@ -3,12 +3,16 @@
Team Admin | Coolify
</x-slot>
<x-team.navbar />
<h2>Admin View</h2>
<div class="subtitle">
Manage users of this instance.
</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>
</form>
<h3 class="pt-4">Users</h3>
<div class="flex flex-col gap-2 ">
<h3 class="py-4">Users</h3>
<div class="grid grid-cols-1 gap-2 lg:grid-cols-2">
@forelse ($users as $user)
<div wire:key="user-{{ $user->id }}"
class="flex items-center justify-center gap-2 bg-white box-without-bg dark:bg-coolgray-100">
@@ -23,7 +27,7 @@
]"
confirmationText="{{ $user->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the User Name below"
shortConfirmationLabel="User Name" step3ButtonText="Permanently Delete" />
shortConfirmationLabel="User Name" step3ButtonText="Confirm" />
</div>
</div>
@empty

View File

@@ -4,17 +4,19 @@
</x-slot>
<x-team.navbar />
<form class="flex flex-col gap-2 pb-6" wire:submit='submit'>
<div class="flex items-end gap-2">
<form class="flex flex-col" wire:submit='submit'>
<h2>General</h2>
<div class="subtitle">
Manage the general settings of this team.
</div>
<div class="flex items-end gap-2 pb-6">
<x-forms.input id="team.name" label="Name" required />
<x-forms.input id="team.description" label="Description" />
<x-forms.button type="submit">
Save
</x-forms.button>
</div>
<div class="flex gap-2">
<x-forms.input id="team.name" label="Name" required />
<x-forms.input id="team.description" label="Description" />
</div>
</form>
<div>

View File

@@ -4,7 +4,9 @@
</x-slot>
<x-team.navbar />
<h2>Members</h2>
<div class="subtitle">
Manage or invite members of this team.
</div>
<div class="flex flex-col">
<div class="flex flex-col">
<div class="overflow-x-auto">
@@ -38,8 +40,8 @@
@else
<h2>Invite New Member</h2>
@if (isInstanceAdmin())
<div class="pb-4 text-xs dark:text-warning">You need to configure (as root team) <a href="/settings#smtp"
class="underline dark:text-warning">Transactional
<div class="pb-4 text-xs dark:text-warning">You need to configure (as root team) <a
href="/settings#smtp" class="underline dark:text-warning">Transactional
Emails</a>
before
you can invite a

View File

@@ -1,8 +0,0 @@
<x-layout>
<x-security.navbar />
<a class="text-center hover:no-underline group" href="{{ route('security.private-key.index') }}">
<div class="dark:group-hover:text-white">
<div>Private Keys</div>
</div>
</a>
</x-layout>

View File

@@ -9,24 +9,28 @@
</x-modal-input>
</div>
<div class="subtitle">Git sources for your applications.</div>
<div class="grid gap-2 lg:grid-cols-2">
<div class="grid gap-4 lg:grid-cols-2">
@forelse ($sources as $source)
@if ($source->getMorphClass() === 'App\Models\GithubApp')
<a class="flex gap-4 text-center hover:no-underline box group"
<a class="flex gap-2 text-center hover:no-underline box group"
href="{{ route('source.github.show', ['github_app_uuid' => data_get($source, 'uuid')]) }}">
<x-git-icon class="dark:text-white w-9 h-9" git="{{ $source->getMorphClass() }}" />
<div class="text-left dark:group-hover:text-white">
{{-- <x-git-icon class="dark:text-white w-8 h-8 mt-1" git="{{ $source->getMorphClass() }}" /> --}}
<div class="text-left dark:group-hover:text-white flex flex-col justify-center mx-6">
<div class="box-title">{{ $source->name }}</div>
@if (is_null($source->app_id))
<span class="box-description text-error! ">Configuration is not finished.</span>
@else
@if ($source->organization)
<span class="box-description">Organization: {{ $source->organization }}</span>
@endif
@endif
</div>
</a>
@endif
@if ($source->getMorphClass() === 'App\Models\GitlabApp')
{{-- @if ($source->getMorphClass() === 'App\Models\GitlabApp')
<a class="flex gap-4 text-center hover:no-underline box group"
href="{{ route('source.gitlab.show', ['gitlab_app_uuid' => data_get($source, 'uuid')]) }}">
<x-git-icon class="dark:text-white w-9 h-9" git="{{ $source->getMorphClass() }}" />
<x-git-icon class="dark:text-white w-8 h-8" git="{{ $source->getMorphClass() }}" />
<div class="text-left dark:group-hover:text-white">
<div>{{ $source->name }}</div>
@if (is_null($source->app_id))
@@ -34,7 +38,7 @@
@endif
</div>
</a>
@endif
@endif --}}
@empty
<div>
<div>No sources found.</div>

View File

@@ -52,7 +52,9 @@ use App\Livewire\Server\Proxy\Show as ProxyShow;
use App\Livewire\Server\Resources as ResourcesShow;
use App\Livewire\Server\Security\Patches;
use App\Livewire\Server\Show as ServerShow;
use App\Livewire\Settings\Advanced as SettingsAdvanced;
use App\Livewire\Settings\Index as SettingsIndex;
use App\Livewire\Settings\Updates as SettingsUpdates;
use App\Livewire\SettingsBackup;
use App\Livewire\SettingsEmail;
use App\Livewire\SettingsOauth;
@@ -105,6 +107,9 @@ Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/subscription/new', SubscriptionIndex::class)->name('subscription.index');
Route::get('/settings', SettingsIndex::class)->name('settings.index');
Route::get('/settings/advanced', SettingsAdvanced::class)->name('settings.advanced');
Route::get('/settings/updates', SettingsUpdates::class)->name('settings.updates');
Route::get('/settings/backup', SettingsBackup::class)->name('settings.backup');
Route::get('/settings/email', SettingsEmail::class)->name('settings.email');
Route::get('/settings/oauth', SettingsOauth::class)->name('settings.oauth');