Merge branch 'next' into feat/disable-default-redirect

This commit is contained in:
Kael
2024-10-25 01:28:08 +11:00
committed by GitHub
100 changed files with 1494 additions and 1249 deletions

View File

@@ -2,6 +2,7 @@
namespace App\Livewire;
use App\Models\InstanceSettings;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
@@ -18,10 +19,12 @@ class NavbarDeleteTeam extends Component
public function delete($password)
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
if (! InstanceSettings::get('disable_two_step_confirmation')) {
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
return;
}
}
$currentTeam = currentTeam();

View File

@@ -18,6 +18,7 @@ class Discord extends Component
'team.discord_notifications_status_changes' => 'nullable|boolean',
'team.discord_notifications_database_backups' => 'nullable|boolean',
'team.discord_notifications_scheduled_tasks' => 'nullable|boolean',
'team.discord_notifications_server_disk_usage' => 'nullable|boolean',
];
protected $validationAttributes = [

View File

@@ -4,6 +4,7 @@ namespace App\Livewire\Notifications;
use App\Models\Team;
use App\Notifications\Test;
use Illuminate\Support\Facades\RateLimiter;
use Livewire\Component;
class Email extends Component
@@ -30,6 +31,7 @@ class Email extends Component
'team.smtp_notifications_status_changes' => 'nullable|boolean',
'team.smtp_notifications_database_backups' => 'nullable|boolean',
'team.smtp_notifications_scheduled_tasks' => 'nullable|boolean',
'team.smtp_notifications_server_disk_usage' => 'nullable|boolean',
'team.use_instance_email_settings' => 'boolean',
'team.resend_enabled' => 'nullable|boolean',
'team.resend_api_key' => 'nullable',
@@ -74,8 +76,23 @@ class Email extends Component
public function sendTestNotification()
{
$this->team?->notify(new Test($this->emails));
$this->dispatch('success', 'Test Email sent.');
try {
$executed = RateLimiter::attempt(
'test-email:'.$this->team->id,
$perMinute = 0,
function () {
$this->team?->notify(new Test($this->emails));
$this->dispatch('success', 'Test Email sent.');
},
$decaySeconds = 10,
);
if (! $executed) {
throw new \Exception('Too many messages sent!');
}
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
public function instantSaveInstance()

View File

@@ -24,6 +24,7 @@ class Telegram extends Component
'team.telegram_notifications_status_changes_message_thread_id' => 'nullable|string',
'team.telegram_notifications_database_backups_message_thread_id' => 'nullable|string',
'team.telegram_notifications_scheduled_tasks_thread_id' => 'nullable|string',
'team.telegram_notifications_server_disk_usage' => 'nullable|boolean',
];
protected $validationAttributes = [

View File

@@ -274,10 +274,10 @@ class General extends Component
}
}
public function resetDefaultLabels()
public function resetDefaultLabels($manualReset = false)
{
try {
if ($this->application->settings->is_container_label_readonly_enabled) {
if ($this->application->settings->is_container_label_readonly_enabled && ! $manualReset) {
return;
}
$this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n");

View File

@@ -2,6 +2,7 @@
namespace App\Livewire\Project\Database;
use App\Models\InstanceSettings;
use App\Models\ScheduledDatabaseBackup;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
@@ -58,10 +59,12 @@ class BackupEdit extends Component
public function delete($password)
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
if (! InstanceSettings::get('disable_two_step_confirmation')) {
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
return;
}
}
try {

View File

@@ -2,10 +2,10 @@
namespace App\Livewire\Project\Database;
use App\Models\InstanceSettings;
use App\Models\ScheduledDatabaseBackup;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Livewire\Attributes\On;
use Livewire\Component;
class BackupExecutions extends Component
@@ -28,7 +28,6 @@ class BackupExecutions extends Component
return [
"echo-private:team.{$userId},BackupCreated" => 'refreshBackupExecutions',
'deleteBackup',
];
}
@@ -41,13 +40,14 @@ class BackupExecutions extends Component
}
}
#[On('deleteBackup')]
public function deleteBackup($executionId, $password)
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
if (! InstanceSettings::get('disable_two_step_confirmation')) {
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
return;
}
}
$execution = $this->backup->executions()->where('id', $executionId)->first();

View File

