Merge branch 'next' into feat/deployment-token
This commit is contained in:
@@ -2,8 +2,10 @@
|
||||
|
||||
namespace App\Livewire\Admin;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -23,7 +25,7 @@ class Index extends Component
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
|
||||
if (auth()->user()->id !== 0) {
|
||||
if (Auth::id() !== 0) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$this->getSubscribers();
|
||||
@@ -41,23 +43,19 @@ class Index extends Component
|
||||
|
||||
public function getSubscribers()
|
||||
{
|
||||
$this->inactiveSubscribers = User::whereDoesntHave('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->count();
|
||||
$this->activeSubscribers = User::whereHas('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->count();
|
||||
$this->inactiveSubscribers = Team::whereRelation('subscription', 'stripe_invoice_paid', false)->count();
|
||||
$this->activeSubscribers = Team::whereRelation('subscription', 'stripe_invoice_paid', true)->count();
|
||||
}
|
||||
|
||||
public function switchUser(int $user_id)
|
||||
{
|
||||
if (auth()->user()->id !== 0) {
|
||||
if (Auth::id() !== 0) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$user = User::find($user_id);
|
||||
$team_to_switch_to = $user->teams->first();
|
||||
Cache::forget("team:{$user->id}");
|
||||
auth()->login($user);
|
||||
Auth::login($user);
|
||||
refreshSession($team_to_switch_to);
|
||||
|
||||
return redirect(request()->header('Referer'));
|
||||
|
||||
@@ -66,11 +66,15 @@ class Index extends Component
|
||||
|
||||
public bool $serverReachable = true;
|
||||
|
||||
public ?string $minDockerVersion = null;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (auth()->user()?->isMember() && auth()->user()->currentTeam()->show_boarding === true) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
|
||||
$this->minDockerVersion = str(config('constants.docker.minimum_required_version'))->before('.');
|
||||
$this->privateKeyName = generate_random_name();
|
||||
$this->remoteServerName = generate_random_name();
|
||||
if (isDev()) {
|
||||
@@ -168,13 +172,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
|
||||
public function getProxyType()
|
||||
{
|
||||
// Set Default Proxy Type
|
||||
$this->selectProxy(ProxyTypes::TRAEFIK->value);
|
||||
// $proxyTypeSet = $this->createdServer->proxy->type;
|
||||
// if (!$proxyTypeSet) {
|
||||
// $this->currentState = 'select-proxy';
|
||||
// return;
|
||||
// }
|
||||
$this->getProjects();
|
||||
}
|
||||
|
||||
@@ -185,7 +183,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
|
||||
return;
|
||||
}
|
||||
$this->createdPrivateKey = PrivateKey::find($this->selectedExistingPrivateKey);
|
||||
$this->createdPrivateKey = PrivateKey::where('team_id', currentTeam()->id)->where('id', $this->selectedExistingPrivateKey)->first();
|
||||
$this->privateKey = $this->createdPrivateKey->private_key;
|
||||
$this->currentState = 'create-server';
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
@@ -18,16 +18,16 @@ class Docker extends Component
|
||||
#[Locked]
|
||||
public Server $selectedServer;
|
||||
|
||||
#[Rule(['required', 'string'])]
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $name;
|
||||
|
||||
#[Rule(['required', 'string'])]
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $network;
|
||||
|
||||
#[Rule(['required', 'string'])]
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $serverId;
|
||||
|
||||
#[Rule(['required', 'boolean'])]
|
||||
#[Validate(['required', 'boolean'])]
|
||||
public bool $isSwarm = false;
|
||||
|
||||
public function mount(?string $server_id = null)
|
||||
@@ -35,9 +35,11 @@ class Docker extends Component
|
||||
$this->network = new Cuid2;
|
||||
$this->servers = Server::isUsable()->get();
|
||||
if ($server_id) {
|
||||
$this->selectedServer = $this->servers->find($server_id);
|
||||
$this->selectedServer = $this->servers->find($server_id) ?: $this->servers->first();
|
||||
$this->serverId = $this->selectedServer->id;
|
||||
} else {
|
||||
$this->selectedServer = $this->servers->first();
|
||||
$this->serverId = $this->selectedServer->id;
|
||||
}
|
||||
$this->generateName();
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
@@ -14,13 +14,13 @@ class Show extends Component
|
||||
#[Locked]
|
||||
public $destination;
|
||||
|
||||
#[Rule(['string', 'required'])]
|
||||
#[Validate(['string', 'required'])]
|
||||
public string $name;
|
||||
|
||||
#[Rule(['string', 'required'])]
|
||||
#[Validate(['string', 'required'])]
|
||||
public string $network;
|
||||
|
||||
#[Rule(['string', 'required'])]
|
||||
#[Validate(['string', 'required'])]
|
||||
public string $serverIp;
|
||||
|
||||
public function mount(string $destination_uuid)
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Dev;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class Compose extends Component
|
||||
{
|
||||
public string $compose = '';
|
||||
|
||||
public string $base64 = '';
|
||||
|
||||
public $services;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->services = get_service_templates();
|
||||
}
|
||||
|
||||
public function setService(string $selected)
|
||||
{
|
||||
$this->base64 = data_get($this->services, $selected.'.compose');
|
||||
if ($this->base64) {
|
||||
$this->compose = base64_decode($this->base64);
|
||||
}
|
||||
}
|
||||
|
||||
public function updatedCompose($value)
|
||||
{
|
||||
$this->base64 = base64_encode($value);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.dev.compose');
|
||||
}
|
||||
}
|
||||
@@ -5,17 +5,17 @@ namespace App\Livewire;
|
||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Help extends Component
|
||||
{
|
||||
use WithRateLimiting;
|
||||
|
||||
#[Rule(['required', 'min:10', 'max:1000'])]
|
||||
#[Validate(['required', 'min:10', 'max:1000'])]
|
||||
public string $description;
|
||||
|
||||
#[Rule(['required', 'min:3'])]
|
||||
#[Validate(['required', 'min:3'])]
|
||||
public string $subject;
|
||||
|
||||
public function submit()
|
||||
|
||||
@@ -31,7 +31,7 @@ class NavbarDeleteTeam extends Component
|
||||
$currentTeam->delete();
|
||||
|
||||
$currentTeam->members->each(function ($user) use ($currentTeam) {
|
||||
if ($user->id === auth()->user()->id) {
|
||||
if ($user->id === Auth::id()) {
|
||||
return;
|
||||
}
|
||||
$user->teams()->detach($currentTeam);
|
||||
|
||||
@@ -4,35 +4,35 @@ namespace App\Livewire\Notifications;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Notifications\Test;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Discord extends Component
|
||||
{
|
||||
public Team $team;
|
||||
|
||||
#[Rule(['boolean'])]
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordEnabled = false;
|
||||
|
||||
#[Rule(['url', 'nullable'])]
|
||||
#[Validate(['url', 'nullable'])]
|
||||
public ?string $discordWebhookUrl = null;
|
||||
|
||||
#[Rule(['boolean'])]
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordNotificationsTest = false;
|
||||
|
||||
#[Rule(['boolean'])]
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordNotificationsDeployments = false;
|
||||
|
||||
#[Rule(['boolean'])]
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordNotificationsStatusChanges = false;
|
||||
|
||||
#[Rule(['boolean'])]
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordNotificationsDatabaseBackups = false;
|
||||
|
||||
#[Rule(['boolean'])]
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordNotificationsScheduledTasks = false;
|
||||
|
||||
#[Rule(['boolean'])]
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordNotificationsServerDiskUsage = false;
|
||||
|
||||
public function mount()
|
||||
@@ -41,7 +41,7 @@ class Discord extends Component
|
||||
$this->team = auth()->user()->currentTeam();
|
||||
$this->syncData();
|
||||
} catch (\Throwable $e) {
|
||||
handleError($e, $this);
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,11 +57,8 @@ class Discord extends Component
|
||||
$this->team->discord_notifications_database_backups = $this->discordNotificationsDatabaseBackups;
|
||||
$this->team->discord_notifications_scheduled_tasks = $this->discordNotificationsScheduledTasks;
|
||||
$this->team->discord_notifications_server_disk_usage = $this->discordNotificationsServerDiskUsage;
|
||||
try {
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
} else {
|
||||
$this->discordEnabled = $this->team->discord_enabled;
|
||||
$this->discordWebhookUrl = $this->team->discord_webhook_url;
|
||||
@@ -74,6 +71,22 @@ class Discord extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveDiscordEnabled()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'discordWebhookUrl' => 'required',
|
||||
], [
|
||||
'discordWebhookUrl.required' => 'Discord Webhook URL is required.',
|
||||
]);
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
$this->discordEnabled = false;
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
@@ -96,7 +109,7 @@ class Discord extends Component
|
||||
|
||||
public function saveModel()
|
||||
{
|
||||
$this->team->save();
|
||||
$this->syncData(true);
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
}
|
||||
|
||||
@@ -5,83 +5,151 @@ namespace App\Livewire\Notifications;
|
||||
use App\Models\Team;
|
||||
use App\Notifications\Test;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Email extends Component
|
||||
{
|
||||
public Team $team;
|
||||
|
||||
#[Locked]
|
||||
public string $emails;
|
||||
|
||||
public bool $sharedEmailEnabled = false;
|
||||
#[Validate(['boolean'])]
|
||||
public bool $smtpEnabled = false;
|
||||
|
||||
protected $rules = [
|
||||
'team.smtp_enabled' => 'nullable|boolean',
|
||||
'team.smtp_from_address' => 'required|email',
|
||||
'team.smtp_from_name' => 'required',
|
||||
'team.smtp_recipients' => 'nullable',
|
||||
'team.smtp_host' => 'required',
|
||||
'team.smtp_port' => 'required',
|
||||
'team.smtp_encryption' => 'nullable',
|
||||
'team.smtp_username' => 'nullable',
|
||||
'team.smtp_password' => 'nullable',
|
||||
'team.smtp_timeout' => 'nullable',
|
||||
'team.smtp_notifications_test' => 'nullable|boolean',
|
||||
'team.smtp_notifications_deployments' => 'nullable|boolean',
|
||||
'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',
|
||||
];
|
||||
#[Validate(['boolean'])]
|
||||
public bool $useInstanceEmailSettings = false;
|
||||
|
||||
protected $validationAttributes = [
|
||||
'team.smtp_from_address' => 'From Address',
|
||||
'team.smtp_from_name' => 'From Name',
|
||||
'team.smtp_recipients' => 'Recipients',
|
||||
'team.smtp_host' => 'Host',
|
||||
'team.smtp_port' => 'Port',
|
||||
'team.smtp_encryption' => 'Encryption',
|
||||
'team.smtp_username' => 'Username',
|
||||
'team.smtp_password' => 'Password',
|
||||
'team.smtp_timeout' => 'Timeout',
|
||||
'team.resend_enabled' => 'Resend Enabled',
|
||||
'team.resend_api_key' => 'Resend API Key',
|
||||
];
|
||||
#[Validate(['nullable', 'email'])]
|
||||
public ?string $smtpFromAddress = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $smtpFromName = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $smtpRecipients = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $smtpHost = null;
|
||||
|
||||
#[Validate(['nullable', 'numeric'])]
|
||||
public ?int $smtpPort = null;
|
||||
|
||||
#[Validate(['nullable', 'string', 'in:tls,ssl,none'])]
|
||||
public ?string $smtpEncryption = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $smtpUsername = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $smtpPassword = null;
|
||||
|
||||
#[Validate(['nullable', 'numeric'])]
|
||||
public ?int $smtpTimeout = null;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $smtpNotificationsTest = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $smtpNotificationsDeployments = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $smtpNotificationsStatusChanges = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $smtpNotificationsDatabaseBackups = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $smtpNotificationsScheduledTasks = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $smtpNotificationsServerDiskUsage = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $resendEnabled;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $resendApiKey = null;
|
||||
|
||||
#[Validate(['nullable', 'email'])]
|
||||
public ?string $testEmailAddress = null;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->team = auth()->user()->currentTeam();
|
||||
['sharedEmailEnabled' => $this->sharedEmailEnabled] = $this->team->limits;
|
||||
$this->emails = auth()->user()->email;
|
||||
}
|
||||
|
||||
public function submitFromFields()
|
||||
{
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate([
|
||||
'team.smtp_from_address' => 'required|email',
|
||||
'team.smtp_from_name' => 'required',
|
||||
]);
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
$this->team = auth()->user()->currentTeam();
|
||||
$this->emails = auth()->user()->email;
|
||||
$this->syncData();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function sendTestNotification()
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->team->smtp_enabled = $this->smtpEnabled;
|
||||
$this->team->smtp_from_address = $this->smtpFromAddress;
|
||||
$this->team->smtp_from_name = $this->smtpFromName;
|
||||
$this->team->smtp_host = $this->smtpHost;
|
||||
$this->team->smtp_port = $this->smtpPort;
|
||||
$this->team->smtp_encryption = $this->smtpEncryption;
|
||||
$this->team->smtp_username = $this->smtpUsername;
|
||||
$this->team->smtp_password = $this->smtpPassword;
|
||||
$this->team->smtp_timeout = $this->smtpTimeout;
|
||||
$this->team->smtp_recipients = $this->smtpRecipients;
|
||||
$this->team->smtp_notifications_test = $this->smtpNotificationsTest;
|
||||
$this->team->smtp_notifications_deployments = $this->smtpNotificationsDeployments;
|
||||
$this->team->smtp_notifications_status_changes = $this->smtpNotificationsStatusChanges;
|
||||
$this->team->smtp_notifications_database_backups = $this->smtpNotificationsDatabaseBackups;
|
||||
$this->team->smtp_notifications_scheduled_tasks = $this->smtpNotificationsScheduledTasks;
|
||||
$this->team->smtp_notifications_server_disk_usage = $this->smtpNotificationsServerDiskUsage;
|
||||
$this->team->use_instance_email_settings = $this->useInstanceEmailSettings;
|
||||
$this->team->resend_enabled = $this->resendEnabled;
|
||||
$this->team->resend_api_key = $this->resendApiKey;
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
} else {
|
||||
$this->smtpEnabled = $this->team->smtp_enabled;
|
||||
$this->smtpFromAddress = $this->team->smtp_from_address;
|
||||
$this->smtpFromName = $this->team->smtp_from_name;
|
||||
$this->smtpHost = $this->team->smtp_host;
|
||||
$this->smtpPort = $this->team->smtp_port;
|
||||
$this->smtpEncryption = $this->team->smtp_encryption;
|
||||
$this->smtpUsername = $this->team->smtp_username;
|
||||
$this->smtpPassword = $this->team->smtp_password;
|
||||
$this->smtpTimeout = $this->team->smtp_timeout;
|
||||
$this->smtpRecipients = $this->team->smtp_recipients;
|
||||
$this->smtpNotificationsTest = $this->team->smtp_notifications_test;
|
||||
$this->smtpNotificationsDeployments = $this->team->smtp_notifications_deployments;
|
||||
$this->smtpNotificationsStatusChanges = $this->team->smtp_notifications_status_changes;
|
||||
$this->smtpNotificationsDatabaseBackups = $this->team->smtp_notifications_database_backups;
|
||||
$this->smtpNotificationsScheduledTasks = $this->team->smtp_notifications_scheduled_tasks;
|
||||
$this->smtpNotificationsServerDiskUsage = $this->team->smtp_notifications_server_disk_usage;
|
||||
$this->useInstanceEmailSettings = $this->team->use_instance_email_settings;
|
||||
$this->resendEnabled = $this->team->resend_enabled;
|
||||
$this->resendApiKey = $this->team->resend_api_key;
|
||||
}
|
||||
}
|
||||
|
||||
public function sendTestEmail()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'testEmailAddress' => 'required|email',
|
||||
], [
|
||||
'testEmailAddress.required' => 'Test email address is required.',
|
||||
'testEmailAddress.email' => 'Please enter a valid email address.',
|
||||
]);
|
||||
|
||||
$executed = RateLimiter::attempt(
|
||||
'test-email:'.$this->team->id,
|
||||
$perMinute = 0,
|
||||
function () {
|
||||
$this->team?->notify(new Test($this->emails));
|
||||
$this->team?->notify(new Test($this->testEmailAddress));
|
||||
$this->dispatch('success', 'Test Email sent.');
|
||||
},
|
||||
$decaySeconds = 10,
|
||||
@@ -98,38 +166,45 @@ class Email extends Component
|
||||
public function instantSaveInstance()
|
||||
{
|
||||
try {
|
||||
if (! $this->sharedEmailEnabled) {
|
||||
throw new \Exception('Not allowed to change settings. Please upgrade your subscription.');
|
||||
}
|
||||
$this->team->smtp_enabled = false;
|
||||
$this->team->resend_enabled = false;
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
$this->smtpEnabled = false;
|
||||
$this->resendEnabled = false;
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveSmtpEnabled()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'smtpHost' => 'required',
|
||||
'smtpPort' => 'required|numeric',
|
||||
], [
|
||||
'smtpHost.required' => 'SMTP Host is required.',
|
||||
'smtpPort.required' => 'SMTP Port is required.',
|
||||
]);
|
||||
$this->resendEnabled = false;
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
$this->smtpEnabled = false;
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveResend()
|
||||
{
|
||||
try {
|
||||
$this->team->smtp_enabled = false;
|
||||
$this->submitResend();
|
||||
$this->validate([
|
||||
'resendApiKey' => 'required',
|
||||
], [
|
||||
'resendApiKey.required' => 'Resend API Key is required.',
|
||||
]);
|
||||
$this->smtpEnabled = false;
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->smtp_enabled = false;
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->team->resend_enabled = false;
|
||||
$this->submit();
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->smtp_enabled = false;
|
||||
$this->resendEnabled = false;
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -137,7 +212,7 @@ class Email extends Component
|
||||
|
||||
public function saveModel()
|
||||
{
|
||||
$this->team->save();
|
||||
$this->syncData(true);
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
}
|
||||
@@ -146,43 +221,8 @@ class Email extends Component
|
||||
{
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
if (! $this->team->use_instance_email_settings) {
|
||||
$this->validate([
|
||||
'team.smtp_from_address' => 'required|email',
|
||||
'team.smtp_from_name' => 'required',
|
||||
'team.smtp_host' => 'required',
|
||||
'team.smtp_port' => 'required|numeric',
|
||||
'team.smtp_encryption' => 'nullable',
|
||||
'team.smtp_username' => 'nullable',
|
||||
'team.smtp_password' => 'nullable',
|
||||
'team.smtp_timeout' => 'nullable',
|
||||
]);
|
||||
}
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->smtp_enabled = false;
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submitResend()
|
||||
{
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate([
|
||||
'team.smtp_from_address' => 'required|email',
|
||||
'team.smtp_from_name' => 'required',
|
||||
'team.resend_api_key' => 'required',
|
||||
]);
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->team->resend_enabled = false;
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
@@ -190,35 +230,28 @@ class Email extends Component
|
||||
public function copyFromInstanceSettings()
|
||||
{
|
||||
$settings = instanceSettings();
|
||||
|
||||
if ($settings->smtp_enabled) {
|
||||
$team = currentTeam();
|
||||
$team->update([
|
||||
'smtp_enabled' => $settings->smtp_enabled,
|
||||
'smtp_from_address' => $settings->smtp_from_address,
|
||||
'smtp_from_name' => $settings->smtp_from_name,
|
||||
'smtp_recipients' => $settings->smtp_recipients,
|
||||
'smtp_host' => $settings->smtp_host,
|
||||
'smtp_port' => $settings->smtp_port,
|
||||
'smtp_encryption' => $settings->smtp_encryption,
|
||||
'smtp_username' => $settings->smtp_username,
|
||||
'smtp_password' => $settings->smtp_password,
|
||||
'smtp_timeout' => $settings->smtp_timeout,
|
||||
]);
|
||||
refreshSession();
|
||||
$this->team = $team;
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
$this->smtpEnabled = true;
|
||||
$this->smtpFromAddress = $settings->smtp_from_address;
|
||||
$this->smtpFromName = $settings->smtp_from_name;
|
||||
$this->smtpRecipients = $settings->smtp_recipients;
|
||||
$this->smtpHost = $settings->smtp_host;
|
||||
$this->smtpPort = $settings->smtp_port;
|
||||
$this->smtpEncryption = $settings->smtp_encryption;
|
||||
$this->smtpUsername = $settings->smtp_username;
|
||||
$this->smtpPassword = $settings->smtp_password;
|
||||
$this->smtpTimeout = $settings->smtp_timeout;
|
||||
$this->resendEnabled = false;
|
||||
$this->saveModel();
|
||||
|
||||
return;
|
||||
}
|
||||
if ($settings->resend_enabled) {
|
||||
$team = currentTeam();
|
||||
$team->update([
|
||||
'resend_enabled' => $settings->resend_enabled,
|
||||
'resend_api_key' => $settings->resend_api_key,
|
||||
]);
|
||||
refreshSession();
|
||||
$this->team = $team;
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
$this->resendEnabled = true;
|
||||
$this->resendApiKey = $settings->resend_api_key;
|
||||
$this->smtpEnabled = false;
|
||||
$this->saveModel();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
131
app/Livewire/Notifications/Slack.php
Normal file
131
app/Livewire/Notifications/Slack.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Notifications;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Notifications\Test;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Slack extends Component
|
||||
{
|
||||
public Team $team;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackEnabled = false;
|
||||
|
||||
#[Validate(['url', 'nullable'])]
|
||||
public ?string $slackWebhookUrl = null;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsTest = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsDeployments = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsStatusChanges = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsDatabaseBackups = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsScheduledTasks = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsServerDiskUsage = false;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
try {
|
||||
$this->team = auth()->user()->currentTeam();
|
||||
$this->syncData();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->team->slack_enabled = $this->slackEnabled;
|
||||
$this->team->slack_webhook_url = $this->slackWebhookUrl;
|
||||
$this->team->slack_notifications_test = $this->slackNotificationsTest;
|
||||
$this->team->slack_notifications_deployments = $this->slackNotificationsDeployments;
|
||||
$this->team->slack_notifications_status_changes = $this->slackNotificationsStatusChanges;
|
||||
$this->team->slack_notifications_database_backups = $this->slackNotificationsDatabaseBackups;
|
||||
$this->team->slack_notifications_scheduled_tasks = $this->slackNotificationsScheduledTasks;
|
||||
$this->team->slack_notifications_server_disk_usage = $this->slackNotificationsServerDiskUsage;
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
} else {
|
||||
$this->slackEnabled = $this->team->slack_enabled;
|
||||
$this->slackWebhookUrl = $this->team->slack_webhook_url;
|
||||
$this->slackNotificationsTest = $this->team->slack_notifications_test;
|
||||
$this->slackNotificationsDeployments = $this->team->slack_notifications_deployments;
|
||||
$this->slackNotificationsStatusChanges = $this->team->slack_notifications_status_changes;
|
||||
$this->slackNotificationsDatabaseBackups = $this->team->slack_notifications_database_backups;
|
||||
$this->slackNotificationsScheduledTasks = $this->team->slack_notifications_scheduled_tasks;
|
||||
$this->slackNotificationsServerDiskUsage = $this->team->slack_notifications_server_disk_usage;
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveSlackEnabled()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'slackWebhookUrl' => 'required',
|
||||
], [
|
||||
'slackWebhookUrl.required' => 'Slack Webhook URL is required.',
|
||||
]);
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
$this->slackEnabled = false;
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->syncData(true);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->syncData(true);
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function saveModel()
|
||||
{
|
||||
$this->syncData(true);
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
}
|
||||
|
||||
public function sendTestNotification()
|
||||
{
|
||||
try {
|
||||
$this->team->notify(new Test);
|
||||
$this->dispatch('success', 'Test notification sent.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.notifications.slack');
|
||||
}
|
||||
}
|
||||
@@ -4,67 +4,157 @@ namespace App\Livewire\Notifications;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Notifications\Test;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Telegram extends Component
|
||||
{
|
||||
public Team $team;
|
||||
|
||||
protected $rules = [
|
||||
'team.telegram_enabled' => 'nullable|boolean',
|
||||
'team.telegram_token' => 'required|string',
|
||||
'team.telegram_chat_id' => 'required|string',
|
||||
'team.telegram_notifications_test' => 'nullable|boolean',
|
||||
'team.telegram_notifications_deployments' => 'nullable|boolean',
|
||||
'team.telegram_notifications_status_changes' => 'nullable|boolean',
|
||||
'team.telegram_notifications_database_backups' => 'nullable|boolean',
|
||||
'team.telegram_notifications_scheduled_tasks' => 'nullable|boolean',
|
||||
'team.telegram_notifications_test_message_thread_id' => 'nullable|string',
|
||||
'team.telegram_notifications_deployments_message_thread_id' => 'nullable|string',
|
||||
'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',
|
||||
];
|
||||
#[Validate(['boolean'])]
|
||||
public bool $telegramEnabled = false;
|
||||
|
||||
protected $validationAttributes = [
|
||||
'team.telegram_token' => 'Token',
|
||||
'team.telegram_chat_id' => 'Chat ID',
|
||||
];
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $telegramToken = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $telegramChatId = null;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $telegramNotificationsTest = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $telegramNotificationsDeployments = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $telegramNotificationsStatusChanges = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $telegramNotificationsDatabaseBackups = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $telegramNotificationsScheduledTasks = false;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $telegramNotificationsTestMessageThreadId = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $telegramNotificationsDeploymentsMessageThreadId = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $telegramNotificationsStatusChangesMessageThreadId = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $telegramNotificationsDatabaseBackupsMessageThreadId = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $telegramNotificationsScheduledTasksThreadId = null;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $telegramNotificationsServerDiskUsage = false;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->team = auth()->user()->currentTeam();
|
||||
try {
|
||||
$this->team = auth()->user()->currentTeam();
|
||||
$this->syncData();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->team->telegram_enabled = $this->telegramEnabled;
|
||||
$this->team->telegram_token = $this->telegramToken;
|
||||
$this->team->telegram_chat_id = $this->telegramChatId;
|
||||
$this->team->telegram_notifications_test = $this->telegramNotificationsTest;
|
||||
$this->team->telegram_notifications_deployments = $this->telegramNotificationsDeployments;
|
||||
$this->team->telegram_notifications_status_changes = $this->telegramNotificationsStatusChanges;
|
||||
$this->team->telegram_notifications_database_backups = $this->telegramNotificationsDatabaseBackups;
|
||||
$this->team->telegram_notifications_scheduled_tasks = $this->telegramNotificationsScheduledTasks;
|
||||
$this->team->telegram_notifications_test_message_thread_id = $this->telegramNotificationsTestMessageThreadId;
|
||||
$this->team->telegram_notifications_deployments_message_thread_id = $this->telegramNotificationsDeploymentsMessageThreadId;
|
||||
$this->team->telegram_notifications_status_changes_message_thread_id = $this->telegramNotificationsStatusChangesMessageThreadId;
|
||||
$this->team->telegram_notifications_database_backups_message_thread_id = $this->telegramNotificationsDatabaseBackupsMessageThreadId;
|
||||
$this->team->telegram_notifications_scheduled_tasks_thread_id = $this->telegramNotificationsScheduledTasksThreadId;
|
||||
$this->team->telegram_notifications_server_disk_usage = $this->telegramNotificationsServerDiskUsage;
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
} else {
|
||||
$this->telegramEnabled = $this->team->telegram_enabled;
|
||||
$this->telegramToken = $this->team->telegram_token;
|
||||
$this->telegramChatId = $this->team->telegram_chat_id;
|
||||
$this->telegramNotificationsTest = $this->team->telegram_notifications_test;
|
||||
$this->telegramNotificationsDeployments = $this->team->telegram_notifications_deployments;
|
||||
$this->telegramNotificationsStatusChanges = $this->team->telegram_notifications_status_changes;
|
||||
$this->telegramNotificationsDatabaseBackups = $this->team->telegram_notifications_database_backups;
|
||||
$this->telegramNotificationsScheduledTasks = $this->team->telegram_notifications_scheduled_tasks;
|
||||
$this->telegramNotificationsTestMessageThreadId = $this->team->telegram_notifications_test_message_thread_id;
|
||||
$this->telegramNotificationsDeploymentsMessageThreadId = $this->team->telegram_notifications_deployments_message_thread_id;
|
||||
$this->telegramNotificationsStatusChangesMessageThreadId = $this->team->telegram_notifications_status_changes_message_thread_id;
|
||||
$this->telegramNotificationsDatabaseBackupsMessageThreadId = $this->team->telegram_notifications_database_backups_message_thread_id;
|
||||
$this->telegramNotificationsScheduledTasksThreadId = $this->team->telegram_notifications_scheduled_tasks_thread_id;
|
||||
$this->telegramNotificationsServerDiskUsage = $this->team->telegram_notifications_server_disk_usage;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->submit();
|
||||
} catch (\Throwable) {
|
||||
$this->team->telegram_enabled = false;
|
||||
$this->validate();
|
||||
$this->syncData(true);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->resetErrorBag();
|
||||
$this->validate();
|
||||
$this->saveModel();
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->syncData(true);
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveTelegramEnabled()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'telegramToken' => 'required',
|
||||
'telegramChatId' => 'required',
|
||||
], [
|
||||
'telegramToken.required' => 'Telegram Token is required.',
|
||||
'telegramChatId.required' => 'Telegram Chat ID is required.',
|
||||
]);
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
$this->telegramEnabled = false;
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function saveModel()
|
||||
{
|
||||
$this->team->save();
|
||||
$this->syncData(true);
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
}
|
||||
|
||||
public function sendTestNotification()
|
||||
{
|
||||
$this->team?->notify(new Test);
|
||||
$this->dispatch('success', 'Test notification sent.');
|
||||
try {
|
||||
$this->team->notify(new Test);
|
||||
$this->dispatch('success', 'Test notification sent.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Livewire\Profile;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Livewire\Attributes\Validate;
|
||||
@@ -24,9 +25,9 @@ class Index extends Component
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->userId = auth()->user()->id;
|
||||
$this->name = auth()->user()->name;
|
||||
$this->email = auth()->user()->email;
|
||||
$this->userId = Auth::id();
|
||||
$this->name = Auth::user()->name;
|
||||
$this->email = Auth::user()->email;
|
||||
}
|
||||
|
||||
public function submit()
|
||||
@@ -35,7 +36,7 @@ class Index extends Component
|
||||
$this->validate([
|
||||
'name' => 'required',
|
||||
]);
|
||||
auth()->user()->update([
|
||||
Auth::user()->update([
|
||||
'name' => $this->name,
|
||||
]);
|
||||
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
namespace App\Livewire\Project;
|
||||
|
||||
use App\Models\Project;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class AddEmpty extends Component
|
||||
{
|
||||
#[Rule(['required', 'string', 'min:3'])]
|
||||
#[Validate(['required', 'string', 'min:3'])]
|
||||
public string $name;
|
||||
|
||||
#[Rule(['nullable', 'string'])]
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public string $description = '';
|
||||
|
||||
public function submit()
|
||||
|
||||
@@ -3,120 +3,205 @@
|
||||
namespace App\Livewire\Project\Application;
|
||||
|
||||
use App\Models\Application;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Advanced extends Component
|
||||
{
|
||||
public Application $application;
|
||||
|
||||
public bool $is_force_https_enabled;
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isForceHttpsEnabled = false;
|
||||
|
||||
public bool $is_gzip_enabled;
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isGitSubmodulesEnabled = false;
|
||||
|
||||
public bool $is_stripprefix_enabled;
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isGitLfsEnabled = false;
|
||||
|
||||
protected $rules = [
|
||||
'application.settings.is_git_submodules_enabled' => 'boolean|required',
|
||||
'application.settings.is_git_lfs_enabled' => 'boolean|required',
|
||||
'application.settings.is_preview_deployments_enabled' => 'boolean|required',
|
||||
'application.settings.is_auto_deploy_enabled' => 'boolean|required',
|
||||
'is_force_https_enabled' => 'boolean|required',
|
||||
'application.settings.is_log_drain_enabled' => 'boolean|required',
|
||||
'application.settings.is_gpu_enabled' => 'boolean|required',
|
||||
'application.settings.is_build_server_enabled' => 'boolean|required',
|
||||
'application.settings.is_consistent_container_name_enabled' => 'boolean|required',
|
||||
'application.settings.custom_internal_name' => 'string|nullable',
|
||||
'application.settings.is_gzip_enabled' => 'boolean|required',
|
||||
'application.settings.is_stripprefix_enabled' => 'boolean|required',
|
||||
'application.settings.gpu_driver' => 'string|required',
|
||||
'application.settings.gpu_count' => 'string|required',
|
||||
'application.settings.gpu_device_ids' => 'string|required',
|
||||
'application.settings.gpu_options' => 'string|required',
|
||||
'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required',
|
||||
'application.settings.connect_to_docker_network' => 'boolean|required',
|
||||
];
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isPreviewDeploymentsEnabled = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isAutoDeployEnabled = true;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $disableBuildCache = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isLogDrainEnabled = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isGpuEnabled = false;
|
||||
|
||||
#[Validate(['string'])]
|
||||
public string $gpuDriver = '';
|
||||
|
||||
#[Validate(['string', 'nullable'])]
|
||||
public ?string $gpuCount = null;
|
||||
|
||||
#[Validate(['string', 'nullable'])]
|
||||
public ?string $gpuDeviceIds = null;
|
||||
|
||||
#[Validate(['string', 'nullable'])]
|
||||
public ?string $gpuOptions = null;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isBuildServerEnabled = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isConsistentContainerNameEnabled = false;
|
||||
|
||||
#[Validate(['string', 'nullable'])]
|
||||
public ?string $customInternalName = null;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isGzipEnabled = true;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isStripprefixEnabled = true;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isRawComposeDeploymentEnabled = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isConnectToDockerNetworkEnabled = false;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->is_force_https_enabled = $this->application->isForceHttpsEnabled();
|
||||
$this->is_gzip_enabled = $this->application->isGzipEnabled();
|
||||
$this->is_stripprefix_enabled = $this->application->isStripprefixEnabled();
|
||||
try {
|
||||
$this->syncData();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->application->settings->is_force_https_enabled = $this->isForceHttpsEnabled;
|
||||
$this->application->settings->is_git_submodules_enabled = $this->isGitSubmodulesEnabled;
|
||||
$this->application->settings->is_git_lfs_enabled = $this->isGitLfsEnabled;
|
||||
$this->application->settings->is_preview_deployments_enabled = $this->isPreviewDeploymentsEnabled;
|
||||
$this->application->settings->is_auto_deploy_enabled = $this->isAutoDeployEnabled;
|
||||
$this->application->settings->is_log_drain_enabled = $this->isLogDrainEnabled;
|
||||
$this->application->settings->is_gpu_enabled = $this->isGpuEnabled;
|
||||
$this->application->settings->gpu_driver = $this->gpuDriver;
|
||||
$this->application->settings->gpu_count = $this->gpuCount;
|
||||
$this->application->settings->gpu_device_ids = $this->gpuDeviceIds;
|
||||
$this->application->settings->gpu_options = $this->gpuOptions;
|
||||
$this->application->settings->is_build_server_enabled = $this->isBuildServerEnabled;
|
||||
$this->application->settings->is_consistent_container_name_enabled = $this->isConsistentContainerNameEnabled;
|
||||
$this->application->settings->custom_internal_name = $this->customInternalName;
|
||||
$this->application->settings->is_gzip_enabled = $this->isGzipEnabled;
|
||||
$this->application->settings->is_stripprefix_enabled = $this->isStripprefixEnabled;
|
||||
$this->application->settings->is_raw_compose_deployment_enabled = $this->isRawComposeDeploymentEnabled;
|
||||
$this->application->settings->connect_to_docker_network = $this->isConnectToDockerNetworkEnabled;
|
||||
$this->application->settings->disable_build_cache = $this->disableBuildCache;
|
||||
$this->application->settings->save();
|
||||
} else {
|
||||
$this->isForceHttpsEnabled = $this->application->isForceHttpsEnabled();
|
||||
$this->isGzipEnabled = $this->application->isGzipEnabled();
|
||||
$this->isStripprefixEnabled = $this->application->isStripprefixEnabled();
|
||||
$this->isLogDrainEnabled = $this->application->isLogDrainEnabled();
|
||||
|
||||
$this->isGitSubmodulesEnabled = $this->application->settings->is_git_submodules_enabled;
|
||||
$this->isGitLfsEnabled = $this->application->settings->is_git_lfs_enabled;
|
||||
$this->isPreviewDeploymentsEnabled = $this->application->settings->is_preview_deployments_enabled;
|
||||
$this->isAutoDeployEnabled = $this->application->settings->is_auto_deploy_enabled;
|
||||
$this->isGpuEnabled = $this->application->settings->is_gpu_enabled;
|
||||
$this->gpuDriver = $this->application->settings->gpu_driver;
|
||||
$this->gpuCount = $this->application->settings->gpu_count;
|
||||
$this->gpuDeviceIds = $this->application->settings->gpu_device_ids;
|
||||
$this->gpuOptions = $this->application->settings->gpu_options;
|
||||
$this->isBuildServerEnabled = $this->application->settings->is_build_server_enabled;
|
||||
$this->isConsistentContainerNameEnabled = $this->application->settings->is_consistent_container_name_enabled;
|
||||
$this->customInternalName = $this->application->settings->custom_internal_name;
|
||||
$this->isRawComposeDeploymentEnabled = $this->application->settings->is_raw_compose_deployment_enabled;
|
||||
$this->isConnectToDockerNetworkEnabled = $this->application->settings->connect_to_docker_network;
|
||||
$this->disableBuildCache = $this->application->settings->disable_build_cache;
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
if ($this->application->isLogDrainEnabled()) {
|
||||
if (! $this->application->destination->server->isLogDrainEnabled()) {
|
||||
$this->application->settings->is_log_drain_enabled = false;
|
||||
$this->dispatch('error', 'Log drain is not enabled on this server.');
|
||||
try {
|
||||
if ($this->isLogDrainEnabled) {
|
||||
if (! $this->application->destination->server->isLogDrainEnabled()) {
|
||||
$this->isLogDrainEnabled = false;
|
||||
$this->syncData(true);
|
||||
$this->dispatch('error', 'Log drain is not enabled on this server.');
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ($this->application->isForceHttpsEnabled() !== $this->isForceHttpsEnabled ||
|
||||
$this->application->isGzipEnabled() !== $this->isGzipEnabled ||
|
||||
$this->application->isStripprefixEnabled() !== $this->isStripprefixEnabled
|
||||
) {
|
||||
$this->dispatch('resetDefaultLabels', false);
|
||||
}
|
||||
|
||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||
$this->application->oldRawParser();
|
||||
} else {
|
||||
$this->application->parse();
|
||||
}
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
$this->dispatch('configurationChanged');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
if ($this->application->settings->is_force_https_enabled !== $this->is_force_https_enabled) {
|
||||
$this->application->settings->is_force_https_enabled = $this->is_force_https_enabled;
|
||||
$this->dispatch('resetDefaultLabels', false);
|
||||
}
|
||||
if ($this->application->settings->is_gzip_enabled !== $this->is_gzip_enabled) {
|
||||
$this->application->settings->is_gzip_enabled = $this->is_gzip_enabled;
|
||||
$this->dispatch('resetDefaultLabels', false);
|
||||
}
|
||||
if ($this->application->settings->is_stripprefix_enabled !== $this->is_stripprefix_enabled) {
|
||||
$this->application->settings->is_stripprefix_enabled = $this->is_stripprefix_enabled;
|
||||
$this->dispatch('resetDefaultLabels', false);
|
||||
}
|
||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||
$this->application->oldRawParser();
|
||||
} else {
|
||||
$this->application->parse();
|
||||
}
|
||||
$this->application->settings->save();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
$this->dispatch('configurationChanged');
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
if ($this->application->settings->gpu_count && $this->application->settings->gpu_device_ids) {
|
||||
$this->dispatch('error', 'You cannot set both GPU count and GPU device IDs.');
|
||||
$this->application->settings->gpu_count = null;
|
||||
$this->application->settings->gpu_device_ids = null;
|
||||
$this->application->settings->save();
|
||||
try {
|
||||
if ($this->gpuCount && $this->gpuDeviceIds) {
|
||||
$this->dispatch('error', 'You cannot set both GPU count and GPU device IDs.');
|
||||
$this->gpuCount = null;
|
||||
$this->gpuDeviceIds = null;
|
||||
$this->syncData(true);
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
$this->application->settings->save();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
}
|
||||
|
||||
public function saveCustomName()
|
||||
{
|
||||
if (str($this->application->settings->custom_internal_name)->isNotEmpty()) {
|
||||
$this->application->settings->custom_internal_name = str($this->application->settings->custom_internal_name)->slug()->value();
|
||||
if (str($this->customInternalName)->isNotEmpty()) {
|
||||
$this->customInternalName = str($this->customInternalName)->slug()->value();
|
||||
} else {
|
||||
$this->application->settings->custom_internal_name = null;
|
||||
$this->customInternalName = null;
|
||||
}
|
||||
if (is_null($this->application->settings->custom_internal_name)) {
|
||||
$this->application->settings->save();
|
||||
if (is_null($this->customInternalName)) {
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Custom name saved.');
|
||||
|
||||
return;
|
||||
}
|
||||
$customInternalName = $this->application->settings->custom_internal_name;
|
||||
$customInternalName = $this->customInternalName;
|
||||
$server = $this->application->destination->server;
|
||||
$allApplications = $server->applications();
|
||||
|
||||
$foundSameInternalName = $allApplications->filter(function ($application) {
|
||||
return $application->id !== $this->application->id && $application->settings->custom_internal_name === $this->application->settings->custom_internal_name;
|
||||
return $application->id !== $this->application->id && $application->settings->custom_internal_name === $this->customInternalName;
|
||||
});
|
||||
if ($foundSameInternalName->isNotEmpty()) {
|
||||
$this->dispatch('error', 'This custom container name is already in use by another application on this server.');
|
||||
$this->application->settings->custom_internal_name = $customInternalName;
|
||||
$this->application->settings->refresh();
|
||||
$this->customInternalName = $customInternalName;
|
||||
$this->syncData(true);
|
||||
|
||||
return;
|
||||
}
|
||||
$this->application->settings->save();
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Custom name saved.');
|
||||
}
|
||||
|
||||
|
||||
@@ -16,24 +16,30 @@ class Configuration extends Component
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first();
|
||||
if (! $project) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']);
|
||||
if (! $environment) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$application = $environment->applications->where('uuid', request()->route('application_uuid'))->first();
|
||||
if (! $application) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$project = currentTeam()
|
||||
->projects()
|
||||
->select('id', 'uuid', 'team_id')
|
||||
->where('uuid', request()->route('project_uuid'))
|
||||
->firstOrFail();
|
||||
$environment = $project->environments()
|
||||
->select('id', 'name', 'project_id')
|
||||
->where('name', request()->route('environment_name'))
|
||||
->firstOrFail();
|
||||
$application = $environment->applications()
|
||||
->with(['destination'])
|
||||
->where('uuid', request()->route('application_uuid'))
|
||||
->firstOrFail();
|
||||
|
||||
$this->application = $application;
|
||||
$mainServer = $this->application->destination->server;
|
||||
$servers = Server::ownedByCurrentTeam()->get();
|
||||
$this->servers = $servers->filter(function ($server) use ($mainServer) {
|
||||
return $server->id != $mainServer->id;
|
||||
});
|
||||
if ($application->destination && $application->destination->server) {
|
||||
$mainServer = $application->destination->server;
|
||||
$this->servers = Server::ownedByCurrentTeam()
|
||||
->select('id', 'name')
|
||||
->where('id', '!=', $mainServer->id)
|
||||
->get();
|
||||
} else {
|
||||
$this->servers = collect();
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
||||
@@ -84,6 +84,7 @@ class General extends Component
|
||||
'application.pre_deployment_command_container' => 'nullable',
|
||||
'application.post_deployment_command' => 'nullable',
|
||||
'application.post_deployment_command_container' => 'nullable',
|
||||
'application.custom_nginx_configuration' => 'nullable',
|
||||
'application.settings.is_static' => 'boolean|required',
|
||||
'application.settings.is_build_server_enabled' => 'boolean|required',
|
||||
'application.settings.is_container_label_escape_enabled' => 'boolean|required',
|
||||
@@ -121,6 +122,7 @@ class General extends Component
|
||||
'application.custom_docker_run_options' => 'Custom docker run commands',
|
||||
'application.docker_compose_custom_start_command' => 'Docker compose custom start command',
|
||||
'application.docker_compose_custom_build_command' => 'Docker compose custom build command',
|
||||
'application.custom_nginx_configuration' => 'Custom Nginx configuration',
|
||||
'application.settings.is_static' => 'Is static',
|
||||
'application.settings.is_build_server_enabled' => 'Is build server enabled',
|
||||
'application.settings.is_container_label_escape_enabled' => 'Is container label escape enabled',
|
||||
@@ -241,6 +243,13 @@ class General extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function updatedApplicationSettingsIsStatic($value)
|
||||
{
|
||||
if ($value) {
|
||||
$this->generateNginxConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
public function updatedApplicationBuildPack()
|
||||
{
|
||||
if ($this->application->build_pack !== 'nixpacks') {
|
||||
@@ -257,6 +266,7 @@ class General extends Component
|
||||
if ($this->application->build_pack === 'static') {
|
||||
$this->application->ports_exposes = $this->ports_exposes = 80;
|
||||
$this->resetDefaultLabels(false);
|
||||
$this->generateNginxConfiguration();
|
||||
}
|
||||
$this->submit();
|
||||
$this->dispatch('buildPackUpdated');
|
||||
@@ -274,6 +284,13 @@ class General extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function generateNginxConfiguration()
|
||||
{
|
||||
$this->application->custom_nginx_configuration = defaultNginxConfiguration();
|
||||
$this->application->save();
|
||||
$this->dispatch('success', 'Nginx configuration generated.');
|
||||
}
|
||||
|
||||
public function resetDefaultLabels($manualReset = false)
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -36,7 +36,11 @@ class Heading extends Component
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->parameters = [
|
||||
'project_uuid' => $this->application->project()->uuid,
|
||||
'environment_name' => $this->application->environment->name,
|
||||
'application_uuid' => $this->application->uuid,
|
||||
];
|
||||
$lastDeployment = $this->application->get_last_successful_deployment();
|
||||
$this->lastDeploymentInfo = data_get_str($lastDeployment, 'commit')->limit(7).' '.data_get($lastDeployment, 'commit_message');
|
||||
$this->lastDeploymentLink = $this->application->gitCommitLink(data_get($lastDeployment, 'commit'));
|
||||
@@ -45,13 +49,11 @@ class Heading extends Component
|
||||
public function check_status($showNotification = false)
|
||||
{
|
||||
if ($this->application->destination->server->isFunctional()) {
|
||||
GetContainersStatus::dispatch($this->application->destination->server)->onQueue('high');
|
||||
GetContainersStatus::dispatch($this->application->destination->server);
|
||||
}
|
||||
if ($showNotification) {
|
||||
$this->dispatch('success', 'Success', 'Application status updated.');
|
||||
}
|
||||
// Removed because it caused flickering
|
||||
// $this->dispatch('configurationChanged');
|
||||
}
|
||||
|
||||
public function force_deploy_without_cache()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Livewire\Project\Application\Preview;
|
||||
|
||||
use App\Models\Application;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
use Spatie\Url\Url;
|
||||
|
||||
@@ -10,49 +11,53 @@ class Form extends Component
|
||||
{
|
||||
public Application $application;
|
||||
|
||||
public string $preview_url_template;
|
||||
|
||||
protected $rules = [
|
||||
'application.preview_url_template' => 'required',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'application.preview_url_template' => 'preview url template',
|
||||
];
|
||||
|
||||
public function resetToDefault()
|
||||
{
|
||||
$this->application->preview_url_template = '{{pr_id}}.{{domain}}';
|
||||
$this->preview_url_template = $this->application->preview_url_template;
|
||||
$this->application->save();
|
||||
$this->generate_real_url();
|
||||
}
|
||||
|
||||
public function generate_real_url()
|
||||
{
|
||||
if (data_get($this->application, 'fqdn')) {
|
||||
try {
|
||||
$firstFqdn = str($this->application->fqdn)->before(',');
|
||||
$url = Url::fromString($firstFqdn);
|
||||
$host = $url->getHost();
|
||||
$this->preview_url_template = str($this->application->preview_url_template)->replace('{{domain}}', $host);
|
||||
} catch (\Exception) {
|
||||
$this->dispatch('error', 'Invalid FQDN.');
|
||||
}
|
||||
}
|
||||
}
|
||||
#[Validate('required')]
|
||||
public string $previewUrlTemplate;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->generate_real_url();
|
||||
try {
|
||||
$this->previewUrlTemplate = $this->application->preview_url_template;
|
||||
$this->generateRealUrl();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
$this->application->preview_url_template = str_replace(' ', '', $this->application->preview_url_template);
|
||||
$this->application->save();
|
||||
$this->dispatch('success', 'Preview url template updated.');
|
||||
$this->generate_real_url();
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate();
|
||||
$this->application->preview_url_template = str_replace(' ', '', $this->previewUrlTemplate);
|
||||
$this->application->save();
|
||||
$this->dispatch('success', 'Preview url template updated.');
|
||||
$this->generateRealUrl();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function resetToDefault()
|
||||
{
|
||||
try {
|
||||
$this->application->preview_url_template = '{{pr_id}}.{{domain}}';
|
||||
$this->previewUrlTemplate = $this->application->preview_url_template;
|
||||
$this->application->save();
|
||||
$this->generateRealUrl();
|
||||
$this->dispatch('success', 'Preview url template updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function generateRealUrl()
|
||||
{
|
||||
if (data_get($this->application, 'fqdn')) {
|
||||
$firstFqdn = str($this->application->fqdn)->before(',');
|
||||
$url = Url::fromString($firstFqdn);
|
||||
$host = $url->getHost();
|
||||
$this->previewUrlTemplate = str($this->application->preview_url_template)->replace('{{domain}}', $host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Livewire\Project\Application;
|
||||
use App\Models\Application;
|
||||
use App\Models\PrivateKey;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Source extends Component
|
||||
@@ -15,19 +15,19 @@ class Source extends Component
|
||||
#[Locked]
|
||||
public $privateKeys;
|
||||
|
||||
#[Rule(['nullable', 'string'])]
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $privateKeyName = null;
|
||||
|
||||
#[Rule(['nullable', 'integer'])]
|
||||
#[Validate(['nullable', 'integer'])]
|
||||
public ?int $privateKeyId = null;
|
||||
|
||||
#[Rule(['required', 'string'])]
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $gitRepository;
|
||||
|
||||
#[Rule(['required', 'string'])]
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $gitBranch;
|
||||
|
||||
#[Rule(['nullable', 'string'])]
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $gitCommitSha = null;
|
||||
|
||||
public function mount()
|
||||
|
||||
@@ -3,32 +3,55 @@
|
||||
namespace App\Livewire\Project\Application;
|
||||
|
||||
use App\Models\Application;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Swarm extends Component
|
||||
{
|
||||
public Application $application;
|
||||
|
||||
public string $swarm_placement_constraints = '';
|
||||
#[Validate('required')]
|
||||
public int $swarmReplicas;
|
||||
|
||||
protected $rules = [
|
||||
'application.swarm_replicas' => 'required',
|
||||
'application.swarm_placement_constraints' => 'nullable',
|
||||
'application.settings.is_swarm_only_worker_nodes' => 'required',
|
||||
];
|
||||
#[Validate(['nullable'])]
|
||||
public ?string $swarmPlacementConstraints = null;
|
||||
|
||||
#[Validate('required')]
|
||||
public bool $isSwarmOnlyWorkerNodes;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if ($this->application->swarm_placement_constraints) {
|
||||
$this->swarm_placement_constraints = base64_decode($this->application->swarm_placement_constraints);
|
||||
try {
|
||||
$this->syncData();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->application->swarm_replicas = $this->swarmReplicas;
|
||||
$this->application->swarm_placement_constraints = $this->swarmPlacementConstraints ? base64_encode($this->swarmPlacementConstraints) : null;
|
||||
$this->application->settings->is_swarm_only_worker_nodes = $this->isSwarmOnlyWorkerNodes;
|
||||
$this->application->save();
|
||||
$this->application->settings->save();
|
||||
} else {
|
||||
$this->swarmReplicas = $this->application->swarm_replicas;
|
||||
if ($this->application->swarm_placement_constraints) {
|
||||
$this->swarmPlacementConstraints = base64_decode($this->application->swarm_placement_constraints);
|
||||
} else {
|
||||
$this->swarmPlacementConstraints = null;
|
||||
}
|
||||
$this->isSwarmOnlyWorkerNodes = $this->application->settings->is_swarm_only_worker_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->application->settings->save();
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Swarm settings updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
@@ -38,14 +61,7 @@ class Swarm extends Component
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
if ($this->swarm_placement_constraints) {
|
||||
$this->application->swarm_placement_constraints = base64_encode($this->swarm_placement_constraints);
|
||||
} else {
|
||||
$this->application->swarm_placement_constraints = null;
|
||||
}
|
||||
$this->application->save();
|
||||
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Swarm settings updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
|
||||
@@ -4,56 +4,87 @@ namespace App\Livewire\Project\Database;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
use Spatie\Url\Url;
|
||||
|
||||
class BackupEdit extends Component
|
||||
{
|
||||
public ?ScheduledDatabaseBackup $backup;
|
||||
public ScheduledDatabaseBackup $backup;
|
||||
|
||||
#[Locked]
|
||||
public $s3s;
|
||||
|
||||
#[Locked]
|
||||
public $parameters;
|
||||
|
||||
#[Validate(['required', 'boolean'])]
|
||||
public bool $delete_associated_backups_locally = false;
|
||||
|
||||
#[Validate(['required', 'boolean'])]
|
||||
public bool $delete_associated_backups_s3 = false;
|
||||
|
||||
#[Validate(['required', 'boolean'])]
|
||||
public bool $delete_associated_backups_sftp = false;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $status = null;
|
||||
|
||||
public array $parameters;
|
||||
#[Validate(['required', 'boolean'])]
|
||||
public bool $backupEnabled = false;
|
||||
|
||||
protected $rules = [
|
||||
'backup.enabled' => 'required|boolean',
|
||||
'backup.frequency' => 'required|string',
|
||||
'backup.number_of_backups_locally' => 'required|integer|min:1',
|
||||
'backup.save_s3' => 'required|boolean',
|
||||
'backup.s3_storage_id' => 'nullable|integer',
|
||||
'backup.databases_to_backup' => 'nullable',
|
||||
'backup.dump_all' => 'required|boolean',
|
||||
];
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $frequency = '';
|
||||
|
||||
protected $validationAttributes = [
|
||||
'backup.enabled' => 'Enabled',
|
||||
'backup.frequency' => 'Frequency',
|
||||
'backup.number_of_backups_locally' => 'Number of Backups Locally',
|
||||
'backup.save_s3' => 'Save to S3',
|
||||
'backup.s3_storage_id' => 'S3 Storage',
|
||||
'backup.databases_to_backup' => 'Databases to Backup',
|
||||
'backup.dump_all' => 'Backup All Databases',
|
||||
];
|
||||
#[Validate(['required', 'integer', 'min:1'])]
|
||||
public int $numberOfBackupsLocally = 1;
|
||||
|
||||
protected $messages = [
|
||||
'backup.s3_storage_id' => 'Select a S3 Storage',
|
||||
];
|
||||
#[Validate(['required', 'boolean'])]
|
||||
public bool $saveS3 = false;
|
||||
|
||||
#[Validate(['nullable', 'integer'])]
|
||||
public ?int $s3StorageId = 1;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $databasesToBackup = null;
|
||||
|
||||
#[Validate(['required', 'boolean'])]
|
||||
public bool $dumpAll = false;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
if (is_null(data_get($this->backup, 's3_storage_id'))) {
|
||||
data_set($this->backup, 's3_storage_id', 'default');
|
||||
try {
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->syncData();
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->customValidate();
|
||||
$this->backup->enabled = $this->backupEnabled;
|
||||
$this->backup->frequency = $this->frequency;
|
||||
$this->backup->number_of_backups_locally = $this->numberOfBackupsLocally;
|
||||
$this->backup->save_s3 = $this->saveS3;
|
||||
$this->backup->s3_storage_id = $this->s3StorageId;
|
||||
$this->backup->databases_to_backup = $this->databasesToBackup;
|
||||
$this->backup->dump_all = $this->dumpAll;
|
||||
$this->backup->save();
|
||||
} else {
|
||||
$this->backupEnabled = $this->backup->enabled;
|
||||
$this->frequency = $this->backup->frequency;
|
||||
$this->numberOfBackupsLocally = $this->backup->number_of_backups_locally;
|
||||
$this->saveS3 = $this->backup->save_s3;
|
||||
$this->s3StorageId = $this->backup->s3_storage_id;
|
||||
$this->databasesToBackup = $this->backup->databases_to_backup;
|
||||
$this->dumpAll = $this->backup->dump_all;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,16 +127,14 @@ class BackupEdit extends Component
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->custom_validate();
|
||||
$this->backup->save();
|
||||
$this->backup->refresh();
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Backup updated successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->dispatch('error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function custom_validate()
|
||||
private function customValidate()
|
||||
{
|
||||
if (! is_numeric($this->backup->s3_storage_id)) {
|
||||
$this->backup->s3_storage_id = null;
|
||||
@@ -120,19 +149,14 @@ class BackupEdit extends Component
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->custom_validate();
|
||||
if ($this->backup->databases_to_backup === '' || $this->backup->databases_to_backup === null) {
|
||||
$this->backup->databases_to_backup = null;
|
||||
}
|
||||
$this->backup->save();
|
||||
$this->backup->refresh();
|
||||
$this->dispatch('success', 'Backup updated successfully');
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Backup updated successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->dispatch('error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteAssociatedBackupsLocally()
|
||||
private function deleteAssociatedBackupsLocally()
|
||||
{
|
||||
$executions = $this->backup->executions;
|
||||
$backupFolder = null;
|
||||
@@ -152,17 +176,17 @@ class BackupEdit extends Component
|
||||
$execution->delete();
|
||||
}
|
||||
|
||||
if ($backupFolder) {
|
||||
if (str($backupFolder)->isNotEmpty()) {
|
||||
$this->deleteEmptyBackupFolder($backupFolder, $server);
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteAssociatedBackupsS3()
|
||||
private function deleteAssociatedBackupsS3()
|
||||
{
|
||||
//Add function to delete backups from S3
|
||||
}
|
||||
|
||||
public function deleteAssociatedBackupsSftp()
|
||||
private function deleteAssociatedBackupsSftp()
|
||||
{
|
||||
//Add function to delete backups from SFTP
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class General extends Component
|
||||
@@ -15,54 +17,106 @@ class General extends Component
|
||||
|
||||
public StandaloneClickhouse $database;
|
||||
|
||||
public ?string $db_url = null;
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $name;
|
||||
|
||||
public ?string $db_url_public = null;
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $description = null;
|
||||
|
||||
protected $listeners = ['refresh'];
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $clickhouseAdminUser;
|
||||
|
||||
protected $rules = [
|
||||
'database.name' => 'required',
|
||||
'database.description' => 'nullable',
|
||||
'database.clickhouse_admin_user' => 'required',
|
||||
'database.clickhouse_admin_password' => 'required',
|
||||
'database.image' => 'required',
|
||||
'database.ports_mappings' => 'nullable',
|
||||
'database.is_public' => 'nullable|boolean',
|
||||
'database.public_port' => 'nullable|integer',
|
||||
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||
'database.custom_docker_run_options' => 'nullable',
|
||||
];
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $clickhouseAdminPassword;
|
||||
|
||||
protected $validationAttributes = [
|
||||
'database.name' => 'Name',
|
||||
'database.description' => 'Description',
|
||||
'database.clickhouse_admin_user' => 'Postgres User',
|
||||
'database.clickhouse_admin_password' => 'Postgres Password',
|
||||
'database.image' => 'Image',
|
||||
'database.ports_mappings' => 'Port Mapping',
|
||||
'database.is_public' => 'Is Public',
|
||||
'database.public_port' => 'Public Port',
|
||||
'database.custom_docker_run_options' => 'Custom Docker Run Options',
|
||||
];
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $image;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $portsMappings = null;
|
||||
|
||||
#[Validate(['nullable', 'boolean'])]
|
||||
public ?bool $isPublic = null;
|
||||
|
||||
#[Validate(['nullable', 'integer'])]
|
||||
public ?int $publicPort = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $customDockerRunOptions = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $dbUrl = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $dbUrlPublic = null;
|
||||
|
||||
#[Validate(['nullable', 'boolean'])]
|
||||
public bool $isLogDrainEnabled = false;
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$teamId = Auth::user()->currentTeam()->id;
|
||||
|
||||
return [
|
||||
"echo-private:team.{$teamId},DatabaseProxyStopped" => 'databaseProxyStopped',
|
||||
];
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->db_url = $this->database->internal_db_url;
|
||||
$this->db_url_public = $this->database->external_db_url;
|
||||
$this->server = data_get($this->database, 'destination.server');
|
||||
try {
|
||||
$this->syncData();
|
||||
$this->server = data_get($this->database, 'destination.server');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->database->name = $this->name;
|
||||
$this->database->description = $this->description;
|
||||
$this->database->clickhouse_admin_user = $this->clickhouseAdminUser;
|
||||
$this->database->clickhouse_admin_password = $this->clickhouseAdminPassword;
|
||||
$this->database->image = $this->image;
|
||||
$this->database->ports_mappings = $this->portsMappings;
|
||||
$this->database->is_public = $this->isPublic;
|
||||
$this->database->public_port = $this->publicPort;
|
||||
$this->database->custom_docker_run_options = $this->customDockerRunOptions;
|
||||
$this->database->is_log_drain_enabled = $this->isLogDrainEnabled;
|
||||
$this->database->save();
|
||||
|
||||
$this->dbUrl = $this->database->internal_db_url;
|
||||
$this->dbUrlPublic = $this->database->external_db_url;
|
||||
} else {
|
||||
$this->name = $this->database->name;
|
||||
$this->description = $this->database->description;
|
||||
$this->clickhouseAdminUser = $this->database->clickhouse_admin_user;
|
||||
$this->clickhouseAdminPassword = $this->database->clickhouse_admin_password;
|
||||
$this->image = $this->database->image;
|
||||
$this->portsMappings = $this->database->ports_mappings;
|
||||
$this->isPublic = $this->database->is_public;
|
||||
$this->publicPort = $this->database->public_port;
|
||||
$this->customDockerRunOptions = $this->database->custom_docker_run_options;
|
||||
$this->isLogDrainEnabled = $this->database->is_log_drain_enabled;
|
||||
$this->dbUrl = $this->database->internal_db_url;
|
||||
$this->dbUrlPublic = $this->database->external_db_url;
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveAdvanced()
|
||||
{
|
||||
try {
|
||||
if (! $this->server->isLogDrainEnabled()) {
|
||||
$this->database->is_log_drain_enabled = false;
|
||||
$this->isLogDrainEnabled = false;
|
||||
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||
|
||||
return;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->syncData(true);
|
||||
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||
} catch (Exception $e) {
|
||||
@@ -73,16 +127,16 @@ class General extends Component
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
if ($this->database->is_public && ! $this->database->public_port) {
|
||||
if ($this->isPublic && ! $this->publicPort) {
|
||||
$this->dispatch('error', 'Public port is required.');
|
||||
$this->database->is_public = false;
|
||||
$this->isPublic = false;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($this->database->is_public) {
|
||||
if ($this->isPublic) {
|
||||
if (! str($this->database->status)->startsWith('running')) {
|
||||
$this->dispatch('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
$this->isPublic = false;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -92,28 +146,28 @@ class General extends Component
|
||||
StopDatabaseProxy::run($this->database);
|
||||
$this->dispatch('success', 'Database is no longer publicly accessible.');
|
||||
}
|
||||
$this->db_url_public = $this->database->external_db_url;
|
||||
$this->database->save();
|
||||
$this->dbUrlPublic = $this->database->external_db_url;
|
||||
$this->syncData(true);
|
||||
} catch (\Throwable $e) {
|
||||
$this->database->is_public = ! $this->database->is_public;
|
||||
$this->isPublic = ! $this->isPublic;
|
||||
$this->syncData(true);
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function refresh(): void
|
||||
public function databaseProxyStopped()
|
||||
{
|
||||
$this->database->refresh();
|
||||
$this->syncData();
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
if (str($this->database->public_port)->isEmpty()) {
|
||||
$this->database->public_port = null;
|
||||
if (str($this->publicPort)->isEmpty()) {
|
||||
$this->publicPort = null;
|
||||
}
|
||||
$this->validate();
|
||||
$this->database->save();
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
|
||||
@@ -4,59 +4,62 @@ namespace App\Livewire\Project\Database;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class CreateScheduledBackup extends Component
|
||||
{
|
||||
public $database;
|
||||
|
||||
#[Validate(['required', 'string'])]
|
||||
public $frequency;
|
||||
|
||||
#[Validate(['required', 'boolean'])]
|
||||
public bool $saveToS3 = false;
|
||||
|
||||
#[Locked]
|
||||
public $database;
|
||||
|
||||
public bool $enabled = true;
|
||||
|
||||
public bool $save_s3 = false;
|
||||
#[Validate(['nullable', 'integer'])]
|
||||
public ?int $s3StorageId = null;
|
||||
|
||||
public $s3_storage_id;
|
||||
|
||||
public Collection $s3s;
|
||||
|
||||
protected $rules = [
|
||||
'frequency' => 'required|string',
|
||||
'save_s3' => 'required|boolean',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'frequency' => 'Backup Frequency',
|
||||
'save_s3' => 'Save to S3',
|
||||
];
|
||||
public Collection $definedS3s;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->s3s = currentTeam()->s3s;
|
||||
if ($this->s3s->count() > 0) {
|
||||
$this->s3_storage_id = $this->s3s->first()->id;
|
||||
try {
|
||||
$this->definedS3s = currentTeam()->s3s;
|
||||
if ($this->definedS3s->count() > 0) {
|
||||
$this->s3StorageId = $this->definedS3s->first()->id;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit(): void
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
|
||||
$isValid = validate_cron_expression($this->frequency);
|
||||
if (! $isValid) {
|
||||
$this->dispatch('error', 'Invalid Cron / Human expression.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'enabled' => true,
|
||||
'frequency' => $this->frequency,
|
||||
'save_s3' => $this->save_s3,
|
||||
's3_storage_id' => $this->s3_storage_id,
|
||||
'save_s3' => $this->saveToS3,
|
||||
's3_storage_id' => $this->s3StorageId,
|
||||
'database_id' => $this->database->id,
|
||||
'database_type' => $this->database->getMorphClass(),
|
||||
'team_id' => currentTeam()->id,
|
||||
];
|
||||
|
||||
if ($this->database->type() === 'standalone-postgresql') {
|
||||
$payload['databases_to_backup'] = $this->database->postgres_db;
|
||||
} elseif ($this->database->type() === 'standalone-mysql') {
|
||||
@@ -71,11 +74,11 @@ class CreateScheduledBackup extends Component
|
||||
} else {
|
||||
$this->dispatch('refreshScheduledBackups');
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
handleError($e, $this);
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->frequency = '';
|
||||
$this->save_s3 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,60 +7,111 @@ use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class General extends Component
|
||||
{
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public Server $server;
|
||||
|
||||
public StandaloneDragonfly $database;
|
||||
|
||||
public ?string $db_url = null;
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $name;
|
||||
|
||||
public ?string $db_url_public = null;
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $description = null;
|
||||
|
||||
protected $rules = [
|
||||
'database.name' => 'required',
|
||||
'database.description' => 'nullable',
|
||||
'database.dragonfly_password' => 'required',
|
||||
'database.image' => 'required',
|
||||
'database.ports_mappings' => 'nullable',
|
||||
'database.is_public' => 'nullable|boolean',
|
||||
'database.public_port' => 'nullable|integer',
|
||||
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||
'database.custom_docker_run_options' => 'nullable',
|
||||
];
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $dragonflyPassword;
|
||||
|
||||
protected $validationAttributes = [
|
||||
'database.name' => 'Name',
|
||||
'database.description' => 'Description',
|
||||
'database.dragonfly_password' => 'Redis Password',
|
||||
'database.image' => 'Image',
|
||||
'database.ports_mappings' => 'Port Mapping',
|
||||
'database.is_public' => 'Is Public',
|
||||
'database.public_port' => 'Public Port',
|
||||
'database.custom_docker_run_options' => 'Custom Docker Run Options',
|
||||
];
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $image;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $portsMappings = null;
|
||||
|
||||
#[Validate(['nullable', 'boolean'])]
|
||||
public ?bool $isPublic = null;
|
||||
|
||||
#[Validate(['nullable', 'integer'])]
|
||||
public ?int $publicPort = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $customDockerRunOptions = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $dbUrl = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $dbUrlPublic = null;
|
||||
|
||||
#[Validate(['nullable', 'boolean'])]
|
||||
public bool $isLogDrainEnabled = false;
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$teamId = Auth::user()->currentTeam()->id;
|
||||
|
||||
return [
|
||||
"echo-private:team.{$teamId},DatabaseProxyStopped" => 'databaseProxyStopped',
|
||||
];
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->db_url = $this->database->internal_db_url;
|
||||
$this->db_url_public = $this->database->external_db_url;
|
||||
$this->server = data_get($this->database, 'destination.server');
|
||||
try {
|
||||
$this->syncData();
|
||||
$this->server = data_get($this->database, 'destination.server');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->database->name = $this->name;
|
||||
$this->database->description = $this->description;
|
||||
$this->database->dragonfly_password = $this->dragonflyPassword;
|
||||
$this->database->image = $this->image;
|
||||
$this->database->ports_mappings = $this->portsMappings;
|
||||
$this->database->is_public = $this->isPublic;
|
||||
$this->database->public_port = $this->publicPort;
|
||||
$this->database->custom_docker_run_options = $this->customDockerRunOptions;
|
||||
$this->database->is_log_drain_enabled = $this->isLogDrainEnabled;
|
||||
$this->database->save();
|
||||
|
||||
$this->dbUrl = $this->database->internal_db_url;
|
||||
$this->dbUrlPublic = $this->database->external_db_url;
|
||||
} else {
|
||||
$this->name = $this->database->name;
|
||||
$this->description = $this->database->description;
|
||||
$this->dragonflyPassword = $this->database->dragonfly_password;
|
||||
$this->image = $this->database->image;
|
||||
$this->portsMappings = $this->database->ports_mappings;
|
||||
$this->isPublic = $this->database->is_public;
|
||||
$this->publicPort = $this->database->public_port;
|
||||
$this->customDockerRunOptions = $this->database->custom_docker_run_options;
|
||||
$this->isLogDrainEnabled = $this->database->is_log_drain_enabled;
|
||||
$this->dbUrl = $this->database->internal_db_url;
|
||||
$this->dbUrlPublic = $this->database->external_db_url;
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveAdvanced()
|
||||
{
|
||||
try {
|
||||
if (! $this->server->isLogDrainEnabled()) {
|
||||
$this->database->is_log_drain_enabled = false;
|
||||
$this->isLogDrainEnabled = false;
|
||||
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||
|
||||
return;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->syncData(true);
|
||||
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||
} catch (Exception $e) {
|
||||
@@ -68,11 +119,50 @@ class General extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
if ($this->isPublic && ! $this->publicPort) {
|
||||
$this->dispatch('error', 'Public port is required.');
|
||||
$this->isPublic = false;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($this->isPublic) {
|
||||
if (! str($this->database->status)->startsWith('running')) {
|
||||
$this->dispatch('error', 'Database must be started to be publicly accessible.');
|
||||
$this->isPublic = false;
|
||||
|
||||
return;
|
||||
}
|
||||
StartDatabaseProxy::run($this->database);
|
||||
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
StopDatabaseProxy::run($this->database);
|
||||
$this->dispatch('success', 'Database is no longer publicly accessible.');
|
||||
}
|
||||
$this->dbUrlPublic = $this->database->external_db_url;
|
||||
$this->syncData(true);
|
||||
} catch (\Throwable $e) {
|
||||
$this->isPublic = ! $this->isPublic;
|
||||
$this->syncData(true);
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function databaseProxyStopped()
|
||||
{
|
||||
$this->syncData();
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->database->save();
|
||||
if (str($this->publicPort)->isEmpty()) {
|
||||
$this->publicPort = null;
|
||||
}
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
@@ -84,45 +174,4 @@ class General extends Component
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
if ($this->database->is_public && ! $this->database->public_port) {
|
||||
$this->dispatch('error', 'Public port is required.');
|
||||
$this->database->is_public = false;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($this->database->is_public) {
|
||||
if (! str($this->database->status)->startsWith('running')) {
|
||||
$this->dispatch('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
|
||||
return;
|
||||
}
|
||||
StartDatabaseProxy::run($this->database);
|
||||
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
StopDatabaseProxy::run($this->database);
|
||||
$this->dispatch('success', 'Database is no longer publicly accessible.');
|
||||
}
|
||||
$this->db_url_public = $this->database->external_db_url;
|
||||
$this->database->save();
|
||||
} catch (\Throwable $e) {
|
||||
$this->database->is_public = ! $this->database->is_public;
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function refresh(): void
|
||||
{
|
||||
$this->database->refresh();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.database.dragonfly.general');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Actions\Database\RestartDatabase;
|
||||
use App\Actions\Database\StartDatabase;
|
||||
use App\Actions\Database\StopDatabase;
|
||||
use App\Actions\Docker\GetContainersStatus;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Component;
|
||||
|
||||
class Heading extends Component
|
||||
@@ -18,7 +19,7 @@ class Heading extends Component
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$userId = auth()->user()->id;
|
||||
$userId = Auth::id();
|
||||
|
||||
return [
|
||||
"echo-private:user.{$userId},DatabaseStatusChanged" => 'activityFinished',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Livewire\Project\Database;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -46,7 +47,7 @@ class Import extends Component
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$userId = auth()->user()->id;
|
||||
$userId = Auth::id();
|
||||
|
||||
return [
|
||||
"echo-private:user.{$userId},DatabaseStatusChanged" => '$refresh',
|
||||
|
||||
@@ -3,39 +3,39 @@
|
||||
namespace App\Livewire\Project\Database;
|
||||
|
||||
use Exception;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class InitScript extends Component
|
||||
{
|
||||
#[Locked]
|
||||
public array $script;
|
||||
|
||||
#[Locked]
|
||||
public int $index;
|
||||
|
||||
public ?string $filename;
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $filename = null;
|
||||
|
||||
public ?string $content;
|
||||
|
||||
protected $rules = [
|
||||
'filename' => 'required|string',
|
||||
'content' => 'required|string',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'filename' => 'Filename',
|
||||
'content' => 'Content',
|
||||
];
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $content = null;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->index = data_get($this->script, 'index');
|
||||
$this->filename = data_get($this->script, 'filename');
|
||||
$this->content = data_get($this->script, 'content');
|
||||
try {
|
||||
$this->index = data_get($this->script, 'index');
|
||||
$this->filename = data_get($this->script, 'filename');
|
||||
$this->content = data_get($this->script, 'content');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
try {
|
||||
$this->validate();
|
||||
$this->script['index'] = $this->index;
|
||||
$this->script['content'] = $this->content;
|
||||
$this->script['filename'] = $this->filename;
|
||||
@@ -47,6 +47,10 @@ class InitScript extends Component
|
||||
|
||||
public function delete()
|
||||
{
|
||||
$this->dispatch('delete_init_script', $this->script);
|
||||
try {
|
||||
$this->dispatch('delete_init_script', $this->script);
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,62 +7,116 @@ use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class General extends Component
|
||||
{
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public Server $server;
|
||||
|
||||
public StandaloneKeydb $database;
|
||||
|
||||
public ?string $db_url = null;
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $name;
|
||||
|
||||
public ?string $db_url_public = null;
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $description = null;
|
||||
|
||||
protected $rules = [
|
||||
'database.name' => 'required',
|
||||
'database.description' => 'nullable',
|
||||
'database.keydb_conf' => 'nullable',
|
||||
'database.keydb_password' => 'required',
|
||||
'database.image' => 'required',
|
||||
'database.ports_mappings' => 'nullable',
|
||||
'database.is_public' => 'nullable|boolean',
|
||||
'database.public_port' => 'nullable|integer',
|
||||
'database.is_log_drain_enabled' => 'nullable|boolean',
|
||||
'database.custom_docker_run_options' => 'nullable',
|
||||
];
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $keydbConf = null;
|
||||
|
||||
protected $validationAttributes = [
|
||||
'database.name' => 'Name',
|
||||
'database.description' => 'Description',
|
||||
'database.keydb_conf' => 'Redis Configuration',
|
||||
'database.keydb_password' => 'Redis Password',
|
||||
'database.image' => 'Image',
|
||||
'database.ports_mappings' => 'Port Mapping',
|
||||
'database.is_public' => 'Is Public',
|
||||
'database.public_port' => 'Public Port',
|
||||
'database.custom_docker_run_options' => 'Custom Docker Run Options',
|
||||
];
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $keydbPassword;
|
||||
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $image;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $portsMappings = null;
|
||||
|
||||
#[Validate(['nullable', 'boolean'])]
|
||||
public ?bool $isPublic = null;
|
||||
|
||||
#[Validate(['nullable', 'integer'])]
|
||||
public ?int $publicPort = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $customDockerRunOptions = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $dbUrl = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $dbUrlPublic = null;
|
||||
|
||||
#[Validate(['nullable', 'boolean'])]
|
||||
public bool $isLogDrainEnabled = false;
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$teamId = Auth::user()->currentTeam()->id;
|
||||
|
||||
return [
|
||||
"echo-private:team.{$teamId},DatabaseProxyStopped" => 'databaseProxyStopped',
|
||||
];
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->db_url = $this->database->internal_db_url;
|
||||
$this->db_url_public = $this->database->external_db_url;
|
||||
$this->server = data_get($this->database, 'destination.server');
|
||||
try {
|
||||
$this->syncData();
|
||||
$this->server = data_get($this->database, 'destination.server');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->database->name = $this->name;
|
||||
$this->database->description = $this->description;
|
||||
$this->database->keydb_conf = $this->keydbConf;
|
||||
$this->database->keydb_password = $this->keydbPassword;
|
||||
$this->database->image = $this->image;
|
||||
$this->database->ports_mappings = $this->portsMappings;
|
||||
$this->database->is_public = $this->isPublic;
|
||||
$this->database->public_port = $this->publicPort;
|
||||
$this->database->custom_docker_run_options = $this->customDockerRunOptions;
|
||||
$this->database->is_log_drain_enabled = $this->isLogDrainEnabled;
|
||||
$this->database->save();
|
||||
|
||||
$this->dbUrl = $this->database->internal_db_url;
|
||||
$this->dbUrlPublic = $this->database->external_db_url;
|
||||
} else {
|
||||
$this->name = $this->database->name;
|
||||
$this->description = $this->database->description;
|
||||
$this->keydbConf = $this->database->keydb_conf;
|
||||
$this->keydbPassword = $this->database->keydb_password;
|
||||
$this->image = $this->database->image;
|
||||
$this->portsMappings = $this->database->ports_mappings;
|
||||
$this->isPublic = $this->database->is_public;
|
||||
$this->publicPort = $this->database->public_port;
|
||||
$this->customDockerRunOptions = $this->database->custom_docker_run_options;
|
||||
$this->isLogDrainEnabled = $this->database->is_log_drain_enabled;
|
||||
$this->dbUrl = $this->database->internal_db_url;
|
||||
$this->dbUrlPublic = $this->database->external_db_url;
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveAdvanced()
|
||||
{
|
||||
try {
|
||||
if (! $this->server->isLogDrainEnabled()) {
|
||||
$this->database->is_log_drain_enabled = false;
|
||||
$this->isLogDrainEnabled = false;
|
||||
$this->dispatch('error', 'Log drain is not enabled on the server. Please enable it first.');
|
||||
|
||||
return;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->syncData(true);
|
||||
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||
} catch (Exception $e) {
|
||||
@@ -70,14 +124,50 @@ class General extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
if ($this->isPublic && ! $this->publicPort) {
|
||||
$this->dispatch('error', 'Public port is required.');
|
||||
$this->isPublic = false;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($this->isPublic) {
|
||||
if (! str($this->database->status)->startsWith('running')) {
|
||||
$this->dispatch('error', 'Database must be started to be publicly accessible.');
|
||||
$this->isPublic = false;
|
||||
|
||||
return;
|
||||
}
|
||||
StartDatabaseProxy::run($this->database);
|
||||
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
StopDatabaseProxy::run($this->database);
|
||||
$this->dispatch('success', 'Database is no longer publicly accessible.');
|
||||
}
|
||||
$this->dbUrlPublic = $this->database->external_db_url;
|
||||
$this->syncData(true);
|
||||
} catch (\Throwable $e) {
|
||||
$this->isPublic = ! $this->isPublic;
|
||||
$this->syncData(true);
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function databaseProxyStopped()
|
||||
{
|
||||
$this->syncData();
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
if ($this->database->keydb_conf === '') {
|
||||
$this->database->keydb_conf = null;
|
||||
if (str($this->publicPort)->isEmpty()) {
|
||||
$this->publicPort = null;
|
||||
}
|
||||
$this->database->save();
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
@@ -89,45 +179,4 @@ class General extends Component
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
if ($this->database->is_public && ! $this->database->public_port) {
|
||||
$this->dispatch('error', 'Public port is required.');
|
||||
$this->database->is_public = false;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($this->database->is_public) {
|
||||
if (! str($this->database->status)->startsWith('running')) {
|
||||
$this->dispatch('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
|
||||
return;
|
||||
}
|
||||
StartDatabaseProxy::run($this->database);
|
||||
$this->dispatch('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
StopDatabaseProxy::run($this->database);
|
||||
$this->dispatch('success', 'Database is no longer publicly accessible.');
|
||||
}
|
||||
$this->db_url_public = $this->database->external_db_url;
|
||||
$this->database->save();
|
||||
} catch (\Throwable $e) {
|
||||
$this->database->is_public = ! $this->database->is_public;
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function refresh(): void
|
||||
{
|
||||
$this->database->refresh();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.database.keydb.general');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,6 @@ class DeleteEnvironment extends Component
|
||||
return redirect()->route('project.show', parameters: ['project_uuid' => $this->parameters['project_uuid']]);
|
||||
}
|
||||
|
||||
return $this->dispatch('error', 'Environment has defined resources, please delete them first.');
|
||||
return $this->dispatch('error', "<strong>Environment {$environment->name}</strong> has defined resources, please delete them first.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +27,12 @@ class DeleteProject extends Component
|
||||
'project_id' => 'required|int',
|
||||
]);
|
||||
$project = Project::findOrFail($this->project_id);
|
||||
if ($project->applications->count() > 0) {
|
||||
return $this->dispatch('error', 'Project has resources defined, please delete them first.');
|
||||
}
|
||||
$project->delete();
|
||||
if ($project->isEmpty()) {
|
||||
$project->delete();
|
||||
|
||||
return redirect()->route('project.index');
|
||||
return redirect()->route('project.index');
|
||||
}
|
||||
|
||||
return $this->dispatch('error', "<strong>Project {$project->name}</strong> has resources defined, please delete them first.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
namespace App\Livewire\Project;
|
||||
|
||||
use App\Models\Project;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Edit extends Component
|
||||
{
|
||||
public Project $project;
|
||||
|
||||
#[Rule(['required', 'string', 'min:3', 'max:255'])]
|
||||
#[Validate(['required', 'string', 'min:3', 'max:255'])]
|
||||
public string $name;
|
||||
|
||||
#[Rule(['nullable', 'string', 'max:255'])]
|
||||
#[Validate(['nullable', 'string', 'max:255'])]
|
||||
public ?string $description = null;
|
||||
|
||||
public function mount(string $project_uuid)
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Livewire\Project;
|
||||
use App\Models\Application;
|
||||
use App\Models\Project;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class EnvironmentEdit extends Component
|
||||
@@ -17,10 +17,10 @@ class EnvironmentEdit extends Component
|
||||
#[Locked]
|
||||
public $environment;
|
||||
|
||||
#[Rule(['required', 'string', 'min:3', 'max:255'])]
|
||||
#[Validate(['required', 'string', 'min:3', 'max:255'])]
|
||||
public string $name;
|
||||
|
||||
#[Rule(['nullable', 'string', 'max:255'])]
|
||||
#[Validate(['nullable', 'string', 'max:255'])]
|
||||
public ?string $description = null;
|
||||
|
||||
public function mount(string $project_uuid, string $environment_name)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Actions\Docker\GetContainersStatus;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Component;
|
||||
|
||||
class Configuration extends Component
|
||||
@@ -20,7 +21,7 @@ class Configuration extends Component
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$userId = auth()->user()->id;
|
||||
$userId = Auth::id();
|
||||
|
||||
return [
|
||||
"echo-private:user.{$userId},ServiceStatusChanged" => 'check_status',
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Actions\Service\StopService;
|
||||
use App\Actions\Shared\PullImage;
|
||||
use App\Events\ServiceStatusChanged;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Component;
|
||||
use Spatie\Activitylog\Models\Activity;
|
||||
|
||||
@@ -34,7 +35,7 @@ class Navbar extends Component
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$userId = auth()->user()->id;
|
||||
$userId = Auth::id();
|
||||
|
||||
return [
|
||||
"echo-private:user.{$userId},ServiceStatusChanged" => 'serviceStarted',
|
||||
|
||||
@@ -88,6 +88,9 @@ class Show extends Component
|
||||
public function lock()
|
||||
{
|
||||
$this->env->is_shown_once = true;
|
||||
if ($this->isSharedVariable) {
|
||||
unset($this->env->is_required);
|
||||
}
|
||||
$this->serialize();
|
||||
$this->env->save();
|
||||
$this->checkEnvs();
|
||||
|
||||
@@ -168,18 +168,42 @@ class ExecuteContainerCommand extends Component
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Validate container name format
|
||||
if (! preg_match('/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/', $this->selected_container)) {
|
||||
throw new \InvalidArgumentException('Invalid container name format');
|
||||
}
|
||||
|
||||
// Verify container exists in our allowed list
|
||||
$container = collect($this->containers)->firstWhere('container.Names', $this->selected_container);
|
||||
if (is_null($container)) {
|
||||
throw new \RuntimeException('Container not found.');
|
||||
}
|
||||
$server = data_get($this->container, 'server');
|
||||
|
||||
// Verify server ownership and status
|
||||
$server = data_get($container, 'server');
|
||||
if (! $server || ! $server instanceof Server) {
|
||||
throw new \RuntimeException('Invalid server configuration.');
|
||||
}
|
||||
|
||||
if ($server->isForceDisabled()) {
|
||||
throw new \RuntimeException('Server is disabled.');
|
||||
}
|
||||
|
||||
// Additional ownership verification based on resource type
|
||||
$resourceServer = match ($this->type) {
|
||||
'application' => $this->resource->destination->server,
|
||||
'database' => $this->resource->destination->server,
|
||||
'service' => $this->resource->server,
|
||||
default => throw new \RuntimeException('Invalid resource type.')
|
||||
};
|
||||
|
||||
if ($server->id !== $resourceServer->id && ! $this->resource->additional_servers->contains('id', $server->id)) {
|
||||
throw new \RuntimeException('Server ownership verification failed.');
|
||||
}
|
||||
|
||||
$this->dispatch(
|
||||
'send-terminal-command',
|
||||
isset($container),
|
||||
true,
|
||||
data_get($container, 'container.Names'),
|
||||
data_get($container, 'server.uuid')
|
||||
);
|
||||
|
||||
@@ -2,23 +2,60 @@
|
||||
|
||||
namespace App\Livewire\Project\Shared\ScheduledTask;
|
||||
|
||||
use App\Models\ScheduledTask;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Component;
|
||||
|
||||
class Executions extends Component
|
||||
{
|
||||
public $executions = [];
|
||||
public ScheduledTask $task;
|
||||
|
||||
public $selectedKey;
|
||||
#[Locked]
|
||||
public int $taskId;
|
||||
|
||||
public $task;
|
||||
#[Locked]
|
||||
public Collection $executions;
|
||||
|
||||
#[Locked]
|
||||
public ?int $selectedKey = null;
|
||||
|
||||
#[Locked]
|
||||
public ?string $serverTimezone = null;
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$teamId = Auth::user()->currentTeam()->id;
|
||||
|
||||
return [
|
||||
'selectTask',
|
||||
"echo-private:team.{$teamId},ScheduledTaskDone" => 'refreshExecutions',
|
||||
];
|
||||
}
|
||||
|
||||
public function mount($taskId)
|
||||
{
|
||||
try {
|
||||
$this->taskId = $taskId;
|
||||
$this->task = ScheduledTask::findOrFail($taskId);
|
||||
$this->executions = $this->task->executions()->take(20)->get();
|
||||
$this->serverTimezone = data_get($this->task, 'application.destination.server.settings.server_timezone');
|
||||
if (! $this->serverTimezone) {
|
||||
$this->serverTimezone = data_get($this->task, 'service.destination.server.settings.server_timezone');
|
||||
}
|
||||
if (! $this->serverTimezone) {
|
||||
$this->serverTimezone = 'UTC';
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function refreshExecutions(): void
|
||||
{
|
||||
$this->executions = $this->task->executions()->take(20)->get();
|
||||
}
|
||||
|
||||
public function selectTask($key): void
|
||||
{
|
||||
if ($key == $this->selectedKey) {
|
||||
@@ -29,38 +66,9 @@ class Executions extends Component
|
||||
$this->selectedKey = $key;
|
||||
}
|
||||
|
||||
public function server()
|
||||
{
|
||||
if (! $this->task) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->task->application) {
|
||||
if ($this->task->application->destination && $this->task->application->destination->server) {
|
||||
return $this->task->application->destination->server;
|
||||
}
|
||||
} elseif ($this->task->service) {
|
||||
if ($this->task->service->destination && $this->task->service->destination->server) {
|
||||
return $this->task->service->destination->server;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getServerTimezone()
|
||||
{
|
||||
$server = $this->server();
|
||||
if (! $server) {
|
||||
return 'UTC';
|
||||
}
|
||||
|
||||
return $server->settings->server_timezone;
|
||||
}
|
||||
|
||||
public function formatDateInServerTimezone($date)
|
||||
{
|
||||
$serverTimezone = $this->getServerTimezone();
|
||||
$serverTimezone = $this->serverTimezone;
|
||||
$dateObj = new \DateTime($date);
|
||||
try {
|
||||
$dateObj->setTimezone(new \DateTimeZone($serverTimezone));
|
||||
|
||||
@@ -2,74 +2,124 @@
|
||||
|
||||
namespace App\Livewire\Project\Shared\ScheduledTask;
|
||||
|
||||
use App\Jobs\ScheduledTaskJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\ScheduledTask as ModelsScheduledTask;
|
||||
use App\Models\ScheduledTask;
|
||||
use App\Models\Service;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
public $parameters;
|
||||
|
||||
public Application|Service $resource;
|
||||
|
||||
public ModelsScheduledTask $task;
|
||||
public ScheduledTask $task;
|
||||
|
||||
public ?string $modalId = null;
|
||||
#[Locked]
|
||||
public array $parameters;
|
||||
|
||||
#[Locked]
|
||||
public string $type;
|
||||
|
||||
public string $scheduledTaskName;
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isEnabled = false;
|
||||
|
||||
protected $rules = [
|
||||
'task.enabled' => 'required|boolean',
|
||||
'task.name' => 'required|string',
|
||||
'task.command' => 'required|string',
|
||||
'task.frequency' => 'required|string',
|
||||
'task.container' => 'nullable|string',
|
||||
];
|
||||
#[Validate(['string', 'required'])]
|
||||
public string $name;
|
||||
|
||||
protected $validationAttributes = [
|
||||
'name' => 'name',
|
||||
'command' => 'command',
|
||||
'frequency' => 'frequency',
|
||||
'container' => 'container',
|
||||
];
|
||||
#[Validate(['string', 'required'])]
|
||||
public string $command;
|
||||
|
||||
public function mount()
|
||||
#[Validate(['string', 'required'])]
|
||||
public string $frequency;
|
||||
|
||||
#[Validate(['string', 'nullable'])]
|
||||
public ?string $container = null;
|
||||
|
||||
#[Locked]
|
||||
public ?string $application_uuid;
|
||||
|
||||
#[Locked]
|
||||
public ?string $service_uuid;
|
||||
|
||||
#[Locked]
|
||||
public string $task_uuid;
|
||||
|
||||
public function mount(string $task_uuid, string $project_uuid, string $environment_name, ?string $application_uuid = null, ?string $service_uuid = null)
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
try {
|
||||
$this->task_uuid = $task_uuid;
|
||||
if ($application_uuid) {
|
||||
$this->type = 'application';
|
||||
$this->application_uuid = $application_uuid;
|
||||
$this->resource = Application::ownedByCurrentTeam()->where('uuid', $application_uuid)->firstOrFail();
|
||||
} elseif ($service_uuid) {
|
||||
$this->type = 'service';
|
||||
$this->service_uuid = $service_uuid;
|
||||
$this->resource = Service::ownedByCurrentTeam()->where('uuid', $service_uuid)->firstOrFail();
|
||||
}
|
||||
$this->parameters = [
|
||||
'environment_name' => $environment_name,
|
||||
'project_uuid' => $project_uuid,
|
||||
'application_uuid' => $application_uuid,
|
||||
'service_uuid' => $service_uuid,
|
||||
];
|
||||
|
||||
if (data_get($this->parameters, 'application_uuid')) {
|
||||
$this->type = 'application';
|
||||
$this->resource = Application::where('uuid', $this->parameters['application_uuid'])->firstOrFail();
|
||||
} elseif (data_get($this->parameters, 'service_uuid')) {
|
||||
$this->type = 'service';
|
||||
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
|
||||
$this->task = $this->resource->scheduled_tasks()->where('uuid', $task_uuid)->firstOrFail();
|
||||
$this->syncData();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
$this->modalId = new Cuid2;
|
||||
$this->task = ModelsScheduledTask::where('uuid', request()->route('task_uuid'))->first();
|
||||
$this->scheduledTaskName = $this->task->name;
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->task->enabled = $this->isEnabled;
|
||||
$this->task->name = str($this->name)->trim()->value();
|
||||
$this->task->command = str($this->command)->trim()->value();
|
||||
$this->task->frequency = str($this->frequency)->trim()->value();
|
||||
$this->task->container = str($this->container)->trim()->value();
|
||||
$this->task->save();
|
||||
} else {
|
||||
$this->isEnabled = $this->task->enabled;
|
||||
$this->name = $this->task->name;
|
||||
$this->command = $this->task->command;
|
||||
$this->frequency = $this->task->frequency;
|
||||
$this->container = $this->task->container;
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
$this->validateOnly('task.enabled');
|
||||
$this->task->save(['enabled' => $this->task->enabled]);
|
||||
$this->dispatch('success', 'Scheduled task updated.');
|
||||
$this->dispatch('refreshTasks');
|
||||
try {
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Scheduled task updated.');
|
||||
$this->refreshTasks();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
$this->task->name = str($this->task->name)->trim()->value();
|
||||
$this->task->container = str($this->task->container)->trim()->value();
|
||||
$this->task->save();
|
||||
$this->dispatch('success', 'Scheduled task updated.');
|
||||
$this->dispatch('refreshTasks');
|
||||
try {
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Scheduled task updated.');
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function refreshTasks()
|
||||
{
|
||||
try {
|
||||
$this->task->refresh();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete()
|
||||
@@ -78,12 +128,22 @@ class Show extends Component
|
||||
$this->task->delete();
|
||||
|
||||
if ($this->type === 'application') {
|
||||
return redirect()->route('project.application.configuration', $this->parameters, $this->scheduledTaskName);
|
||||
return redirect()->route('project.application.configuration', $this->parameters, $this->task->name);
|
||||
} else {
|
||||
return redirect()->route('project.service.configuration', $this->parameters, $this->scheduledTaskName);
|
||||
return redirect()->route('project.service.configuration', $this->parameters, $this->task->name);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function executeNow()
|
||||
{
|
||||
try {
|
||||
ScheduledTaskJob::dispatch($this->task);
|
||||
$this->dispatch('success', 'Scheduled task executed.');
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ class Tags extends Component
|
||||
$this->validate();
|
||||
$tags = str($this->newTags)->trim()->explode(' ');
|
||||
foreach ($tags as $tag) {
|
||||
$tag = strip_tags($tag);
|
||||
if (strlen($tag) < 2) {
|
||||
$this->dispatch('error', 'Invalid tag.', "Tag <span class='dark:text-warning'>$tag</span> is invalid. Min length is 2.");
|
||||
|
||||
@@ -65,6 +66,7 @@ class Tags extends Component
|
||||
public function addTag(string $id, string $name)
|
||||
{
|
||||
try {
|
||||
$name = strip_tags($name);
|
||||
if ($this->resource->tags()->where('id', $id)->exists()) {
|
||||
$this->dispatch('error', 'Duplicate tags.', "Tag <span class='dark:text-warning'>$name</span> already added.");
|
||||
|
||||
|
||||
@@ -29,11 +29,20 @@ class Terminal extends Component
|
||||
$server = Server::ownedByCurrentTeam()->whereUuid($serverUuid)->firstOrFail();
|
||||
|
||||
if ($isContainer) {
|
||||
// Validate container identifier format (alphanumeric, dashes, and underscores only)
|
||||
if (! preg_match('/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/', $identifier)) {
|
||||
throw new \InvalidArgumentException('Invalid container identifier format');
|
||||
}
|
||||
|
||||
// Verify container exists and belongs to the user's team
|
||||
$status = getContainerStatus($server, $identifier);
|
||||
if ($status !== 'running') {
|
||||
return;
|
||||
}
|
||||
$command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$identifier} sh -c 'PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
|
||||
|
||||
// Escape the identifier for shell usage
|
||||
$escapedIdentifier = escapeshellarg($identifier);
|
||||
$command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$escapedIdentifier} sh -c 'PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
|
||||
} else {
|
||||
$command = SshMultiplexingHelper::generateSshCommand($server, 'PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n "$SHELL" ]; then exec $SHELL; else sh; fi');
|
||||
}
|
||||
|
||||
@@ -4,17 +4,17 @@ namespace App\Livewire\Project;
|
||||
|
||||
use App\Models\Environment;
|
||||
use App\Models\Project;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
public Project $project;
|
||||
|
||||
#[Rule(['required', 'string', 'min:3'])]
|
||||
#[Validate(['required', 'string', 'min:3'])]
|
||||
public string $name;
|
||||
|
||||
#[Rule(['nullable', 'string'])]
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $description = null;
|
||||
|
||||
public function mount(string $project_uuid)
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace App\Livewire\Server;
|
||||
|
||||
use App\Jobs\DockerCleanupJob;
|
||||
use App\Models\Server;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Advanced extends Component
|
||||
@@ -13,28 +13,28 @@ class Advanced extends Component
|
||||
|
||||
public array $parameters = [];
|
||||
|
||||
#[Rule(['integer', 'min:1'])]
|
||||
#[Validate(['integer', 'min:1'])]
|
||||
public int $concurrentBuilds = 1;
|
||||
|
||||
#[Rule(['integer', 'min:1'])]
|
||||
#[Validate(['integer', 'min:1'])]
|
||||
public int $dynamicTimeout = 1;
|
||||
|
||||
#[Rule('boolean')]
|
||||
#[Validate('boolean')]
|
||||
public bool $forceDockerCleanup = false;
|
||||
|
||||
#[Rule('string')]
|
||||
#[Validate(['string', 'required'])]
|
||||
public string $dockerCleanupFrequency = '*/10 * * * *';
|
||||
|
||||
#[Rule(['integer', 'min:1', 'max:99'])]
|
||||
#[Validate(['integer', 'min:1', 'max:99'])]
|
||||
public int $dockerCleanupThreshold = 10;
|
||||
|
||||
#[Rule(['integer', 'min:1', 'max:99'])]
|
||||
#[Validate(['integer', 'min:1', 'max:99'])]
|
||||
public int $serverDiskUsageNotificationThreshold = 50;
|
||||
|
||||
#[Rule('boolean')]
|
||||
#[Validate('boolean')]
|
||||
public bool $deleteUnusedVolumes = false;
|
||||
|
||||
#[Rule('boolean')]
|
||||
#[Validate('boolean')]
|
||||
public bool $deleteUnusedNetworks = false;
|
||||
|
||||
public function mount(string $server_uuid)
|
||||
@@ -78,7 +78,6 @@ class Advanced extends Component
|
||||
try {
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Server updated.');
|
||||
// $this->dispatch('refreshServerShow');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -3,20 +3,23 @@
|
||||
namespace App\Livewire\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class CloudflareTunnels extends Component
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
#[Rule(['required', 'boolean'])]
|
||||
#[Validate(['required', 'boolean'])]
|
||||
public bool $isCloudflareTunnelsEnabled;
|
||||
|
||||
public function mount(string $server_uuid)
|
||||
{
|
||||
try {
|
||||
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
||||
if ($this->server->isLocalhost()) {
|
||||
return redirect()->route('server.show', ['server_uuid' => $server_uuid]);
|
||||
}
|
||||
$this->isCloudflareTunnelsEnabled = $this->server->settings->is_cloudflare_tunnel;
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
|
||||
@@ -19,7 +19,6 @@ class Destinations extends Component
|
||||
try {
|
||||
$this->networks = collect();
|
||||
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
||||
loggy($this->server);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -1,281 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Server;
|
||||
|
||||
use App\Actions\Server\StartSentinel;
|
||||
use App\Actions\Server\StopSentinel;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
class Form extends Component
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
public bool $isValidConnection = false;
|
||||
|
||||
public bool $isValidDocker = false;
|
||||
|
||||
public ?string $wildcard_domain = null;
|
||||
|
||||
public bool $dockerInstallationStarted = false;
|
||||
|
||||
public bool $revalidate = false;
|
||||
|
||||
public $timezones;
|
||||
|
||||
public $delete_unused_volumes = false;
|
||||
|
||||
public $delete_unused_networks = false;
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$teamId = auth()->user()->currentTeam()->id;
|
||||
|
||||
return [
|
||||
"echo-private:team.{$teamId},CloudflareTunnelConfigured" => 'cloudflareTunnelConfigured',
|
||||
'refreshServerShow' => 'serverInstalled',
|
||||
'revalidate' => '$refresh',
|
||||
];
|
||||
}
|
||||
|
||||
protected $rules = [
|
||||
'server.name' => 'required',
|
||||
'server.description' => 'nullable',
|
||||
'server.ip' => 'required',
|
||||
'server.user' => 'required',
|
||||
'server.port' => 'required',
|
||||
'wildcard_domain' => 'nullable|url',
|
||||
'server.settings.is_reachable' => 'required',
|
||||
'server.settings.is_swarm_manager' => 'required|boolean',
|
||||
'server.settings.is_swarm_worker' => 'required|boolean',
|
||||
'server.settings.is_build_server' => 'required|boolean',
|
||||
'server.settings.is_metrics_enabled' => 'required|boolean',
|
||||
'server.settings.sentinel_token' => 'required',
|
||||
'server.settings.sentinel_metrics_refresh_rate_seconds' => 'required|integer|min:1',
|
||||
'server.settings.sentinel_metrics_history_days' => 'required|integer|min:1',
|
||||
'server.settings.sentinel_push_interval_seconds' => 'required|integer|min:10',
|
||||
'server.settings.sentinel_custom_url' => 'nullable|url',
|
||||
'server.settings.is_sentinel_enabled' => 'required|boolean',
|
||||
'server.settings.is_sentinel_debug_enabled' => 'required|boolean',
|
||||
'server.settings.server_timezone' => 'required|string|timezone',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'server.name' => 'Name',
|
||||
'server.description' => 'Description',
|
||||
'server.ip' => 'IP address/Domain',
|
||||
'server.user' => 'User',
|
||||
'server.port' => 'Port',
|
||||
'server.settings.is_reachable' => 'Is reachable',
|
||||
'server.settings.is_swarm_manager' => 'Swarm Manager',
|
||||
'server.settings.is_swarm_worker' => 'Swarm Worker',
|
||||
'server.settings.is_build_server' => 'Build Server',
|
||||
'server.settings.is_metrics_enabled' => 'Metrics',
|
||||
'server.settings.sentinel_token' => 'Metrics Token',
|
||||
'server.settings.sentinel_metrics_refresh_rate_seconds' => 'Metrics Interval',
|
||||
'server.settings.sentinel_metrics_history_days' => 'Metrics History',
|
||||
'server.settings.sentinel_push_interval_seconds' => 'Push Interval',
|
||||
'server.settings.is_sentinel_enabled' => 'Server API',
|
||||
'server.settings.is_sentinel_debug_enabled' => 'Debug',
|
||||
'server.settings.sentinel_custom_url' => 'Coolify URL',
|
||||
'server.settings.server_timezone' => 'Server Timezone',
|
||||
];
|
||||
|
||||
public function mount(Server $server)
|
||||
{
|
||||
$this->server = $server;
|
||||
$this->timezones = collect(timezone_identifiers_list())->sort()->values()->toArray();
|
||||
$this->wildcard_domain = $this->server->settings->wildcard_domain;
|
||||
}
|
||||
|
||||
public function checkSyncStatus()
|
||||
{
|
||||
$this->server->refresh();
|
||||
$this->server->settings->refresh();
|
||||
}
|
||||
|
||||
public function regenerateSentinelToken()
|
||||
{
|
||||
try {
|
||||
$this->server->settings->generateSentinelToken();
|
||||
$this->server->settings->refresh();
|
||||
// $this->restartSentinel(notification: false);
|
||||
$this->dispatch('success', 'Token regenerated & Sentinel restarted.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function updated($field)
|
||||
{
|
||||
if ($field === 'server.settings.docker_cleanup_frequency') {
|
||||
$frequency = $this->server->settings->docker_cleanup_frequency;
|
||||
if (! validate_cron_expression($frequency)) {
|
||||
$this->dispatch('error', 'Invalid Cron / Human expression for Docker Cleanup Frequency. Resetting to default 10 minutes.');
|
||||
$this->server->settings->docker_cleanup_frequency = '*/10 * * * *';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function cloudflareTunnelConfigured()
|
||||
{
|
||||
$this->serverInstalled();
|
||||
$this->dispatch('success', 'Cloudflare Tunnels configured successfully.');
|
||||
}
|
||||
|
||||
public function serverInstalled()
|
||||
{
|
||||
$this->server->refresh();
|
||||
$this->server->settings->refresh();
|
||||
}
|
||||
|
||||
public function updatedServerSettingsIsBuildServer()
|
||||
{
|
||||
$this->dispatch('refreshServerShow');
|
||||
$this->dispatch('serverRefresh');
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
}
|
||||
|
||||
public function updatedServerSettingsIsSentinelEnabled($value)
|
||||
{
|
||||
$this->validate([
|
||||
'server.settings.sentinel_custom_url' => 'required|url',
|
||||
]);
|
||||
if ($value === false) {
|
||||
StopSentinel::dispatch($this->server);
|
||||
$this->server->settings->is_metrics_enabled = false;
|
||||
$this->server->settings->save();
|
||||
$this->server->sentinelHeartbeat(isReset: true);
|
||||
} else {
|
||||
try {
|
||||
StartSentinel::run($this->server);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function updatedServerSettingsIsMetricsEnabled()
|
||||
{
|
||||
$this->restartSentinel();
|
||||
}
|
||||
|
||||
public function updatedServerSettingsIsSentinelDebugEnabled()
|
||||
{
|
||||
$this->restartSentinel();
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
refresh_server_connection($this->server->privateKey);
|
||||
$this->validateServer(false);
|
||||
|
||||
$this->server->settings->save();
|
||||
$this->server->save();
|
||||
$this->dispatch('success', 'Server updated.');
|
||||
$this->dispatch('refreshServerShow');
|
||||
} catch (\Throwable $e) {
|
||||
$this->server->settings->refresh();
|
||||
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
public function saveSentinel()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->server->settings->save();
|
||||
$this->dispatch('success', 'Sentinel updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->checkSyncStatus();
|
||||
}
|
||||
}
|
||||
|
||||
public function restartSentinel($notification = true)
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->validate([
|
||||
'server.settings.sentinel_custom_url' => 'required|url',
|
||||
]);
|
||||
$this->server->settings->save();
|
||||
$this->server->restartSentinel(async: false);
|
||||
if ($notification) {
|
||||
$this->dispatch('success', 'Sentinel restarted.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function revalidate()
|
||||
{
|
||||
$this->revalidate = true;
|
||||
}
|
||||
|
||||
public function checkLocalhostConnection()
|
||||
{
|
||||
$this->submit();
|
||||
['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection();
|
||||
if ($uptime) {
|
||||
$this->dispatch('success', 'Server is reachable.');
|
||||
$this->server->settings->is_reachable = true;
|
||||
$this->server->settings->is_usable = true;
|
||||
$this->server->settings->save();
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
} else {
|
||||
$this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: '.$error);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public function validateServer($install = true)
|
||||
{
|
||||
$this->server->update([
|
||||
'validation_logs' => null,
|
||||
]);
|
||||
$this->dispatch('init', $install);
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
if (isCloud() && ! isDev()) {
|
||||
$this->validate();
|
||||
$this->validate([
|
||||
'server.ip' => 'required',
|
||||
]);
|
||||
} else {
|
||||
$this->validate();
|
||||
}
|
||||
$uniqueIPs = Server::all()->reject(function (Server $server) {
|
||||
return $server->id === $this->server->id;
|
||||
})->pluck('ip')->toArray();
|
||||
if (in_array($this->server->ip, $uniqueIPs)) {
|
||||
$this->dispatch('error', 'IP address is already in use by another team.');
|
||||
|
||||
return;
|
||||
}
|
||||
refresh_server_connection($this->server->privateKey);
|
||||
$this->server->settings->wildcard_domain = $this->wildcard_domain;
|
||||
$currentTimezone = $this->server->settings->getOriginal('server_timezone');
|
||||
$newTimezone = $this->server->settings->server_timezone;
|
||||
if ($currentTimezone !== $newTimezone || $currentTimezone === '') {
|
||||
$this->server->settings->server_timezone = $newTimezone;
|
||||
}
|
||||
$this->server->settings->save();
|
||||
$this->server->save();
|
||||
|
||||
$this->dispatch('success', 'Server updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,38 +5,38 @@ namespace App\Livewire\Server;
|
||||
use App\Actions\Server\StartLogDrain;
|
||||
use App\Actions\Server\StopLogDrain;
|
||||
use App\Models\Server;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class LogDrains extends Component
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
#[Rule(['boolean'])]
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isLogDrainNewRelicEnabled = false;
|
||||
|
||||
#[Rule(['boolean'])]
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isLogDrainCustomEnabled = false;
|
||||
|
||||
#[Rule(['boolean'])]
|
||||
#[Validate(['boolean'])]
|
||||
public bool $isLogDrainAxiomEnabled = false;
|
||||
|
||||
#[Rule(['string', 'nullable'])]
|
||||
#[Validate(['string', 'nullable'])]
|
||||
public ?string $logDrainNewRelicLicenseKey = null;
|
||||
|
||||
#[Rule(['url', 'nullable'])]
|
||||
#[Validate(['url', 'nullable'])]
|
||||
public ?string $logDrainNewRelicBaseUri = null;
|
||||
|
||||
#[Rule(['string', 'nullable'])]
|
||||
#[Validate(['string', 'nullable'])]
|
||||
public ?string $logDrainAxiomDatasetName = null;
|
||||
|
||||
#[Rule(['string', 'nullable'])]
|
||||
#[Validate(['string', 'nullable'])]
|
||||
public ?string $logDrainAxiomApiKey = null;
|
||||
|
||||
#[Rule(['string', 'nullable'])]
|
||||
#[Validate(['string', 'nullable'])]
|
||||
public ?string $logDrainCustomConfig = null;
|
||||
|
||||
#[Rule(['string', 'nullable'])]
|
||||
#[Validate(['string', 'nullable'])]
|
||||
public ?string $logDrainCustomConfigParser = null;
|
||||
|
||||
public function mount(string $server_uuid)
|
||||
@@ -49,36 +49,114 @@ class LogDrains extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false)
|
||||
public function syncDataNewRelic(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->server->settings->is_logdrain_newrelic_enabled = $this->isLogDrainNewRelicEnabled;
|
||||
$this->server->settings->is_logdrain_axiom_enabled = $this->isLogDrainAxiomEnabled;
|
||||
$this->server->settings->is_logdrain_custom_enabled = $this->isLogDrainCustomEnabled;
|
||||
|
||||
$this->server->settings->logdrain_newrelic_license_key = $this->logDrainNewRelicLicenseKey;
|
||||
$this->server->settings->logdrain_newrelic_base_uri = $this->logDrainNewRelicBaseUri;
|
||||
$this->server->settings->logdrain_axiom_dataset_name = $this->logDrainAxiomDatasetName;
|
||||
$this->server->settings->logdrain_axiom_api_key = $this->logDrainAxiomApiKey;
|
||||
$this->server->settings->logdrain_custom_config = $this->logDrainCustomConfig;
|
||||
$this->server->settings->logdrain_custom_config_parser = $this->logDrainCustomConfigParser;
|
||||
|
||||
$this->server->settings->save();
|
||||
} else {
|
||||
$this->isLogDrainNewRelicEnabled = $this->server->settings->is_logdrain_newrelic_enabled;
|
||||
$this->isLogDrainAxiomEnabled = $this->server->settings->is_logdrain_axiom_enabled;
|
||||
$this->isLogDrainCustomEnabled = $this->server->settings->is_logdrain_custom_enabled;
|
||||
|
||||
$this->logDrainNewRelicLicenseKey = $this->server->settings->logdrain_newrelic_license_key;
|
||||
$this->logDrainNewRelicBaseUri = $this->server->settings->logdrain_newrelic_base_uri;
|
||||
}
|
||||
}
|
||||
|
||||
public function syncDataAxiom(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->server->settings->is_logdrain_axiom_enabled = $this->isLogDrainAxiomEnabled;
|
||||
$this->server->settings->logdrain_axiom_dataset_name = $this->logDrainAxiomDatasetName;
|
||||
$this->server->settings->logdrain_axiom_api_key = $this->logDrainAxiomApiKey;
|
||||
} else {
|
||||
$this->isLogDrainAxiomEnabled = $this->server->settings->is_logdrain_axiom_enabled;
|
||||
$this->logDrainAxiomDatasetName = $this->server->settings->logdrain_axiom_dataset_name;
|
||||
$this->logDrainAxiomApiKey = $this->server->settings->logdrain_axiom_api_key;
|
||||
}
|
||||
}
|
||||
|
||||
public function syncDataCustom(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->server->settings->is_logdrain_custom_enabled = $this->isLogDrainCustomEnabled;
|
||||
$this->server->settings->logdrain_custom_config = $this->logDrainCustomConfig;
|
||||
$this->server->settings->logdrain_custom_config_parser = $this->logDrainCustomConfigParser;
|
||||
} else {
|
||||
$this->isLogDrainCustomEnabled = $this->server->settings->is_logdrain_custom_enabled;
|
||||
$this->logDrainCustomConfig = $this->server->settings->logdrain_custom_config;
|
||||
$this->logDrainCustomConfigParser = $this->server->settings->logdrain_custom_config_parser;
|
||||
}
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false, ?string $type = null)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->customValidation();
|
||||
if ($type === 'newrelic') {
|
||||
$this->syncDataNewRelic($toModel);
|
||||
} elseif ($type === 'axiom') {
|
||||
$this->syncDataAxiom($toModel);
|
||||
} elseif ($type === 'custom') {
|
||||
$this->syncDataCustom($toModel);
|
||||
} else {
|
||||
$this->syncDataNewRelic($toModel);
|
||||
$this->syncDataAxiom($toModel);
|
||||
$this->syncDataCustom($toModel);
|
||||
}
|
||||
$this->server->settings->save();
|
||||
} else {
|
||||
if ($type === 'newrelic') {
|
||||
$this->syncDataNewRelic($toModel);
|
||||
} elseif ($type === 'axiom') {
|
||||
$this->syncDataAxiom($toModel);
|
||||
} elseif ($type === 'custom') {
|
||||
$this->syncDataCustom($toModel);
|
||||
} else {
|
||||
$this->syncDataNewRelic($toModel);
|
||||
$this->syncDataAxiom($toModel);
|
||||
$this->syncDataCustom($toModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function customValidation()
|
||||
{
|
||||
if ($this->isLogDrainNewRelicEnabled) {
|
||||
try {
|
||||
$this->validate([
|
||||
'logDrainNewRelicLicenseKey' => ['required'],
|
||||
'logDrainNewRelicBaseUri' => ['required', 'url'],
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
$this->isLogDrainNewRelicEnabled = false;
|
||||
|
||||
throw $e;
|
||||
}
|
||||
} elseif ($this->isLogDrainAxiomEnabled) {
|
||||
try {
|
||||
$this->validate([
|
||||
'logDrainAxiomDatasetName' => ['required'],
|
||||
'logDrainAxiomApiKey' => ['required'],
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
$this->isLogDrainAxiomEnabled = false;
|
||||
|
||||
throw $e;
|
||||
}
|
||||
} elseif ($this->isLogDrainCustomEnabled) {
|
||||
try {
|
||||
$this->validate([
|
||||
'logDrainCustomConfig' => ['required'],
|
||||
'logDrainCustomConfigParser' => ['string', 'nullable'],
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
$this->isLogDrainCustomEnabled = false;
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
@@ -98,7 +176,7 @@ class LogDrains extends Component
|
||||
public function submit(string $type)
|
||||
{
|
||||
try {
|
||||
$this->syncData(true);
|
||||
$this->syncData(true, $type);
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
|
||||
@@ -6,64 +6,60 @@ use App\Enums\ProxyTypes;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class ByIp extends Component
|
||||
{
|
||||
#[Locked]
|
||||
public $private_keys;
|
||||
|
||||
#[Locked]
|
||||
public $limit_reached;
|
||||
|
||||
#[Validate('nullable|integer', as: 'Private Key')]
|
||||
public ?int $private_key_id = null;
|
||||
|
||||
#[Validate('nullable|string', as: 'Private Key Name')]
|
||||
public $new_private_key_name;
|
||||
|
||||
#[Validate('nullable|string', as: 'Private Key Description')]
|
||||
public $new_private_key_description;
|
||||
|
||||
#[Validate('nullable|string', as: 'Private Key Value')]
|
||||
public $new_private_key_value;
|
||||
|
||||
#[Validate('required|string', as: 'Name')]
|
||||
public string $name;
|
||||
|
||||
#[Validate('nullable|string', as: 'Description')]
|
||||
public ?string $description = null;
|
||||
|
||||
#[Validate('required|string', as: 'IP Address/Domain')]
|
||||
public string $ip;
|
||||
|
||||
#[Validate('required|string', as: 'User')]
|
||||
public string $user = 'root';
|
||||
|
||||
#[Validate('required|integer|between:1,65535', as: 'Port')]
|
||||
public int $port = 22;
|
||||
|
||||
#[Validate('required|boolean', as: 'Swarm Manager')]
|
||||
public bool $is_swarm_manager = false;
|
||||
|
||||
#[Validate('required|boolean', as: 'Swarm Worker')]
|
||||
public bool $is_swarm_worker = false;
|
||||
|
||||
#[Validate('nullable|integer', as: 'Swarm Cluster')]
|
||||
public $selected_swarm_cluster = null;
|
||||
|
||||
#[Validate('required|boolean', as: 'Build Server')]
|
||||
public bool $is_build_server = false;
|
||||
|
||||
#[Locked]
|
||||
public Collection $swarm_managers;
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|string',
|
||||
'description' => 'nullable|string',
|
||||
'ip' => 'required',
|
||||
'user' => 'required|string',
|
||||
'port' => 'required|integer',
|
||||
'is_swarm_manager' => 'required|boolean',
|
||||
'is_swarm_worker' => 'required|boolean',
|
||||
'is_build_server' => 'required|boolean',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'name' => 'Name',
|
||||
'description' => 'Description',
|
||||
'ip' => 'IP Address/Domain',
|
||||
'user' => 'User',
|
||||
'port' => 'Port',
|
||||
'is_swarm_manager' => 'Swarm Manager',
|
||||
'is_swarm_worker' => 'Swarm Worker',
|
||||
'is_build_server' => 'Build Server',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->name = generate_random_name();
|
||||
@@ -88,6 +84,12 @@ class ByIp extends Component
|
||||
{
|
||||
$this->validate();
|
||||
try {
|
||||
if (Server::where('team_id', currentTeam()->id)
|
||||
->where('ip', $this->ip)
|
||||
->exists()) {
|
||||
return $this->dispatch('error', 'This IP/Domain is already in use by another server in your team.');
|
||||
}
|
||||
|
||||
if (is_null($this->private_key_id)) {
|
||||
return $this->dispatch('error', 'You must select a private key');
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Livewire\Server;
|
||||
|
||||
use App\Actions\Proxy\CheckConfiguration;
|
||||
use App\Actions\Proxy\SaveConfiguration;
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -16,6 +15,8 @@ class Proxy extends Component
|
||||
|
||||
public $proxy_settings = null;
|
||||
|
||||
public bool $redirect_enabled = true;
|
||||
|
||||
public ?string $redirect_url = null;
|
||||
|
||||
protected $listeners = ['proxyStatusUpdated', 'saveConfiguration' => 'submit'];
|
||||
@@ -27,6 +28,7 @@ class Proxy extends Component
|
||||
public function mount()
|
||||
{
|
||||
$this->selectedProxy = $this->server->proxyType();
|
||||
$this->redirect_enabled = data_get($this->server, 'proxy.redirect_enabled', true);
|
||||
$this->redirect_url = data_get($this->server, 'proxy.redirect_url');
|
||||
}
|
||||
|
||||
@@ -39,19 +41,18 @@ class Proxy extends Component
|
||||
{
|
||||
$this->server->proxy = null;
|
||||
$this->server->save();
|
||||
$this->dispatch('proxyChanged');
|
||||
$this->dispatch('reloadWindow');
|
||||
}
|
||||
|
||||
public function selectProxy($proxy_type)
|
||||
{
|
||||
$this->server->proxy->set('status', 'exited');
|
||||
$this->server->proxy->set('type', $proxy_type);
|
||||
$this->server->save();
|
||||
$this->selectedProxy = $this->server->proxy->type;
|
||||
if ($this->server->proxySet()) {
|
||||
StartProxy::run($this->server, false);
|
||||
try {
|
||||
$this->server->changeProxy($proxy_type, async: false);
|
||||
$this->selectedProxy = $this->server->proxy->type;
|
||||
$this->dispatch('reloadWindow');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
@@ -65,13 +66,25 @@ class Proxy extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveRedirect()
|
||||
{
|
||||
try {
|
||||
$this->server->proxy->redirect_enabled = $this->redirect_enabled;
|
||||
$this->server->save();
|
||||
$this->server->setupDefaultRedirect();
|
||||
$this->dispatch('success', 'Proxy configuration saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
SaveConfiguration::run($this->server, $this->proxy_settings);
|
||||
$this->server->proxy->redirect_url = $this->redirect_url;
|
||||
$this->server->save();
|
||||
$this->server->setupDefault404Redirect();
|
||||
$this->server->setupDefaultRedirect();
|
||||
$this->dispatch('success', 'Proxy configuration saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
|
||||
@@ -65,7 +65,7 @@ class Deploy extends Component
|
||||
public function restart()
|
||||
{
|
||||
try {
|
||||
$this->stop(forceStop: false);
|
||||
$this->stop();
|
||||
$this->dispatch('checkProxy');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
@@ -105,6 +105,7 @@ class Deploy extends Component
|
||||
|
||||
$startTime = Carbon::now()->getTimestamp();
|
||||
while ($process->running()) {
|
||||
ray('running');
|
||||
if (Carbon::now()->getTimestamp() - $startTime >= $timeout) {
|
||||
$this->forceStopContainer($containerName);
|
||||
break;
|
||||
|
||||
@@ -5,88 +5,87 @@ namespace App\Livewire\Server;
|
||||
use App\Actions\Server\StartSentinel;
|
||||
use App\Actions\Server\StopSentinel;
|
||||
use App\Models\Server;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public string $name;
|
||||
|
||||
#[Rule(['nullable'])]
|
||||
public ?string $description;
|
||||
#[Validate(['nullable'])]
|
||||
public ?string $description = null;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public string $ip;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public string $user;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public string $port;
|
||||
|
||||
#[Rule(['nullable'])]
|
||||
#[Validate(['nullable'])]
|
||||
public ?string $validationLogs = null;
|
||||
|
||||
#[Rule(['nullable', 'url'])]
|
||||
public ?string $wildcardDomain;
|
||||
#[Validate(['nullable', 'url'])]
|
||||
public ?string $wildcardDomain = null;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public bool $isReachable;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public bool $isUsable;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public bool $isSwarmManager;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public bool $isSwarmWorker;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public bool $isBuildServer;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public bool $isMetricsEnabled;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public string $sentinelToken;
|
||||
|
||||
#[Rule(['nullable'])]
|
||||
public ?string $sentinelUpdatedAt;
|
||||
#[Validate(['nullable'])]
|
||||
public ?string $sentinelUpdatedAt = null;
|
||||
|
||||
#[Rule(['required', 'integer', 'min:1'])]
|
||||
#[Validate(['required', 'integer', 'min:1'])]
|
||||
public int $sentinelMetricsRefreshRateSeconds;
|
||||
|
||||
#[Rule(['required', 'integer', 'min:1'])]
|
||||
#[Validate(['required', 'integer', 'min:1'])]
|
||||
public int $sentinelMetricsHistoryDays;
|
||||
|
||||
#[Rule(['required', 'integer', 'min:10'])]
|
||||
#[Validate(['required', 'integer', 'min:10'])]
|
||||
public int $sentinelPushIntervalSeconds;
|
||||
|
||||
#[Rule(['nullable', 'url'])]
|
||||
public ?string $sentinelCustomUrl;
|
||||
#[Validate(['nullable', 'url'])]
|
||||
public ?string $sentinelCustomUrl = null;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public bool $isSentinelEnabled;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public bool $isSentinelDebugEnabled;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public string $serverTimezone;
|
||||
|
||||
public array $timezones;
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$teamId = auth()->user()->currentTeam()->id;
|
||||
|
||||
return [
|
||||
"echo-private:team.{$teamId},CloudflareTunnelConfigured" => '$refresh',
|
||||
'refreshServerShow' => '$refresh',
|
||||
"echo-private:team.{$teamId},CloudflareTunnelConfigured" => 'refresh',
|
||||
'refreshServerShow' => 'refresh',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -94,17 +93,34 @@ class Show extends Component
|
||||
{
|
||||
try {
|
||||
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
||||
$this->timezones = collect(timezone_identifiers_list())->sort()->values()->toArray();
|
||||
$this->syncData();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function timezones(): array
|
||||
{
|
||||
return collect(timezone_identifiers_list())
|
||||
->sort()
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
|
||||
if (Server::where('team_id', currentTeam()->id)
|
||||
->where('ip', $this->ip)
|
||||
->where('id', '!=', $this->server->id)
|
||||
->exists()) {
|
||||
$this->ip = $this->server->ip;
|
||||
throw new \Exception('This IP/Domain is already in use by another server in your team.');
|
||||
}
|
||||
|
||||
$this->server->name = $this->name;
|
||||
$this->server->description = $this->description;
|
||||
$this->server->ip = $this->ip;
|
||||
@@ -114,6 +130,7 @@ class Show extends Component
|
||||
$this->server->save();
|
||||
|
||||
$this->server->settings->is_swarm_manager = $this->isSwarmManager;
|
||||
$this->server->settings->wildcard_domain = $this->wildcardDomain;
|
||||
$this->server->settings->is_swarm_worker = $this->isSwarmWorker;
|
||||
$this->server->settings->is_build_server = $this->isBuildServer;
|
||||
$this->server->settings->is_metrics_enabled = $this->isMetricsEnabled;
|
||||
@@ -124,7 +141,14 @@ class Show extends Component
|
||||
$this->server->settings->sentinel_custom_url = $this->sentinelCustomUrl;
|
||||
$this->server->settings->is_sentinel_enabled = $this->isSentinelEnabled;
|
||||
$this->server->settings->is_sentinel_debug_enabled = $this->isSentinelDebugEnabled;
|
||||
$this->server->settings->server_timezone = $this->serverTimezone;
|
||||
|
||||
if (! validate_timezone($this->serverTimezone)) {
|
||||
$this->serverTimezone = config('app.timezone');
|
||||
throw new \Exception('Invalid timezone.');
|
||||
} else {
|
||||
$this->server->settings->server_timezone = $this->serverTimezone;
|
||||
}
|
||||
|
||||
$this->server->settings->save();
|
||||
} else {
|
||||
$this->name = $this->server->name;
|
||||
@@ -132,6 +156,7 @@ class Show extends Component
|
||||
$this->ip = $this->server->ip;
|
||||
$this->user = $this->server->user;
|
||||
$this->port = $this->server->port;
|
||||
|
||||
$this->wildcardDomain = $this->server->settings->wildcard_domain;
|
||||
$this->isReachable = $this->server->settings->is_reachable;
|
||||
$this->isUsable = $this->server->settings->is_usable;
|
||||
@@ -151,6 +176,12 @@ class Show extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function refresh()
|
||||
{
|
||||
$this->syncData();
|
||||
$this->dispatch('$refresh');
|
||||
}
|
||||
|
||||
public function validateServer($install = true)
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -159,7 +159,8 @@ class ValidateAndInstall extends Component
|
||||
$this->dispatch('refreshBoardingIndex');
|
||||
$this->dispatch('success', 'Server validated.');
|
||||
} else {
|
||||
$this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
$requiredDockerVersion = str(config('constants.docker.minimum_required_version'))->before('.');
|
||||
$this->error = 'Minimum Docker Engine version '.$requiredDockerVersion.' is not instaled. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
$this->server->update([
|
||||
'validation_logs' => $this->error,
|
||||
]);
|
||||
|
||||
@@ -7,8 +7,8 @@ use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
@@ -17,61 +17,58 @@ class Index extends Component
|
||||
|
||||
protected Server $server;
|
||||
|
||||
#[Locked]
|
||||
public $timezones;
|
||||
|
||||
#[Rule('boolean')]
|
||||
#[Validate('boolean')]
|
||||
public bool $is_auto_update_enabled;
|
||||
|
||||
#[Rule('nullable|string|max:255')]
|
||||
#[Validate('nullable|string|max:255')]
|
||||
public ?string $fqdn = null;
|
||||
|
||||
#[Rule('nullable|string|max:255')]
|
||||
#[Validate('nullable|string|max:255')]
|
||||
public ?string $resale_license = null;
|
||||
|
||||
#[Rule('required|integer|min:1025|max:65535')]
|
||||
#[Validate('required|integer|min:1025|max:65535')]
|
||||
public int $public_port_min;
|
||||
|
||||
#[Rule('required|integer|min:1025|max:65535')]
|
||||
#[Validate('required|integer|min:1025|max:65535')]
|
||||
public int $public_port_max;
|
||||
|
||||
#[Rule('nullable|string')]
|
||||
#[Validate('nullable|string')]
|
||||
public ?string $custom_dns_servers = null;
|
||||
|
||||
#[Rule('nullable|string|max:255')]
|
||||
#[Validate('nullable|string|max:255')]
|
||||
public ?string $instance_name = null;
|
||||
|
||||
#[Rule('nullable|string')]
|
||||
#[Validate('nullable|string')]
|
||||
public ?string $allowed_ips = null;
|
||||
|
||||
#[Rule('nullable|string')]
|
||||
#[Validate('nullable|string')]
|
||||
public ?string $public_ipv4 = null;
|
||||
|
||||
#[Rule('nullable|string')]
|
||||
#[Validate('nullable|string')]
|
||||
public ?string $public_ipv6 = null;
|
||||
|
||||
#[Rule('string')]
|
||||
#[Validate('string')]
|
||||
public string $auto_update_frequency;
|
||||
|
||||
#[Rule('string')]
|
||||
#[Validate('string|required')]
|
||||
public string $update_check_frequency;
|
||||
|
||||
#[Rule('required|string|timezone')]
|
||||
#[Validate('required|string|timezone')]
|
||||
public string $instance_timezone;
|
||||
|
||||
#[Rule('boolean')]
|
||||
#[Validate('boolean')]
|
||||
public bool $do_not_track;
|
||||
|
||||
#[Rule('boolean')]
|
||||
#[Validate('boolean')]
|
||||
public bool $is_registration_enabled;
|
||||
|
||||
#[Rule('boolean')]
|
||||
#[Validate('boolean')]
|
||||
public bool $is_dns_validation_enabled;
|
||||
|
||||
#[Rule('boolean')]
|
||||
#[Validate('boolean')]
|
||||
public bool $is_api_enabled;
|
||||
|
||||
#[Rule('boolean')]
|
||||
#[Validate('boolean')]
|
||||
public bool $disable_two_step_confirmation;
|
||||
|
||||
public function render()
|
||||
@@ -101,14 +98,29 @@ class Index extends Component
|
||||
$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->timezones = collect(timezone_identifiers_list())->sort()->values()->toArray();
|
||||
$this->instance_timezone = $this->settings->instance_timezone;
|
||||
$this->disable_two_step_confirmation = $this->settings->disable_two_step_confirmation;
|
||||
}
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function timezones(): array
|
||||
{
|
||||
return collect(timezone_identifiers_list())
|
||||
->sort()
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
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->resale_license = $this->resale_license;
|
||||
$this->settings->public_port_min = $this->public_port_min;
|
||||
@@ -139,6 +151,14 @@ class Index extends Component
|
||||
$error_show = false;
|
||||
$this->server = Server::findOrFail(0);
|
||||
$this->resetErrorBag();
|
||||
|
||||
if (! validate_timezone($this->instance_timezone)) {
|
||||
$this->instance_timezone = config('app.timezone');
|
||||
throw new \Exception('Invalid timezone.');
|
||||
} else {
|
||||
$this->settings->instance_timezone = $this->instance_timezone;
|
||||
}
|
||||
|
||||
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.');
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Settings;
|
||||
|
||||
use App\Actions\License\CheckResaleLicense;
|
||||
use App\Models\InstanceSettings;
|
||||
use Livewire\Component;
|
||||
|
||||
class License extends Component
|
||||
{
|
||||
public InstanceSettings $settings;
|
||||
|
||||
public ?string $instance_id = null;
|
||||
|
||||
protected $rules = [
|
||||
'settings.resale_license' => 'nullable',
|
||||
'settings.is_resale_license_active' => 'nullable',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'settings.resale_license' => 'License',
|
||||
'instance_id' => 'Instance Id (Do not change this)',
|
||||
'settings.is_resale_license_active' => 'Is License Active',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (! isCloud()) {
|
||||
abort(404);
|
||||
}
|
||||
if (! isInstanceAdmin()) {
|
||||
return redirect()->route('home');
|
||||
}
|
||||
$this->instance_id = config('app.id');
|
||||
$this->settings = instanceSettings();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.settings.license');
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
$this->settings->save();
|
||||
if ($this->settings->resale_license) {
|
||||
try {
|
||||
CheckResaleLicense::run();
|
||||
$this->dispatch('reloadWindow');
|
||||
} catch (\Throwable $e) {
|
||||
session()->flash('error', 'Something went wrong. Please contact support. <br>Error: '.$e->getMessage());
|
||||
|
||||
return redirect()->route('settings.license');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class SettingsBackup extends Component
|
||||
@@ -25,19 +25,19 @@ class SettingsBackup extends Component
|
||||
#[Locked]
|
||||
public $executions = [];
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public string $uuid;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public string $name;
|
||||
|
||||
#[Rule(['nullable'])]
|
||||
#[Validate(['nullable'])]
|
||||
public ?string $description = null;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public string $postgres_user;
|
||||
|
||||
#[Rule(['required'])]
|
||||
#[Validate(['required'])]
|
||||
public string $postgres_password;
|
||||
|
||||
public function mount()
|
||||
@@ -99,6 +99,14 @@ class SettingsBackup extends Component
|
||||
$this->database->refresh();
|
||||
$this->backup->refresh();
|
||||
$this->s3s = S3Storage::whereTeamId(0)->get();
|
||||
|
||||
$this->uuid = $this->database->uuid;
|
||||
$this->name = $this->database->name;
|
||||
$this->description = $this->database->description;
|
||||
$this->postgres_user = $this->database->postgres_user;
|
||||
$this->postgres_password = $this->database->postgres_password;
|
||||
$this->executions = $this->backup->executions;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -3,44 +3,44 @@
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use Livewire\Attributes\Rule;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class SettingsEmail extends Component
|
||||
{
|
||||
public InstanceSettings $settings;
|
||||
|
||||
#[Rule(['boolean'])]
|
||||
#[Validate(['boolean'])]
|
||||
public bool $smtpEnabled = false;
|
||||
|
||||
#[Rule(['nullable', 'string'])]
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $smtpHost = null;
|
||||
|
||||
#[Rule(['nullable', 'numeric', 'min:1', 'max:65535'])]
|
||||
#[Validate(['nullable', 'numeric', 'min:1', 'max:65535'])]
|
||||
public ?int $smtpPort = null;
|
||||
|
||||
#[Rule(['nullable', 'string'])]
|
||||
#[Validate(['nullable', 'string', 'in:tls,ssl,none'])]
|
||||
public ?string $smtpEncryption = null;
|
||||
|
||||
#[Rule(['nullable', 'string'])]
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $smtpUsername = null;
|
||||
|
||||
#[Rule(['nullable'])]
|
||||
#[Validate(['nullable'])]
|
||||
public ?string $smtpPassword = null;
|
||||
|
||||
#[Rule(['nullable', 'numeric'])]
|
||||
#[Validate(['nullable', 'numeric'])]
|
||||
public ?int $smtpTimeout = null;
|
||||
|
||||
#[Rule(['nullable', 'email'])]
|
||||
#[Validate(['nullable', 'email'])]
|
||||
public ?string $smtpFromAddress = null;
|
||||
|
||||
#[Rule(['nullable', 'string'])]
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $smtpFromName = null;
|
||||
|
||||
#[Rule(['boolean'])]
|
||||
#[Validate(['boolean'])]
|
||||
public bool $resendEnabled = false;
|
||||
|
||||
#[Rule(['nullable', 'string'])]
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $resendApiKey = null;
|
||||
|
||||
public function mount()
|
||||
@@ -63,6 +63,8 @@ class SettingsEmail extends Component
|
||||
$this->settings->smtp_username = $this->smtpUsername;
|
||||
$this->settings->smtp_password = $this->smtpPassword;
|
||||
$this->settings->smtp_timeout = $this->smtpTimeout;
|
||||
$this->settings->smtp_from_address = $this->smtpFromAddress;
|
||||
$this->settings->smtp_from_name = $this->smtpFromName;
|
||||
|
||||
$this->settings->resend_enabled = $this->resendEnabled;
|
||||
$this->settings->resend_api_key = $this->resendApiKey;
|
||||
|
||||
@@ -16,7 +16,7 @@ class Show extends Component
|
||||
|
||||
public array $parameters;
|
||||
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey'];
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey', 'environmentVariableDeleted' => '$refresh'];
|
||||
|
||||
public function saveKey($data)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ class Show extends Component
|
||||
{
|
||||
public Project $project;
|
||||
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey'];
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey', 'environmentVariableDeleted' => '$refresh'];
|
||||
|
||||
public function saveKey($data)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ class Index extends Component
|
||||
{
|
||||
public Team $team;
|
||||
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey'];
|
||||
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey', 'environmentVariableDeleted' => '$refresh'];
|
||||
|
||||
public function saveKey($data)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,11 @@ namespace App\Livewire\Source\Github;
|
||||
|
||||
use App\Jobs\GithubAppPermissionJob;
|
||||
use App\Models\GithubApp;
|
||||
use App\Models\PrivateKey;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Lcobucci\JWT\Configuration;
|
||||
use Lcobucci\JWT\Signer\Key\InMemory;
|
||||
use Lcobucci\JWT\Signer\Rsa\Sha256;
|
||||
use Livewire\Component;
|
||||
|
||||
class Change extends Component
|
||||
@@ -51,12 +56,20 @@ class Change extends Component
|
||||
'github_app.administration' => 'nullable|string',
|
||||
];
|
||||
|
||||
public function boot()
|
||||
{
|
||||
if ($this->github_app) {
|
||||
$this->github_app->makeVisible(['client_secret', 'webhook_secret']);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkPermissions()
|
||||
{
|
||||
GithubAppPermissionJob::dispatchSync($this->github_app);
|
||||
$this->github_app->refresh()->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||
$this->dispatch('success', 'Github App permissions updated.');
|
||||
}
|
||||
|
||||
// public function check()
|
||||
// {
|
||||
|
||||
@@ -90,15 +103,16 @@ class Change extends Component
|
||||
|
||||
// ray($runners_by_repository);
|
||||
// }
|
||||
|
||||
public function mount()
|
||||
{
|
||||
try {
|
||||
$github_app_uuid = request()->github_app_uuid;
|
||||
$this->github_app = GithubApp::ownedByCurrentTeam()->whereUuid($github_app_uuid)->firstOrFail();
|
||||
$this->github_app->makeVisible(['client_secret', 'webhook_secret']);
|
||||
|
||||
$this->applications = $this->github_app->applications;
|
||||
$settings = instanceSettings();
|
||||
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||
|
||||
$this->name = str($this->github_app->name)->kebab();
|
||||
$this->fqdn = $settings->fqdn;
|
||||
@@ -142,6 +156,77 @@ class Change extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function getGithubAppNameUpdatePath()
|
||||
{
|
||||
if (str($this->github_app->organization)->isNotEmpty()) {
|
||||
return "{$this->github_app->html_url}/organizations/{$this->github_app->organization}/settings/apps/{$this->github_app->name}";
|
||||
}
|
||||
|
||||
return "{$this->github_app->html_url}/settings/apps/{$this->github_app->name}";
|
||||
}
|
||||
|
||||
private function generateGithubJwt($private_key, $app_id): string
|
||||
{
|
||||
$configuration = Configuration::forAsymmetricSigner(
|
||||
new Sha256,
|
||||
InMemory::plainText($private_key),
|
||||
InMemory::plainText($private_key)
|
||||
);
|
||||
|
||||
$now = time();
|
||||
|
||||
return $configuration->builder()
|
||||
->issuedBy((string) $app_id)
|
||||
->permittedFor('https://api.github.com')
|
||||
->identifiedBy((string) $now)
|
||||
->issuedAt(new \DateTimeImmutable("@{$now}"))
|
||||
->expiresAt(new \DateTimeImmutable('@'.($now + 600)))
|
||||
->getToken($configuration->signer(), $configuration->signingKey())
|
||||
->toString();
|
||||
}
|
||||
|
||||
public function updateGithubAppName()
|
||||
{
|
||||
try {
|
||||
$privateKey = PrivateKey::ownedByCurrentTeam()->find($this->github_app->private_key_id);
|
||||
|
||||
if (! $privateKey) {
|
||||
$this->dispatch('error', 'No private key found for this GitHub App.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$jwt = $this->generateGithubJwt($privateKey->private_key, $this->github_app->app_id);
|
||||
|
||||
$response = Http::withHeaders([
|
||||
'Accept' => 'application/vnd.github+json',
|
||||
'X-GitHub-Api-Version' => '2022-11-28',
|
||||
'Authorization' => "Bearer {$jwt}",
|
||||
])->get("{$this->github_app->api_url}/app");
|
||||
|
||||
if ($response->successful()) {
|
||||
$app_data = $response->json();
|
||||
$app_slug = $app_data['slug'] ?? null;
|
||||
|
||||
if ($app_slug) {
|
||||
$this->github_app->name = $app_slug;
|
||||
$this->name = str($app_slug)->kebab();
|
||||
$privateKey->name = "github-app-{$app_slug}";
|
||||
$privateKey->save();
|
||||
$this->github_app->save();
|
||||
$this->dispatch('success', 'GitHub App name and SSH key name synchronized successfully.');
|
||||
} else {
|
||||
$this->dispatch('info', 'Could not find App Name (slug) in GitHub response.');
|
||||
}
|
||||
} else {
|
||||
$error_message = $response->json()['message'] ?? 'Unknown error';
|
||||
$this->dispatch('error', "Failed to fetch GitHub App information: {$error_message}");
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -23,7 +23,7 @@ class Create extends Component
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->name = substr(generate_random_name(), 0, 34); // GitHub Apps names can only be 34 characters long
|
||||
$this->name = substr(generate_random_name(), 0, 30);
|
||||
}
|
||||
|
||||
public function createGitHubApp()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Livewire\Subscription;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Livewire\Component;
|
||||
use Stripe\Checkout\Session;
|
||||
use Stripe\Stripe;
|
||||
@@ -26,7 +27,7 @@ class PricingPlans extends Component
|
||||
$payload = [
|
||||
'allow_promotion_codes' => true,
|
||||
'billing_address_collection' => 'required',
|
||||
'client_reference_id' => auth()->user()->id.':'.currentTeam()->id,
|
||||
'client_reference_id' => Auth::id().':'.currentTeam()->id,
|
||||
'line_items' => [[
|
||||
'price' => $priceId,
|
||||
'adjustable_quantity' => [
|
||||
@@ -43,7 +44,7 @@ class PricingPlans extends Component
|
||||
],
|
||||
'subscription_data' => [
|
||||
'metadata' => [
|
||||
'user_id' => auth()->user()->id,
|
||||
'user_id' => Auth::id(),
|
||||
'team_id' => currentTeam()->id,
|
||||
],
|
||||
],
|
||||
@@ -60,7 +61,7 @@ class PricingPlans extends Component
|
||||
'name' => 'auto',
|
||||
];
|
||||
} else {
|
||||
$payload['customer_email'] = auth()->user()->email;
|
||||
$payload['customer_email'] = Auth::user()->email;
|
||||
}
|
||||
$session = Session::create($payload);
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Tags;
|
||||
|
||||
use App\Http\Controllers\Api\DeployController;
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Attributes\Url;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Title('Tags | Coolify')]
|
||||
class Index extends Component
|
||||
{
|
||||
#[Url()]
|
||||
public ?string $tag = null;
|
||||
|
||||
public Collection $tags;
|
||||
|
||||
public Collection $applications;
|
||||
|
||||
public Collection $services;
|
||||
|
||||
public $webhook = null;
|
||||
|
||||
public $deploymentsPerTagPerServer = [];
|
||||
|
||||
protected $listeners = ['deployments' => 'updateDeployments'];
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.tags.index');
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->tags = Tag::ownedByCurrentTeam()->get()->unique('name')->sortBy('name');
|
||||
if ($this->tag) {
|
||||
$this->tagUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
public function updateDeployments($deployments)
|
||||
{
|
||||
$this->deploymentsPerTagPerServer = $deployments;
|
||||
}
|
||||
|
||||
public function tagUpdated()
|
||||
{
|
||||
if ($this->tag === '') {
|
||||
return;
|
||||
}
|
||||
$sanitizedTag = htmlspecialchars($this->tag, ENT_QUOTES, 'UTF-8');
|
||||
$tag = $this->tags->where('name', $sanitizedTag)->first();
|
||||
if (! $tag) {
|
||||
$this->dispatch('error', 'Tag ('.e($sanitizedTag).') not found.');
|
||||
$this->tag = '';
|
||||
|
||||
return;
|
||||
}
|
||||
$this->webhook = generateTagDeployWebhook($tag->name);
|
||||
$this->applications = $tag->applications()->get();
|
||||
$this->services = $tag->services()->get();
|
||||
}
|
||||
|
||||
public function redeployAll()
|
||||
{
|
||||
try {
|
||||
$this->applications->each(function ($resource) {
|
||||
$deploy = new DeployController;
|
||||
$deploy->deploy_resource($resource);
|
||||
});
|
||||
$this->services->each(function ($resource) {
|
||||
$deploy = new DeployController;
|
||||
$deploy->deploy_resource($resource);
|
||||
});
|
||||
$this->dispatch('success', 'Mass deployment started.');
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,43 +5,57 @@ namespace App\Livewire\Tags;
|
||||
use App\Http\Controllers\Api\DeployController;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\Tag;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Title('Tags | Coolify')]
|
||||
class Show extends Component
|
||||
{
|
||||
public $tags;
|
||||
#[Locked]
|
||||
public ?string $tagName = null;
|
||||
|
||||
public Tag $tag;
|
||||
#[Locked]
|
||||
public ?Collection $tags = null;
|
||||
|
||||
public $applications;
|
||||
#[Locked]
|
||||
public ?Tag $tag = null;
|
||||
|
||||
public $services;
|
||||
#[Locked]
|
||||
public ?Collection $applications = null;
|
||||
|
||||
public $webhook = null;
|
||||
#[Locked]
|
||||
public ?Collection $services = null;
|
||||
|
||||
public $deployments_per_tag_per_server = [];
|
||||
#[Locked]
|
||||
public ?string $webhook = null;
|
||||
|
||||
#[Locked]
|
||||
public ?array $deploymentsPerTagPerServer = null;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->tags = Tag::ownedByCurrentTeam()->get()->unique('name')->sortBy('name');
|
||||
$tag = $this->tags->where('name', request()->tag_name)->first();
|
||||
if (! $tag) {
|
||||
return redirect()->route('tags.index');
|
||||
try {
|
||||
$this->tags = Tag::ownedByCurrentTeam()->get()->unique('name')->sortBy('name');
|
||||
if (str($this->tagName)->isNotEmpty()) {
|
||||
$tag = $this->tags->where('name', $this->tagName)->first();
|
||||
$this->webhook = generateTagDeployWebhook($tag->name);
|
||||
$this->applications = $tag->applications()->get();
|
||||
$this->services = $tag->services()->get();
|
||||
$this->tag = $tag;
|
||||
$this->getDeployments();
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
$this->webhook = generateTagDeployWebhook($tag->name);
|
||||
$this->applications = $tag->applications()->get();
|
||||
$this->services = $tag->services()->get();
|
||||
$this->tag = $tag;
|
||||
$this->get_deployments();
|
||||
}
|
||||
|
||||
public function get_deployments()
|
||||
public function getDeployments()
|
||||
{
|
||||
try {
|
||||
$resource_ids = $this->applications->pluck('id');
|
||||
$this->deployments_per_tag_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('application_id', $resource_ids)->get([
|
||||
$this->deploymentsPerTagPerServer = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('application_id', $resource_ids)->get([
|
||||
'id',
|
||||
'application_id',
|
||||
'application_name',
|
||||
@@ -56,7 +70,7 @@ class Show extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function redeploy_all()
|
||||
public function redeployAll()
|
||||
{
|
||||
try {
|
||||
$message = collect([]);
|
||||
|
||||
@@ -3,28 +3,21 @@
|
||||
namespace App\Livewire\Team;
|
||||
|
||||
use App\Models\Team;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Create extends Component
|
||||
{
|
||||
#[Validate(['required', 'min:3', 'max:255'])]
|
||||
public string $name = '';
|
||||
|
||||
#[Validate(['nullable', 'min:3', 'max:255'])]
|
||||
public ?string $description = null;
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|min:3|max:255',
|
||||
'description' => 'nullable|min:3|max:255',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'name' => 'name',
|
||||
'description' => 'description',
|
||||
];
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
try {
|
||||
$this->validate();
|
||||
$team = Team::create([
|
||||
'name' => $this->name,
|
||||
'description' => $this->description,
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Livewire\Team;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Models\TeamInvitation;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -55,7 +56,7 @@ class Index extends Component
|
||||
$currentTeam->delete();
|
||||
|
||||
$currentTeam->members->each(function ($user) use ($currentTeam) {
|
||||
if ($user->id === auth()->user()->id) {
|
||||
if ($user->id === Auth::id()) {
|
||||
return;
|
||||
}
|
||||
$user->teams()->detach($currentTeam);
|
||||
|
||||
@@ -14,13 +14,25 @@ class Index extends Component
|
||||
|
||||
public $containers = [];
|
||||
|
||||
public bool $isLoadingContainers = true;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (! auth()->user()->isAdmin()) {
|
||||
abort(403);
|
||||
}
|
||||
$this->servers = Server::isReachable()->get();
|
||||
$this->containers = $this->getAllActiveContainers();
|
||||
}
|
||||
|
||||
public function loadContainers()
|
||||
{
|
||||
try {
|
||||
$this->containers = $this->getAllActiveContainers();
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->isLoadingContainers = false;
|
||||
}
|
||||
}
|
||||
|
||||
private function getAllActiveContainers()
|
||||
|
||||
@@ -23,6 +23,9 @@ class Upgrade extends Component
|
||||
try {
|
||||
$this->latestVersion = get_latest_version_of_coolify();
|
||||
$this->isUpgradeAvailable = data_get(InstanceSettings::get(), 'new_version_available', false);
|
||||
if (isDev()) {
|
||||
$this->isUpgradeAvailable = true;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class Index extends Component
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (config('coolify.waitlist') == false) {
|
||||
if (config('constants.waitlist.enabled') == false) {
|
||||
return redirect()->route('register');
|
||||
}
|
||||
$this->waitingInLine = Waitlist::whereVerified(true)->count();
|
||||
|
||||
Reference in New Issue
Block a user