fix: settings view
feat: add separate views for settings
This commit is contained in:
@@ -2,9 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Console;
|
namespace App\Console;
|
||||||
|
|
||||||
use App\Jobs\CheckLogDrainContainerJob;
|
use App\Jobs\CheckForUpdatesJob;
|
||||||
use App\Jobs\CleanupInstanceStuffsJob;
|
use App\Jobs\CleanupInstanceStuffsJob;
|
||||||
use App\Jobs\ContainerStatusJob;
|
|
||||||
use App\Jobs\DatabaseBackupJob;
|
use App\Jobs\DatabaseBackupJob;
|
||||||
use App\Jobs\DockerCleanupJob;
|
use App\Jobs\DockerCleanupJob;
|
||||||
use App\Jobs\PullCoolifyImageJob;
|
use App\Jobs\PullCoolifyImageJob;
|
||||||
@@ -13,9 +12,7 @@ use App\Jobs\PullSentinelImageJob;
|
|||||||
use App\Jobs\PullTemplatesFromCDN;
|
use App\Jobs\PullTemplatesFromCDN;
|
||||||
use App\Jobs\ScheduledTaskJob;
|
use App\Jobs\ScheduledTaskJob;
|
||||||
use App\Jobs\ServerCheckJob;
|
use App\Jobs\ServerCheckJob;
|
||||||
use App\Jobs\ServerStatusJob;
|
|
||||||
use App\Jobs\UpdateCoolifyJob;
|
use App\Jobs\UpdateCoolifyJob;
|
||||||
use App\Jobs\CheckForUpdatesJob;
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\ScheduledTask;
|
use App\Models\ScheduledTask;
|
||||||
@@ -41,8 +38,6 @@ class Kernel extends ConsoleKernel
|
|||||||
// Server Jobs
|
// Server Jobs
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->checkResourcesNew($schedule);
|
$this->checkResourcesNew($schedule);
|
||||||
// $this->check_resources($schedule);
|
|
||||||
$this->check_scheduled_backups($schedule);
|
|
||||||
$this->check_scheduled_tasks($schedule);
|
$this->check_scheduled_tasks($schedule);
|
||||||
$schedule->command('uploads:clear')->everyTwoMinutes();
|
$schedule->command('uploads:clear')->everyTwoMinutes();
|
||||||
} else {
|
} else {
|
||||||
@@ -57,7 +52,6 @@ class Kernel extends ConsoleKernel
|
|||||||
// Server Jobs
|
// Server Jobs
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->checkResourcesNew($schedule);
|
$this->checkResourcesNew($schedule);
|
||||||
// $this->check_resources($schedule);
|
|
||||||
$this->pull_images($schedule);
|
$this->pull_images($schedule);
|
||||||
$this->check_scheduled_tasks($schedule);
|
$this->check_scheduled_tasks($schedule);
|
||||||
|
|
||||||
@@ -106,44 +100,6 @@ class Kernel extends ConsoleKernel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkResourcesNew($schedule)
|
|
||||||
{
|
|
||||||
if (isCloud()) {
|
|
||||||
$servers = $this->all_servers->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
|
||||||
$own = Team::find(0)->servers;
|
|
||||||
$servers = $servers->merge($own);
|
|
||||||
} else {
|
|
||||||
$servers = $this->all_servers->where('ip', '!=', '1.2.3.4');
|
|
||||||
}
|
|
||||||
foreach ($servers as $server) {
|
|
||||||
$schedule->job(new ServerCheckJob($server))->everyMinute()->onOneServer();
|
|
||||||
$schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->onOneServer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function check_resources($schedule)
|
|
||||||
{
|
|
||||||
if (isCloud()) {
|
|
||||||
$servers = $this->all_servers->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
|
||||||
$own = Team::find(0)->servers;
|
|
||||||
$servers = $servers->merge($own);
|
|
||||||
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
|
||||||
} else {
|
|
||||||
$servers = $this->all_servers->where('ip', '!=', '1.2.3.4');
|
|
||||||
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
|
||||||
}
|
|
||||||
foreach ($containerServers as $server) {
|
|
||||||
$schedule->job(new ContainerStatusJob($server))->everyMinute()->onOneServer();
|
|
||||||
if ($server->isLogDrainEnabled()) {
|
|
||||||
$schedule->job(new CheckLogDrainContainerJob($server))->everyMinute()->onOneServer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($servers as $server) {
|
|
||||||
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
|
|
||||||
$schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->onOneServer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function check_scheduled_backups($schedule)
|
private function check_scheduled_backups($schedule)
|
||||||
{
|
{
|
||||||
$scheduled_backups = ScheduledDatabaseBackup::all();
|
$scheduled_backups = ScheduledDatabaseBackup::all();
|
||||||
@@ -214,4 +170,4 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
require base_path('routes/console.php');
|
require base_path('routes/console.php');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,177 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Livewire\Settings;
|
|
||||||
|
|
||||||
use App\Models\InstanceSettings as ModelsInstanceSettings;
|
|
||||||
use App\Models\Server;
|
|
||||||
use Livewire\Component;
|
|
||||||
use Cron\CronExpression;
|
|
||||||
|
|
||||||
class Configuration extends Component
|
|
||||||
{
|
|
||||||
public ModelsInstanceSettings $settings;
|
|
||||||
|
|
||||||
public bool $do_not_track;
|
|
||||||
|
|
||||||
public bool $is_auto_update_enabled;
|
|
||||||
|
|
||||||
public bool $is_registration_enabled;
|
|
||||||
|
|
||||||
public bool $is_dns_validation_enabled;
|
|
||||||
|
|
||||||
public bool $is_api_enabled;
|
|
||||||
|
|
||||||
public string $auto_update_frequency;
|
|
||||||
|
|
||||||
public string $update_check_frequency;
|
|
||||||
|
|
||||||
protected string $dynamic_config_path = '/data/coolify/proxy/dynamic';
|
|
||||||
|
|
||||||
protected Server $server;
|
|
||||||
|
|
||||||
protected $rules = [
|
|
||||||
'settings.fqdn' => 'nullable',
|
|
||||||
'settings.resale_license' => 'nullable',
|
|
||||||
'settings.public_port_min' => 'required',
|
|
||||||
'settings.public_port_max' => 'required',
|
|
||||||
'settings.custom_dns_servers' => 'nullable',
|
|
||||||
'settings.instance_name' => 'nullable',
|
|
||||||
'settings.allowed_ips' => 'nullable',
|
|
||||||
'settings.is_auto_update_enabled' => 'boolean',
|
|
||||||
'auto_update_frequency' => 'nullable|string',
|
|
||||||
'update_check_frequency' => 'nullable|string',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected $validationAttributes = [
|
|
||||||
'settings.fqdn' => 'FQDN',
|
|
||||||
'settings.resale_license' => 'Resale License',
|
|
||||||
'settings.public_port_min' => 'Public port min',
|
|
||||||
'settings.public_port_max' => 'Public port max',
|
|
||||||
'settings.custom_dns_servers' => 'Custom DNS servers',
|
|
||||||
'settings.allowed_ips' => 'Allowed IPs',
|
|
||||||
'settings.is_auto_update_enabled' => 'Auto Update Enabled',
|
|
||||||
'auto_update_frequency' => 'Auto Update Frequency',
|
|
||||||
'update_check_frequency' => 'Update Check Frequency',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mount()
|
|
||||||
{
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function instantSave()
|
|
||||||
{
|
|
||||||
$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->save();
|
|
||||||
$this->dispatch('success', 'Settings updated!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function submit()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$error_show = false;
|
|
||||||
$this->server = Server::findOrFail(0);
|
|
||||||
$this->resetErrorBag();
|
|
||||||
if ($this->settings->public_port_min > $this->settings->public_port_max) {
|
|
||||||
$this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->validate();
|
|
||||||
|
|
||||||
if ($this->is_auto_update_enabled && !$this->validateCronExpression($this->auto_update_frequency)) {
|
|
||||||
$this->dispatch('error', 'Invalid Cron / Human expression for Auto Update Frequency.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->validateCronExpression($this->update_check_frequency)) {
|
|
||||||
$this->dispatch('error', 'Invalid Cron / Human expression for Update Check Frequency.');
|
|
||||||
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.");
|
|
||||||
$error_show = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->settings->fqdn) {
|
|
||||||
check_domain_usage(domain: $this->settings->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->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->save();
|
|
||||||
$this->server->setupDynamicProxyConfiguration();
|
|
||||||
if (! $error_show) {
|
|
||||||
$this->dispatch('success', 'Instance settings updated successfully!');
|
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return handleError($e, $this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function validateCronExpression($expression): bool
|
|
||||||
{
|
|
||||||
if (empty($expression)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$isValid = false;
|
|
||||||
try {
|
|
||||||
$cronExpression = new CronExpression($expression);
|
|
||||||
$isValid = $cronExpression->getNextRunDate() !== false;
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$isValid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset(VALID_CRON_STRINGS[$expression])) {
|
|
||||||
$isValid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $isValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updatedAutoUpdateFrequency()
|
|
||||||
{
|
|
||||||
if (!$this->validateCronExpression($this->auto_update_frequency)) {
|
|
||||||
$this->dispatch('error', 'Invalid Cron / Human expression for Auto Update Frequency.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updatedUpdateCheckFrequency()
|
|
||||||
{
|
|
||||||
if (!$this->validateCronExpression($this->update_check_frequency)) {
|
|
||||||
$this->dispatch('error', 'Invalid Cron / Human expression for Update Check Frequency.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,38 +3,185 @@
|
|||||||
namespace App\Livewire\Settings;
|
namespace App\Livewire\Settings;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\S3Storage;
|
use App\Models\Server;
|
||||||
use App\Models\StandalonePostgresql;
|
use Cron\CronExpression;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Index extends Component
|
class Index extends Component
|
||||||
{
|
{
|
||||||
public InstanceSettings $settings;
|
public InstanceSettings $settings;
|
||||||
|
|
||||||
public StandalonePostgresql $database;
|
public bool $do_not_track;
|
||||||
|
|
||||||
public $s3s;
|
public bool $is_auto_update_enabled;
|
||||||
|
|
||||||
|
public bool $is_registration_enabled;
|
||||||
|
|
||||||
|
public bool $is_dns_validation_enabled;
|
||||||
|
|
||||||
|
public bool $is_api_enabled;
|
||||||
|
|
||||||
|
public string $auto_update_frequency;
|
||||||
|
|
||||||
|
public string $update_check_frequency;
|
||||||
|
|
||||||
|
protected string $dynamic_config_path = '/data/coolify/proxy/dynamic';
|
||||||
|
|
||||||
|
protected Server $server;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'settings.fqdn' => 'nullable',
|
||||||
|
'settings.resale_license' => 'nullable',
|
||||||
|
'settings.public_port_min' => 'required',
|
||||||
|
'settings.public_port_max' => 'required',
|
||||||
|
'settings.custom_dns_servers' => 'nullable',
|
||||||
|
'settings.instance_name' => 'nullable',
|
||||||
|
'settings.allowed_ips' => 'nullable',
|
||||||
|
'settings.is_auto_update_enabled' => 'boolean',
|
||||||
|
'auto_update_frequency' => 'nullable|string',
|
||||||
|
'update_check_frequency' => 'nullable|string',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $validationAttributes = [
|
||||||
|
'settings.fqdn' => 'FQDN',
|
||||||
|
'settings.resale_license' => 'Resale License',
|
||||||
|
'settings.public_port_min' => 'Public port min',
|
||||||
|
'settings.public_port_max' => 'Public port max',
|
||||||
|
'settings.custom_dns_servers' => 'Custom DNS servers',
|
||||||
|
'settings.allowed_ips' => 'Allowed IPs',
|
||||||
|
'settings.is_auto_update_enabled' => 'Auto Update Enabled',
|
||||||
|
'auto_update_frequency' => 'Auto Update Frequency',
|
||||||
|
'update_check_frequency' => 'Update Check Frequency',
|
||||||
|
];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
if (isInstanceAdmin()) {
|
if (isInstanceAdmin()) {
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$this->settings = InstanceSettings::get();
|
||||||
$database = StandalonePostgresql::whereName('coolify-db')->first();
|
$this->do_not_track = $this->settings->do_not_track;
|
||||||
$s3s = S3Storage::whereTeamId(0)->get() ?? [];
|
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
|
||||||
if ($database) {
|
$this->is_registration_enabled = $this->settings->is_registration_enabled;
|
||||||
if ($database->status !== 'running') {
|
$this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
|
||||||
$database->status = 'running';
|
$this->is_api_enabled = $this->settings->is_api_enabled;
|
||||||
$database->save();
|
$this->auto_update_frequency = $this->settings->auto_update_frequency;
|
||||||
}
|
$this->update_check_frequency = $this->settings->update_check_frequency;
|
||||||
$this->database = $database;
|
|
||||||
}
|
|
||||||
$this->settings = $settings;
|
|
||||||
$this->s3s = $s3s;
|
|
||||||
} else {
|
} else {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
$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->save();
|
||||||
|
$this->dispatch('success', 'Settings updated!');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$error_show = false;
|
||||||
|
$this->server = Server::findOrFail(0);
|
||||||
|
$this->resetErrorBag();
|
||||||
|
if ($this->settings->public_port_min > $this->settings->public_port_max) {
|
||||||
|
$this->addError('settings.public_port_min', 'The minimum port must be lower than the maximum port.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->validate();
|
||||||
|
|
||||||
|
if ($this->is_auto_update_enabled && ! $this->validateCronExpression($this->auto_update_frequency)) {
|
||||||
|
$this->dispatch('error', 'Invalid Cron / Human expression for Auto Update Frequency.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->validateCronExpression($this->update_check_frequency)) {
|
||||||
|
$this->dispatch('error', 'Invalid Cron / Human expression for Update Check Frequency.');
|
||||||
|
|
||||||
|
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.");
|
||||||
|
$error_show = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($this->settings->fqdn) {
|
||||||
|
check_domain_usage(domain: $this->settings->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->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->save();
|
||||||
|
$this->server->setupDynamicProxyConfiguration();
|
||||||
|
if (! $error_show) {
|
||||||
|
$this->dispatch('success', 'Instance settings updated successfully!');
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validateCronExpression($expression): bool
|
||||||
|
{
|
||||||
|
if (empty($expression)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$isValid = false;
|
||||||
|
try {
|
||||||
|
$cronExpression = new CronExpression($expression);
|
||||||
|
$isValid = $cronExpression->getNextRunDate() !== false;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset(VALID_CRON_STRINGS[$expression])) {
|
||||||
|
$isValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatedAutoUpdateFrequency()
|
||||||
|
{
|
||||||
|
if (! $this->validateCronExpression($this->auto_update_frequency)) {
|
||||||
|
$this->dispatch('error', 'Invalid Cron / Human expression for Auto Update Frequency.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatedUpdateCheckFrequency()
|
||||||
|
{
|
||||||
|
if (! $this->validateCronExpression($this->update_check_frequency)) {
|
||||||
|
$this->dispatch('error', 'Invalid Cron / Human expression for Update Check Frequency.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.settings.index');
|
return view('livewire.settings.index');
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Livewire\Settings;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use App\Jobs\DatabaseBackupJob;
|
use App\Jobs\DatabaseBackupJob;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
@@ -10,7 +10,7 @@ use App\Models\Server;
|
|||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Backup extends Component
|
class SettingsBackup extends Component
|
||||||
{
|
{
|
||||||
public InstanceSettings $settings;
|
public InstanceSettings $settings;
|
||||||
|
|
||||||
@@ -41,8 +41,24 @@ class Backup extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->backup = $this->database?->scheduledBackups->first() ?? null;
|
if (isInstanceAdmin()) {
|
||||||
$this->executions = $this->backup?->executions ?? [];
|
$settings = InstanceSettings::get();
|
||||||
|
$database = StandalonePostgresql::whereName('coolify-db')->first();
|
||||||
|
$s3s = S3Storage::whereTeamId(0)->get() ?? [];
|
||||||
|
if ($database) {
|
||||||
|
if ($database->status !== 'running') {
|
||||||
|
$database->status = 'running';
|
||||||
|
$database->save();
|
||||||
|
}
|
||||||
|
$this->database = $database;
|
||||||
|
}
|
||||||
|
$this->settings = $settings;
|
||||||
|
$this->s3s = $s3s;
|
||||||
|
$this->backup = $this->database?->scheduledBackups?->first() ?? null;
|
||||||
|
$this->executions = $this->backup?->executions ?? [];
|
||||||
|
} else {
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function add_coolify_database()
|
public function add_coolify_database()
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Livewire\Settings;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Notifications\TransactionalEmails\Test;
|
use App\Notifications\TransactionalEmails\Test;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Email extends Component
|
class SettingsEmail extends Component
|
||||||
{
|
{
|
||||||
public InstanceSettings $settings;
|
public InstanceSettings $settings;
|
||||||
|
|
||||||
@@ -42,7 +42,13 @@ class Email extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->emails = auth()->user()->email;
|
if (isInstanceAdmin()) {
|
||||||
|
$this->settings = InstanceSettings::get();
|
||||||
|
$this->emails = auth()->user()->email;
|
||||||
|
} else {
|
||||||
|
return redirect()->route('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function submitFromFields()
|
public function submitFromFields()
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Livewire\Settings;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use App\Models\OauthSetting;
|
use App\Models\OauthSetting;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Auth extends Component
|
class SettingsOauth extends Component
|
||||||
{
|
{
|
||||||
public $oauth_settings_map;
|
public $oauth_settings_map;
|
||||||
|
|
||||||
@@ -13,6 +13,18 @@
|
|||||||
<button>Resale License</button>
|
<button>Resale License</button>
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
|
<a class="{{ request()->routeIs('settings.backup') ? 'dark:text-white' : '' }}"
|
||||||
|
href="{{ route('settings.backup') }}">
|
||||||
|
<button>Backup</button>
|
||||||
|
</a>
|
||||||
|
<a class="{{ request()->routeIs('settings.email') ? 'dark:text-white' : '' }}"
|
||||||
|
href="{{ route('settings.email') }}">
|
||||||
|
<button>Transactional Email</button>
|
||||||
|
</a>
|
||||||
|
<a class="{{ request()->routeIs('settings.oauth') ? 'dark:text-white' : '' }}"
|
||||||
|
href="{{ route('settings.oauth') }}">
|
||||||
|
<button>OAuth</button>
|
||||||
|
</a>
|
||||||
<div class="flex-1"></div>
|
<div class="flex-1"></div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
<div>
|
<div>
|
||||||
|
<x-slot:title>
|
||||||
|
Settings | Coolify
|
||||||
|
</x-slot>
|
||||||
|
<x-settings.navbar />
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<h2>Backup</h2>
|
<h2>Backup</h2>
|
||||||
@@ -8,7 +12,7 @@
|
|||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="pb-4">Backup your Coolify instance settings</div>
|
<div class="pb-4">Backup configuration for Coolify instance.</div>
|
||||||
<div>
|
<div>
|
||||||
@if (isset($database))
|
@if (isset($database))
|
||||||
<div class="flex flex-col gap-3 pb-4">
|
<div class="flex flex-col gap-3 pb-4">
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
<div>
|
<div>
|
||||||
|
<x-slot:title>
|
||||||
|
Settings | Coolify
|
||||||
|
</x-slot>
|
||||||
|
<x-settings.navbar />
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<h2>Transactional Email</h2>
|
<h2>Transactional Email</h2>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
<div>
|
<div>
|
||||||
|
<x-slot:title>
|
||||||
|
Settings | Coolify
|
||||||
|
</x-slot>
|
||||||
|
<x-settings.navbar />
|
||||||
<form wire:submit='submit' class="flex flex-col">
|
<form wire:submit='submit' class="flex flex-col">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
@@ -7,20 +11,26 @@
|
|||||||
Save
|
Save
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="pb-4 ">Custom authentication (OAuth) configurations.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2 pt-4">
|
<div class="flex flex-col gap-2 pt-4">
|
||||||
@foreach ($oauth_settings_map as $oauth_setting)
|
@foreach ($oauth_settings_map as $oauth_setting)
|
||||||
<div class="p-4 border dark:border-coolgray-300">
|
<div class="p-4 border dark:border-coolgray-300">
|
||||||
<h3>{{ucfirst($oauth_setting->provider)}} Oauth</h3>
|
<h3>{{ ucfirst($oauth_setting->provider) }} Oauth</h3>
|
||||||
<div class="w-32">
|
<div class="w-32">
|
||||||
<x-forms.checkbox instantSave id="oauth_settings_map.{{$oauth_setting->provider}}.enabled" label="Enabled" />
|
<x-forms.checkbox instantSave id="oauth_settings_map.{{ $oauth_setting->provider }}.enabled"
|
||||||
|
label="Enabled" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
<div class="flex flex-col w-full gap-2 xl:flex-row">
|
||||||
<x-forms.input id="oauth_settings_map.{{$oauth_setting->provider}}.client_id" label="Client ID" />
|
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.client_id"
|
||||||
<x-forms.input id="oauth_settings_map.{{$oauth_setting->provider}}.client_secret" type="password" label="Client Secret" />
|
label="Client ID" />
|
||||||
<x-forms.input id="oauth_settings_map.{{$oauth_setting->provider}}.redirect_uri" label="Redirect URI" />
|
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.client_secret"
|
||||||
|
type="password" label="Client Secret" />
|
||||||
|
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.redirect_uri"
|
||||||
|
label="Redirect URI" />
|
||||||
@if ($oauth_setting->provider == 'azure')
|
@if ($oauth_setting->provider == 'azure')
|
||||||
<x-forms.input id="oauth_settings_map.{{$oauth_setting->provider}}.tenant" label="Tenant" />
|
<x-forms.input id="oauth_settings_map.{{ $oauth_setting->provider }}.tenant"
|
||||||
|
label="Tenant" />
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
<div>
|
|
||||||
<form wire:submit='submit' class="flex flex-col">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<h2>Configuration</h2>
|
|
||||||
<x-forms.button type="submit">
|
|
||||||
Save
|
|
||||||
</x-forms.button>
|
|
||||||
</div>
|
|
||||||
<div>General configuration for your Coolify instance.</div>
|
|
||||||
|
|
||||||
<div class="flex flex-col gap-2 pt-4">
|
|
||||||
<div class="flex flex-wrap items-end gap-2">
|
|
||||||
<h3 class="pt-6">Instance Settings</h3>
|
|
||||||
<x-forms.input id="settings.fqdn" label="Instance's Domain" placeholder="https://coolify.io" />
|
|
||||||
<x-forms.input id="settings.instance_name" label="Instance's Name" placeholder="Coolify" />
|
|
||||||
<h3 class="pt-6 w-full">DNS Validation</h3>
|
|
||||||
<div class="flex flex-wrap items-end gap-2 md:w-96">
|
|
||||||
<x-forms.checkbox instantSave id="is_dns_validation_enabled" label="Enable DNS validation" />
|
|
||||||
</div>
|
|
||||||
<x-forms.input id="settings.custom_dns_servers" label="DNS Servers"
|
|
||||||
helper="DNS servers for validation FQDNs againts. 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="settings.public_port_min" label="Public Port Min" />
|
|
||||||
<x-forms.input type="number" id="settings.public_port_max" label="Public Port Max" />
|
|
||||||
</div> --}}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<h3 class="pt-6">API</h3>
|
|
||||||
<div class="md:w-96">
|
|
||||||
<x-forms.checkbox instantSave id="is_api_enabled" label="Enabled" />
|
|
||||||
</div>
|
|
||||||
<x-forms.input id="settings.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" />
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<h2 class="pt-6">Advanced</h2>
|
|
||||||
<div class="text-right md:w-96">
|
|
||||||
@if (!is_null(env('AUTOUPDATE', null)))
|
|
||||||
<x-forms.checkbox instantSave helper="AUTOUPDATE is set in .env file, you need to modify it there." disabled
|
|
||||||
id="is_auto_update_enabled" label="Auto Update Coolify" />
|
|
||||||
@else
|
|
||||||
<x-forms.checkbox instantSave id="is_auto_update_enabled" label="Auto Update Coolify" />
|
|
||||||
@if($is_auto_update_enabled)
|
|
||||||
<x-forms.input id="auto_update_frequency" label="Auto Update Frequency" placeholder="0 0 * * *" helper="Cron expression for auto update frequency (automatically update coolify). Default is every day at 00:00" />
|
|
||||||
@endif
|
|
||||||
<x-forms.input id="update_check_frequency" label="Update Check Frequency" placeholder="0 */11 * * *" helper="Cron expression for update check frequency (check for new Coolify versions and pull new Service Templates from CDN). Default is every 12 hours at 11:00 and 23:00" />
|
|
||||||
@endif
|
|
||||||
<x-forms.checkbox instantSave id="is_registration_enabled" label="Registration Allowed" />
|
|
||||||
<x-forms.checkbox instantSave id="do_not_track" label="Do Not Track" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -3,32 +3,70 @@
|
|||||||
Settings | Coolify
|
Settings | Coolify
|
||||||
</x-slot>
|
</x-slot>
|
||||||
<x-settings.navbar />
|
<x-settings.navbar />
|
||||||
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex flex-col h-full gap-8 pt-1 sm:flex-row">
|
<form wire:submit='submit' class="flex flex-col">
|
||||||
<div class="flex gap-6 overflow-x-scroll sm:gap-2 sm:overflow-x-hidden scrollbar sm:flex-col whitespace-nowrap">
|
<div class="flex items-center gap-2">
|
||||||
<a class="menu-item" :class="activeTab === 'general' && 'menu-item-active'"
|
<h2>Configuration</h2>
|
||||||
@click.prevent="activeTab = 'general'; window.location.hash = 'general'" href="#">General</a>
|
<x-forms.button type="submit">
|
||||||
<a class="menu-item" :class="activeTab === 'backup' && 'menu-item-active'"
|
Save
|
||||||
@click.prevent="activeTab = 'backup'; window.location.hash = 'backup'" href="#">Instance Backup</a>
|
</x-forms.button>
|
||||||
<a class="menu-item" :class="activeTab === 'smtp' && 'menu-item-active'"
|
|
||||||
@click.prevent="activeTab = 'smtp'; window.location.hash = 'smtp'" href="#">Transactional
|
|
||||||
Email</a>
|
|
||||||
<a class="menu-item" :class="activeTab === 'auth' && 'menu-item-active'"
|
|
||||||
@click.prevent="activeTab = 'auth'; window.location.hash = 'auth'" href="#">Authentication
|
|
||||||
(OAuth)</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full">
|
<div>General configuration for your Coolify instance.</div>
|
||||||
<div x-cloak x-show="activeTab === 'general'" class="h-full">
|
|
||||||
<livewire:settings.configuration :settings="$settings" />
|
<div class="flex flex-col gap-2">
|
||||||
</div>
|
<div class="flex flex-wrap items-end gap-2">
|
||||||
<div x-cloak x-show="activeTab === 'backup'" class="h-full">
|
<h4 class="pt-6">Instance Settings</h4>
|
||||||
<livewire:settings.backup :settings="$settings" :database="$database" :s3s="$s3s" />
|
<x-forms.input id="settings.fqdn" label="Instance's Domain" placeholder="https://coolify.io" />
|
||||||
</div>
|
<x-forms.input id="settings.instance_name" label="Instance's Name" placeholder="Coolify" />
|
||||||
<div x-cloak x-show="activeTab === 'smtp'" class="h-full">
|
<h4 class="w-full pt-6">DNS Validation</h4>
|
||||||
<livewire:settings.email :settings="$settings" />
|
<div class="flex flex-wrap items-end gap-2 md:w-96">
|
||||||
</div>
|
<x-forms.checkbox instantSave id="is_dns_validation_enabled" label="Enable DNS validation" />
|
||||||
<div x-cloak x-show="activeTab === 'auth'" class="h-full">
|
</div>
|
||||||
<livewire:settings.auth />
|
<x-forms.input id="settings.custom_dns_servers" label="DNS Servers"
|
||||||
|
helper="DNS servers for validation FQDNs againts. A comma separated list of DNS servers."
|
||||||
|
placeholder="1.1.1.1,8.8.8.8" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- <div class="flex gap-2 ">
|
||||||
|
<x-forms.input type="number" id="settings.public_port_min" label="Public Port Min" />
|
||||||
|
<x-forms.input type="number" id="settings.public_port_max" label="Public Port Max" />
|
||||||
|
</div> --}}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<h4 class="pt-6">API</h4>
|
||||||
|
<div class="md:w-96">
|
||||||
|
<x-forms.checkbox instantSave id="is_api_enabled" label="Enabled" />
|
||||||
|
</div>
|
||||||
|
<x-forms.input id="settings.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" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h4 class="pt-6">Advanced</h4>
|
||||||
|
|
||||||
|
|
||||||
|
<h5 class="pt-4 font-bold text-white">Auto Update</h5>
|
||||||
|
@if (!is_null(env('AUTOUPDATE', null)))
|
||||||
|
<div class="text-right md:w-96">
|
||||||
|
<x-forms.checkbox instantSave helper="AUTOUPDATE is set in .env file, you need to modify it there." disabled
|
||||||
|
id="is_auto_update_enabled" label="Enabled" />
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<div class="text-right md:w-96">
|
||||||
|
<x-forms.checkbox instantSave id="is_auto_update_enabled" label="Enabled" />
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 ">
|
||||||
|
@if ($is_auto_update_enabled)
|
||||||
|
<x-forms.input id="auto_update_frequency" label="Auto Update Frequency" placeholder="0 0 * * *"
|
||||||
|
helper="Cron expression for auto update frequency (automatically update coolify). Default is every day at 00:00" />
|
||||||
|
@endif
|
||||||
|
<x-forms.input id="update_check_frequency" label="Update Check Frequency" placeholder="0 */11 * * *"
|
||||||
|
helper="Cron expression for update check frequency (check for new Coolify versions and pull new Service Templates from CDN). Default is every 12 hours at 11:00 and 23:00" />
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<h5 class="pt-4 font-bold text-white">Others</h5>
|
||||||
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ use App\Livewire\Server\Resources as ResourcesShow;
|
|||||||
use App\Livewire\Server\Show as ServerShow;
|
use App\Livewire\Server\Show as ServerShow;
|
||||||
use App\Livewire\Settings\Index as SettingsIndex;
|
use App\Livewire\Settings\Index as SettingsIndex;
|
||||||
use App\Livewire\Settings\License as SettingsLicense;
|
use App\Livewire\Settings\License as SettingsLicense;
|
||||||
|
use App\Livewire\SettingsBackup;
|
||||||
|
use App\Livewire\SettingsEmail;
|
||||||
|
use App\Livewire\SettingsOauth;
|
||||||
use App\Livewire\SharedVariables\Environment\Index as EnvironmentSharedVariablesIndex;
|
use App\Livewire\SharedVariables\Environment\Index as EnvironmentSharedVariablesIndex;
|
||||||
use App\Livewire\SharedVariables\Environment\Show as EnvironmentSharedVariablesShow;
|
use App\Livewire\SharedVariables\Environment\Show as EnvironmentSharedVariablesShow;
|
||||||
use App\Livewire\SharedVariables\Index as SharedVariablesIndex;
|
use App\Livewire\SharedVariables\Index as SharedVariablesIndex;
|
||||||
@@ -113,6 +116,9 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
|||||||
Route::get('/subscription/new', SubscriptionIndex::class)->name('subscription.index');
|
Route::get('/subscription/new', SubscriptionIndex::class)->name('subscription.index');
|
||||||
|
|
||||||
Route::get('/settings', SettingsIndex::class)->name('settings.index');
|
Route::get('/settings', SettingsIndex::class)->name('settings.index');
|
||||||
|
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');
|
||||||
Route::get('/settings/license', SettingsLicense::class)->name('settings.license');
|
Route::get('/settings/license', SettingsLicense::class)->name('settings.license');
|
||||||
|
|
||||||
Route::get('/profile', ProfileIndex::class)->name('profile');
|
Route::get('/profile', ProfileIndex::class)->name('profile');
|
||||||
|
|||||||
Reference in New Issue
Block a user