@@ -3,6 +3,7 @@
namespace App\Livewire\Project\Service;
use App\Models\Application;
use App\Models\InstanceSettings;
use App\Models\LocalFileVolume;
use App\Models\ServiceApplication;
use App\Models\ServiceDatabase;
@@ -87,10 +88,12 @@ class FileStorage extends Component
public function delete($password)
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
if (! InstanceSettings::get('disable_two_step_confirmation')) {
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
return;
}
}
try {

View File

@@ -2,6 +2,7 @@
namespace App\Livewire\Project\Service;
use App\Models\InstanceSettings;
use App\Models\ServiceApplication;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
@@ -49,10 +50,12 @@ class ServiceApplicationView extends Component
public function delete($password)
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
if (! InstanceSettings::get('disable_two_step_confirmation')) {
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
return;
}
}
try {

View File

@@ -3,6 +3,7 @@
namespace App\Livewire\Project\Shared;
use App\Jobs\DeleteResourceJob;
use App\Models\InstanceSettings;
use App\Models\Service;
use App\Models\ServiceApplication;
use App\Models\ServiceDatabase;
@@ -91,7 +92,7 @@ class Danger extends Component
public function delete($password)
{
if (isProduction()) {
if (! InstanceSettings::get('disable_two_step_confirmation')) {
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');

View File

@@ -5,7 +5,7 @@ namespace App\Livewire\Project\Shared;
use App\Actions\Application\StopApplicationOneServer;
use App\Actions\Docker\GetContainersStatus;
use App\Events\ApplicationStatusChanged;
use App\Jobs\ContainerStatusJob;
use App\Models\InstanceSettings;
use App\Models\Server;
use App\Models\StandaloneDocker;
use Illuminate\Support\Facades\Auth;
@@ -119,10 +119,12 @@ class Destination extends Component
public function removeServer(int $network_id, int $server_id, $password)
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
if (! InstanceSettings::get('disable_two_step_confirmation')) {
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
return;
}
}
if ($this->resource->destination->server->id == $server_id && $this->resource->destination->id == $network_id) {

View File

@@ -112,14 +112,20 @@ class Show extends Component
$this->validate();
}
if ($this->env->is_required && str($this->env->real_value)->isEmpty()) {
if (! $this->isSharedVariable && $this->env->is_required && str($this->env->real_value)->isEmpty()) {
$oldValue = $this->env->getOriginal('value');
$this->env->value = $oldValue;
$this->dispatch('error', 'Required environment variable cannot be empty.');
return;
}
$this->serialize();
if ($this->isSharedVariable) {
unset($this->env->is_required);
}
$this->env->save();
$this->dispatch('success', 'Environment variable updated.');
$this->dispatch('envsUpdated');

View File

@@ -52,6 +52,7 @@ class ExecuteContainerCommand extends Component
$this->servers = $this->servers->push($server);
}
}
$this->loadContainers();
} elseif (data_get($this->parameters, 'database_uuid')) {
$this->type = 'database';
$resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id'));
@@ -62,12 +63,18 @@ class ExecuteContainerCommand extends Component
if ($this->resource->destination->server->isFunctional()) {
$this->servers = $this->servers->push($this->resource->destination->server);
}
$this->loadContainers();
} elseif (data_get($this->parameters, 'service_uuid')) {
$this->type = 'service';
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
if ($this->resource->server->isFunctional()) {
$this->servers = $this->servers->push($this->resource->server);
}
$this->loadContainers();
} elseif (data_get($this->parameters, 'server_uuid')) {
$this->type = 'server';
$this->resource = Server::where('uuid', $this->parameters['server_uuid'])->firstOrFail();
$this->server = $this->resource;
}
}
@@ -130,6 +137,28 @@ class ExecuteContainerCommand extends Component
if ($this->containers->count() > 0) {
$this->container = $this->containers->first();
}
if ($this->containers->count() === 1) {
$this->selected_container = data_get($this->containers->first(), 'container.Names');
}
}
#[On('connectToServer')]
public function connectToServer()
{
try {
if ($this->server->isForceDisabled()) {
throw new \RuntimeException('Server is disabled.');
}
$this->dispatch(
'send-terminal-command',
false,
data_get($this->server, 'name'),
data_get($this->server, 'uuid')
);
} catch (\Throwable $e) {
return handleError($e, $this);
}
}
#[On('connectToContainer')]

View File

@@ -2,6 +2,7 @@
namespace App\Livewire\Project\Shared\Storages;
use App\Models\InstanceSettings;
use App\Models\LocalPersistentVolume;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
@@ -40,10 +41,12 @@ class Show extends Component
public function delete($password)
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
if (! InstanceSettings::get('disable_two_step_confirmation')) {
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
return;
}
}
$this->storage->delete();

View File

@@ -16,6 +16,7 @@ class Advanced extends Component
'server.settings.force_docker_cleanup' => 'required|boolean',
'server.settings.docker_cleanup_frequency' => 'required_if:server.settings.force_docker_cleanup,true|string',
'server.settings.docker_cleanup_threshold' => 'required_if:server.settings.force_docker_cleanup,false|integer|min:1|max:100',
'server.settings.server_disk_usage_notification_threshold' => 'required|integer|min:50|max:100',
'server.settings.delete_unused_volumes' => 'boolean',
'server.settings.delete_unused_networks' => 'boolean',
];
@@ -27,6 +28,7 @@ class Advanced extends Component
'server.settings.force_docker_cleanup' => 'Force Docker Cleanup',
'server.settings.docker_cleanup_frequency' => 'Docker Cleanup Frequency',
'server.settings.docker_cleanup_threshold' => 'Docker Cleanup Threshold',
'server.settings.server_disk_usage_notification_threshold' => 'Server Disk Usage Notification Threshold',
'server.settings.delete_unused_volumes' => 'Delete Unused Volumes',
'server.settings.delete_unused_networks' => 'Delete Unused Networks',
];

