Merge branch 'services' into next
This commit is contained in:
@@ -24,3 +24,4 @@ yarn-error.log
|
||||
/.ssh
|
||||
.ignition.json
|
||||
.env.dusk.local
|
||||
docker/coolify-realtime/node_modules
|
||||
|
@@ -11,7 +11,7 @@ on:
|
||||
- docker/coolify-helper/Dockerfile
|
||||
- docker/coolify-realtime/Dockerfile
|
||||
- docker/testing-host/Dockerfile
|
||||
- templates/service-templates.json
|
||||
- templates/*
|
||||
|
||||
env:
|
||||
GITHUB_REGISTRY: ghcr.io
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
- name: Get Version
|
||||
id: version
|
||||
run: |
|
||||
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT]
|
||||
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and Push Image
|
||||
uses: docker/build-push-action@v6
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
- name: Get Version
|
||||
id: version
|
||||
run: |
|
||||
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT]
|
||||
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app php:8.2-alpine3.16 php bootstrap/getVersion.php)"|xargs >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and Push Image
|
||||
uses: docker/build-push-action@v6
|
||||
|
1
.github/workflows/coolify-realtime-next.yml
vendored
1
.github/workflows/coolify-realtime-next.yml
vendored
@@ -8,6 +8,7 @@ on:
|
||||
- docker/coolify-realtime/Dockerfile
|
||||
- docker/coolify-realtime/terminal-server.js
|
||||
- docker/coolify-realtime/package.json
|
||||
- docker/coolify-realtime/package-lock.json
|
||||
- docker/coolify-realtime/soketi-entrypoint.sh
|
||||
|
||||
env:
|
||||
|
1
.github/workflows/coolify-realtime.yml
vendored
1
.github/workflows/coolify-realtime.yml
vendored
@@ -8,6 +8,7 @@ on:
|
||||
- docker/coolify-realtime/Dockerfile
|
||||
- docker/coolify-realtime/terminal-server.js
|
||||
- docker/coolify-realtime/package.json
|
||||
- docker/coolify-realtime/package-lock.json
|
||||
- docker/coolify-realtime/soketi-entrypoint.sh
|
||||
|
||||
env:
|
||||
|
2
.github/workflows/coolify-staging-build.yml
vendored
2
.github/workflows/coolify-staging-build.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
- docker/coolify-helper/Dockerfile
|
||||
- docker/coolify-realtime/Dockerfile
|
||||
- docker/testing-host/Dockerfile
|
||||
- templates/service-templates.json
|
||||
- templates/*
|
||||
|
||||
env:
|
||||
GITHUB_REGISTRY: ghcr.io
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -34,3 +34,4 @@ _ide_helper_models.php
|
||||
scripts/load-test/*
|
||||
.ignition.json
|
||||
.env.dusk.local
|
||||
docker/coolify-realtime/node_modules
|
||||
|
@@ -9,6 +9,7 @@ use App\Jobs\ApplicationDeploymentJob;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Process\ProcessResult;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Spatie\Activitylog\Models\Activity;
|
||||
|
||||
@@ -124,6 +125,7 @@ class RunRemoteProcess
|
||||
]));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('Error calling event: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -99,8 +99,8 @@ class StartClickhouse
|
||||
}
|
||||
|
||||
// Add custom docker run options
|
||||
$docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
$docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
|
@@ -49,7 +49,7 @@ class StartDatabase
|
||||
break;
|
||||
}
|
||||
if ($database->is_public && $database->public_port) {
|
||||
StartDatabaseProxy::dispatch($database);
|
||||
StartDatabaseProxy::dispatch($database)->onQueue('high');
|
||||
}
|
||||
|
||||
return $activity;
|
||||
|
@@ -96,8 +96,8 @@ class StartDragonfly
|
||||
}
|
||||
|
||||
// Add custom docker run options
|
||||
$docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
$docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
|
@@ -107,8 +107,8 @@ class StartKeydb
|
||||
}
|
||||
|
||||
// Add custom docker run options
|
||||
$docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
$docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
||||
|
@@ -101,8 +101,8 @@ class StartMariadb
|
||||
}
|
||||
|
||||
// Add custom docker run options
|
||||
$docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
$docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
|
@@ -25,6 +25,10 @@ class StartMongodb
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||
|
||||
if (isDev()) {
|
||||
$this->configuration_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/databases/'.$container_name;
|
||||
}
|
||||
|
||||
$this->commands = [
|
||||
"echo 'Starting {$database->name}.'",
|
||||
"mkdir -p $this->configuration_dir",
|
||||
@@ -117,8 +121,8 @@ class StartMongodb
|
||||
];
|
||||
|
||||
// Add custom docker run options
|
||||
$docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
$docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
|
@@ -101,8 +101,8 @@ class StartMysql
|
||||
}
|
||||
|
||||
// Add custom docker run options
|
||||
$docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
$docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
|
@@ -122,8 +122,8 @@ class StartPostgresql
|
||||
];
|
||||
}
|
||||
// Add custom docker run options
|
||||
$docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
$docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
|
@@ -110,8 +110,8 @@ class StartRedis
|
||||
}
|
||||
|
||||
// Add custom docker run options
|
||||
$docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
$docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options);
|
||||
$docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
|
||||
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Events\DatabaseStatusChanged;
|
||||
use App\Events\DatabaseProxyStopped;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
@@ -27,7 +27,11 @@ class StopDatabaseProxy
|
||||
$server = data_get($database, 'service.server');
|
||||
}
|
||||
instant_remote_process(["docker rm -f {$uuid}-proxy"], $server);
|
||||
|
||||
$database->is_public = false;
|
||||
$database->save();
|
||||
DatabaseStatusChanged::dispatch();
|
||||
|
||||
DatabaseProxyStopped::dispatch();
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -30,7 +30,7 @@ class GetContainersStatus
|
||||
$this->containerReplicates = $containerReplicates;
|
||||
$this->server = $server;
|
||||
if (! $this->server->isFunctional()) {
|
||||
return 'Server is not ready.';
|
||||
return 'Server is not functional.';
|
||||
}
|
||||
$this->applications = $this->server->applications();
|
||||
$skip_these_applications = collect([]);
|
||||
@@ -107,6 +107,8 @@ class GetContainersStatus
|
||||
$statusFromDb = $preview->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$preview->update(['status' => $containerStatus]);
|
||||
} else {
|
||||
$preview->update(['last_online_at' => now()]);
|
||||
}
|
||||
} else {
|
||||
//Notify user that this container should not be there.
|
||||
@@ -118,6 +120,8 @@ class GetContainersStatus
|
||||
$statusFromDb = $application->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$application->update(['status' => $containerStatus]);
|
||||
} else {
|
||||
$application->update(['last_online_at' => now()]);
|
||||
}
|
||||
} else {
|
||||
//Notify user that this container should not be there.
|
||||
@@ -160,7 +164,10 @@ class GetContainersStatus
|
||||
$statusFromDb = $database->status;
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
$database->update(['status' => $containerStatus]);
|
||||
} else {
|
||||
$database->update(['last_online_at' => now()]);
|
||||
}
|
||||
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->server->isSwarm()) {
|
||||
@@ -171,7 +178,7 @@ class GetContainersStatus
|
||||
})->first();
|
||||
if (! $foundTcpProxy) {
|
||||
StartDatabaseProxy::run($database);
|
||||
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
|
||||
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -202,6 +209,8 @@ class GetContainersStatus
|
||||
if ($statusFromDb !== $containerStatus) {
|
||||
// ray('Updating status: ' . $containerStatus);
|
||||
$service->update(['status' => $containerStatus]);
|
||||
} else {
|
||||
$service->update(['last_online_at' => now()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -40,7 +40,7 @@ class CreateNewUser implements CreatesNewUsers
|
||||
$user = User::create([
|
||||
'id' => 0,
|
||||
'name' => $input['name'],
|
||||
'email' => $input['email'],
|
||||
'email' => strtolower($input['email']),
|
||||
'password' => Hash::make($input['password']),
|
||||
]);
|
||||
$team = $user->teams()->first();
|
||||
@@ -52,7 +52,7 @@ class CreateNewUser implements CreatesNewUsers
|
||||
} else {
|
||||
$user = User::create([
|
||||
'name' => $input['name'],
|
||||
'email' => $input['email'],
|
||||
'email' => strtolower($input['email']),
|
||||
'password' => Hash::make($input['password']),
|
||||
]);
|
||||
$team = $user->teams()->first();
|
||||
|
@@ -4,6 +4,7 @@ namespace App\Actions\Proxy;
|
||||
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
@@ -88,6 +89,7 @@ class CheckProxy
|
||||
$portsToCheck = [];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error checking proxy: '.$e->getMessage());
|
||||
}
|
||||
if (count($portsToCheck) === 0) {
|
||||
return false;
|
||||
|
@@ -12,11 +12,11 @@ class InstallDocker
|
||||
|
||||
public function handle(Server $server)
|
||||
{
|
||||
$dockerVersion = config('constants.docker_install_version');
|
||||
$supported_os_type = $server->validateOS();
|
||||
if (! $supported_os_type) {
|
||||
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/installation#manually">documentation</a>.');
|
||||
}
|
||||
$dockerVersion = '26.0';
|
||||
$config = base64_encode('{
|
||||
"log-driver": "json-file",
|
||||
"log-opts": {
|
||||
|
41
app/Actions/Server/ResourcesCheck.php
Normal file
41
app/Actions/Server/ResourcesCheck.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
use App\Models\StandaloneKeydb;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class ResourcesCheck
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$seconds = 60;
|
||||
try {
|
||||
Application::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
|
||||
ServiceApplication::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
|
||||
ServiceDatabase::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
|
||||
StandalonePostgresql::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
|
||||
StandaloneRedis::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
|
||||
StandaloneMongodb::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
|
||||
StandaloneMysql::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
|
||||
StandaloneMariadb::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
|
||||
StandaloneKeydb::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
|
||||
StandaloneDragonfly::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
|
||||
StandaloneClickhouse::where('last_online_at', '<', now()->subSeconds($seconds))->update(['status' => 'exited']);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
}
|
269
app/Actions/Server/ServerCheck.php
Normal file
269
app/Actions/Server/ServerCheck.php
Normal file
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Proxy\CheckProxy;
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Jobs\CheckAndStartSentinelJob;
|
||||
use App\Jobs\ServerStorageCheckJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceApplication;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Notifications\Container\ContainerRestarted;
|
||||
use Illuminate\Support\Arr;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class ServerCheck
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public Server $server;
|
||||
|
||||
public bool $isSentinel = false;
|
||||
|
||||
public $containers;
|
||||
|
||||
public $databases;
|
||||
|
||||
public function handle(Server $server, $data = null)
|
||||
{
|
||||
$this->server = $server;
|
||||
try {
|
||||
if ($this->server->isFunctional() === false) {
|
||||
return 'Server is not functional.';
|
||||
}
|
||||
|
||||
if (! $this->server->isSwarmWorker() && ! $this->server->isBuildServer()) {
|
||||
|
||||
if (isset($data)) {
|
||||
$data = collect($data);
|
||||
|
||||
$this->server->sentinelHeartbeat();
|
||||
|
||||
$this->containers = collect(data_get($data, 'containers'));
|
||||
|
||||
$filesystemUsageRoot = data_get($data, 'filesystem_usage_root.used_percentage');
|
||||
ServerStorageCheckJob::dispatch($this->server, $filesystemUsageRoot);
|
||||
|
||||
$containerReplicates = null;
|
||||
$this->isSentinel = true;
|
||||
|
||||
} else {
|
||||
['containers' => $this->containers, 'containerReplicates' => $containerReplicates] = $this->server->getContainers();
|
||||
// ServerStorageCheckJob::dispatch($this->server);
|
||||
}
|
||||
|
||||
if (is_null($this->containers)) {
|
||||
return 'No containers found.';
|
||||
}
|
||||
|
||||
if (isset($containerReplicates)) {
|
||||
foreach ($containerReplicates as $containerReplica) {
|
||||
$name = data_get($containerReplica, 'Name');
|
||||
$this->containers = $this->containers->map(function ($container) use ($name, $containerReplica) {
|
||||
if (data_get($container, 'Spec.Name') === $name) {
|
||||
$replicas = data_get($containerReplica, 'Replicas');
|
||||
$running = str($replicas)->explode('/')[0];
|
||||
$total = str($replicas)->explode('/')[1];
|
||||
if ($running === $total) {
|
||||
data_set($container, 'State.Status', 'running');
|
||||
data_set($container, 'State.Health.Status', 'healthy');
|
||||
} else {
|
||||
data_set($container, 'State.Status', 'starting');
|
||||
data_set($container, 'State.Health.Status', 'unhealthy');
|
||||
}
|
||||
}
|
||||
|
||||
return $container;
|
||||
});
|
||||
}
|
||||
}
|
||||
$this->checkContainers();
|
||||
|
||||
if ($this->server->isSentinelEnabled() && $this->isSentinel === false) {
|
||||
CheckAndStartSentinelJob::dispatch($this->server);
|
||||
}
|
||||
|
||||
if ($this->server->isLogDrainEnabled()) {
|
||||
$this->checkLogDrainContainer();
|
||||
}
|
||||
|
||||
if ($this->server->proxySet() && ! $this->server->proxy->force_stop) {
|
||||
$foundProxyContainer = $this->containers->filter(function ($value, $key) {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
|
||||
} else {
|
||||
return data_get($value, 'Name') === '/coolify-proxy';
|
||||
}
|
||||
})->first();
|
||||
if (! $foundProxyContainer) {
|
||||
try {
|
||||
$shouldStart = CheckProxy::run($this->server);
|
||||
if ($shouldStart) {
|
||||
StartProxy::run($this->server, false);
|
||||
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
} else {
|
||||
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
||||
$this->server->save();
|
||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
private function checkLogDrainContainer()
|
||||
{
|
||||
$foundLogDrainContainer = $this->containers->filter(function ($value, $key) {
|
||||
return data_get($value, 'Name') === '/coolify-log-drain';
|
||||
})->first();
|
||||
if ($foundLogDrainContainer) {
|
||||
$status = data_get($foundLogDrainContainer, 'State.Status');
|
||||
if ($status !== 'running') {
|
||||
StartLogDrain::dispatch($this->server)->onQueue('high');
|
||||
}
|
||||
} else {
|
||||
StartLogDrain::dispatch($this->server)->onQueue('high');
|
||||
}
|
||||
}
|
||||
|
||||
private function checkContainers()
|
||||
{
|
||||
foreach ($this->containers as $container) {
|
||||
if ($this->isSentinel) {
|
||||
$labels = Arr::undot(data_get($container, 'labels'));
|
||||
} else {
|
||||
if ($this->server->isSwarm()) {
|
||||
$labels = Arr::undot(data_get($container, 'Spec.Labels'));
|
||||
} else {
|
||||
$labels = Arr::undot(data_get($container, 'Config.Labels'));
|
||||
}
|
||||
|
||||
}
|
||||
$managed = data_get($labels, 'coolify.managed');
|
||||
if (! $managed) {
|
||||
continue;
|
||||
}
|
||||
$uuid = data_get($labels, 'coolify.name');
|
||||
if (! $uuid) {
|
||||
$uuid = data_get($labels, 'com.docker.compose.service');
|
||||
}
|
||||
|
||||
if ($this->isSentinel) {
|
||||
$containerStatus = data_get($container, 'state');
|
||||
$containerHealth = data_get($container, 'health_status');
|
||||
} else {
|
||||
$containerStatus = data_get($container, 'State.Status');
|
||||
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
|
||||
}
|
||||
$containerStatus = "$containerStatus ($containerHealth)";
|
||||
|
||||
$applicationId = data_get($labels, 'coolify.applicationId');
|
||||
$serviceId = data_get($labels, 'coolify.serviceId');
|
||||
$databaseId = data_get($labels, 'coolify.databaseId');
|
||||
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
||||
|
||||
if ($applicationId) {
|
||||
// Application
|
||||
if ($pullRequestId != 0) {
|
||||
if (str($applicationId)->contains('-')) {
|
||||
$applicationId = str($applicationId)->before('-');
|
||||
}
|
||||
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
||||
if ($preview) {
|
||||
$preview->update(['status' => $containerStatus]);
|
||||
}
|
||||
} else {
|
||||
$application = Application::where('id', $applicationId)->first();
|
||||
if ($application) {
|
||||
$application->update([
|
||||
'status' => $containerStatus,
|
||||
'last_online_at' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
} elseif (isset($serviceId)) {
|
||||
// Service
|
||||
$subType = data_get($labels, 'coolify.service.subType');
|
||||
$subId = data_get($labels, 'coolify.service.subId');
|
||||
$service = Service::where('id', $serviceId)->first();
|
||||
if (! $service) {
|
||||
continue;
|
||||
}
|
||||
if ($subType === 'application') {
|
||||
$service = ServiceApplication::where('id', $subId)->first();
|
||||
} else {
|
||||
$service = ServiceDatabase::where('id', $subId)->first();
|
||||
}
|
||||
if ($service) {
|
||||
$service->update([
|
||||
'status' => $containerStatus,
|
||||
'last_online_at' => now(),
|
||||
]);
|
||||
if ($subType === 'database') {
|
||||
$isPublic = data_get($service, 'is_public');
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->isSentinel) {
|
||||
return data_get($value, 'name') === $uuid.'-proxy';
|
||||
} else {
|
||||
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||
}
|
||||
}
|
||||
})->first();
|
||||
if (! $foundTcpProxy) {
|
||||
StartDatabaseProxy::run($service);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Database
|
||||
if (is_null($this->databases)) {
|
||||
$this->databases = $this->server->databases();
|
||||
}
|
||||
$database = $this->databases->where('uuid', $uuid)->first();
|
||||
if ($database) {
|
||||
$database->update([
|
||||
'status' => $containerStatus,
|
||||
'last_online_at' => now(),
|
||||
]);
|
||||
|
||||
$isPublic = data_get($database, 'is_public');
|
||||
if ($isPublic) {
|
||||
$foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
|
||||
if ($this->isSentinel) {
|
||||
return data_get($value, 'name') === $uuid.'-proxy';
|
||||
} else {
|
||||
if ($this->server->isSwarm()) {
|
||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
||||
} else {
|
||||
|
||||
return data_get($value, 'Name') === "/$uuid-proxy";
|
||||
}
|
||||
}
|
||||
})->first();
|
||||
if (! $foundTcpProxy) {
|
||||
StartDatabaseProxy::run($database);
|
||||
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@ namespace App\Actions\Service;
|
||||
|
||||
use App\Actions\Server\CleanupDocker;
|
||||
use App\Models\Service;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class DeleteService
|
||||
@@ -39,7 +40,8 @@ class DeleteService
|
||||
if (! empty($commands)) {
|
||||
foreach ($commands as $command) {
|
||||
$result = instant_remote_process([$command], $server, false);
|
||||
if ($result !== 0) {
|
||||
if ($result !== null && $result !== 0) {
|
||||
Log::error('Error deleting volumes: '.$result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ use App\Models\Environment;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
@@ -38,9 +39,10 @@ class Init extends Command
|
||||
}
|
||||
|
||||
// Backward compatibility
|
||||
$this->disable_metrics();
|
||||
// $this->disable_metrics();
|
||||
$this->replace_slash_in_environment_name();
|
||||
$this->restore_coolify_db_backup();
|
||||
$this->update_user_emails();
|
||||
//
|
||||
$this->update_traefik_labels();
|
||||
if (! isCloud() || $this->option('force-cloud')) {
|
||||
@@ -78,17 +80,26 @@ class Init extends Command
|
||||
}
|
||||
}
|
||||
|
||||
private function disable_metrics()
|
||||
// private function disable_metrics()
|
||||
// {
|
||||
// if (version_compare('4.0.0-beta.312', config('version'), '<=')) {
|
||||
// foreach ($this->servers as $server) {
|
||||
// if ($server->settings->is_metrics_enabled === true) {
|
||||
// $server->settings->update(['is_metrics_enabled' => false]);
|
||||
// }
|
||||
// if ($server->isFunctional()) {
|
||||
// StopSentinel::dispatch($server)->onQueue('high');
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private function update_user_emails()
|
||||
{
|
||||
if (version_compare('4.0.0-beta.312', config('version'), '<=')) {
|
||||
foreach ($this->servers as $server) {
|
||||
if ($server->settings->is_metrics_enabled === true) {
|
||||
$server->settings->update(['is_metrics_enabled' => false]);
|
||||
}
|
||||
if ($server->isFunctional()) {
|
||||
StopSentinel::dispatch($server);
|
||||
}
|
||||
}
|
||||
try {
|
||||
User::whereRaw('email ~ \'[A-Z]\'')->get()->each(fn (User $user) => $user->update(['email' => strtolower($user->email)]));
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in updating user emails: {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -96,7 +96,7 @@ class ServicesDelete extends Command
|
||||
if (! $confirmed) {
|
||||
break;
|
||||
}
|
||||
DeleteResourceJob::dispatch($toDelete);
|
||||
DeleteResourceJob::dispatch($toDelete)->onQueue('high');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ class ServicesDelete extends Command
|
||||
if (! $confirmed) {
|
||||
return;
|
||||
}
|
||||
DeleteResourceJob::dispatch($toDelete);
|
||||
DeleteResourceJob::dispatch($toDelete)->onQueue('high');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,7 +148,7 @@ class ServicesDelete extends Command
|
||||
if (! $confirmed) {
|
||||
return;
|
||||
}
|
||||
DeleteResourceJob::dispatch($toDelete);
|
||||
DeleteResourceJob::dispatch($toDelete)->onQueue('high');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
58
app/Console/Commands/Weird.php
Normal file
58
app/Console/Commands/Weird.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Actions\Server\ServerCheck;
|
||||
use App\Enums\ProxyStatus;
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Console\Command;
|
||||
use Str;
|
||||
|
||||
class Weird extends Command
|
||||
{
|
||||
protected $signature = 'weird {--number=1} {--run}';
|
||||
|
||||
protected $description = 'Weird stuff';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
if (! isDev()) {
|
||||
$this->error('This command can only be run in development mode');
|
||||
|
||||
return;
|
||||
}
|
||||
$run = $this->option('run');
|
||||
if ($run) {
|
||||
$servers = Server::all();
|
||||
foreach ($servers as $server) {
|
||||
ServerCheck::dispatch($server);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
$number = $this->option('number');
|
||||
for ($i = 0; $i < $number; $i++) {
|
||||
$uuid = Str::uuid();
|
||||
$server = Server::create([
|
||||
'name' => 'localhost-'.$uuid,
|
||||
'description' => 'This is a test docker container in development mode',
|
||||
'ip' => 'coolify-testing-host',
|
||||
'team_id' => 0,
|
||||
'private_key_id' => 1,
|
||||
'proxy' => [
|
||||
'type' => ProxyTypes::NONE->value,
|
||||
'status' => ProxyStatus::EXITED->value,
|
||||
],
|
||||
]);
|
||||
$server->settings->update([
|
||||
'is_usable' => true,
|
||||
'is_reachable' => true,
|
||||
]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,6 +13,7 @@ use App\Jobs\PullTemplatesFromCDN;
|
||||
use App\Jobs\ScheduledTaskJob;
|
||||
use App\Jobs\ServerCheckJob;
|
||||
use App\Jobs\ServerCleanupMux;
|
||||
use App\Jobs\ServerStorageCheckJob;
|
||||
use App\Jobs\UpdateCoolifyJob;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
@@ -29,11 +30,17 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
private InstanceSettings $settings;
|
||||
|
||||
private string $updateCheckFrequency;
|
||||
|
||||
private string $instanceTimezone;
|
||||
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
$this->allServers = Server::where('ip', '!=', '1.2.3.4')->get();
|
||||
$this->allServers = Server::where('ip', '!=', '1.2.3.4');
|
||||
|
||||
$this->settings = instanceSettings();
|
||||
$this->updateCheckFrequency = $this->settings->update_check_frequency ?: '0 * * * *';
|
||||
$this->instanceTimezone = $this->settings->instance_timezone ?: config('app.timezone');
|
||||
|
||||
$schedule->job(new CleanupStaleMultiplexedConnections)->hourly();
|
||||
|
||||
@@ -41,25 +48,30 @@ class Kernel extends ConsoleKernel
|
||||
// Instance Jobs
|
||||
$schedule->command('horizon:snapshot')->everyMinute();
|
||||
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
|
||||
$schedule->job(new CheckHelperImageJob)->everyTenMinutes()->onOneServer();
|
||||
|
||||
// Server Jobs
|
||||
$this->checkScheduledBackups($schedule);
|
||||
$this->checkResources($schedule);
|
||||
|
||||
$this->checkScheduledBackups($schedule);
|
||||
$this->checkScheduledTasks($schedule);
|
||||
|
||||
$schedule->command('uploads:clear')->everyTwoMinutes();
|
||||
|
||||
$schedule->job(new CheckHelperImageJob)->everyFiveMinutes()->onOneServer();
|
||||
} else {
|
||||
// Instance Jobs
|
||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||
$schedule->command('cleanup:unreachable-servers')->daily()->onOneServer();
|
||||
$schedule->job(new PullTemplatesFromCDN)->cron($this->settings->update_check_frequency)->timezone($this->settings->instance_timezone)->onOneServer();
|
||||
$schedule->job(new PullTemplatesFromCDN)->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer();
|
||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
||||
$this->scheduleUpdates($schedule);
|
||||
|
||||
// Server Jobs
|
||||
$this->checkScheduledBackups($schedule);
|
||||
$this->checkResources($schedule);
|
||||
|
||||
$this->pullImages($schedule);
|
||||
|
||||
$this->checkScheduledBackups($schedule);
|
||||
$this->checkScheduledTasks($schedule);
|
||||
|
||||
$schedule->command('cleanup:database --yes')->daily();
|
||||
@@ -69,33 +81,32 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
private function pullImages($schedule): void
|
||||
{
|
||||
$servers = $this->allServers->whereRelation('settings', 'is_usable', true)->whereRelation('settings', 'is_reachable', true);
|
||||
$servers = $this->allServers->whereRelation('settings', 'is_usable', true)->whereRelation('settings', 'is_reachable', true)->get();
|
||||
foreach ($servers as $server) {
|
||||
if ($server->isSentinelEnabled()) {
|
||||
$schedule->job(function () use ($server) {
|
||||
CheckAndStartSentinelJob::dispatch($server);
|
||||
})->cron($this->settings->update_check_frequency)->timezone($this->settings->instance_timezone)->onOneServer();
|
||||
})->cron($this->updateCheckFrequency)->timezone($this->instanceTimezone)->onOneServer();
|
||||
}
|
||||
}
|
||||
$schedule->job(new CheckHelperImageJob)
|
||||
->cron($this->settings->update_check_frequency)
|
||||
->timezone($this->settings->instance_timezone)
|
||||
->cron($this->updateCheckFrequency)
|
||||
->timezone($this->instanceTimezone)
|
||||
->onOneServer();
|
||||
}
|
||||
|
||||
private function scheduleUpdates($schedule): void
|
||||
{
|
||||
$updateCheckFrequency = $this->settings->update_check_frequency;
|
||||
$schedule->job(new CheckForUpdatesJob)
|
||||
->cron($updateCheckFrequency)
|
||||
->timezone($this->settings->instance_timezone)
|
||||
->cron($this->updateCheckFrequency)
|
||||
->timezone($this->instanceTimezone)
|
||||
->onOneServer();
|
||||
|
||||
if ($this->settings->is_auto_update_enabled) {
|
||||
$autoUpdateFrequency = $this->settings->auto_update_frequency;
|
||||
$schedule->job(new UpdateCoolifyJob)
|
||||
->cron($autoUpdateFrequency)
|
||||
->timezone($this->settings->instance_timezone)
|
||||
->timezone($this->instanceTimezone)
|
||||
->onOneServer();
|
||||
}
|
||||
}
|
||||
@@ -103,23 +114,32 @@ class Kernel extends ConsoleKernel
|
||||
private function checkResources($schedule): void
|
||||
{
|
||||
if (isCloud()) {
|
||||
$servers = $this->allServers->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false);
|
||||
$servers = $this->allServers->whereHas('team.subscription')->get();
|
||||
$own = Team::find(0)->servers;
|
||||
$servers = $servers->merge($own);
|
||||
} else {
|
||||
$servers = $this->allServers;
|
||||
$servers = $this->allServers->get();
|
||||
}
|
||||
|
||||
foreach ($servers as $server) {
|
||||
$lastSentinelUpdate = $server->sentinel_updated_at;
|
||||
$serverTimezone = $server->settings->server_timezone;
|
||||
|
||||
// Sentinel check
|
||||
$lastSentinelUpdate = $server->sentinel_updated_at;
|
||||
if (Carbon::parse($lastSentinelUpdate)->isBefore(now()->subSeconds($server->waitBeforeDoingSshCheck()))) {
|
||||
// Check container status every minute if Sentinel does not activated
|
||||
$schedule->job(new ServerCheckJob($server))->everyMinute()->onOneServer();
|
||||
// $schedule->job(new \App\Jobs\ServerCheckNewJob($server))->everyMinute()->onOneServer();
|
||||
|
||||
// Check storage usage every 10 minutes if Sentinel does not activated
|
||||
$schedule->job(new ServerStorageCheckJob($server))->everyTenMinutes()->onOneServer();
|
||||
}
|
||||
if ($server->settings->force_docker_cleanup) {
|
||||
$schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer();
|
||||
} else {
|
||||
$schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->timezone($serverTimezone)->onOneServer();
|
||||
}
|
||||
|
||||
// Cleanup multiplexed connections every hour
|
||||
$schedule->job(new ServerCleanupMux($server))->hourly()->onOneServer();
|
||||
|
||||
@@ -134,14 +154,11 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
private function checkScheduledBackups($schedule): void
|
||||
{
|
||||
$scheduled_backups = ScheduledDatabaseBackup::all();
|
||||
$scheduled_backups = ScheduledDatabaseBackup::where('enabled', true)->get();
|
||||
if ($scheduled_backups->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
foreach ($scheduled_backups as $scheduled_backup) {
|
||||
if (! $scheduled_backup->enabled) {
|
||||
continue;
|
||||
}
|
||||
if (is_null(data_get($scheduled_backup, 'database'))) {
|
||||
$scheduled_backup->delete();
|
||||
|
||||
@@ -150,30 +167,26 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
$server = $scheduled_backup->server();
|
||||
|
||||
if (! $server) {
|
||||
if (is_null($server)) {
|
||||
continue;
|
||||
}
|
||||
$serverTimezone = $server->settings->server_timezone;
|
||||
|
||||
if (isset(VALID_CRON_STRINGS[$scheduled_backup->frequency])) {
|
||||
$scheduled_backup->frequency = VALID_CRON_STRINGS[$scheduled_backup->frequency];
|
||||
}
|
||||
$schedule->job(new DatabaseBackupJob(
|
||||
backup: $scheduled_backup
|
||||
))->cron($scheduled_backup->frequency)->timezone($serverTimezone)->onOneServer();
|
||||
))->cron($scheduled_backup->frequency)->timezone($this->instanceTimezone)->onOneServer();
|
||||
}
|
||||
}
|
||||
|
||||
private function checkScheduledTasks($schedule): void
|
||||
{
|
||||
$scheduled_tasks = ScheduledTask::all();
|
||||
$scheduled_tasks = ScheduledTask::where('enabled', true)->get();
|
||||
if ($scheduled_tasks->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
foreach ($scheduled_tasks as $scheduled_task) {
|
||||
if ($scheduled_task->enabled === false) {
|
||||
continue;
|
||||
}
|
||||
$service = $scheduled_task->service;
|
||||
$application = $scheduled_task->application;
|
||||
|
||||
@@ -197,14 +210,13 @@ class Kernel extends ConsoleKernel
|
||||
if (! $server) {
|
||||
continue;
|
||||
}
|
||||
$serverTimezone = $server->settings->server_timezone ?: config('app.timezone');
|
||||
|
||||
if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) {
|
||||
$scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency];
|
||||
}
|
||||
$schedule->job(new ScheduledTaskJob(
|
||||
task: $scheduled_task
|
||||
))->cron($scheduled_task->frequency)->timezone($serverTimezone)->onOneServer();
|
||||
))->cron($scheduled_task->frequency)->timezone($this->instanceTimezone)->onOneServer();
|
||||
}
|
||||
}
|
||||
|
||||
|
35
app/Events/DatabaseProxyStopped.php
Normal file
35
app/Events/DatabaseProxyStopped.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class DatabaseProxyStopped implements ShouldBroadcast
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
public $teamId;
|
||||
|
||||
public function __construct($teamId = null)
|
||||
{
|
||||
if (is_null($teamId)) {
|
||||
$teamId = Auth::user()->currentTeam()->id ?? null;
|
||||
}
|
||||
if (is_null($teamId)) {
|
||||
throw new \Exception('Team id is null');
|
||||
}
|
||||
$this->teamId = $teamId;
|
||||
}
|
||||
|
||||
public function broadcastOn(): array
|
||||
{
|
||||
return [
|
||||
new PrivateChannel("team.{$this->teamId}"),
|
||||
];
|
||||
}
|
||||
}
|
@@ -7,27 +7,29 @@ use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class DatabaseStatusChanged implements ShouldBroadcast
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
public ?string $userId = null;
|
||||
public $userId = null;
|
||||
|
||||
public function __construct($userId = null)
|
||||
{
|
||||
if (is_null($userId)) {
|
||||
$userId = auth()->user()->id ?? null;
|
||||
$userId = Auth::id() ?? null;
|
||||
}
|
||||
if (is_null($userId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
public function broadcastOn(): ?array
|
||||
{
|
||||
if ($this->userId) {
|
||||
if (! is_null($this->userId)) {
|
||||
return [
|
||||
new PrivateChannel("user.{$this->userId}"),
|
||||
];
|
||||
|
34
app/Events/ScheduledTaskDone.php
Normal file
34
app/Events/ScheduledTaskDone.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ScheduledTaskDone implements ShouldBroadcast
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
public $teamId;
|
||||
|
||||
public function __construct($teamId = null)
|
||||
{
|
||||
if (is_null($teamId)) {
|
||||
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||
}
|
||||
if (is_null($teamId)) {
|
||||
throw new \Exception('Team id is null');
|
||||
}
|
||||
$this->teamId = $teamId;
|
||||
}
|
||||
|
||||
public function broadcastOn(): array
|
||||
{
|
||||
return [
|
||||
new PrivateChannel("team.{$this->teamId}"),
|
||||
];
|
||||
}
|
||||
}
|
@@ -7,6 +7,7 @@ use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ServiceStatusChanged implements ShouldBroadcast
|
||||
{
|
||||
@@ -17,7 +18,7 @@ class ServiceStatusChanged implements ShouldBroadcast
|
||||
public function __construct($userId = null)
|
||||
{
|
||||
if (is_null($userId)) {
|
||||
$userId = auth()->user()->id ?? null;
|
||||
$userId = Auth::id() ?? null;
|
||||
}
|
||||
if (is_null($userId)) {
|
||||
return false;
|
||||
|
@@ -1203,7 +1203,7 @@ class ApplicationsController extends Controller
|
||||
$service->name = "service-$service->uuid";
|
||||
$service->parse(isNew: true);
|
||||
if ($instantDeploy) {
|
||||
StartService::dispatch($service);
|
||||
StartService::dispatch($service)->onQueue('high');
|
||||
}
|
||||
|
||||
return response()->json(serializeApiResponse([
|
||||
@@ -1358,7 +1358,7 @@ class ApplicationsController extends Controller
|
||||
deleteVolumes: $request->query->get('delete_volumes', true),
|
||||
dockerCleanup: $request->query->get('docker_cleanup', true),
|
||||
deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
|
||||
);
|
||||
)->onQueue('high');
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Application deletion request queued.',
|
||||
@@ -2482,7 +2482,7 @@ class ApplicationsController extends Controller
|
||||
if (! $application) {
|
||||
return response()->json(['message' => 'Application not found.'], 404);
|
||||
}
|
||||
StopApplication::dispatch($application);
|
||||
StopApplication::dispatch($application)->onQueue('high');
|
||||
|
||||
return response()->json(
|
||||
[
|
||||
|
@@ -497,9 +497,9 @@ class DatabasesController extends Controller
|
||||
$database->update($request->all());
|
||||
|
||||
if ($whatToDoWithDatabaseProxy === 'start') {
|
||||
StartDatabaseProxy::dispatch($database);
|
||||
StartDatabaseProxy::dispatch($database)->onQueue('high');
|
||||
} elseif ($whatToDoWithDatabaseProxy === 'stop') {
|
||||
StopDatabaseProxy::dispatch($database);
|
||||
StopDatabaseProxy::dispatch($database)->onQueue('high');
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
@@ -1151,7 +1151,7 @@ class DatabasesController extends Controller
|
||||
}
|
||||
$database = create_standalone_postgresql($environment->id, $destination->uuid, $request->all());
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
StartDatabase::dispatch($database)->onQueue('high');
|
||||
}
|
||||
$database->refresh();
|
||||
$payload = [
|
||||
@@ -1206,7 +1206,7 @@ class DatabasesController extends Controller
|
||||
}
|
||||
$database = create_standalone_mariadb($environment->id, $destination->uuid, $request->all());
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
StartDatabase::dispatch($database)->onQueue('high');
|
||||
}
|
||||
|
||||
$database->refresh();
|
||||
@@ -1264,7 +1264,7 @@ class DatabasesController extends Controller
|
||||
}
|
||||
$database = create_standalone_mysql($environment->id, $destination->uuid, $request->all());
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
StartDatabase::dispatch($database)->onQueue('high');
|
||||
}
|
||||
|
||||
$database->refresh();
|
||||
@@ -1320,7 +1320,7 @@ class DatabasesController extends Controller
|
||||
}
|
||||
$database = create_standalone_redis($environment->id, $destination->uuid, $request->all());
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
StartDatabase::dispatch($database)->onQueue('high');
|
||||
}
|
||||
|
||||
$database->refresh();
|
||||
@@ -1357,7 +1357,7 @@ class DatabasesController extends Controller
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
$database = create_standalone_dragonfly($environment->id, $destination->uuid, $request->all());
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
StartDatabase::dispatch($database)->onQueue('high');
|
||||
}
|
||||
|
||||
return response()->json(serializeApiResponse([
|
||||
@@ -1406,7 +1406,7 @@ class DatabasesController extends Controller
|
||||
}
|
||||
$database = create_standalone_keydb($environment->id, $destination->uuid, $request->all());
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
StartDatabase::dispatch($database)->onQueue('high');
|
||||
}
|
||||
|
||||
$database->refresh();
|
||||
@@ -1442,7 +1442,7 @@ class DatabasesController extends Controller
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
$database = create_standalone_clickhouse($environment->id, $destination->uuid, $request->all());
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
StartDatabase::dispatch($database)->onQueue('high');
|
||||
}
|
||||
|
||||
$database->refresh();
|
||||
@@ -1500,7 +1500,7 @@ class DatabasesController extends Controller
|
||||
}
|
||||
$database = create_standalone_mongodb($environment->id, $destination->uuid, $request->all());
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
StartDatabase::dispatch($database)->onQueue('high');
|
||||
}
|
||||
|
||||
$database->refresh();
|
||||
@@ -1593,7 +1593,7 @@ class DatabasesController extends Controller
|
||||
deleteVolumes: $request->query->get('delete_volumes', true),
|
||||
dockerCleanup: $request->query->get('docker_cleanup', true),
|
||||
deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
|
||||
);
|
||||
)->onQueue('high');
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Database deletion request queued.',
|
||||
@@ -1666,7 +1666,7 @@ class DatabasesController extends Controller
|
||||
if (str($database->status)->contains('running')) {
|
||||
return response()->json(['message' => 'Database is already running.'], 400);
|
||||
}
|
||||
StartDatabase::dispatch($database);
|
||||
StartDatabase::dispatch($database)->onQueue('high');
|
||||
|
||||
return response()->json(
|
||||
[
|
||||
@@ -1742,7 +1742,7 @@ class DatabasesController extends Controller
|
||||
if (str($database->status)->contains('stopped') || str($database->status)->contains('exited')) {
|
||||
return response()->json(['message' => 'Database is already stopped.'], 400);
|
||||
}
|
||||
StopDatabase::dispatch($database);
|
||||
StopDatabase::dispatch($database)->onQueue('high');
|
||||
|
||||
return response()->json(
|
||||
[
|
||||
@@ -1815,7 +1815,7 @@ class DatabasesController extends Controller
|
||||
if (! $database) {
|
||||
return response()->json(['message' => 'Database not found.'], 404);
|
||||
}
|
||||
RestartDatabase::dispatch($database);
|
||||
RestartDatabase::dispatch($database)->onQueue('high');
|
||||
|
||||
return response()->json(
|
||||
[
|
||||
|
@@ -307,7 +307,7 @@ class DeployController extends Controller
|
||||
break;
|
||||
default:
|
||||
// Database resource
|
||||
StartDatabase::dispatch($resource);
|
||||
StartDatabase::dispatch($resource)->onQueue('high');
|
||||
$resource->update([
|
||||
'started_at' => now(),
|
||||
]);
|
||||
|
@@ -422,7 +422,7 @@ class ProjectController extends Controller
|
||||
if (! $project) {
|
||||
return response()->json(['message' => 'Project not found.'], 404);
|
||||
}
|
||||
if ($project->resource_count() > 0) {
|
||||
if (! $project->isEmpty()) {
|
||||
return response()->json(['message' => 'Project has resources, so it cannot be deleted.'], 400);
|
||||
}
|
||||
|
||||
|
@@ -538,7 +538,7 @@ class ServersController extends Controller
|
||||
'is_build_server' => $request->is_build_server,
|
||||
]);
|
||||
if ($request->instant_validate) {
|
||||
ValidateServer::dispatch($server);
|
||||
ValidateServer::dispatch($server)->onQueue('high');
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
@@ -651,7 +651,7 @@ class ServersController extends Controller
|
||||
]);
|
||||
}
|
||||
if ($request->instant_validate) {
|
||||
ValidateServer::dispatch($server);
|
||||
ValidateServer::dispatch($server)->onQueue('high');
|
||||
}
|
||||
|
||||
return response()->json(serializeApiResponse($server))->setStatusCode(201);
|
||||
@@ -787,7 +787,7 @@ class ServersController extends Controller
|
||||
if (! $server) {
|
||||
return response()->json(['message' => 'Server not found.'], 404);
|
||||
}
|
||||
ValidateServer::dispatch($server);
|
||||
ValidateServer::dispatch($server)->onQueue('high');
|
||||
|
||||
return response()->json(['message' => 'Validation started.']);
|
||||
}
|
||||
|
@@ -342,7 +342,7 @@ class ServicesController extends Controller
|
||||
}
|
||||
$service->parse(isNew: true);
|
||||
if ($instantDeploy) {
|
||||
StartService::dispatch($service);
|
||||
StartService::dispatch($service)->onQueue('high');
|
||||
}
|
||||
$domains = $service->applications()->get()->pluck('fqdn')->sort();
|
||||
$domains = $domains->map(function ($domain) {
|
||||
@@ -487,7 +487,7 @@ class ServicesController extends Controller
|
||||
deleteVolumes: $request->query->get('delete_volumes', true),
|
||||
dockerCleanup: $request->query->get('docker_cleanup', true),
|
||||
deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
|
||||
);
|
||||
)->onQueue('high');
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Service deletion request queued.',
|
||||
@@ -1076,7 +1076,7 @@ class ServicesController extends Controller
|
||||
if (str($service->status())->contains('running')) {
|
||||
return response()->json(['message' => 'Service is already running.'], 400);
|
||||
}
|
||||
StartService::dispatch($service);
|
||||
StartService::dispatch($service)->onQueue('high');
|
||||
|
||||
return response()->json(
|
||||
[
|
||||
@@ -1154,7 +1154,7 @@ class ServicesController extends Controller
|
||||
if (str($service->status())->contains('stopped') || str($service->status())->contains('exited')) {
|
||||
return response()->json(['message' => 'Service is already stopped.'], 400);
|
||||
}
|
||||
StopService::dispatch($service);
|
||||
StopService::dispatch($service)->onQueue('high');
|
||||
|
||||
return response()->json(
|
||||
[
|
||||
@@ -1229,7 +1229,7 @@ class ServicesController extends Controller
|
||||
if (! $service) {
|
||||
return response()->json(['message' => 'Service not found.'], 404);
|
||||
}
|
||||
RestartService::dispatch($service);
|
||||
RestartService::dispatch($service)->onQueue('high');
|
||||
|
||||
return response()->json(
|
||||
[
|
||||
|
@@ -110,13 +110,19 @@ class Controller extends BaseController
|
||||
return redirect()->route('login')->with('error', 'Invalid credentials.');
|
||||
}
|
||||
|
||||
public function accept_invitation()
|
||||
public function acceptInvitation()
|
||||
{
|
||||
$resetPassword = request()->query('reset-password');
|
||||
$invitationUuid = request()->route('uuid');
|
||||
|
||||
$invitation = TeamInvitation::whereUuid($invitationUuid)->firstOrFail();
|
||||
$user = User::whereEmail($invitation->email)->firstOrFail();
|
||||
|
||||
if (Auth::id() !== $user->id) {
|
||||
abort(400, 'You are not allowed to accept this invitation.');
|
||||
}
|
||||
$invitationValid = $invitation->isValid();
|
||||
|
||||
if ($invitationValid) {
|
||||
if ($resetPassword) {
|
||||
$user->update([
|
||||
@@ -131,14 +137,12 @@ class Controller extends BaseController
|
||||
}
|
||||
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
||||
$invitation->delete();
|
||||
if (auth()->user()?->id !== $user->id) {
|
||||
return redirect()->route('login');
|
||||
}
|
||||
|
||||
refreshSession($invitation->team);
|
||||
|
||||
return redirect()->route('team.index');
|
||||
} else {
|
||||
abort(401);
|
||||
abort(400, 'Invitation expired.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,10 +150,10 @@ class Controller extends BaseController
|
||||
{
|
||||
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
||||
$user = User::whereEmail($invitation->email)->firstOrFail();
|
||||
if (is_null(auth()->user())) {
|
||||
if (is_null(Auth::user())) {
|
||||
return redirect()->route('login');
|
||||
}
|
||||
if (auth()->user()->id !== $user->id) {
|
||||
if (Auth::id() !== $user->id) {
|
||||
abort(401);
|
||||
}
|
||||
$invitation->delete();
|
||||
|
@@ -230,7 +230,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->application_deployment_queue->update([
|
||||
'status' => ApplicationDeploymentStatus::IN_PROGRESS->value,
|
||||
]);
|
||||
if (! $this->server->isFunctional()) {
|
||||
if ($this->server->isFunctional() === false) {
|
||||
$this->application_deployment_queue->addLogEntry('Server is not functional.');
|
||||
$this->fail('Server is not functional.');
|
||||
|
||||
@@ -1836,7 +1836,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
|
||||
if ($this->pull_request_id === 0) {
|
||||
$custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options);
|
||||
$custom_compose = convertDockerRunToCompose($this->application->custom_docker_run_options);
|
||||
if ((bool) $this->application->settings->is_consistent_container_name_enabled) {
|
||||
if (! $this->application->settings->custom_internal_name) {
|
||||
$docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name];
|
||||
|
@@ -3,14 +3,15 @@
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\TeamInvitation;
|
||||
use App\Models\Waitlist;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue
|
||||
{
|
||||
@@ -18,34 +19,21 @@ class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, Sho
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
// public function uniqueId(): string
|
||||
// {
|
||||
// return $this->container_name;
|
||||
// }
|
||||
public function middleware(): array
|
||||
{
|
||||
return [(new WithoutOverlapping('cleanup-instance-stuffs'))->dontRelease()];
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
// $this->cleanup_waitlist();
|
||||
$this->cleanupInvitationLink();
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('CleanupInstanceStuffsJob failed with error: '.$e->getMessage());
|
||||
}
|
||||
try {
|
||||
$this->cleanup_invitation_link();
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('CleanupInstanceStuffsJob failed with error: '.$e->getMessage());
|
||||
Log::error('CleanupInstanceStuffsJob failed with error: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function cleanup_waitlist()
|
||||
{
|
||||
$waitlist = Waitlist::whereVerified(false)->where('created_at', '<', now()->subMinutes(config('constants.waitlist.expiration')))->get();
|
||||
foreach ($waitlist as $item) {
|
||||
$item->delete();
|
||||
}
|
||||
}
|
||||
|
||||
private function cleanup_invitation_link()
|
||||
private function cleanupInvitationLink()
|
||||
{
|
||||
$invitation = TeamInvitation::all();
|
||||
foreach ($invitation as $item) {
|
||||
|
@@ -10,6 +10,7 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
@@ -23,6 +24,11 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
public ?string $usageBefore = null;
|
||||
|
||||
public function middleware(): array
|
||||
{
|
||||
return [(new WithoutOverlapping($this->server->id))->dontRelease()];
|
||||
}
|
||||
|
||||
public function __construct(public Server $server, public bool $manualCleanup = false) {}
|
||||
|
||||
public function handle(): void
|
||||
|
@@ -360,7 +360,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
||||
private function checkLogDrainContainer()
|
||||
{
|
||||
if ($this->server->isLogDrainEnabled() && $this->foundLogDrainContainer === false) {
|
||||
StartLogDrain::dispatch($this->server);
|
||||
StartLogDrain::dispatch($this->server)->onQueue('high');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Events\ScheduledTaskDone;
|
||||
use App\Models\Application;
|
||||
use App\Models\ScheduledTask;
|
||||
use App\Models\ScheduledTaskExecution;
|
||||
@@ -19,7 +20,7 @@ class ScheduledTaskJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public ?Team $team = null;
|
||||
public Team $team;
|
||||
|
||||
public Server $server;
|
||||
|
||||
@@ -47,7 +48,7 @@ class ScheduledTaskJob implements ShouldQueue
|
||||
} else {
|
||||
throw new \RuntimeException('ScheduledTaskJob failed: No resource found.');
|
||||
}
|
||||
$this->team = Team::find($task->team_id);
|
||||
$this->team = Team::findOrFail($task->team_id);
|
||||
$this->server_timezone = $this->getServerTimezone();
|
||||
}
|
||||
|
||||
@@ -125,6 +126,7 @@ class ScheduledTaskJob implements ShouldQueue
|
||||
// send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage());
|
||||
throw $e;
|
||||
} finally {
|
||||
ScheduledTaskDone::dispatch($this->team->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
@@ -25,6 +26,11 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
public $containers;
|
||||
|
||||
public function middleware(): array
|
||||
{
|
||||
return [(new WithoutOverlapping($this->server->id))->dontRelease()];
|
||||
}
|
||||
|
||||
public function __construct(public Server $server) {}
|
||||
|
||||
public function handle()
|
||||
@@ -39,7 +45,6 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if (is_null($this->containers)) {
|
||||
return 'No containers found.';
|
||||
}
|
||||
ServerStorageCheckJob::dispatch($this->server);
|
||||
GetContainersStatus::run($this->server, $this->containers, $containerReplicates);
|
||||
|
||||
if ($this->server->isSentinelEnabled()) {
|
||||
@@ -89,10 +94,10 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($foundLogDrainContainer) {
|
||||
$status = data_get($foundLogDrainContainer, 'State.Status');
|
||||
if ($status !== 'running') {
|
||||
StartLogDrain::dispatch($this->server);
|
||||
StartLogDrain::dispatch($this->server)->onQueue('high');
|
||||
}
|
||||
} else {
|
||||
StartLogDrain::dispatch($this->server);
|
||||
StartLogDrain::dispatch($this->server)->onQueue('high');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
34
app/Jobs/ServerCheckNewJob.php
Normal file
34
app/Jobs/ServerCheckNewJob.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Actions\Server\ResourcesCheck;
|
||||
use App\Actions\Server\ServerCheck;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ServerCheckNewJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
public $timeout = 60;
|
||||
|
||||
public function __construct(public Server $server) {}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
ServerCheck::run($this->server);
|
||||
ResourcesCheck::dispatch($this->server);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -30,8 +30,7 @@ class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
try {
|
||||
$servers = $this->team->servers;
|
||||
$servers_count = $servers->count();
|
||||
$limit = data_get($this->team->limits, 'serverLimit', 2);
|
||||
$number_of_servers_to_disable = $servers_count - $limit;
|
||||
$number_of_servers_to_disable = $servers_count - $this->team->limits;
|
||||
if ($number_of_servers_to_disable > 0) {
|
||||
$servers = $servers->sortbyDesc('created_at');
|
||||
$servers_to_disable = $servers->take($number_of_servers_to_disable);
|
||||
|
@@ -30,8 +30,8 @@ class ServerStorageCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
if (! $this->server->isFunctional()) {
|
||||
return 'Server is not ready.';
|
||||
if ($this->server->isFunctional() === false) {
|
||||
return 'Server is not functional.';
|
||||
}
|
||||
$team = data_get($this->server, 'team');
|
||||
$serverDiskUsageNotificationThreshold = data_get($this->server, 'settings.server_disk_usage_notification_threshold');
|
||||
|
@@ -3,16 +3,21 @@
|
||||
namespace App\Livewire\Admin;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Container\Attributes\Auth as AttributesAuth;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
public $active_subscribers = [];
|
||||
public int $activeSubscribers;
|
||||
|
||||
public $inactive_subscribers = [];
|
||||
public int $inactiveSubscribers;
|
||||
|
||||
public $search = '';
|
||||
public Collection $foundUsers;
|
||||
|
||||
public string $search = '';
|
||||
|
||||
public function mount()
|
||||
{
|
||||
@@ -20,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();
|
||||
@@ -29,50 +34,32 @@ class Index extends Component
|
||||
public function submitSearch()
|
||||
{
|
||||
if ($this->search !== '') {
|
||||
$this->inactive_subscribers = User::whereDoesntHave('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->where(function ($query) {
|
||||
$this->foundUsers = User::where(function ($query) {
|
||||
$query->where('name', 'like', "%{$this->search}%")
|
||||
->orWhere('email', 'like', "%{$this->search}%");
|
||||
})->get()->filter(function ($user) {
|
||||
return $user->id !== 0;
|
||||
});
|
||||
$this->active_subscribers = User::whereHas('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->where(function ($query) {
|
||||
$query->where('name', 'like', "%{$this->search}%")
|
||||
->orWhere('email', 'like', "%{$this->search}%");
|
||||
})->get()->filter(function ($user) {
|
||||
return $user->id !== 0;
|
||||
});
|
||||
} else {
|
||||
$this->getSubscribers();
|
||||
})->get();
|
||||
}
|
||||
}
|
||||
|
||||
public function getSubscribers()
|
||||
{
|
||||
$this->inactive_subscribers = User::whereDoesntHave('teams', function ($query) {
|
||||
$this->inactiveSubscribers = User::whereDoesntHave('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->get()->filter(function ($user) {
|
||||
return $user->id !== 0;
|
||||
});
|
||||
$this->active_subscribers = User::whereHas('teams', function ($query) {
|
||||
})->count();
|
||||
$this->activeSubscribers = User::whereHas('teams', function ($query) {
|
||||
$query->whereRelation('subscription', 'stripe_subscription_id', '!=', null);
|
||||
})->get()->filter(function ($user) {
|
||||
return $user->id !== 0;
|
||||
});
|
||||
})->count();
|
||||
}
|
||||
|
||||
public function switchUser(int $user_id)
|
||||
{
|
||||
if (auth()->user()->id !== 0) {
|
||||
if (AttributesAuth::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'));
|
||||
|
@@ -16,28 +16,28 @@ class Dashboard extends Component
|
||||
|
||||
public Collection $servers;
|
||||
|
||||
public Collection $private_keys;
|
||||
public Collection $privateKeys;
|
||||
|
||||
public $deployments_per_server;
|
||||
public array $deploymentsPerServer = [];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->private_keys = PrivateKey::ownedByCurrentTeam()->get();
|
||||
$this->privateKeys = PrivateKey::ownedByCurrentTeam()->get();
|
||||
$this->servers = Server::ownedByCurrentTeam()->get();
|
||||
$this->projects = Project::ownedByCurrentTeam()->get();
|
||||
$this->get_deployments();
|
||||
$this->loadDeployments();
|
||||
}
|
||||
|
||||
public function cleanup_queue()
|
||||
public function cleanupQueue()
|
||||
{
|
||||
Artisan::queue('cleanup:deployment-queue', [
|
||||
'--team-id' => currentTeam()->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function get_deployments()
|
||||
public function loadDeployments()
|
||||
{
|
||||
$this->deployments_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('server_id', $this->servers->pluck('id'))->get([
|
||||
$this->deploymentsPerServer = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('server_id', $this->servers->pluck('id'))->get([
|
||||
'id',
|
||||
'application_id',
|
||||
'application_name',
|
||||
|
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Destination;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class Form extends Component
|
||||
{
|
||||
public mixed $destination;
|
||||
|
||||
protected $rules = [
|
||||
'destination.name' => 'required',
|
||||
'destination.network' => 'required',
|
||||
'destination.server.ip' => 'required',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'destination.name' => 'name',
|
||||
'destination.network' => 'network',
|
||||
'destination.server.ip' => 'IP Address/Domain',
|
||||
];
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
$this->destination->save();
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
try {
|
||||
if ($this->destination->getMorphClass() === \App\Models\StandaloneDocker::class) {
|
||||
if ($this->destination->attachedTo()) {
|
||||
return $this->dispatch('error', 'You must delete all resources before deleting this destination.');
|
||||
}
|
||||
instant_remote_process(["docker network disconnect {$this->destination->network} coolify-proxy"], $this->destination->server, throwError: false);
|
||||
instant_remote_process(['docker network rm -f '.$this->destination->network], $this->destination->server);
|
||||
}
|
||||
$this->destination->delete();
|
||||
|
||||
return redirect()->route('destination.all');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
23
app/Livewire/Destination/Index.php
Normal file
23
app/Livewire/Destination/Index.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Destination;
|
||||
|
||||
use App\Models\Server;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
#[Locked]
|
||||
public $servers;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->servers = Server::isUsable()->get();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.destination.index');
|
||||
}
|
||||
}
|
@@ -3,111 +3,91 @@
|
||||
namespace App\Livewire\Destination\New;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker as ModelsStandaloneDocker;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
class Docker extends Component
|
||||
{
|
||||
#[Locked]
|
||||
public $servers;
|
||||
|
||||
#[Locked]
|
||||
public Server $selectedServer;
|
||||
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $name;
|
||||
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $network;
|
||||
|
||||
public ?Collection $servers = null;
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $serverId;
|
||||
|
||||
public Server $server;
|
||||
#[Validate(['required', 'boolean'])]
|
||||
public bool $isSwarm = false;
|
||||
|
||||
public ?int $server_id = null;
|
||||
|
||||
public bool $is_swarm = false;
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|string',
|
||||
'network' => 'required|string',
|
||||
'server_id' => 'required|integer',
|
||||
'is_swarm' => 'boolean',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'name' => 'name',
|
||||
'network' => 'network',
|
||||
'server_id' => 'server',
|
||||
'is_swarm' => 'swarm',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
public function mount(?string $server_id = null)
|
||||
{
|
||||
if (is_null($this->servers)) {
|
||||
$this->servers = Server::isReachable()->get();
|
||||
}
|
||||
if (request()->query('server_id')) {
|
||||
$this->server_id = request()->query('server_id');
|
||||
$this->network = new Cuid2;
|
||||
$this->servers = Server::isUsable()->get();
|
||||
if ($server_id) {
|
||||
$this->selectedServer = $this->servers->find($server_id);
|
||||
$this->serverId = $this->selectedServer->id;
|
||||
} else {
|
||||
if ($this->servers->count() > 0) {
|
||||
$this->server_id = $this->servers->first()->id;
|
||||
}
|
||||
}
|
||||
if (request()->query('network_name')) {
|
||||
$this->network = request()->query('network_name');
|
||||
} else {
|
||||
$this->network = new Cuid2;
|
||||
}
|
||||
if ($this->servers->count() > 0) {
|
||||
$this->name = str("{$this->servers->first()->name}-{$this->network}")->kebab();
|
||||
$this->selectedServer = $this->servers->first();
|
||||
$this->serverId = $this->selectedServer->id;
|
||||
}
|
||||
$this->generateName();
|
||||
}
|
||||
|
||||
public function generate_name()
|
||||
public function updatedServerId()
|
||||
{
|
||||
$this->server = Server::find($this->server_id);
|
||||
$this->name = str("{$this->server->name}-{$this->network}")->kebab();
|
||||
$this->selectedServer = $this->servers->find($this->serverId);
|
||||
$this->generateName();
|
||||
}
|
||||
|
||||
public function generateName()
|
||||
{
|
||||
$name = data_get($this->selectedServer, 'name', new Cuid2);
|
||||
$this->name = str("{$name}-{$this->network}")->kebab();
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
try {
|
||||
$this->server = Server::find($this->server_id);
|
||||
if ($this->is_swarm) {
|
||||
$found = $this->server->swarmDockers()->where('network', $this->network)->first();
|
||||
$this->validate();
|
||||
if ($this->isSwarm) {
|
||||
$found = $this->selectedServer->swarmDockers()->where('network', $this->network)->first();
|
||||
if ($found) {
|
||||
$this->dispatch('error', 'Network already added to this server.');
|
||||
|
||||
return;
|
||||
throw new \Exception('Network already added to this server.');
|
||||
} else {
|
||||
$docker = SwarmDocker::create([
|
||||
'name' => $this->name,
|
||||
'network' => $this->network,
|
||||
'server_id' => $this->server_id,
|
||||
'server_id' => $this->selectedServer->id,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$found = $this->server->standaloneDockers()->where('network', $this->network)->first();
|
||||
$found = $this->selectedServer->standaloneDockers()->where('network', $this->network)->first();
|
||||
if ($found) {
|
||||
$this->dispatch('error', 'Network already added to this server.');
|
||||
|
||||
return;
|
||||
throw new \Exception('Network already added to this server.');
|
||||
} else {
|
||||
$docker = ModelsStandaloneDocker::create([
|
||||
$docker = StandaloneDocker::create([
|
||||
'name' => $this->name,
|
||||
'network' => $this->network,
|
||||
'server_id' => $this->server_id,
|
||||
'server_id' => $this->selectedServer->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
$this->createNetworkAndAttachToProxy();
|
||||
|
||||
return redirect()->route('destination.show', $docker->uuid);
|
||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->selectedServer);
|
||||
instant_remote_process($connectProxyToDockerNetworks, $this->selectedServer, false);
|
||||
$this->dispatch('reloadWindow');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
private function createNetworkAndAttachToProxy()
|
||||
{
|
||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||
}
|
||||
}
|
||||
|
@@ -5,71 +5,91 @@ namespace App\Livewire\Destination;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
public Server $server;
|
||||
#[Locked]
|
||||
public $destination;
|
||||
|
||||
public Collection|array $networks = [];
|
||||
#[Validate(['string', 'required'])]
|
||||
public string $name;
|
||||
|
||||
private function createNetworkAndAttachToProxy()
|
||||
#[Validate(['string', 'required'])]
|
||||
public string $network;
|
||||
|
||||
#[Validate(['string', 'required'])]
|
||||
public string $serverIp;
|
||||
|
||||
public function mount(string $destination_uuid)
|
||||
{
|
||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||
}
|
||||
try {
|
||||
$destination = StandaloneDocker::whereUuid($destination_uuid)->first() ??
|
||||
SwarmDocker::whereUuid($destination_uuid)->firstOrFail();
|
||||
|
||||
public function add($name)
|
||||
{
|
||||
if ($this->server->isSwarm()) {
|
||||
$found = $this->server->swarmDockers()->where('network', $name)->first();
|
||||
if ($found) {
|
||||
$this->dispatch('error', 'Network already added to this server.');
|
||||
|
||||
return;
|
||||
} else {
|
||||
SwarmDocker::create([
|
||||
'name' => $this->server->name.'-'.$name,
|
||||
'network' => $this->name,
|
||||
'server_id' => $this->server->id,
|
||||
]);
|
||||
$ownedByTeam = Server::ownedByCurrentTeam()->each(function ($server) use ($destination) {
|
||||
if ($server->standaloneDockers->contains($destination) || $server->swarmDockers->contains($destination)) {
|
||||
$this->destination = $destination;
|
||||
$this->syncData();
|
||||
}
|
||||
});
|
||||
if ($ownedByTeam === false) {
|
||||
return redirect()->route('destination.index');
|
||||
}
|
||||
} else {
|
||||
$found = $this->server->standaloneDockers()->where('network', $name)->first();
|
||||
if ($found) {
|
||||
$this->dispatch('error', 'Network already added to this server.');
|
||||
|
||||
return;
|
||||
} else {
|
||||
StandaloneDocker::create([
|
||||
'name' => $this->server->name.'-'.$name,
|
||||
'network' => $name,
|
||||
'server_id' => $this->server->id,
|
||||
]);
|
||||
}
|
||||
$this->createNetworkAndAttachToProxy();
|
||||
$this->destination = $destination;
|
||||
$this->syncData();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function scan()
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($this->server->isSwarm()) {
|
||||
$alreadyAddedNetworks = $this->server->swarmDockers;
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->destination->name = $this->name;
|
||||
$this->destination->network = $this->network;
|
||||
$this->destination->server->ip = $this->serverIp;
|
||||
$this->destination->save();
|
||||
} else {
|
||||
$alreadyAddedNetworks = $this->server->standaloneDockers;
|
||||
$this->name = $this->destination->name;
|
||||
$this->network = $this->destination->network;
|
||||
$this->serverIp = $this->destination->server->ip;
|
||||
}
|
||||
$networks = instant_remote_process(['docker network ls --format "{{json .}}"'], $this->server, false);
|
||||
$this->networks = format_docker_command_output_to_json($networks)->filter(function ($network) {
|
||||
return $network['Name'] !== 'bridge' && $network['Name'] !== 'host' && $network['Name'] !== 'none';
|
||||
})->filter(function ($network) use ($alreadyAddedNetworks) {
|
||||
return ! $alreadyAddedNetworks->contains('network', $network['Name']);
|
||||
});
|
||||
if ($this->networks->count() === 0) {
|
||||
$this->dispatch('success', 'No new destinations found on this server.');
|
||||
}
|
||||
|
||||
return;
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Destination saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
$this->dispatch('success', 'Scan done.');
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
try {
|
||||
if ($this->destination->getMorphClass() === \App\Models\StandaloneDocker::class) {
|
||||
if ($this->destination->attachedTo()) {
|
||||
return $this->dispatch('error', 'You must delete all resources before deleting this destination.');
|
||||
}
|
||||
instant_remote_process(["docker network disconnect {$this->destination->network} coolify-proxy"], $this->destination->server, throwError: false);
|
||||
instant_remote_process(['docker network rm -f '.$this->destination->network], $this->destination->server);
|
||||
}
|
||||
$this->destination->delete();
|
||||
|
||||
return redirect()->route('destination.index');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.destination.show');
|
||||
}
|
||||
}
|
||||
|
@@ -5,55 +5,39 @@ namespace App\Livewire;
|
||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Help extends Component
|
||||
{
|
||||
use WithRateLimiting;
|
||||
|
||||
#[Validate(['required', 'min:10', 'max:1000'])]
|
||||
public string $description;
|
||||
|
||||
#[Validate(['required', 'min:3'])]
|
||||
public string $subject;
|
||||
|
||||
public ?string $path = null;
|
||||
|
||||
protected $rules = [
|
||||
'description' => 'required|min:10',
|
||||
'subject' => 'required|min:3',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->path = Route::current()?->uri() ?? null;
|
||||
if (isDev()) {
|
||||
$this->description = "I'm having trouble with {$this->path}";
|
||||
$this->subject = "Help with {$this->path}";
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->rateLimit(3, 30);
|
||||
$this->validate();
|
||||
$debug = "Route: {$this->path}";
|
||||
$this->rateLimit(3, 30);
|
||||
|
||||
$settings = instanceSettings();
|
||||
$mail = new MailMessage;
|
||||
$mail->view(
|
||||
'emails.help',
|
||||
[
|
||||
'description' => $this->description,
|
||||
'debug' => $debug,
|
||||
]
|
||||
);
|
||||
$mail->subject("[HELP]: {$this->subject}");
|
||||
$settings = instanceSettings();
|
||||
$type = set_transanctional_email_settings($settings);
|
||||
if (! $type) {
|
||||
|
||||
// Sending feedback through Cloud API
|
||||
if ($type === false) {
|
||||
$url = 'https://app.coolify.io/api/feedback';
|
||||
if (isDev()) {
|
||||
$url = 'http://localhost:80/api/feedback';
|
||||
}
|
||||
Http::post($url, [
|
||||
'content' => 'User: `'.auth()->user()?->email.'` with subject: `'.$this->subject.'` has the following problem: `'.$this->description.'`',
|
||||
]);
|
||||
|
@@ -3,6 +3,7 @@
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use Illuminate\Container\Attributes\Auth as AttributesAuth;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
@@ -31,7 +32,7 @@ class NavbarDeleteTeam extends Component
|
||||
$currentTeam->delete();
|
||||
|
||||
$currentTeam->members->each(function ($user) use ($currentTeam) {
|
||||
if ($user->id === auth()->user()->id) {
|
||||
if ($user->id === AttributesAuth::id()) {
|
||||
return;
|
||||
}
|
||||
$user->teams()->detach($currentTeam);
|
||||
|
@@ -4,60 +4,124 @@ namespace App\Livewire\Notifications;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Notifications\Test;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Discord extends Component
|
||||
{
|
||||
public Team $team;
|
||||
|
||||
protected $rules = [
|
||||
'team.discord_enabled' => 'nullable|boolean',
|
||||
'team.discord_webhook_url' => 'required|url',
|
||||
'team.discord_notifications_test' => 'nullable|boolean',
|
||||
'team.discord_notifications_deployments' => 'nullable|boolean',
|
||||
'team.discord_notifications_status_changes' => 'nullable|boolean',
|
||||
'team.discord_notifications_database_backups' => 'nullable|boolean',
|
||||
'team.discord_notifications_scheduled_tasks' => 'nullable|boolean',
|
||||
'team.discord_notifications_server_disk_usage' => 'nullable|boolean',
|
||||
];
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordEnabled = false;
|
||||
|
||||
protected $validationAttributes = [
|
||||
'team.discord_webhook_url' => 'Discord Webhook',
|
||||
];
|
||||
#[Validate(['url', 'nullable'])]
|
||||
public ?string $discordWebhookUrl = null;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordNotificationsTest = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordNotificationsDeployments = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordNotificationsStatusChanges = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordNotificationsDatabaseBackups = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordNotificationsScheduledTasks = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $discordNotificationsServerDiskUsage = 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->discord_enabled = $this->discordEnabled;
|
||||
$this->team->discord_webhook_url = $this->discordWebhookUrl;
|
||||
$this->team->discord_notifications_test = $this->discordNotificationsTest;
|
||||
$this->team->discord_notifications_deployments = $this->discordNotificationsDeployments;
|
||||
$this->team->discord_notifications_status_changes = $this->discordNotificationsStatusChanges;
|
||||
$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;
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
} else {
|
||||
$this->discordEnabled = $this->team->discord_enabled;
|
||||
$this->discordWebhookUrl = $this->team->discord_webhook_url;
|
||||
$this->discordNotificationsTest = $this->team->discord_notifications_test;
|
||||
$this->discordNotificationsDeployments = $this->team->discord_notifications_deployments;
|
||||
$this->discordNotificationsStatusChanges = $this->team->discord_notifications_status_changes;
|
||||
$this->discordNotificationsDatabaseBackups = $this->team->discord_notifications_database_backups;
|
||||
$this->discordNotificationsScheduledTasks = $this->team->discord_notifications_scheduled_tasks;
|
||||
$this->discordNotificationsServerDiskUsage = $this->team->discord_notifications_server_disk_usage;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
$this->submit();
|
||||
} catch (\Throwable) {
|
||||
$this->team->discord_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 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()
|
||||
|
@@ -5,75 +5,133 @@ 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'])]
|
||||
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;
|
||||
|
||||
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 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 sendTestNotification()
|
||||
{
|
||||
try {
|
||||
@@ -98,38 +156,44 @@ 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' => '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 +201,7 @@ class Email extends Component
|
||||
|
||||
public function saveModel()
|
||||
{
|
||||
$this->team->save();
|
||||
$this->syncData(true);
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
}
|
||||
@@ -146,43 +210,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 +219,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;
|
||||
}
|
||||
|
@@ -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,24 +3,17 @@
|
||||
namespace App\Livewire\Project;
|
||||
|
||||
use App\Models\Project;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class AddEmpty extends Component
|
||||
{
|
||||
public string $name = '';
|
||||
#[Validate(['required', 'string', 'min:3'])]
|
||||
public string $name;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public string $description = '';
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|string|min:3',
|
||||
'description' => 'nullable|string',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'name' => 'Project Name',
|
||||
'description' => 'Project Description',
|
||||
];
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
@@ -34,8 +27,6 @@ class AddEmpty extends Component
|
||||
return redirect()->route('project.show', $project->uuid);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->name = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Project;
|
||||
|
||||
use App\Models\Environment;
|
||||
use App\Models\Project;
|
||||
use Livewire\Component;
|
||||
|
||||
class AddEnvironment extends Component
|
||||
{
|
||||
public Project $project;
|
||||
|
||||
public string $name = '';
|
||||
|
||||
public string $description = '';
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|string|min:3',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
'name' => 'Environment Name',
|
||||
];
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$environment = Environment::create([
|
||||
'name' => $this->name,
|
||||
'project_id' => $this->project->id,
|
||||
]);
|
||||
|
||||
return redirect()->route('project.resource.index', [
|
||||
'project_uuid' => $this->project->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
handleError($e, $this);
|
||||
} finally {
|
||||
$this->name = '';
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,120 +3,200 @@
|
||||
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 $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->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;
|
||||
}
|
||||
}
|
||||
|
||||
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.');
|
||||
}
|
||||
|
||||
|
@@ -10,49 +10,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,55 +4,92 @@ namespace App\Livewire\Project\Application;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\PrivateKey;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Source extends Component
|
||||
{
|
||||
public $applicationId;
|
||||
|
||||
public Application $application;
|
||||
|
||||
public $private_keys;
|
||||
#[Locked]
|
||||
public $privateKeys;
|
||||
|
||||
protected $rules = [
|
||||
'application.git_repository' => 'required',
|
||||
'application.git_branch' => 'required',
|
||||
'application.git_commit_sha' => 'nullable',
|
||||
];
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $privateKeyName = null;
|
||||
|
||||
protected $validationAttributes = [
|
||||
'application.git_repository' => 'repository',
|
||||
'application.git_branch' => 'branch',
|
||||
'application.git_commit_sha' => 'commit sha',
|
||||
];
|
||||
#[Validate(['nullable', 'integer'])]
|
||||
public ?int $privateKeyId = null;
|
||||
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $gitRepository;
|
||||
|
||||
#[Validate(['required', 'string'])]
|
||||
public string $gitBranch;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $gitCommitSha = null;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->get_private_keys();
|
||||
try {
|
||||
$this->syncData();
|
||||
$this->getPrivateKeys();
|
||||
} catch (\Throwable $e) {
|
||||
handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_private_keys()
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
$this->private_keys = PrivateKey::whereTeamId(currentTeam()->id)->get()->reject(function ($key) {
|
||||
return $key->id == $this->application->private_key_id;
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->application->update([
|
||||
'git_repository' => $this->gitRepository,
|
||||
'git_branch' => $this->gitBranch,
|
||||
'git_commit_sha' => $this->gitCommitSha,
|
||||
'private_key_id' => $this->privateKeyId,
|
||||
]);
|
||||
} else {
|
||||
$this->gitRepository = $this->application->git_repository;
|
||||
$this->gitBranch = $this->application->git_branch;
|
||||
$this->gitCommitSha = $this->application->git_commit_sha;
|
||||
$this->privateKeyId = $this->application->private_key_id;
|
||||
$this->privateKeyName = data_get($this->application, 'private_key.name');
|
||||
}
|
||||
}
|
||||
|
||||
private function getPrivateKeys()
|
||||
{
|
||||
$this->privateKeys = PrivateKey::whereTeamId(currentTeam()->id)->get()->reject(function ($key) {
|
||||
return $key->id == $this->privateKeyId;
|
||||
});
|
||||
}
|
||||
|
||||
public function setPrivateKey(int $private_key_id)
|
||||
public function setPrivateKey(int $privateKeyId)
|
||||
{
|
||||
$this->application->private_key_id = $private_key_id;
|
||||
$this->application->save();
|
||||
$this->application->refresh();
|
||||
$this->get_private_keys();
|
||||
try {
|
||||
$this->privateKeyId = $privateKeyId;
|
||||
$this->syncData(true);
|
||||
$this->getPrivateKeys();
|
||||
$this->application->refresh();
|
||||
$this->privateKeyName = $this->application->private_key->name;
|
||||
$this->dispatch('success', 'Private key updated!');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
if (! $this->application->git_commit_sha) {
|
||||
$this->application->git_commit_sha = 'HEAD';
|
||||
try {
|
||||
if (str($this->gitCommitSha)->isEmpty()) {
|
||||
$this->gitCommitSha = 'HEAD';
|
||||
}
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Application source updated!');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
$this->application->save();
|
||||
$this->dispatch('success', 'Application source updated!');
|
||||
}
|
||||
}
|
||||
|
@@ -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,44 +4,45 @@ 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(['required', 'integer'])]
|
||||
public int $s3StorageId;
|
||||
|
||||
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.');
|
||||
@@ -51,8 +52,8 @@ class CreateScheduledBackup extends Component
|
||||
$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,
|
||||
@@ -72,10 +73,10 @@ class CreateScheduledBackup extends Component
|
||||
$this->dispatch('refreshScheduledBackups');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
handleError($e, $this);
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->frequency = '';
|
||||
$this->save_s3 = true;
|
||||
$this->saveToS3 = 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,34 +3,47 @@
|
||||
namespace App\Livewire\Project;
|
||||
|
||||
use App\Models\Project;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Edit extends Component
|
||||
{
|
||||
public Project $project;
|
||||
|
||||
protected $rules = [
|
||||
'project.name' => 'required|min:3|max:255',
|
||||
'project.description' => 'nullable|string|max:255',
|
||||
];
|
||||
#[Validate(['required', 'string', 'min:3', 'max:255'])]
|
||||
public string $name;
|
||||
|
||||
public function mount()
|
||||
#[Validate(['nullable', 'string', 'max:255'])]
|
||||
public ?string $description = null;
|
||||
|
||||
public function mount(string $project_uuid)
|
||||
{
|
||||
$projectUuid = request()->route('project_uuid');
|
||||
$teamId = currentTeam()->id;
|
||||
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
|
||||
if (! $project) {
|
||||
return redirect()->route('dashboard');
|
||||
try {
|
||||
$this->project = Project::where('team_id', currentTeam()->id)->where('uuid', $project_uuid)->firstOrFail();
|
||||
$this->syncData();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->project->update([
|
||||
'name' => $this->name,
|
||||
'description' => $this->description,
|
||||
]);
|
||||
} else {
|
||||
$this->name = $this->project->name;
|
||||
$this->description = $this->project->description;
|
||||
}
|
||||
$this->project = $project;
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->project->save();
|
||||
$this->dispatch('saved');
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Project updated.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
|
@@ -4,6 +4,8 @@ namespace App\Livewire\Project;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\Project;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class EnvironmentEdit extends Component
|
||||
@@ -12,29 +14,45 @@ class EnvironmentEdit extends Component
|
||||
|
||||
public Application $application;
|
||||
|
||||
#[Locked]
|
||||
public $environment;
|
||||
|
||||
public array $parameters;
|
||||
#[Validate(['required', 'string', 'min:3', 'max:255'])]
|
||||
public string $name;
|
||||
|
||||
protected $rules = [
|
||||
'environment.name' => 'required|min:3|max:255',
|
||||
'environment.description' => 'nullable|min:3|max:255',
|
||||
];
|
||||
#[Validate(['nullable', 'string', 'max:255'])]
|
||||
public ?string $description = null;
|
||||
|
||||
public function mount()
|
||||
public function mount(string $project_uuid, string $environment_name)
|
||||
{
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->project = Project::ownedByCurrentTeam()->where('uuid', request()->route('project_uuid'))->first();
|
||||
$this->environment = $this->project->environments()->where('name', request()->route('environment_name'))->first();
|
||||
try {
|
||||
$this->project = Project::ownedByCurrentTeam()->where('uuid', $project_uuid)->firstOrFail();
|
||||
$this->environment = $this->project->environments()->where('name', $environment_name)->firstOrFail();
|
||||
$this->syncData();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->environment->update([
|
||||
'name' => $this->name,
|
||||
'description' => $this->description,
|
||||
]);
|
||||
} else {
|
||||
$this->name = $this->environment->name;
|
||||
$this->description = $this->environment->description;
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
try {
|
||||
$this->environment->save();
|
||||
|
||||
return redirect()->route('project.environment.edit', ['project_uuid' => $this->project->uuid, 'environment_name' => $this->environment->name]);
|
||||
$this->syncData(true);
|
||||
$this->redirectRoute('project.environment.edit', ['environment_name' => $this->environment->name, 'project_uuid' => $this->project->uuid]);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
@@ -158,7 +158,7 @@ class Select extends Component
|
||||
[
|
||||
'id' => 'mariadb',
|
||||
'name' => 'MariaDB',
|
||||
'description' => 'MariaDB is a community-developed, commercially supported fork of the MySQL relational database management system, intended to remain free and open-source software under the GNU General Public License.',
|
||||
'description' => 'MariaDB is a community-developed, commercially supported fork of the MySQL relational database management system, intended to remain free and open-source.',
|
||||
'logo' => '<svg class="w-[4.5rem] aspect-square h-[4.5rem] p-2 transition-all duration-200 opacity-30 grayscale group-hover:grayscale-0 group-hover:opacity-100 dark:fill-white fill-black" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"><path fill="currentColor" d="M127.434 12.182a1.727 1.727 0 0 0-1.174-.392c-1.168 0-2.68.793-3.495 1.218l-.322.165a11.095 11.095 0 0 1-4.365 1.1c-1.554.049-2.892.14-4.635.322-10.327 1.06-14.933 8.975-19.37 16.63-2.416 4.164-4.91 8.489-8.33 11.793a22.472 22.472 0 0 1-2.252 1.913c-3.54 2.631-7.985 4.51-11.443 5.84-3.329 1.273-6.964 2.417-10.474 3.524-3.219 1.012-6.255 1.97-9.048 3.008a96.902 96.902 0 0 1-3.275 1.14c-2.545.825-4.378 1.458-7.06 3.304a45.386 45.386 0 0 0-2.804 2.066 29.585 29.585 0 0 0-5.597 5.894 34.802 34.802 0 0 1-4.701 5.642c-.566.554-1.57.826-3.074.826-1.76 0-3.895-.363-6.154-.747-2.33-.413-4.738-.805-6.803-.805-1.677 0-2.962.273-3.92.826 0 0-1.617.942-2.298 2.16l.67.302a13.718 13.718 0 0 1 2.859 2.065 14.342 14.342 0 0 0 2.973 2.115 2.553 2.553 0 0 1 .918.582c-.281.413-.694.946-1.129 1.516-2.384 3.119-3.774 5.09-2.977 6.163a2.507 2.507 0 0 0 1.239.28c5.196 0 7.989-1.35 11.52-3.06 1.024-.495 2.066-1.004 3.305-1.528 2.065-.896 4.288-2.325 6.647-3.838 3.084-2.01 6.31-4.076 9.442-5.072a25.734 25.734 0 0 1 7.943-1.115c3.305 0 6.783.441 10.138.872 2.499.322 5.089.652 7.63.805.986.057 1.9.086 2.787.086a32.307 32.307 0 0 0 3.557-.185l.284-.1c1.781-1.094 2.617-3.444 3.425-5.717.52-1.462.96-2.775 1.652-3.61a1.054 1.054 0 0 1 .132-.11.166.166 0 0 1 .202.032v.066c-.412 8.885-3.99 14.527-7.608 19.543l-2.416 2.59s3.383 0 5.307-.744c7.024-2.099 12.324-6.725 16.181-14.103a60.185 60.185 0 0 0 2.549-5.82c.065-.165.673-.47.616.384-.021.252-.038.533-.059.827 0 .173 0 .35-.033.528-.1 1.24-.392 3.859-.392 3.859l2.169-1.162c5.229-3.304 9.26-9.97 12.318-20.343 1.272-4.321 2.205-8.613 3.027-12.392.983-4.545 1.83-8.44 2.801-9.952 1.524-2.37 3.85-3.973 6.101-5.53.306-.211.616-.414.917-.637 2.83-1.986 5.643-4.279 6.263-8.555v-.095c.45-3.189.07-4.002-.364-4.373zm-7.283 103.727h-10.327V97.92h9.315c3.56 0 6.952.67 6.902 4.66 0 2.813-1.747 3.59-3.589 3.886 2.615.224 4.188 1.892 4.188 4.586.017 4.035-3.523 4.858-6.489 4.858zm-.772-10.14c3.565 0 4.362-1.372 4.362-3.115 0-2.619-1.595-3.214-4.362-3.214h-7.402v6.328zm.099 1.52h-7.501v7.1h7.823c2.194 0 4.511-.723 4.511-3.486 0-3.19-2.665-3.615-4.833-3.615zm-31.497-9.37h8.125c6.828 0 10.24 3.764 10.19 8.994.05 5.436-3.716 8.997-9.591 8.997H87.98zm2.242 1.596v14.825h6.197c5.432 0 7.501-3.665 7.501-7.477 0-4.309-2.59-7.348-7.5-7.348zm-10.838 5.357v-2.095h3.404v13.132h-3.392v-2.114c-.896 1.52-2.739 2.391-4.982 2.391-4.684 0-7.303-3.305-7.303-7.105 0-3.664 2.479-6.609 6.804-6.609 2.454.029 4.498.855 5.469 2.4zm-8.675 4.387c0 2.416 1.52 4.485 4.462 4.485 2.841 0 4.386-2.02 4.386-4.411 0-2.392-1.599-4.436-4.544-4.436-2.828 0-4.3 2.04-4.3 4.362zm-10.013-9.947a1.722 1.722 0 0 1 1.818-1.768 1.788 1.788 0 0 1 1.847 1.821 1.714 1.714 0 0 1-1.847 1.744 1.743 1.743 0 0 1-1.818-1.797zm.15 3.465h3.39v9.596c0 .595.125 1.02.62 1.02a3.657 3.657 0 0 0 .648-.073l.525 2.478a5.931 5.931 0 0 1-2.242.414c-1.421 0-2.942-.414-2.942-3.64zM52.15 115.91h-3.386v-13.132h3.386v2.942a5.197 5.197 0 0 1 4.735-3.218 6.13 6.13 0 0 1 2.119.347l-.723 2.479a7.435 7.435 0 0 0-1.793-.249c-2.445 0-4.338 1.843-4.338 4.545zm-11.037-11.037v-2.095h3.392v13.132h-3.392v-2.114c-.896 1.52-2.738 2.391-4.982 2.391-4.688 0-7.303-3.305-7.303-7.105 0-3.664 2.479-6.609 6.804-6.609 2.466.029 4.51.855 5.481 2.4zm-8.675 4.387c0 2.416 1.52 4.485 4.462 4.485 2.838 0 4.383-2.02 4.383-4.411 0-2.391-1.595-4.436-4.544-4.436-2.826 0-4.296 2.04-4.296 4.362zm-9.24-11.34 4.651 17.99h-3.51L21.24 102.95 15.4 115.91h-2.965l-5.808-12.883-3.19 12.883H0l4.61-17.99h3.04l6.28 13.93 6.253-13.93z"/></svg>',
|
||||
],
|
||||
[
|
||||
@@ -326,7 +326,7 @@ class Select extends Component
|
||||
|
||||
public function loadServers()
|
||||
{
|
||||
$this->servers = Server::isUsable()->get();
|
||||
$this->servers = Server::isUsable()->get()->sortBy('name');
|
||||
$this->allServers = $this->servers;
|
||||
}
|
||||
}
|
||||
|
@@ -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',
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,27 +2,46 @@
|
||||
|
||||
namespace App\Livewire\Project;
|
||||
|
||||
use App\Models\Environment;
|
||||
use App\Models\Project;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
public Project $project;
|
||||
|
||||
public $environments;
|
||||
#[Validate(['required', 'string', 'min:3'])]
|
||||
public string $name;
|
||||
|
||||
public function mount()
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $description = null;
|
||||
|
||||
public function mount(string $project_uuid)
|
||||
{
|
||||
$projectUuid = request()->route('project_uuid');
|
||||
$teamId = currentTeam()->id;
|
||||
|
||||
$project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first();
|
||||
if (! $project) {
|
||||
return redirect()->route('dashboard');
|
||||
try {
|
||||
$this->project = Project::where('team_id', currentTeam()->id)->where('uuid', $project_uuid)->firstOrFail();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->environments = $project->environments->sortBy('created_at');
|
||||
$this->project = $project;
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$environment = Environment::create([
|
||||
'name' => $this->name,
|
||||
'project_id' => $this->project->id,
|
||||
]);
|
||||
|
||||
return redirect()->route('project.resource.index', [
|
||||
'project_uuid' => $this->project->uuid,
|
||||
'environment_name' => $environment->name,
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
@@ -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')]
|
||||
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)
|
||||
|
@@ -3,14 +3,14 @@
|
||||
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)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Server\Destination;
|
||||
namespace App\Livewire\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
@@ -8,7 +8,7 @@ use App\Models\SwarmDocker;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
class Destinations extends Component
|
||||
{
|
||||
public Server $server;
|
||||
|
||||
@@ -19,7 +19,6 @@ class Show extends Component
|
||||
try {
|
||||
$this->networks = collect();
|
||||
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
||||
loggy($this->server);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
@@ -86,6 +85,6 @@ class Show extends Component
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.destination.show');
|
||||
return view('livewire.server.destinations');
|
||||
}
|
||||
}
|
@@ -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)
|
||||
@@ -52,7 +52,7 @@ class LogDrains extends Component
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->customValidation();
|
||||
$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;
|
||||
@@ -79,6 +79,44 @@ class LogDrains extends Component
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@@ -5,79 +5,81 @@ 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\Locked;
|
||||
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;
|
||||
|
||||
#[Locked]
|
||||
public array $timezones;
|
||||
|
||||
public function getListeners()
|
||||
@@ -85,8 +87,8 @@ class Show extends Component
|
||||
$teamId = auth()->user()->currentTeam()->id;
|
||||
|
||||
return [
|
||||
"echo-private:team.{$teamId},CloudflareTunnelConfigured" => '$refresh',
|
||||
'refreshServerShow' => '$refresh',
|
||||
"echo-private:team.{$teamId},CloudflareTunnelConfigured" => 'refresh',
|
||||
'refreshServerShow' => 'refresh',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -151,6 +153,12 @@ class Show extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function refresh()
|
||||
{
|
||||
$this->syncData();
|
||||
$this->dispatch('$refresh');
|
||||
}
|
||||
|
||||
public function validateServer($install = true)
|
||||
{
|
||||
try {
|
||||
|
@@ -8,7 +8,7 @@ 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\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
@@ -20,58 +20,58 @@ class Index extends Component
|
||||
#[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')]
|
||||
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()
|
||||
|
@@ -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()
|
||||
|
@@ -3,102 +3,83 @@
|
||||
namespace App\Livewire;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class SettingsEmail extends Component
|
||||
{
|
||||
public InstanceSettings $settings;
|
||||
|
||||
public string $emails;
|
||||
#[Validate(['boolean'])]
|
||||
public bool $smtpEnabled = false;
|
||||
|
||||
protected $rules = [
|
||||
'settings.smtp_enabled' => 'nullable|boolean',
|
||||
'settings.smtp_host' => 'required',
|
||||
'settings.smtp_port' => 'required|numeric',
|
||||
'settings.smtp_encryption' => 'nullable',
|
||||
'settings.smtp_username' => 'nullable',
|
||||
'settings.smtp_password' => 'nullable',
|
||||
'settings.smtp_timeout' => 'nullable',
|
||||
'settings.smtp_from_address' => 'required|email',
|
||||
'settings.smtp_from_name' => 'required',
|
||||
'settings.resend_enabled' => 'nullable|boolean',
|
||||
'settings.resend_api_key' => 'nullable',
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $smtpHost = null;
|
||||
|
||||
];
|
||||
#[Validate(['nullable', 'numeric', 'min:1', 'max:65535'])]
|
||||
public ?int $smtpPort = null;
|
||||
|
||||
protected $validationAttributes = [
|
||||
'settings.smtp_from_address' => 'From Address',
|
||||
'settings.smtp_from_name' => 'From Name',
|
||||
'settings.smtp_recipients' => 'Recipients',
|
||||
'settings.smtp_host' => 'Host',
|
||||
'settings.smtp_port' => 'Port',
|
||||
'settings.smtp_encryption' => 'Encryption',
|
||||
'settings.smtp_username' => 'Username',
|
||||
'settings.smtp_password' => 'Password',
|
||||
'settings.smtp_timeout' => 'Timeout',
|
||||
'settings.resend_api_key' => 'Resend API Key',
|
||||
];
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $smtpEncryption = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $smtpUsername = null;
|
||||
|
||||
#[Validate(['nullable'])]
|
||||
public ?string $smtpPassword = null;
|
||||
|
||||
#[Validate(['nullable', 'numeric'])]
|
||||
public ?int $smtpTimeout = null;
|
||||
|
||||
#[Validate(['nullable', 'email'])]
|
||||
public ?string $smtpFromAddress = null;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $smtpFromName = null;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $resendEnabled = false;
|
||||
|
||||
#[Validate(['nullable', 'string'])]
|
||||
public ?string $resendApiKey = null;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (isInstanceAdmin()) {
|
||||
$this->settings = instanceSettings();
|
||||
$this->emails = auth()->user()->email;
|
||||
} else {
|
||||
if (isInstanceAdmin() === false) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$this->settings = instanceSettings();
|
||||
$this->syncData();
|
||||
}
|
||||
|
||||
public function submitFromFields()
|
||||
public function syncData(bool $toModel = false)
|
||||
{
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate([
|
||||
'settings.smtp_from_address' => 'required|email',
|
||||
'settings.smtp_from_name' => 'required',
|
||||
]);
|
||||
if ($toModel) {
|
||||
$this->validate();
|
||||
$this->settings->smtp_enabled = $this->smtpEnabled;
|
||||
$this->settings->smtp_host = $this->smtpHost;
|
||||
$this->settings->smtp_port = $this->smtpPort;
|
||||
$this->settings->smtp_encryption = $this->smtpEncryption;
|
||||
$this->settings->smtp_username = $this->smtpUsername;
|
||||
$this->settings->smtp_password = $this->smtpPassword;
|
||||
$this->settings->smtp_timeout = $this->smtpTimeout;
|
||||
|
||||
$this->settings->resend_enabled = $this->resendEnabled;
|
||||
$this->settings->resend_api_key = $this->resendApiKey;
|
||||
$this->settings->save();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->smtpEnabled = $this->settings->smtp_enabled;
|
||||
$this->smtpHost = $this->settings->smtp_host;
|
||||
$this->smtpPort = $this->settings->smtp_port;
|
||||
$this->smtpEncryption = $this->settings->smtp_encryption;
|
||||
$this->smtpUsername = $this->settings->smtp_username;
|
||||
$this->smtpPassword = $this->settings->smtp_password;
|
||||
$this->smtpTimeout = $this->settings->smtp_timeout;
|
||||
$this->smtpFromAddress = $this->settings->smtp_from_address;
|
||||
$this->smtpFromName = $this->settings->smtp_from_name;
|
||||
|
||||
public function submitResend()
|
||||
{
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate([
|
||||
'settings.smtp_from_address' => 'required|email',
|
||||
'settings.smtp_from_name' => 'required',
|
||||
'settings.resend_api_key' => 'required',
|
||||
]);
|
||||
$this->settings->save();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->settings->resend_enabled = false;
|
||||
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveResend()
|
||||
{
|
||||
try {
|
||||
$this->settings->smtp_enabled = false;
|
||||
$this->submitResend();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->settings->resend_enabled = false;
|
||||
$this->submit();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
$this->resendEnabled = $this->settings->resend_enabled;
|
||||
$this->resendApiKey = $this->settings->resend_api_key;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,20 +87,29 @@ class SettingsEmail extends Component
|
||||
{
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->validate([
|
||||
'settings.smtp_from_address' => 'required|email',
|
||||
'settings.smtp_from_name' => 'required',
|
||||
'settings.smtp_host' => 'required',
|
||||
'settings.smtp_port' => 'required|numeric',
|
||||
'settings.smtp_encryption' => 'nullable',
|
||||
'settings.smtp_username' => 'nullable',
|
||||
'settings.smtp_password' => 'nullable',
|
||||
'settings.smtp_timeout' => 'nullable',
|
||||
]);
|
||||
$this->settings->save();
|
||||
$this->syncData(true);
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave(string $type)
|
||||
{
|
||||
try {
|
||||
if ($type === 'SMTP') {
|
||||
$this->resendEnabled = false;
|
||||
} else {
|
||||
$this->smtpEnabled = false;
|
||||
}
|
||||
$this->syncData(true);
|
||||
if ($this->smtpEnabled || $this->resendEnabled) {
|
||||
$this->dispatch('success', "{$type} enabled.");
|
||||
} else {
|
||||
$this->dispatch('success', "{$type} disabled.");
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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([]);
|
||||
|
@@ -74,6 +74,9 @@ class AdminView extends Component
|
||||
|
||||
public function delete($id, $password)
|
||||
{
|
||||
if (! isInstanceAdmin()) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) {
|
||||
if (! Hash::check($password, Auth::user()->password)) {
|
||||
$this->addError('password', 'The provided password is incorrect.');
|
||||
|
@@ -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);
|
||||
|
@@ -117,14 +117,31 @@ class Application extends BaseModel
|
||||
if ($application->fqdn === '') {
|
||||
$application->fqdn = null;
|
||||
}
|
||||
$application->forceFill([
|
||||
'fqdn' => $application->fqdn,
|
||||
'install_command' => str($application->install_command)->trim(),
|
||||
'build_command' => str($application->build_command)->trim(),
|
||||
'start_command' => str($application->start_command)->trim(),
|
||||
'base_directory' => str($application->base_directory)->trim(),
|
||||
'publish_directory' => str($application->publish_directory)->trim(),
|
||||
]);
|
||||
$payload = [];
|
||||
if ($application->isDirty('fqdn')) {
|
||||
$payload['fqdn'] = $application->fqdn;
|
||||
}
|
||||
if ($application->isDirty('install_command')) {
|
||||
$payload['install_command'] = str($application->install_command)->trim();
|
||||
}
|
||||
if ($application->isDirty('build_command')) {
|
||||
$payload['build_command'] = str($application->build_command)->trim();
|
||||
}
|
||||
if ($application->isDirty('start_command')) {
|
||||
$payload['start_command'] = str($application->start_command)->trim();
|
||||
}
|
||||
if ($application->isDirty('base_directory')) {
|
||||
$payload['base_directory'] = str($application->base_directory)->trim();
|
||||
}
|
||||
if ($application->isDirty('publish_directory')) {
|
||||
$payload['publish_directory'] = str($application->publish_directory)->trim();
|
||||
}
|
||||
if ($application->isDirty('status')) {
|
||||
$payload['last_online_at'] = now();
|
||||
}
|
||||
if (count($payload) > 0) {
|
||||
$application->forceFill($payload);
|
||||
}
|
||||
});
|
||||
static::created(function ($application) {
|
||||
ApplicationSetting::create([
|
||||
@@ -155,6 +172,11 @@ class Application extends BaseModel
|
||||
return Application::whereRelation('environment.project.team', 'id', $teamId)->orderBy('name');
|
||||
}
|
||||
|
||||
public static function ownedByCurrentTeam()
|
||||
{
|
||||
return Application::whereRelation('environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
||||
}
|
||||
|
||||
public function getContainersToStop(bool $previewDeployments = false): array
|
||||
{
|
||||
$containers = $previewDeployments
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user