View File

@@ -34,12 +34,6 @@ class Charts extends Component
try {
$cpuMetrics = $this->server->getCpuMetrics($this->interval);
$memoryMetrics = $this->server->getMemoryMetrics($this->interval);
// $cpuMetrics = collect($cpuMetrics)->map(function ($metric) {
// return [$metric[0], $metric[1]];
// });
// $memoryMetrics = collect($memoryMetrics)->map(function ($metric) {
// return [$metric[0], $metric[1]];
// });
$this->dispatch("refreshChartData-{$this->chartId}-cpu", [
'seriesData' => $cpuMetrics,
]);

View File

@@ -3,6 +3,7 @@
namespace App\Livewire\Server;
use App\Actions\Server\DeleteServer;
use App\Models\InstanceSettings;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
@@ -16,10 +17,12 @@ class Delete extends Component
public function delete($password)
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
if (! InstanceSettings::get('disable_two_step_confirmation')) {
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
return;
}
}
try {
$this->authorize('delete', $this->server);

View File

@@ -97,7 +97,7 @@ class Form extends Component
try {
$this->server->settings->generateSentinelToken();
$this->server->settings->refresh();
$this->restartSentinel(notification: false);
// $this->restartSentinel(notification: false);
$this->dispatch('success', 'Token regenerated & Sentinel restarted.');
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -136,7 +136,6 @@ class Form extends Component
public function updatedServerSettingsIsSentinelEnabled($value)
{
$this->validate();
$this->validate([
'server.settings.sentinel_custom_url' => 'required|url',
]);
@@ -162,6 +161,7 @@ class Form extends Component
public function instantSave()
{
try {
$this->validate();
refresh_server_connection($this->server->privateKey);
$this->validateServer(false);
@@ -170,13 +170,11 @@ class Form extends Component
$this->server->save();
$this->dispatch('success', 'Server updated.');
$this->dispatch('refreshServerShow');
$this->server->settings->save();
} catch (\Throwable $e) {
$this->server->settings->refresh();
return handleError($e, $this);
}
} finally {}
}
public function restartSentinel($notification = true)
@@ -186,10 +184,9 @@ class Form extends Component
$this->validate([
'server.settings.sentinel_custom_url' => 'required|url',
]);
$version = get_latest_sentinel_version();
StartSentinel::run($this->server, $version, true);
$this->server->restartSentinel();
if ($notification) {
$this->dispatch('success', 'Sentinel started.');
$this->dispatch('success', 'Sentinel restarted.');
}
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -247,11 +244,6 @@ class Form extends Component
}
refresh_server_connection($this->server->privateKey);
$this->server->settings->wildcard_domain = $this->wildcard_domain;
// if ($this->server->settings->force_docker_cleanup) {
// $this->server->settings->docker_cleanup_frequency = $this->server->settings->docker_cleanup_frequency;
// } else {
// $this->server->settings->docker_cleanup_threshold = $this->server->settings->docker_cleanup_threshold;
// }
$currentTimezone = $this->server->settings->getOriginal('server_timezone');
$newTimezone = $this->server->settings->server_timezone;
if ($currentTimezone !== $newTimezone || $currentTimezone === '') {

View File

@@ -2,6 +2,7 @@
namespace App\Livewire\Server;
use App\Models\PrivateKey;
use App\Models\Server;
use Livewire\Component;
@@ -20,6 +21,13 @@ class ShowPrivateKey extends Component
public function setPrivateKey($privateKeyId)
{
$ownedPrivateKey = PrivateKey::ownedByCurrentTeam()->find($privateKeyId);
if (is_null($ownedPrivateKey)) {
$this->dispatch('error', 'You are not allowed to use this private key.');
return;
}
$originalPrivateKeyId = $this->server->getOriginal('private_key_id');
try {
$this->server->update(['private_key_id' => $privateKeyId]);

View File

@@ -3,7 +3,6 @@
namespace App\Livewire;
use App\Models\InstanceSettings;
use App\Notifications\TransactionalEmails\Test;
use Livewire\Component;
class SettingsEmail extends Component
@@ -124,10 +123,4 @@ class SettingsEmail extends Component
return handleError($e, $this);
}
}
public function sendTestNotification()
{
$this->settings?->notify(new Test($this->emails));
$this->dispatch('success', 'Test email sent.');
}
}

View File

@@ -93,52 +93,55 @@ class Change extends Component
// }
public function mount()
{
$github_app_uuid = request()->github_app_uuid;
$this->github_app = GithubApp::where('uuid', $github_app_uuid)->first();
if (! $this->github_app) {
return redirect()->route('source.all');
}
$this->applications = $this->github_app->applications;
$settings = instanceSettings();
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
try {
$github_app_uuid = request()->github_app_uuid;
$this->github_app = GithubApp::ownedByCurrentTeam()->whereUuid($github_app_uuid)->firstOrFail();
$this->name = str($this->github_app->name)->kebab();
$this->fqdn = $settings->fqdn;
$this->applications = $this->github_app->applications;
$settings = instanceSettings();
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
if ($settings->public_ipv4) {
$this->ipv4 = 'http://'.$settings->public_ipv4.':'.config('app.port');
}
if ($settings->public_ipv6) {
$this->ipv6 = 'http://'.$settings->public_ipv6.':'.config('app.port');
}
if ($this->github_app->installation_id && session('from')) {
$source_id = data_get(session('from'), 'source_id');
if (! $source_id || $this->github_app->id !== $source_id) {
session()->forget('from');
} else {
$parameters = data_get(session('from'), 'parameters');
$back = data_get(session('from'), 'back');
$environment_name = data_get($parameters, 'environment_name');
$project_uuid = data_get($parameters, 'project_uuid');
$type = data_get($parameters, 'type');
$destination = data_get($parameters, 'destination');
session()->forget('from');
$this->name = str($this->github_app->name)->kebab();
$this->fqdn = $settings->fqdn;
return redirect()->route($back, [
'environment_name' => $environment_name,
'project_uuid' => $project_uuid,
'type' => $type,
'destination' => $destination,
]);
if ($settings->public_ipv4) {
$this->ipv4 = 'http://'.$settings->public_ipv4.':'.config('app.port');
}
if ($settings->public_ipv6) {
$this->ipv6 = 'http://'.$settings->public_ipv6.':'.config('app.port');
}
if ($this->github_app->installation_id && session('from')) {
$source_id = data_get(session('from'), 'source_id');
if (! $source_id || $this->github_app->id !== $source_id) {
session()->forget('from');
} else {
$parameters = data_get(session('from'), 'parameters');
$back = data_get(session('from'), 'back');
$environment_name = data_get($parameters, 'environment_name');
$project_uuid = data_get($parameters, 'project_uuid');
$type = data_get($parameters, 'type');
$destination = data_get($parameters, 'destination');
session()->forget('from');
return redirect()->route($back, [
'environment_name' => $environment_name,
'project_uuid' => $project_uuid,
'type' => $type,
'destination' => $destination,
]);
}
}
$this->parameters = get_route_parameters();
if (isCloud() && ! isDev()) {
$this->webhook_endpoint = config('app.url');
} else {
$this->webhook_endpoint = $this->ipv4;
$this->is_system_wide = $this->github_app->is_system_wide;
}
} catch (\Throwable $e) {
return handleError($e, $this);
}
$this->parameters = get_route_parameters();
if (isCloud() && ! isDev()) {
$this->webhook_endpoint = config('app.url');
} else {
$this->webhook_endpoint = $this->ipv4;
$this->is_system_wide = $this->github_app->is_system_wide;
}
}
public function submit()

View File

@@ -2,6 +2,7 @@
namespace App\Livewire\Team;
use App\Models\InstanceSettings;
use App\Models\Team;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
@@ -77,10 +78,12 @@ class AdminView extends Component
public function delete($id, $password)
{
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
if (! InstanceSettings::get('disable_two_step_confirmation')) {
if (! Hash::check($password, Auth::user()->password)) {
$this->addError('password', 'The provided password is incorrect.');
return;
return;
}
}
if (! auth()->user()->isInstanceAdmin()) {
return $this->dispatch('error', 'You are not authorized to delete users');

View File

@@ -13,17 +13,18 @@ class Invitations extends Component
public function deleteInvitation(int $invitation_id)
{
$initiation_found = TeamInvitation::find($invitation_id);
if (! $initiation_found) {
try {
$initiation_found = TeamInvitation::ownedByCurrentTeam()->findOrFail($invitation_id);
$initiation_found->delete();
$this->refreshInvitations();
$this->dispatch('success', 'Invitation revoked.');
} catch (\Exception $e) {
return $this->dispatch('error', 'Invitation not found.');
}
$initiation_found->delete();
$this->refreshInvitations();
$this->dispatch('success', 'Invitation revoked.');
}
public function refreshInvitations()
{
$this->invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get();
$this->invitations = TeamInvitation::ownedByCurrentTeam()->get();
}
}