@@ -3,7 +3,6 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
@@ -148,15 +147,17 @@ class Application extends BaseModel
|
||||
if ($application->isDirty('status')) {
|
||||
$payload['last_online_at'] = now();
|
||||
}
|
||||
if ($application->isDirty('custom_nginx_configuration') && $application->custom_nginx_configuration === '') {
|
||||
$payload['custom_nginx_configuration'] = null;
|
||||
if ($application->isDirty('custom_nginx_configuration')) {
|
||||
if ($application->custom_nginx_configuration === '') {
|
||||
$payload['custom_nginx_configuration'] = null;
|
||||
}
|
||||
}
|
||||
if ($payload !== []) {
|
||||
if (count($payload) > 0) {
|
||||
$application->forceFill($payload);
|
||||
}
|
||||
});
|
||||
static::created(function ($application) {
|
||||
ApplicationSetting::query()->create([
|
||||
ApplicationSetting::create([
|
||||
'application_id' => $application->id,
|
||||
]);
|
||||
$application->compose_parsing_version = self::$parserVersion;
|
||||
@@ -181,12 +182,12 @@ class Application extends BaseModel
|
||||
|
||||
public static function ownedByCurrentTeamAPI(int $teamId)
|
||||
{
|
||||
return \App\Models\Application::query()->whereRelation('environment.project.team', 'id', $teamId)->orderBy('name');
|
||||
return Application::whereRelation('environment.project.team', 'id', $teamId)->orderBy('name');
|
||||
}
|
||||
|
||||
public static function ownedByCurrentTeam()
|
||||
{
|
||||
return \App\Models\Application::query()->whereRelation('environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
||||
return Application::whereRelation('environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
||||
}
|
||||
|
||||
public function getContainersToStop(bool $previewDeployments = false): array
|
||||
@@ -206,11 +207,11 @@ class Application extends BaseModel
|
||||
}
|
||||
|
||||
$startTime = time();
|
||||
while ($processes !== []) {
|
||||
while (count($processes) > 0) {
|
||||
$finishedProcesses = array_filter($processes, function ($process) {
|
||||
return ! $process->running();
|
||||
});
|
||||
foreach (array_keys($finishedProcesses) as $containerName) {
|
||||
foreach ($finishedProcesses as $containerName => $process) {
|
||||
unset($processes[$containerName]);
|
||||
$this->removeContainer($containerName, $server);
|
||||
}
|
||||
@@ -251,17 +252,17 @@ class Application extends BaseModel
|
||||
}
|
||||
}
|
||||
|
||||
public function delete_volumes(?Collection $collection)
|
||||
public function delete_volumes(?Collection $persistentStorages)
|
||||
{
|
||||
if ($this->build_pack === 'dockercompose') {
|
||||
$server = data_get($this, 'destination.server');
|
||||
instant_remote_process(["cd {$this->dirOnServer()} && docker compose down -v"], $server, false);
|
||||
} else {
|
||||
if ($collection->count() === 0) {
|
||||
if ($persistentStorages->count() === 0) {
|
||||
return;
|
||||
}
|
||||
$server = data_get($this, 'destination.server');
|
||||
foreach ($collection as $storage) {
|
||||
foreach ($persistentStorages as $storage) {
|
||||
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||
}
|
||||
}
|
||||
@@ -288,12 +289,20 @@ class Application extends BaseModel
|
||||
|
||||
public function is_public_repository(): bool
|
||||
{
|
||||
return (bool) data_get($this, 'source.is_public');
|
||||
if (data_get($this, 'source.is_public')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function is_github_based(): bool
|
||||
{
|
||||
return (bool) data_get($this, 'source');
|
||||
if (data_get($this, 'source')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isForceHttpsEnabled()
|
||||
@@ -477,12 +486,13 @@ class Application extends BaseModel
|
||||
set: function ($value) {
|
||||
if (is_null($value) || $value === '') {
|
||||
return '/Dockerfile';
|
||||
}
|
||||
if ($value !== '/') {
|
||||
return Str::start(Str::replaceEnd('/', '', $value), '/');
|
||||
}
|
||||
} else {
|
||||
if ($value !== '/') {
|
||||
return Str::start(Str::replaceEnd('/', '', $value), '/');
|
||||
}
|
||||
|
||||
return Str::start($value, '/');
|
||||
return Str::start($value, '/');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -493,12 +503,13 @@ class Application extends BaseModel
|
||||
set: function ($value) {
|
||||
if (is_null($value) || $value === '') {
|
||||
return '/docker-compose.yaml';
|
||||
}
|
||||
if ($value !== '/') {
|
||||
return Str::start(Str::replaceEnd('/', '', $value), '/');
|
||||
}
|
||||
} else {
|
||||
if ($value !== '/') {
|
||||
return Str::start(Str::replaceEnd('/', '', $value), '/');
|
||||
}
|
||||
|
||||
return Str::start($value, '/');
|
||||
return Str::start($value, '/');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -569,18 +580,33 @@ class Application extends BaseModel
|
||||
{
|
||||
return Attribute::make(
|
||||
set: function ($value) {
|
||||
if (str($value)->contains('(')) {
|
||||
$status = str($value)->before('(')->trim()->value();
|
||||
$health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy';
|
||||
} elseif (str($value)->contains(':')) {
|
||||
$status = str($value)->before(':')->trim()->value();
|
||||
$health = str($value)->after(':')->trim()->value() ?? 'unhealthy';
|
||||
} else {
|
||||
$status = $value;
|
||||
$health = 'unhealthy';
|
||||
}
|
||||
if ($this->additional_servers->count() === 0) {
|
||||
if (str($value)->contains('(')) {
|
||||
$status = str($value)->before('(')->trim()->value();
|
||||
$health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy';
|
||||
} elseif (str($value)->contains(':')) {
|
||||
$status = str($value)->before(':')->trim()->value();
|
||||
$health = str($value)->after(':')->trim()->value() ?? 'unhealthy';
|
||||
} else {
|
||||
$status = $value;
|
||||
$health = 'unhealthy';
|
||||
}
|
||||
|
||||
return "$status:$health";
|
||||
return "$status:$health";
|
||||
} else {
|
||||
if (str($value)->contains('(')) {
|
||||
$status = str($value)->before('(')->trim()->value();
|
||||
$health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy';
|
||||
} elseif (str($value)->contains(':')) {
|
||||
$status = str($value)->before(':')->trim()->value();
|
||||
$health = str($value)->after(':')->trim()->value() ?? 'unhealthy';
|
||||
} else {
|
||||
$status = $value;
|
||||
$health = 'unhealthy';
|
||||
}
|
||||
|
||||
return "$status:$health";
|
||||
}
|
||||
},
|
||||
get: function ($value) {
|
||||
if ($this->additional_servers->count() === 0) {
|
||||
@@ -597,24 +623,25 @@ class Application extends BaseModel
|
||||
}
|
||||
|
||||
return "$status:$health";
|
||||
}
|
||||
$complex_status = null;
|
||||
$complex_health = null;
|
||||
$complex_status = $main_server_status = str($value)->before(':')->value();
|
||||
$complex_health = $main_server_health = str($value)->after(':')->value() ?? 'unhealthy';
|
||||
$additional_servers_status = $this->additional_servers->pluck('pivot.status');
|
||||
foreach ($additional_servers_status as $status) {
|
||||
$server_status = str($status)->before(':')->value();
|
||||
$server_health = str($status)->after(':')->value() ?? 'unhealthy';
|
||||
if ($main_server_status !== $server_status) {
|
||||
$complex_status = 'degraded';
|
||||
} else {
|
||||
$complex_status = null;
|
||||
$complex_health = null;
|
||||
$complex_status = $main_server_status = str($value)->before(':')->value();
|
||||
$complex_health = $main_server_health = str($value)->after(':')->value() ?? 'unhealthy';
|
||||
$additional_servers_status = $this->additional_servers->pluck('pivot.status');
|
||||
foreach ($additional_servers_status as $status) {
|
||||
$server_status = str($status)->before(':')->value();
|
||||
$server_health = str($status)->after(':')->value() ?? 'unhealthy';
|
||||
if ($main_server_status !== $server_status) {
|
||||
$complex_status = 'degraded';
|
||||
}
|
||||
if ($main_server_health !== $server_health) {
|
||||
$complex_health = 'unhealthy';
|
||||
}
|
||||
}
|
||||
if ($main_server_health !== $server_health) {
|
||||
$complex_health = 'unhealthy';
|
||||
}
|
||||
}
|
||||
|
||||
return "$complex_status:$complex_health";
|
||||
return "$complex_status:$complex_health";
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -763,24 +790,27 @@ class Application extends BaseModel
|
||||
|
||||
public function isDeploymentInprogress()
|
||||
{
|
||||
$deployments = ApplicationDeploymentQueue::query()->where('application_id', $this->id)->whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS, ApplicationDeploymentStatus::QUEUED])->count();
|
||||
$deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS, ApplicationDeploymentStatus::QUEUED])->count();
|
||||
if ($deployments > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $deployments > 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function get_last_successful_deployment()
|
||||
{
|
||||
return ApplicationDeploymentQueue::query()->where('application_id', $this->id)->where('status', ApplicationDeploymentStatus::FINISHED)->where('pull_request_id', 0)->orderBy('created_at', 'desc')->first();
|
||||
return ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', ApplicationDeploymentStatus::FINISHED)->where('pull_request_id', 0)->orderBy('created_at', 'desc')->first();
|
||||
}
|
||||
|
||||
public function get_last_days_deployments()
|
||||
{
|
||||
return ApplicationDeploymentQueue::query()->where('application_id', $this->id)->where('created_at', '>=', now()->subDays(7))->orderBy('created_at', 'desc')->get();
|
||||
return ApplicationDeploymentQueue::where('application_id', $this->id)->where('created_at', '>=', now()->subDays(7))->orderBy('created_at', 'desc')->get();
|
||||
}
|
||||
|
||||
public function deployments(int $skip = 0, int $take = 10)
|
||||
{
|
||||
$deployments = ApplicationDeploymentQueue::query()->where('application_id', $this->id)->orderBy('created_at', 'desc');
|
||||
$deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->orderBy('created_at', 'desc');
|
||||
$count = $deployments->count();
|
||||
$deployments = $deployments->skip($skip)->take($take)->get();
|
||||
|
||||
@@ -792,17 +822,25 @@ class Application extends BaseModel
|
||||
|
||||
public function get_deployment(string $deployment_uuid)
|
||||
{
|
||||
return Activity::query()->where('subject_id', $this->id)->where('properties->type_uuid', '=', $deployment_uuid)->first();
|
||||
return Activity::where('subject_id', $this->id)->where('properties->type_uuid', '=', $deployment_uuid)->first();
|
||||
}
|
||||
|
||||
public function isDeployable(): bool
|
||||
{
|
||||
return (bool) $this->settings->is_auto_deploy_enabled;
|
||||
if ($this->settings->is_auto_deploy_enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isPRDeployable(): bool
|
||||
{
|
||||
return (bool) $this->settings->is_preview_deployments_enabled;
|
||||
if ($this->settings->is_preview_deployments_enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function deploymentType()
|
||||
@@ -812,17 +850,21 @@ class Application extends BaseModel
|
||||
}
|
||||
if (data_get($this, 'private_key_id')) {
|
||||
return 'deploy_key';
|
||||
}
|
||||
if (data_get($this, 'source')) {
|
||||
} elseif (data_get($this, 'source')) {
|
||||
return 'source';
|
||||
} else {
|
||||
return 'other';
|
||||
}
|
||||
|
||||
return 'other';
|
||||
throw new \Exception('No deployment type found');
|
||||
}
|
||||
|
||||
public function could_set_build_commands(): bool
|
||||
{
|
||||
return $this->build_pack === 'nixpacks';
|
||||
if ($this->build_pack === 'nixpacks') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function git_based(): bool
|
||||
@@ -830,13 +872,20 @@ class Application extends BaseModel
|
||||
if ($this->dockerfile) {
|
||||
return false;
|
||||
}
|
||||
if ($this->build_pack === 'dockerimage') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->build_pack !== 'dockerimage';
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isHealthcheckDisabled(): bool
|
||||
{
|
||||
return data_get($this, 'health_check_enabled') === false;
|
||||
if (data_get($this, 'health_check_enabled') === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function workdir()
|
||||
@@ -869,13 +918,14 @@ class Application extends BaseModel
|
||||
}
|
||||
if ($oldConfigHash === $newConfigHash) {
|
||||
return false;
|
||||
}
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
} else {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function customRepository()
|
||||
@@ -907,7 +957,7 @@ class Application extends BaseModel
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git submodule update --init --recursive";
|
||||
}
|
||||
if ($this->settings->is_git_lfs_enabled) {
|
||||
return "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git lfs pull";
|
||||
$git_clone_command = "{$git_clone_command} && cd {$baseDir} && GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null\" git lfs pull";
|
||||
}
|
||||
|
||||
return $git_clone_command;
|
||||
@@ -923,7 +973,7 @@ class Application extends BaseModel
|
||||
'is_accessible' => true,
|
||||
'error' => null,
|
||||
];
|
||||
} catch (RuntimeException $ex) {
|
||||
} catch (\RuntimeException $ex) {
|
||||
return [
|
||||
'is_accessible' => false,
|
||||
'error' => $ex->getMessage(),
|
||||
@@ -1027,8 +1077,6 @@ class Application extends BaseModel
|
||||
'fullRepoUrl' => $fullRepoUrl,
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function generateGitImportCommands(string $deployment_uuid, int $pull_request_id = 0, ?string $git_type = null, bool $exec_in_docker = true, bool $only_checkout = false, ?string $custom_base_dir = null, ?string $commit = null)
|
||||
@@ -1050,7 +1098,7 @@ class Application extends BaseModel
|
||||
$source_html_url_host = $url['host'];
|
||||
$source_html_url_scheme = $url['scheme'];
|
||||
|
||||
if ($this->source->getMorphClass() === GithubApp::class) {
|
||||
if ($this->source->getMorphClass() === \App\Models\GithubApp::class) {
|
||||
if ($this->source->is_public) {
|
||||
$fullRepoUrl = "{$this->source->html_url}/{$customRepository}";
|
||||
$git_clone_command = "{$git_clone_command} {$this->source->html_url}/{$customRepository} {$baseDir}";
|
||||
@@ -1207,16 +1255,14 @@ class Application extends BaseModel
|
||||
'fullRepoUrl' => $fullRepoUrl,
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function oldRawParser()
|
||||
{
|
||||
try {
|
||||
$yaml = Yaml::parse($this->docker_compose_raw);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception($e->getMessage(), $e->getCode(), $e);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
$services = data_get($yaml, 'services');
|
||||
|
||||
@@ -1224,27 +1270,24 @@ class Application extends BaseModel
|
||||
$services = collect($services)->map(function ($service) use ($commands) {
|
||||
$serviceVolumes = collect(data_get($service, 'volumes', []));
|
||||
if ($serviceVolumes->count() > 0) {
|
||||
foreach ($serviceVolumes as $serviceVolume) {
|
||||
foreach ($serviceVolumes as $volume) {
|
||||
$workdir = $this->workdir();
|
||||
$type = null;
|
||||
$source = null;
|
||||
if (is_string($serviceVolume)) {
|
||||
$source = str($serviceVolume)->before(':');
|
||||
if (is_string($volume)) {
|
||||
$source = str($volume)->before(':');
|
||||
if ($source->startsWith('./') || $source->startsWith('/') || $source->startsWith('~')) {
|
||||
$type = str('bind');
|
||||
}
|
||||
} elseif (is_array($serviceVolume)) {
|
||||
$type = data_get_str($serviceVolume, 'type');
|
||||
$source = data_get_str($serviceVolume, 'source');
|
||||
} elseif (is_array($volume)) {
|
||||
$type = data_get_str($volume, 'type');
|
||||
$source = data_get_str($volume, 'source');
|
||||
}
|
||||
if ($type?->value() === 'bind') {
|
||||
if ($source->value() === '/var/run/docker.sock') {
|
||||
continue;
|
||||
}
|
||||
if ($source->value() === '/tmp') {
|
||||
continue;
|
||||
}
|
||||
if ($source->value() === '/tmp/') {
|
||||
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||
continue;
|
||||
}
|
||||
if ($source->startsWith('.')) {
|
||||
@@ -1279,28 +1322,27 @@ class Application extends BaseModel
|
||||
{
|
||||
if ((int) $this->compose_parsing_version >= 3) {
|
||||
return newParser($this, $pull_request_id, $preview_id);
|
||||
}
|
||||
if ($this->docker_compose_raw) {
|
||||
} elseif ($this->docker_compose_raw) {
|
||||
return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id);
|
||||
} else {
|
||||
return collect([]);
|
||||
}
|
||||
|
||||
return collect([]);
|
||||
}
|
||||
|
||||
public function loadComposeFile($isInit = false)
|
||||
{
|
||||
$initialDockerComposeLocation = $this->docker_compose_location;
|
||||
if ($isInit && $this->docker_compose_raw) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
$cuid2 = new Cuid2;
|
||||
['commands' => $cloneCommand] = $this->generateGitImportCommands(deployment_uuid: $cuid2, only_checkout: true, exec_in_docker: false, custom_base_dir: '.');
|
||||
$uuid = new Cuid2;
|
||||
['commands' => $cloneCommand] = $this->generateGitImportCommands(deployment_uuid: $uuid, only_checkout: true, exec_in_docker: false, custom_base_dir: '.');
|
||||
$workdir = rtrim($this->base_directory, '/');
|
||||
$composeFile = $this->docker_compose_location;
|
||||
$fileList = collect([".$workdir$composeFile"]);
|
||||
$gitRemoteStatus = $this->getGitRemoteStatus(deployment_uuid: $cuid2);
|
||||
$gitRemoteStatus = $this->getGitRemoteStatus(deployment_uuid: $uuid);
|
||||
if (! $gitRemoteStatus['is_accessible']) {
|
||||
throw new RuntimeException("Failed to read Git source:\n\n{$gitRemoteStatus['error']}");
|
||||
throw new \RuntimeException("Failed to read Git source:\n\n{$gitRemoteStatus['error']}");
|
||||
}
|
||||
$getGitVersion = instant_remote_process(['git --version'], $this->destination->server, false);
|
||||
$gitVersion = str($getGitVersion)->explode(' ')->last();
|
||||
@@ -1311,7 +1353,7 @@ class Application extends BaseModel
|
||||
$paths = collect();
|
||||
$currentPath = '';
|
||||
foreach ($parts as $part) {
|
||||
$currentPath .= ($currentPath !== '' && $currentPath !== '0' ? '/' : '').$part;
|
||||
$currentPath .= ($currentPath ? '/' : '').$part;
|
||||
if (str($currentPath)->isNotEmpty()) {
|
||||
$paths->push($currentPath);
|
||||
}
|
||||
@@ -1320,9 +1362,9 @@ class Application extends BaseModel
|
||||
return $paths;
|
||||
})->flatten()->unique()->values();
|
||||
$commands = collect([
|
||||
"rm -rf /tmp/{$cuid2}",
|
||||
"mkdir -p /tmp/{$cuid2}",
|
||||
"cd /tmp/{$cuid2}",
|
||||
"rm -rf /tmp/{$uuid}",
|
||||
"mkdir -p /tmp/{$uuid}",
|
||||
"cd /tmp/{$uuid}",
|
||||
$cloneCommand,
|
||||
'git sparse-checkout init',
|
||||
"git sparse-checkout set {$fileList->implode(' ')}",
|
||||
@@ -1331,9 +1373,9 @@ class Application extends BaseModel
|
||||
]);
|
||||
} else {
|
||||
$commands = collect([
|
||||
"rm -rf /tmp/{$cuid2}",
|
||||
"mkdir -p /tmp/{$cuid2}",
|
||||
"cd /tmp/{$cuid2}",
|
||||
"rm -rf /tmp/{$uuid}",
|
||||
"mkdir -p /tmp/{$uuid}",
|
||||
"cd /tmp/{$uuid}",
|
||||
$cloneCommand,
|
||||
'git sparse-checkout init --cone',
|
||||
"git sparse-checkout set {$fileList->implode(' ')}",
|
||||
@@ -1343,22 +1385,22 @@ class Application extends BaseModel
|
||||
}
|
||||
try {
|
||||
$composeFileContent = instant_remote_process($commands, $this->destination->server);
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
if (str($e->getMessage())->contains('No such file')) {
|
||||
throw new RuntimeException("Docker Compose file not found at: $workdir$composeFile<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.", $e->getCode(), $e);
|
||||
throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
|
||||
}
|
||||
if (str($e->getMessage())->contains('fatal: repository') && str($e->getMessage())->contains('does not exist')) {
|
||||
if ($this->deploymentType() === 'deploy_key') {
|
||||
throw new RuntimeException('Your deploy key does not have access to the repository. Please check your deploy key and try again.', $e->getCode(), $e);
|
||||
throw new \RuntimeException('Your deploy key does not have access to the repository. Please check your deploy key and try again.');
|
||||
}
|
||||
throw new RuntimeException('Repository does not exist. Please check your repository URL and try again.', $e->getCode(), $e);
|
||||
throw new \RuntimeException('Repository does not exist. Please check your repository URL and try again.');
|
||||
}
|
||||
throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
|
||||
throw new \RuntimeException($e->getMessage());
|
||||
} finally {
|
||||
$this->docker_compose_location = $initialDockerComposeLocation;
|
||||
$this->save();
|
||||
$commands = collect([
|
||||
"rm -rf /tmp/{$cuid2}",
|
||||
"rm -rf /tmp/{$uuid}",
|
||||
]);
|
||||
instant_remote_process($commands, $this->destination->server, false);
|
||||
}
|
||||
@@ -1374,7 +1416,11 @@ class Application extends BaseModel
|
||||
$json = $json->filter(function ($value, $key) use ($diff) {
|
||||
return ! in_array($key, $diff);
|
||||
});
|
||||
$this->docker_compose_domains = $json ? json_encode($json) : null;
|
||||
if ($json) {
|
||||
$this->docker_compose_domains = json_encode($json);
|
||||
} else {
|
||||
$this->docker_compose_domains = null;
|
||||
}
|
||||
$this->save();
|
||||
}
|
||||
|
||||
@@ -1382,15 +1428,16 @@ class Application extends BaseModel
|
||||
'parsedServices' => $parsedServices,
|
||||
'initialDockerComposeLocation' => $this->docker_compose_location,
|
||||
];
|
||||
} else {
|
||||
throw new \RuntimeException("Docker Compose file not found at: $workdir$composeFile<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
|
||||
}
|
||||
throw new RuntimeException("Docker Compose file not found at: $workdir$composeFile<br><br>Check if you used the right extension (.yaml or .yml) in the compose file name.");
|
||||
}
|
||||
|
||||
public function parseContainerLabels(?ApplicationPreview $applicationPreview = null)
|
||||
public function parseContainerLabels(?ApplicationPreview $preview = null)
|
||||
{
|
||||
$customLabels = data_get($this, 'custom_labels');
|
||||
if (! $customLabels) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
if (base64_encode(base64_decode($customLabels, true)) !== $customLabels) {
|
||||
$this->custom_labels = str($customLabels)->replace(',', "\n");
|
||||
@@ -1398,7 +1445,7 @@ class Application extends BaseModel
|
||||
}
|
||||
$customLabels = base64_decode($this->custom_labels);
|
||||
if (mb_detect_encoding($customLabels, 'ASCII', true) === false) {
|
||||
$customLabels = str(implode('|coolify|', generateLabelsApplication($this, $applicationPreview)))->replace('|coolify|', "\n");
|
||||
$customLabels = str(implode('|coolify|', generateLabelsApplication($this, $preview)))->replace('|coolify|', "\n");
|
||||
}
|
||||
$this->custom_labels = base64_encode($customLabels);
|
||||
$this->save();
|
||||
@@ -1472,7 +1519,7 @@ class Application extends BaseModel
|
||||
if (isset($healthcheckCommand) && str_contains($trimmedLine, '\\')) {
|
||||
$healthcheckCommand .= ' '.trim($trimmedLine, '\\ ');
|
||||
}
|
||||
if (isset($healthcheckCommand) && ! str_contains($trimmedLine, '\\') && ($healthcheckCommand !== '' && $healthcheckCommand !== '0')) {
|
||||
if (isset($healthcheckCommand) && ! str_contains($trimmedLine, '\\') && ! empty($healthcheckCommand)) {
|
||||
$healthcheckCommand .= ' '.$trimmedLine;
|
||||
break;
|
||||
}
|
||||
@@ -1522,8 +1569,8 @@ class Application extends BaseModel
|
||||
$template = $this->preview_url_template;
|
||||
$host = $url->getHost();
|
||||
$schema = $url->getScheme();
|
||||
$cuid2 = new Cuid2;
|
||||
$preview_fqdn = str_replace('{{random}}', $cuid2, $template);
|
||||
$random = new Cuid2;
|
||||
$preview_fqdn = str_replace('{{random}}', $random, $template);
|
||||
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
|
||||
$preview_fqdn = str_replace('{{pr_id}}', $pull_request_id, $preview_fqdn);
|
||||
$preview_fqdn = "$schema://$preview_fqdn";
|
||||
@@ -1536,7 +1583,7 @@ class Application extends BaseModel
|
||||
|
||||
public static function getDomainsByUuid(string $uuid): array
|
||||
{
|
||||
$application = self::query()->where('uuid', $uuid)->first();
|
||||
$application = self::where('uuid', $uuid)->first();
|
||||
|
||||
if ($application) {
|
||||
return $application->fqdns;
|
||||
@@ -1558,7 +1605,7 @@ class Application extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -1567,8 +1614,6 @@ class Application extends BaseModel
|
||||
|
||||
return $parsedCollection->toArray();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getMemoryMetrics(int $mins = 5)
|
||||
@@ -1584,7 +1629,7 @@ class Application extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -1593,14 +1638,12 @@ class Application extends BaseModel
|
||||
|
||||
return $parsedCollection->toArray();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function generateConfig($is_json = false)
|
||||
{
|
||||
$config = collect([]);
|
||||
if (($this->build_pack = 'nixpacks') !== '') {
|
||||
if ($this->build_pack = 'nixpacks') {
|
||||
$config = collect([
|
||||
'build_pack' => 'nixpacks',
|
||||
'docker_registry_image_name' => $this->docker_registry_image_name,
|
||||
@@ -1634,7 +1677,7 @@ class Application extends BaseModel
|
||||
'config' => 'required|json',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
throw new Exception('Invalid JSON format');
|
||||
throw new \Exception('Invalid JSON format');
|
||||
}
|
||||
$config = json_decode($config, true);
|
||||
|
||||
@@ -1646,7 +1689,7 @@ class Application extends BaseModel
|
||||
'config.settings.is_static' => 'required|boolean',
|
||||
]);
|
||||
if ($deepValidator->fails()) {
|
||||
throw new Exception('Invalid data');
|
||||
throw new \Exception('Invalid data');
|
||||
}
|
||||
$config = $deepValidator->validated()['config'];
|
||||
|
||||
@@ -1655,8 +1698,8 @@ class Application extends BaseModel
|
||||
data_forget($config, 'settings');
|
||||
$this->update($config);
|
||||
$this->settings()->update($settings);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('Failed to update application settings', $e->getCode(), $e);
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception('Failed to update application settings');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,14 +43,14 @@ class ApplicationDeploymentQueue extends Model
|
||||
public function application(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Application::query()->find($this->application_id),
|
||||
get: fn () => Application::find($this->application_id),
|
||||
);
|
||||
}
|
||||
|
||||
public function server(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Server::query()->find($this->server_id),
|
||||
get: fn () => Server::find($this->server_id),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class ApplicationPreview extends BaseModel
|
||||
|
||||
public static function findPreviewByApplicationAndPullId(int $application_id, int $pull_request_id)
|
||||
{
|
||||
return self::query()->where('application_id', $application_id)->where('pull_request_id', $pull_request_id)->firstOrFail();
|
||||
return self::where('application_id', $application_id)->where('pull_request_id', $pull_request_id)->firstOrFail();
|
||||
}
|
||||
|
||||
public function isRunning()
|
||||
|
||||
@@ -30,6 +30,23 @@ class DiscordNotificationSettings extends Model
|
||||
'server_unreachable_discord_notifications',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'discord_enabled' => 'boolean',
|
||||
'discord_webhook_url' => 'encrypted',
|
||||
|
||||
'deployment_success_discord_notifications' => 'boolean',
|
||||
'deployment_failure_discord_notifications' => 'boolean',
|
||||
'status_change_discord_notifications' => 'boolean',
|
||||
'backup_success_discord_notifications' => 'boolean',
|
||||
'backup_failure_discord_notifications' => 'boolean',
|
||||
'scheduled_task_success_discord_notifications' => 'boolean',
|
||||
'scheduled_task_failure_discord_notifications' => 'boolean',
|
||||
'docker_cleanup_discord_notifications' => 'boolean',
|
||||
'server_disk_usage_discord_notifications' => 'boolean',
|
||||
'server_reachable_discord_notifications' => 'boolean',
|
||||
'server_unreachable_discord_notifications' => 'boolean',
|
||||
];
|
||||
|
||||
public function team()
|
||||
{
|
||||
return $this->belongsTo(Team::class);
|
||||
@@ -39,24 +56,4 @@ class DiscordNotificationSettings extends Model
|
||||
{
|
||||
return $this->discord_enabled;
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'discord_enabled' => 'boolean',
|
||||
'discord_webhook_url' => 'encrypted',
|
||||
|
||||
'deployment_success_discord_notifications' => 'boolean',
|
||||
'deployment_failure_discord_notifications' => 'boolean',
|
||||
'status_change_discord_notifications' => 'boolean',
|
||||
'backup_success_discord_notifications' => 'boolean',
|
||||
'backup_failure_discord_notifications' => 'boolean',
|
||||
'scheduled_task_success_discord_notifications' => 'boolean',
|
||||
'scheduled_task_failure_discord_notifications' => 'boolean',
|
||||
'docker_cleanup_discord_notifications' => 'boolean',
|
||||
'server_disk_usage_discord_notifications' => 'boolean',
|
||||
'server_reachable_discord_notifications' => 'boolean',
|
||||
'server_unreachable_discord_notifications' => 'boolean',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,32 @@ class EmailNotificationSettings extends Model
|
||||
'server_disk_usage_email_notifications',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'smtp_enabled' => 'boolean',
|
||||
'smtp_from_address' => 'encrypted',
|
||||
'smtp_from_name' => 'encrypted',
|
||||
'smtp_recipients' => 'encrypted',
|
||||
'smtp_host' => 'encrypted',
|
||||
'smtp_port' => 'integer',
|
||||
'smtp_username' => 'encrypted',
|
||||
'smtp_password' => 'encrypted',
|
||||
'smtp_timeout' => 'integer',
|
||||
|
||||
'resend_enabled' => 'boolean',
|
||||
'resend_api_key' => 'encrypted',
|
||||
|
||||
'use_instance_email_settings' => 'boolean',
|
||||
|
||||
'deployment_success_email_notifications' => 'boolean',
|
||||
'deployment_failure_email_notifications' => 'boolean',
|
||||
'status_change_email_notifications' => 'boolean',
|
||||
'backup_success_email_notifications' => 'boolean',
|
||||
'backup_failure_email_notifications' => 'boolean',
|
||||
'scheduled_task_success_email_notifications' => 'boolean',
|
||||
'scheduled_task_failure_email_notifications' => 'boolean',
|
||||
'server_disk_usage_email_notifications' => 'boolean',
|
||||
];
|
||||
|
||||
public function team()
|
||||
{
|
||||
return $this->belongsTo(Team::class);
|
||||
@@ -50,33 +76,4 @@ class EmailNotificationSettings extends Model
|
||||
|
||||
return $this->smtp_enabled || $this->resend_enabled || $this->use_instance_email_settings;
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'smtp_enabled' => 'boolean',
|
||||
'smtp_from_address' => 'encrypted',
|
||||
'smtp_from_name' => 'encrypted',
|
||||
'smtp_recipients' => 'encrypted',
|
||||
'smtp_host' => 'encrypted',
|
||||
'smtp_port' => 'integer',
|
||||
'smtp_username' => 'encrypted',
|
||||
'smtp_password' => 'encrypted',
|
||||
'smtp_timeout' => 'integer',
|
||||
|
||||
'resend_enabled' => 'boolean',
|
||||
'resend_api_key' => 'encrypted',
|
||||
|
||||
'use_instance_email_settings' => 'boolean',
|
||||
|
||||
'deployment_success_email_notifications' => 'boolean',
|
||||
'deployment_failure_email_notifications' => 'boolean',
|
||||
'status_change_email_notifications' => 'boolean',
|
||||
'backup_success_email_notifications' => 'boolean',
|
||||
'backup_failure_email_notifications' => 'boolean',
|
||||
'scheduled_task_success_email_notifications' => 'boolean',
|
||||
'scheduled_task_failure_email_notifications' => 'boolean',
|
||||
'server_disk_usage_email_notifications' => 'boolean',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,30 +55,30 @@ class EnvironmentVariable extends Model
|
||||
}
|
||||
});
|
||||
|
||||
static::created(function (EnvironmentVariable $environmentVariable) {
|
||||
if ($environmentVariable->resourceable_type === Application::class && ! $environmentVariable->is_preview) {
|
||||
$found = ModelsEnvironmentVariable::where('key', $environmentVariable->key)
|
||||
static::created(function (EnvironmentVariable $environment_variable) {
|
||||
if ($environment_variable->resourceable_type === Application::class && ! $environment_variable->is_preview) {
|
||||
$found = ModelsEnvironmentVariable::where('key', $environment_variable->key)
|
||||
->where('resourceable_type', Application::class)
|
||||
->where('resourceable_id', $environmentVariable->resourceable_id)
|
||||
->where('resourceable_id', $environment_variable->resourceable_id)
|
||||
->where('is_preview', true)
|
||||
->first();
|
||||
|
||||
if (! $found) {
|
||||
$application = Application::query()->find($environmentVariable->resourceable_id);
|
||||
$application = Application::find($environment_variable->resourceable_id);
|
||||
if ($application && $application->build_pack !== 'dockerfile') {
|
||||
ModelsEnvironmentVariable::create([
|
||||
'key' => $environmentVariable->key,
|
||||
'value' => $environmentVariable->value,
|
||||
'is_build_time' => $environmentVariable->is_build_time,
|
||||
'is_multiline' => $environmentVariable->is_multiline ?? false,
|
||||
'key' => $environment_variable->key,
|
||||
'value' => $environment_variable->value,
|
||||
'is_build_time' => $environment_variable->is_build_time,
|
||||
'is_multiline' => $environment_variable->is_multiline ?? false,
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $environmentVariable->resourceable_id,
|
||||
'resourceable_id' => $environment_variable->resourceable_id,
|
||||
'is_preview' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$environmentVariable->update([
|
||||
$environment_variable->update([
|
||||
'version' => config('constants.coolify.version'),
|
||||
]);
|
||||
});
|
||||
@@ -143,15 +143,18 @@ class EnvironmentVariable extends Model
|
||||
return Attribute::make(
|
||||
get: function () {
|
||||
$type = str($this->value)->after('{{')->before('.')->value;
|
||||
if (str($this->value)->startsWith('{{'.$type) && str($this->value)->endsWith('}}')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return str($this->value)->startsWith('{{'.$type) && str($this->value)->endsWith('}}');
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private function get_real_environment_variables(?string $environment_variable = null, $resource = null)
|
||||
{
|
||||
if (is_null($resource)) {
|
||||
if ((is_null($environment_variable) && $environment_variable === '') || is_null($resource)) {
|
||||
return null;
|
||||
}
|
||||
$environment_variable = trim($environment_variable);
|
||||
@@ -159,12 +162,12 @@ class EnvironmentVariable extends Model
|
||||
if ($sharedEnvsFound->isEmpty()) {
|
||||
return $environment_variable;
|
||||
}
|
||||
foreach ($sharedEnvsFound as $sharedEnvFound) {
|
||||
$type = str($sharedEnvFound)->match('/(.*?)\./');
|
||||
foreach ($sharedEnvsFound as $sharedEnv) {
|
||||
$type = str($sharedEnv)->match('/(.*?)\./');
|
||||
if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
|
||||
continue;
|
||||
}
|
||||
$variable = str($sharedEnvFound)->match('/\.(.*)/');
|
||||
$variable = str($sharedEnv)->match('/\.(.*)/');
|
||||
if ($type->value() === 'environment') {
|
||||
$id = $resource->environment->id;
|
||||
} elseif ($type->value() === 'project') {
|
||||
@@ -175,9 +178,9 @@ class EnvironmentVariable extends Model
|
||||
if (is_null($id)) {
|
||||
continue;
|
||||
}
|
||||
$environment_variable_found = SharedEnvironmentVariable::query()->where('type', $type)->where('key', $variable)->where('team_id', $resource->team()->id)->where("{$type}_id", $id)->first();
|
||||
$environment_variable_found = SharedEnvironmentVariable::where('type', $type)->where('key', $variable)->where('team_id', $resource->team()->id)->where("{$type}_id", $id)->first();
|
||||
if ($environment_variable_found) {
|
||||
$environment_variable = str($environment_variable)->replace("{{{$sharedEnvFound}}}", $environment_variable_found->value);
|
||||
$environment_variable = str($environment_variable)->replace("{{{$sharedEnv}}}", $environment_variable_found->value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
|
||||
class GithubApp extends BaseModel
|
||||
@@ -11,6 +10,11 @@ class GithubApp extends BaseModel
|
||||
|
||||
protected $appends = ['type'];
|
||||
|
||||
protected $casts = [
|
||||
'is_public' => 'boolean',
|
||||
'type' => 'string',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'client_secret',
|
||||
'webhook_secret',
|
||||
@@ -18,12 +22,12 @@ class GithubApp extends BaseModel
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::deleting(function (GithubApp $githubApp) {
|
||||
$applications_count = Application::query()->where('source_id', $githubApp->id)->count();
|
||||
static::deleting(function (GithubApp $github_app) {
|
||||
$applications_count = Application::where('source_id', $github_app->id)->count();
|
||||
if ($applications_count > 0) {
|
||||
throw new Exception('You cannot delete this GitHub App because it is in use by '.$applications_count.' application(s). Delete them first.');
|
||||
throw new \Exception('You cannot delete this GitHub App because it is in use by '.$applications_count.' application(s). Delete them first.');
|
||||
}
|
||||
$githubApp->privateKey()->delete();
|
||||
$github_app->privateKey()->delete();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -67,12 +71,4 @@ class GithubApp extends BaseModel
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'is_public' => 'boolean',
|
||||
'type' => 'string',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,32 @@ class InstanceSettings extends Model implements SendsEmail
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $casts = [
|
||||
'smtp_enabled' => 'boolean',
|
||||
'smtp_from_address' => 'encrypted',
|
||||
'smtp_from_name' => 'encrypted',
|
||||
'smtp_recipients' => 'encrypted',
|
||||
'smtp_host' => 'encrypted',
|
||||
'smtp_port' => 'integer',
|
||||
'smtp_username' => 'encrypted',
|
||||
'smtp_password' => 'encrypted',
|
||||
'smtp_timeout' => 'integer',
|
||||
|
||||
'resend_enabled' => 'boolean',
|
||||
'resend_api_key' => 'encrypted',
|
||||
|
||||
'allowed_ip_ranges' => 'array',
|
||||
'is_auto_update_enabled' => 'boolean',
|
||||
'auto_update_frequency' => 'string',
|
||||
'update_check_frequency' => 'string',
|
||||
'sentinel_token' => 'encrypted',
|
||||
];
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::updated(function ($settings) {
|
||||
if ($settings->isDirty('helper_version')) {
|
||||
Server::query()->chunkById(100, function ($servers) {
|
||||
Server::chunkById(100, function ($servers) {
|
||||
foreach ($servers as $server) {
|
||||
PullHelperImageJob::dispatch($server);
|
||||
}
|
||||
@@ -68,7 +89,7 @@ class InstanceSettings extends Model implements SendsEmail
|
||||
|
||||
public static function get()
|
||||
{
|
||||
return \App\Models\InstanceSettings::query()->findOrFail(0);
|
||||
return InstanceSettings::findOrFail(0);
|
||||
}
|
||||
|
||||
public function getRecipients($notification)
|
||||
@@ -103,27 +124,4 @@ class InstanceSettings extends Model implements SendsEmail
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'smtp_enabled' => 'boolean',
|
||||
'smtp_from_address' => 'encrypted',
|
||||
'smtp_from_name' => 'encrypted',
|
||||
'smtp_recipients' => 'encrypted',
|
||||
'smtp_host' => 'encrypted',
|
||||
'smtp_port' => 'integer',
|
||||
'smtp_username' => 'encrypted',
|
||||
'smtp_password' => 'encrypted',
|
||||
'smtp_timeout' => 'integer',
|
||||
|
||||
'resend_enabled' => 'boolean',
|
||||
'resend_api_key' => 'encrypted',
|
||||
|
||||
'allowed_ip_ranges' => 'array',
|
||||
'is_auto_update_enabled' => 'boolean',
|
||||
'auto_update_frequency' => 'string',
|
||||
'update_check_frequency' => 'string',
|
||||
'sentinel_token' => 'encrypted',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Events\FileStorageChanged;
|
||||
use App\Jobs\ServerStorageSaveJob;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class LocalFileVolume extends BaseModel
|
||||
@@ -15,9 +13,9 @@ class LocalFileVolume extends BaseModel
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function (LocalFileVolume $localFileVolume) {
|
||||
$localFileVolume->load(['service']);
|
||||
dispatch(new ServerStorageSaveJob($localFileVolume));
|
||||
static::created(function (LocalFileVolume $fileVolume) {
|
||||
$fileVolume->load(['service']);
|
||||
dispatch(new \App\Jobs\ServerStorageSaveJob($fileVolume));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -37,7 +35,7 @@ class LocalFileVolume extends BaseModel
|
||||
$workdir = $this->resource->workdir();
|
||||
$server = $this->resource->destination->server;
|
||||
}
|
||||
collect([]);
|
||||
$commands = collect([]);
|
||||
$path = data_get_str($this, 'fs_path');
|
||||
if ($path->startsWith('.')) {
|
||||
$path = $path->after('.');
|
||||
@@ -82,8 +80,6 @@ class LocalFileVolume extends BaseModel
|
||||
if ($commands->count() > 0) {
|
||||
return instant_remote_process($commands, $server);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function saveStorageOnServer()
|
||||
@@ -122,13 +118,12 @@ class LocalFileVolume extends BaseModel
|
||||
$this->content = $content;
|
||||
$this->save();
|
||||
FileStorageChanged::dispatch(data_get($server, 'team_id'));
|
||||
throw new Exception('The following file is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory.');
|
||||
}
|
||||
if ($isDir === 'OK' && ! $this->is_directory) {
|
||||
throw new \Exception('The following file is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory.');
|
||||
} elseif ($isDir === 'OK' && ! $this->is_directory) {
|
||||
if ($path === '/' || $path === '.' || $path === '..' || $path === '' || str($path)->isEmpty() || is_null($path)) {
|
||||
$this->is_directory = true;
|
||||
$this->save();
|
||||
throw new Exception('The following file is a directory on the server, but you are trying to mark it as a file. <br><br>Please delete the directory on the server or mark it as directory.');
|
||||
throw new \Exception('The following file is a directory on the server, but you are trying to mark it as a file. <br><br>Please delete the directory on the server or mark it as directory.');
|
||||
}
|
||||
instant_remote_process([
|
||||
"rm -fr $path",
|
||||
|
||||
@@ -49,9 +49,9 @@ class LocalPersistentVolume extends Model
|
||||
set: function (?string $value) {
|
||||
if ($value) {
|
||||
return str($value)->trim()->start('/')->value;
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ class OauthSetting extends Model
|
||||
protected function clientSecret(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn (?string $value) => $value === null || $value === '' || $value === '0' ? null : Crypt::decryptString($value),
|
||||
set: fn (?string $value) => $value === null || $value === '' || $value === '0' ? null : Crypt::encryptString($value),
|
||||
get: fn (?string $value) => empty($value) ? null : Crypt::decryptString($value),
|
||||
set: fn (?string $value) => empty($value) ? null : Crypt::encryptString($value),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
namespace App\Models;
|
||||
|
||||
use DanHarrin\LivewireRateLimiting\WithRateLimiting;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use OpenApi\Attributes as OA;
|
||||
use phpseclib3\Crypt\PublicKeyLoader;
|
||||
use Throwable;
|
||||
|
||||
#[OA\Schema(
|
||||
description: 'Private Key model',
|
||||
@@ -38,6 +36,10 @@ class PrivateKey extends BaseModel
|
||||
'fingerprint',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'private_key' => 'encrypted',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::saving(function ($key) {
|
||||
@@ -80,7 +82,7 @@ class PrivateKey extends BaseModel
|
||||
PublicKeyLoader::load($privateKey);
|
||||
|
||||
return true;
|
||||
} catch (Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -109,8 +111,8 @@ class PrivateKey extends BaseModel
|
||||
'private_key' => $keyPair['private'],
|
||||
'public_key' => $keyPair['public'],
|
||||
];
|
||||
} catch (Throwable $e) {
|
||||
throw new Exception("Failed to generate new {$type} key: ".$e->getMessage(), $e->getCode(), $e);
|
||||
} catch (\Throwable $e) {
|
||||
throw new \Exception("Failed to generate new {$type} key: ".$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +122,7 @@ class PrivateKey extends BaseModel
|
||||
$key = PublicKeyLoader::load($privateKey);
|
||||
|
||||
return $key->getPublicKey()->toString('OpenSSH', ['comment' => '']);
|
||||
} catch (Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -185,17 +187,10 @@ class PrivateKey extends BaseModel
|
||||
|
||||
public function isInUse()
|
||||
{
|
||||
if ($this->servers()->exists()) {
|
||||
return true;
|
||||
}
|
||||
if ($this->applications()->exists()) {
|
||||
return true;
|
||||
}
|
||||
if ($this->githubApps()->exists()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (bool) $this->gitlabApps()->exists();
|
||||
return $this->servers()->exists()
|
||||
|| $this->applications()->exists()
|
||||
|| $this->githubApps()->exists()
|
||||
|| $this->gitlabApps()->exists();
|
||||
}
|
||||
|
||||
public function safeDelete()
|
||||
@@ -216,22 +211,22 @@ class PrivateKey extends BaseModel
|
||||
$publicKey = $key->getPublicKey();
|
||||
|
||||
return $publicKey->getFingerprint('sha256');
|
||||
} catch (Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static function fingerprintExists($fingerprint, $excludeId = null)
|
||||
{
|
||||
$builder = self::query()
|
||||
$query = self::query()
|
||||
->where('fingerprint', $fingerprint)
|
||||
->where('id', '!=', $excludeId);
|
||||
|
||||
if (currentTeam()) {
|
||||
$builder->where('team_id', currentTeam()->id);
|
||||
$query->where('team_id', currentTeam()->id);
|
||||
}
|
||||
|
||||
return $builder->exists();
|
||||
return $query->exists();
|
||||
}
|
||||
|
||||
public static function cleanupUnusedKeys()
|
||||
@@ -240,11 +235,4 @@ class PrivateKey extends BaseModel
|
||||
$privateKey->safeDelete();
|
||||
});
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'private_key' => 'encrypted',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,10 +33,10 @@ class Project extends BaseModel
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($project) {
|
||||
ProjectSetting::query()->create([
|
||||
ProjectSetting::create([
|
||||
'project_id' => $project->id,
|
||||
]);
|
||||
Environment::query()->create([
|
||||
Environment::create([
|
||||
'name' => 'production',
|
||||
'project_id' => $project->id,
|
||||
'uuid' => (string) new Cuid2,
|
||||
|
||||
@@ -31,6 +31,24 @@ class PushoverNotificationSettings extends Model
|
||||
'server_unreachable_pushover_notifications',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'pushover_enabled' => 'boolean',
|
||||
'pushover_user_key' => 'encrypted',
|
||||
'pushover_api_token' => 'encrypted',
|
||||
|
||||
'deployment_success_pushover_notifications' => 'boolean',
|
||||
'deployment_failure_pushover_notifications' => 'boolean',
|
||||
'status_change_pushover_notifications' => 'boolean',
|
||||
'backup_success_pushover_notifications' => 'boolean',
|
||||
'backup_failure_pushover_notifications' => 'boolean',
|
||||
'scheduled_task_success_pushover_notifications' => 'boolean',
|
||||
'scheduled_task_failure_pushover_notifications' => 'boolean',
|
||||
'docker_cleanup_pushover_notifications' => 'boolean',
|
||||
'server_disk_usage_pushover_notifications' => 'boolean',
|
||||
'server_reachable_pushover_notifications' => 'boolean',
|
||||
'server_unreachable_pushover_notifications' => 'boolean',
|
||||
];
|
||||
|
||||
public function team()
|
||||
{
|
||||
return $this->belongsTo(Team::class);
|
||||
@@ -40,25 +58,4 @@ class PushoverNotificationSettings extends Model
|
||||
{
|
||||
return $this->pushover_enabled;
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'pushover_enabled' => 'boolean',
|
||||
'pushover_user_key' => 'encrypted',
|
||||
'pushover_api_token' => 'encrypted',
|
||||
|
||||
'deployment_success_pushover_notifications' => 'boolean',
|
||||
'deployment_failure_pushover_notifications' => 'boolean',
|
||||
'status_change_pushover_notifications' => 'boolean',
|
||||
'backup_success_pushover_notifications' => 'boolean',
|
||||
'backup_failure_pushover_notifications' => 'boolean',
|
||||
'scheduled_task_success_pushover_notifications' => 'boolean',
|
||||
'scheduled_task_failure_pushover_notifications' => 'boolean',
|
||||
'docker_cleanup_pushover_notifications' => 'boolean',
|
||||
'server_disk_usage_pushover_notifications' => 'boolean',
|
||||
'server_reachable_pushover_notifications' => 'boolean',
|
||||
'server_unreachable_pushover_notifications' => 'boolean',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace App\Models;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Throwable;
|
||||
|
||||
class S3Storage extends BaseModel
|
||||
{
|
||||
@@ -13,6 +12,12 @@ class S3Storage extends BaseModel
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $casts = [
|
||||
'is_usable' => 'boolean',
|
||||
'key' => 'encrypted',
|
||||
'secret' => 'encrypted',
|
||||
];
|
||||
|
||||
public static function ownedByCurrentTeam(array $select = ['*'])
|
||||
{
|
||||
$selectArray = collect($select)->concat(['id']);
|
||||
@@ -52,12 +57,12 @@ class S3Storage extends BaseModel
|
||||
Storage::disk('custom-s3')->files();
|
||||
$this->unusable_email_sent = false;
|
||||
$this->is_usable = true;
|
||||
} catch (Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->is_usable = false;
|
||||
if ($this->unusable_email_sent === false && is_transactional_emails_enabled()) {
|
||||
$mailMessage = new MailMessage;
|
||||
$mailMessage->subject('Coolify: S3 Storage Connection Error');
|
||||
$mailMessage->view('emails.s3-connection-error', ['name' => $this->name, 'reason' => $e->getMessage(), 'url' => route('storage.show', ['storage_uuid' => $this->uuid])]);
|
||||
$mail = new MailMessage;
|
||||
$mail->subject('Coolify: S3 Storage Connection Error');
|
||||
$mail->view('emails.s3-connection-error', ['name' => $this->name, 'reason' => $e->getMessage(), 'url' => route('storage.show', ['storage_uuid' => $this->uuid])]);
|
||||
$users = collect([]);
|
||||
$members = $this->team->members()->get();
|
||||
foreach ($members as $user) {
|
||||
@@ -66,7 +71,7 @@ class S3Storage extends BaseModel
|
||||
}
|
||||
}
|
||||
foreach ($users as $user) {
|
||||
send_user_an_email($mailMessage, $user->email);
|
||||
send_user_an_email($mail, $user->email);
|
||||
}
|
||||
$this->unusable_email_sent = true;
|
||||
}
|
||||
@@ -78,13 +83,4 @@ class S3Storage extends BaseModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'is_usable' => 'boolean',
|
||||
'key' => 'encrypted',
|
||||
'secret' => 'encrypted',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ use App\Events\ServerReachabilityChanged;
|
||||
use App\Jobs\CheckAndStartSentinelJob;
|
||||
use App\Notifications\Server\Reachable;
|
||||
use App\Notifications\Server\Unreachable;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -20,13 +19,11 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Stringable;
|
||||
use Log;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
|
||||
use Spatie\SchemalessAttributes\SchemalessAttributesTrait;
|
||||
use Spatie\Url\Url;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Throwable;
|
||||
|
||||
#[OA\Schema(
|
||||
description: 'Server model',
|
||||
@@ -75,37 +72,39 @@ class Server extends BaseModel
|
||||
}
|
||||
});
|
||||
static::created(function ($server) {
|
||||
ServerSetting::query()->create([
|
||||
ServerSetting::create([
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
if ($server->id === 0) {
|
||||
if ($server->isSwarm()) {
|
||||
SwarmDocker::query()->create([
|
||||
SwarmDocker::create([
|
||||
'id' => 0,
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify-overlay',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
} else {
|
||||
StandaloneDocker::query()->create([
|
||||
StandaloneDocker::create([
|
||||
'id' => 0,
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
}
|
||||
} elseif ($server->isSwarm()) {
|
||||
SwarmDocker::query()->create([
|
||||
'name' => 'coolify-overlay',
|
||||
'network' => 'coolify-overlay',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
} else {
|
||||
StandaloneDocker::query()->create([
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
if ($server->isSwarm()) {
|
||||
SwarmDocker::create([
|
||||
'name' => 'coolify-overlay',
|
||||
'network' => 'coolify-overlay',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
} else {
|
||||
StandaloneDocker::create([
|
||||
'name' => 'coolify',
|
||||
'network' => 'coolify',
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
if (! isset($server->proxy->redirect_enabled)) {
|
||||
$server->proxy->redirect_enabled = true;
|
||||
@@ -125,6 +124,17 @@ class Server extends BaseModel
|
||||
});
|
||||
}
|
||||
|
||||
protected $casts = [
|
||||
'proxy' => SchemalessAttributes::class,
|
||||
'logdrain_axiom_api_key' => 'encrypted',
|
||||
'logdrain_newrelic_license_key' => 'encrypted',
|
||||
'delete_unused_volumes' => 'boolean',
|
||||
'delete_unused_networks' => 'boolean',
|
||||
'unreachable_notification_sent' => 'boolean',
|
||||
'is_build_server' => 'boolean',
|
||||
'force_disabled' => 'boolean',
|
||||
];
|
||||
|
||||
protected $schemalessAttributes = [
|
||||
'proxy',
|
||||
];
|
||||
@@ -192,8 +202,10 @@ class Server extends BaseModel
|
||||
$proxy_type = $this->proxyType();
|
||||
$redirect_enabled = $this->proxy->redirect_enabled ?? true;
|
||||
$redirect_url = $this->proxy->redirect_url;
|
||||
if (isDev() && $proxy_type === ProxyTypes::CADDY->value) {
|
||||
$dynamic_conf_path = '/data/coolify/proxy/caddy/dynamic';
|
||||
if (isDev()) {
|
||||
if ($proxy_type === ProxyTypes::CADDY->value) {
|
||||
$dynamic_conf_path = '/data/coolify/proxy/caddy/dynamic';
|
||||
}
|
||||
}
|
||||
if ($proxy_type === ProxyTypes::TRAEFIK->value) {
|
||||
$default_redirect_file = "$dynamic_conf_path/default_redirect_503.yaml";
|
||||
@@ -211,11 +223,15 @@ class Server extends BaseModel
|
||||
instant_remote_process(["rm -f $default_redirect_file"], $this);
|
||||
} else {
|
||||
if ($proxy_type === ProxyTypes::CADDY->value) {
|
||||
$conf = filled($redirect_url) ? ":80, :443 {
|
||||
if (filled($redirect_url)) {
|
||||
$conf = ":80, :443 {
|
||||
redir $redirect_url
|
||||
}" : ':80, :443 {
|
||||
}";
|
||||
} else {
|
||||
$conf = ':80, :443 {
|
||||
respond 503
|
||||
}';
|
||||
}
|
||||
} elseif ($proxy_type === ProxyTypes::TRAEFIK->value) {
|
||||
$dynamic_conf = [
|
||||
'http' => [
|
||||
@@ -458,9 +474,17 @@ $schema://$host {
|
||||
if ($proxyType === ProxyTypes::TRAEFIK->value) {
|
||||
// Do nothing
|
||||
} elseif ($proxyType === ProxyTypes::CADDY->value) {
|
||||
$proxy_path = isDev() ? '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/caddy' : $proxy_path.'/caddy';
|
||||
if (isDev()) {
|
||||
$proxy_path = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/caddy';
|
||||
} else {
|
||||
$proxy_path = $proxy_path.'/caddy';
|
||||
}
|
||||
} elseif ($proxyType === ProxyTypes::NGINX->value) {
|
||||
$proxy_path = isDev() ? '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/nginx' : $proxy_path.'/nginx';
|
||||
if (isDev()) {
|
||||
$proxy_path = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/nginx';
|
||||
} else {
|
||||
$proxy_path = $proxy_path.'/nginx';
|
||||
}
|
||||
}
|
||||
|
||||
return $proxy_path;
|
||||
@@ -521,7 +545,7 @@ $schema://$host {
|
||||
{
|
||||
$wait = $this->settings->sentinel_push_interval_seconds * 3;
|
||||
if ($wait < 120) {
|
||||
return 120;
|
||||
$wait = 120;
|
||||
}
|
||||
|
||||
return $wait;
|
||||
@@ -563,7 +587,7 @@ $schema://$host {
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$cpu = json_decode($cpu, true);
|
||||
|
||||
@@ -571,8 +595,6 @@ $schema://$host {
|
||||
return [(int) $metric['time'], (float) $metric['percent']];
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getMemoryMetrics(int $mins = 5)
|
||||
@@ -586,7 +608,7 @@ $schema://$host {
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$memory = json_decode($memory, true);
|
||||
$parsedCollection = collect($memory)->map(function ($metric) {
|
||||
@@ -597,8 +619,6 @@ $schema://$host {
|
||||
|
||||
return $parsedCollection->toArray();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getDiskUsage(): ?string
|
||||
@@ -641,11 +661,11 @@ $schema://$host {
|
||||
$containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this, false);
|
||||
if ($containerReplicates) {
|
||||
$containerReplicates = format_docker_command_output_to_json($containerReplicates);
|
||||
foreach ($containerReplicates as $containerReplicate) {
|
||||
$name = data_get($containerReplicate, 'Name');
|
||||
$containers = $containers->map(function ($container) use ($name, $containerReplicate) {
|
||||
foreach ($containerReplicates as $containerReplica) {
|
||||
$name = data_get($containerReplica, 'Name');
|
||||
$containers = $containers->map(function ($container) use ($name, $containerReplica) {
|
||||
if (data_get($container, 'Spec.Name') === $name) {
|
||||
$replicas = data_get($containerReplicate, 'Replicas');
|
||||
$replicas = data_get($containerReplica, 'Replicas');
|
||||
$running = str($replicas)->explode('/')[0];
|
||||
$total = str($replicas)->explode('/')[1];
|
||||
if ($running === $total) {
|
||||
@@ -717,9 +737,9 @@ $schema://$host {
|
||||
$containers = $containers->filter();
|
||||
|
||||
return collect($containers);
|
||||
} else {
|
||||
return collect([]);
|
||||
}
|
||||
|
||||
return collect([]);
|
||||
}
|
||||
|
||||
public function hasDefinedResources()
|
||||
@@ -727,8 +747,11 @@ $schema://$host {
|
||||
$applications = $this->applications()->count() > 0;
|
||||
$databases = $this->databases()->count() > 0;
|
||||
$services = $this->services()->count() > 0;
|
||||
if ($applications || $databases || $services) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $applications || $databases || $services;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function databases()
|
||||
@@ -758,7 +781,7 @@ $schema://$host {
|
||||
$additionalApplicationIds = collect($additionalApplicationIds)->map(function ($item) {
|
||||
return $item->application_id;
|
||||
});
|
||||
Application::query()->whereIn('id', $additionalApplicationIds)->get()->each(function ($application) use ($applications) {
|
||||
Application::whereIn('id', $additionalApplicationIds)->get()->each(function ($application) use ($applications) {
|
||||
$applications->push($application);
|
||||
});
|
||||
|
||||
@@ -776,7 +799,7 @@ $schema://$host {
|
||||
{
|
||||
return $this->previews()->filter(function ($preview) {
|
||||
$applicationId = data_get($preview, 'application_id');
|
||||
$application = Application::query()->find($applicationId);
|
||||
$application = Application::find($applicationId);
|
||||
if (! $application) {
|
||||
return false;
|
||||
}
|
||||
@@ -878,7 +901,11 @@ $schema://$host {
|
||||
public function isProxyShouldRun()
|
||||
{
|
||||
// TODO: Do we need "|| $this->proxy->force_stop" here?
|
||||
return $this->proxyType() !== ProxyTypes::NONE->value && ! $this->isBuildServer();
|
||||
if ($this->proxyType() === ProxyTypes::NONE->value || $this->isBuildServer()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function skipServer()
|
||||
@@ -886,8 +913,11 @@ $schema://$host {
|
||||
if ($this->ip === '1.2.3.4') {
|
||||
return true;
|
||||
}
|
||||
if ($this->settings->force_disabled === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->settings->force_disabled === true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isFunctional()
|
||||
@@ -911,8 +941,8 @@ $schema://$host {
|
||||
$os_release = instant_remote_process(['cat /etc/os-release'], $this);
|
||||
$releaseLines = collect(explode("\n", $os_release));
|
||||
$collectedData = collect([]);
|
||||
foreach ($releaseLines as $releaseLine) {
|
||||
$item = str($releaseLine)->trim();
|
||||
foreach ($releaseLines as $line) {
|
||||
$item = str($line)->trim();
|
||||
$collectedData->put($item->before('=')->value(), $item->after('=')->lower()->replace('"', '')->value());
|
||||
}
|
||||
$ID = data_get($collectedData, 'ID');
|
||||
@@ -926,10 +956,10 @@ $schema://$host {
|
||||
if ($supported->count() === 1) {
|
||||
// ray('supported');
|
||||
return str($supported->first());
|
||||
} else {
|
||||
// ray('not supported');
|
||||
return false;
|
||||
}
|
||||
|
||||
// ray('not supported');
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isSwarm()
|
||||
@@ -952,8 +982,11 @@ $schema://$host {
|
||||
if ($this->status() === false) {
|
||||
return false;
|
||||
}
|
||||
if ($this->isFunctional() === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->isFunctional() !== false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function status(): bool
|
||||
@@ -993,19 +1026,19 @@ $schema://$host {
|
||||
$unreachableNotificationSent = (bool) $this->unreachable_notification_sent;
|
||||
$isReachable = (bool) $this->settings->is_reachable;
|
||||
|
||||
Log::debug('Server reachability check', [
|
||||
\Log::debug('Server reachability check', [
|
||||
'server_id' => $this->id,
|
||||
'is_reachable' => $isReachable,
|
||||
'notification_sent' => $unreachableNotificationSent,
|
||||
'unreachable_count' => $this->unreachable_count,
|
||||
]);
|
||||
|
||||
if ($isReachable) {
|
||||
if ($isReachable === true) {
|
||||
$this->unreachable_count = 0;
|
||||
$this->save();
|
||||
|
||||
if ($unreachableNotificationSent) {
|
||||
Log::debug('Server is now reachable, sending notification', [
|
||||
if ($unreachableNotificationSent === true) {
|
||||
\Log::debug('Server is now reachable, sending notification', [
|
||||
'server_id' => $this->id,
|
||||
]);
|
||||
$this->sendReachableNotification();
|
||||
@@ -1015,7 +1048,7 @@ $schema://$host {
|
||||
}
|
||||
|
||||
$this->increment('unreachable_count');
|
||||
Log::debug('Incremented unreachable count', [
|
||||
\Log::debug('Incremented unreachable count', [
|
||||
'server_id' => $this->id,
|
||||
'new_count' => $this->unreachable_count,
|
||||
]);
|
||||
@@ -1023,7 +1056,7 @@ $schema://$host {
|
||||
if ($this->unreachable_count === 1) {
|
||||
$this->settings->is_reachable = true;
|
||||
$this->settings->save();
|
||||
Log::debug('First unreachable attempt, marking as reachable', [
|
||||
\Log::debug('First unreachable attempt, marking as reachable', [
|
||||
'server_id' => $this->id,
|
||||
]);
|
||||
|
||||
@@ -1034,7 +1067,7 @@ $schema://$host {
|
||||
$failedChecks = 0;
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$status = $this->serverStatus();
|
||||
Log::debug('Additional reachability check', [
|
||||
\Log::debug('Additional reachability check', [
|
||||
'server_id' => $this->id,
|
||||
'attempt' => $i + 1,
|
||||
'status' => $status,
|
||||
@@ -1046,7 +1079,7 @@ $schema://$host {
|
||||
}
|
||||
|
||||
if ($failedChecks === 3 && ! $unreachableNotificationSent) {
|
||||
Log::debug('Server confirmed unreachable after 3 attempts, sending notification', [
|
||||
\Log::debug('Server confirmed unreachable after 3 attempts, sending notification', [
|
||||
'server_id' => $this->id,
|
||||
]);
|
||||
$this->sendUnreachableNotification();
|
||||
@@ -1086,7 +1119,7 @@ $schema://$host {
|
||||
}
|
||||
|
||||
return ['uptime' => true, 'error' => null];
|
||||
} catch (Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
if ($justCheckingNewKey) {
|
||||
return ['uptime' => false, 'error' => 'This key is not valid for this server.'];
|
||||
}
|
||||
@@ -1112,18 +1145,18 @@ $schema://$host {
|
||||
$this->settings->is_usable = false;
|
||||
$this->settings->save();
|
||||
if ($throwError) {
|
||||
throw new Exception('Server is not usable. Docker Engine is not installed.');
|
||||
throw new \Exception('Server is not usable. Docker Engine is not installed.');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
instant_remote_process(['docker version'], $this);
|
||||
} catch (Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->settings->is_usable = false;
|
||||
$this->settings->save();
|
||||
if ($throwError) {
|
||||
throw new Exception('Server is not usable. Docker Engine is not running.', $e->getCode(), $e);
|
||||
throw new \Exception('Server is not usable. Docker Engine is not running.');
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -1142,7 +1175,7 @@ $schema://$host {
|
||||
$this->settings->is_usable = false;
|
||||
$this->settings->save();
|
||||
if ($throwError) {
|
||||
throw new Exception('Server is not usable. Docker Compose is not installed.');
|
||||
throw new \Exception('Server is not usable. Docker Compose is not installed.');
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -1158,7 +1191,9 @@ $schema://$host {
|
||||
$swarmStatus = instant_remote_process(['docker info|grep -i swarm'], $this, false);
|
||||
$swarmStatus = str($swarmStatus)->trim()->after(':')->trim();
|
||||
if ($swarmStatus === 'inactive') {
|
||||
throw new Exception('Docker Swarm is not initiated. Please join the server to a swarm before continuing.');
|
||||
throw new \Exception('Docker Swarm is not initiated. Please join the server to a swarm before continuing.');
|
||||
|
||||
return false;
|
||||
}
|
||||
$this->settings->is_usable = true;
|
||||
$this->settings->save();
|
||||
@@ -1190,13 +1225,13 @@ $schema://$host {
|
||||
public function validateCoolifyNetwork($isSwarm = false, $isBuildServer = false)
|
||||
{
|
||||
if ($isBuildServer) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
if ($isSwarm) {
|
||||
return instant_remote_process(['docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true'], $this, false);
|
||||
} else {
|
||||
return instant_remote_process(['docker network create coolify --attachable >/dev/null 2>&1 || true'], $this, false);
|
||||
}
|
||||
|
||||
return instant_remote_process(['docker network create coolify --attachable >/dev/null 2>&1 || true'], $this, false);
|
||||
}
|
||||
|
||||
public function isNonRoot()
|
||||
@@ -1225,7 +1260,7 @@ $schema://$host {
|
||||
public function updateWithPrivateKey(array $data, ?PrivateKey $privateKey = null)
|
||||
{
|
||||
$this->update($data);
|
||||
if ($privateKey instanceof PrivateKey) {
|
||||
if ($privateKey) {
|
||||
$this->privateKey()->associate($privateKey);
|
||||
$this->save();
|
||||
}
|
||||
@@ -1255,11 +1290,9 @@ $schema://$host {
|
||||
} else {
|
||||
StartSentinel::run($this, true);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function url()
|
||||
@@ -1289,21 +1322,7 @@ $schema://$host {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Exception('Invalid proxy type.');
|
||||
throw new \Exception('Invalid proxy type.');
|
||||
}
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'proxy' => SchemalessAttributes::class,
|
||||
'logdrain_axiom_api_key' => 'encrypted',
|
||||
'logdrain_newrelic_license_key' => 'encrypted',
|
||||
'delete_unused_volumes' => 'boolean',
|
||||
'delete_unused_networks' => 'boolean',
|
||||
'unreachable_notification_sent' => 'boolean',
|
||||
'is_build_server' => 'boolean',
|
||||
'force_disabled' => 'boolean',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Throwable;
|
||||
|
||||
#[OA\Schema(
|
||||
description: 'Server Settings model',
|
||||
@@ -54,6 +53,14 @@ class ServerSetting extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
protected $casts = [
|
||||
'force_docker_cleanup' => 'boolean',
|
||||
'docker_cleanup_threshold' => 'integer',
|
||||
'sentinel_token' => 'encrypted',
|
||||
'is_reachable' => 'boolean',
|
||||
'is_usable' => 'boolean',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function ($setting) {
|
||||
@@ -64,7 +71,7 @@ class ServerSetting extends Model
|
||||
if (str($setting->sentinel_custom_url)->isEmpty()) {
|
||||
$setting->generateSentinelUrl(save: false, ignoreEvent: true);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('Error creating server setting: '.$e->getMessage());
|
||||
}
|
||||
});
|
||||
@@ -141,15 +148,4 @@ class ServerSetting extends Model
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'force_docker_cleanup' => 'boolean',
|
||||
'docker_cleanup_threshold' => 'integer',
|
||||
'sentinel_token' => 'encrypted',
|
||||
'is_reachable' => 'boolean',
|
||||
'is_usable' => 'boolean',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,13 +84,14 @@ class Service extends BaseModel
|
||||
}
|
||||
if ($oldConfigHash === $newConfigHash) {
|
||||
return false;
|
||||
}
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
} else {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected function serverStatus(): Attribute
|
||||
@@ -134,7 +135,7 @@ class Service extends BaseModel
|
||||
|
||||
public static function ownedByCurrentTeam()
|
||||
{
|
||||
return \App\Models\Service::query()->whereRelation('environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
||||
return Service::whereRelation('environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
||||
}
|
||||
|
||||
public function getContainersToStop(): array
|
||||
@@ -160,7 +161,7 @@ class Service extends BaseModel
|
||||
}
|
||||
|
||||
$startTime = time();
|
||||
while ($processes !== []) {
|
||||
while (count($processes) > 0) {
|
||||
$finishedProcesses = array_filter($processes, function ($process) {
|
||||
return ! $process->running();
|
||||
});
|
||||
@@ -230,7 +231,11 @@ class Service extends BaseModel
|
||||
continue;
|
||||
}
|
||||
if ($status->startsWith('running')) {
|
||||
$complexStatus = $complexStatus === 'exited' ? 'degraded' : 'running';
|
||||
if ($complexStatus === 'exited') {
|
||||
$complexStatus = 'degraded';
|
||||
} else {
|
||||
$complexStatus = 'running';
|
||||
}
|
||||
} elseif ($status->startsWith('restarting')) {
|
||||
$complexStatus = 'degraded';
|
||||
} elseif ($status->startsWith('exited')) {
|
||||
@@ -255,7 +260,11 @@ class Service extends BaseModel
|
||||
continue;
|
||||
}
|
||||
if ($status->startsWith('running')) {
|
||||
$complexStatus = $complexStatus === 'exited' ? 'degraded' : 'running';
|
||||
if ($complexStatus === 'exited') {
|
||||
$complexStatus = 'degraded';
|
||||
} else {
|
||||
$complexStatus = 'running';
|
||||
}
|
||||
} elseif ($status->startsWith('restarting')) {
|
||||
$complexStatus = 'degraded';
|
||||
} elseif ($status->startsWith('exited')) {
|
||||
@@ -1278,10 +1287,12 @@ class Service extends BaseModel
|
||||
$real_value = $env->real_value;
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
} elseif ($env->is_literal || $env->is_multiline) {
|
||||
$real_value = '\''.$real_value.'\'';
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
if ($env->is_literal || $env->is_multiline) {
|
||||
$real_value = '\''.$real_value.'\'';
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
}
|
||||
$commands[] = "echo \"{$env->key}={$real_value}\" >> .env";
|
||||
}
|
||||
@@ -1296,12 +1307,11 @@ class Service extends BaseModel
|
||||
{
|
||||
if ((int) $this->compose_parsing_version >= 3) {
|
||||
return newParser($this);
|
||||
}
|
||||
if ($this->docker_compose_raw) {
|
||||
} elseif ($this->docker_compose_raw) {
|
||||
return parseDockerComposeFile($this, $isNew);
|
||||
} else {
|
||||
return collect([]);
|
||||
}
|
||||
|
||||
return collect([]);
|
||||
}
|
||||
|
||||
public function networks()
|
||||
|
||||
@@ -34,12 +34,12 @@ class ServiceApplication extends BaseModel
|
||||
|
||||
public static function ownedByCurrentTeamAPI(int $teamId)
|
||||
{
|
||||
return \App\Models\ServiceApplication::query()->whereRelation('service.environment.project.team', 'id', $teamId)->orderBy('name');
|
||||
return ServiceApplication::whereRelation('service.environment.project.team', 'id', $teamId)->orderBy('name');
|
||||
}
|
||||
|
||||
public static function ownedByCurrentTeam()
|
||||
{
|
||||
return \App\Models\ServiceApplication::query()->whereRelation('service.environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
||||
return ServiceApplication::whereRelation('service.environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
||||
}
|
||||
|
||||
public function isRunning()
|
||||
|
||||
@@ -26,12 +26,12 @@ class ServiceDatabase extends BaseModel
|
||||
|
||||
public static function ownedByCurrentTeamAPI(int $teamId)
|
||||
{
|
||||
return \App\Models\ServiceDatabase::query()->whereRelation('service.environment.project.team', 'id', $teamId)->orderBy('name');
|
||||
return ServiceDatabase::whereRelation('service.environment.project.team', 'id', $teamId)->orderBy('name');
|
||||
}
|
||||
|
||||
public static function ownedByCurrentTeam()
|
||||
{
|
||||
return \App\Models\ServiceDatabase::query()->whereRelation('service.environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
||||
return ServiceDatabase::whereRelation('service.environment.project.team', 'id', currentTeam()->id)->orderBy('name');
|
||||
}
|
||||
|
||||
public function restart()
|
||||
@@ -133,19 +133,10 @@ class ServiceDatabase extends BaseModel
|
||||
|
||||
public function isBackupSolutionAvailable()
|
||||
{
|
||||
if (str($this->databaseType())->contains('mysql')) {
|
||||
return true;
|
||||
}
|
||||
if (str($this->databaseType())->contains('postgres')) {
|
||||
return true;
|
||||
}
|
||||
if (str($this->databaseType())->contains('postgis')) {
|
||||
return true;
|
||||
}
|
||||
if (str($this->databaseType())->contains('mariadb')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (bool) str($this->databaseType())->contains('mongodb');
|
||||
return str($this->databaseType())->contains('mysql') ||
|
||||
str($this->databaseType())->contains('postgres') ||
|
||||
str($this->databaseType())->contains('postgis') ||
|
||||
str($this->databaseType())->contains('mariadb') ||
|
||||
str($this->databaseType())->contains('mongodb');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,8 @@ class SharedEnvironmentVariable extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'key' => 'string',
|
||||
'value' => 'encrypted',
|
||||
];
|
||||
}
|
||||
protected $casts = [
|
||||
'key' => 'string',
|
||||
'value' => 'encrypted',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -30,6 +30,23 @@ class SlackNotificationSettings extends Model
|
||||
'server_unreachable_slack_notifications',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'slack_enabled' => 'boolean',
|
||||
'slack_webhook_url' => 'encrypted',
|
||||
|
||||
'deployment_success_slack_notifications' => 'boolean',
|
||||
'deployment_failure_slack_notifications' => 'boolean',
|
||||
'status_change_slack_notifications' => 'boolean',
|
||||
'backup_success_slack_notifications' => 'boolean',
|
||||
'backup_failure_slack_notifications' => 'boolean',
|
||||
'scheduled_task_success_slack_notifications' => 'boolean',
|
||||
'scheduled_task_failure_slack_notifications' => 'boolean',
|
||||
'docker_cleanup_slack_notifications' => 'boolean',
|
||||
'server_disk_usage_slack_notifications' => 'boolean',
|
||||
'server_reachable_slack_notifications' => 'boolean',
|
||||
'server_unreachable_slack_notifications' => 'boolean',
|
||||
];
|
||||
|
||||
public function team()
|
||||
{
|
||||
return $this->belongsTo(Team::class);
|
||||
@@ -39,24 +56,4 @@ class SlackNotificationSettings extends Model
|
||||
{
|
||||
return $this->slack_enabled;
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'slack_enabled' => 'boolean',
|
||||
'slack_webhook_url' => 'encrypted',
|
||||
|
||||
'deployment_success_slack_notifications' => 'boolean',
|
||||
'deployment_failure_slack_notifications' => 'boolean',
|
||||
'status_change_slack_notifications' => 'boolean',
|
||||
'backup_success_slack_notifications' => 'boolean',
|
||||
'backup_failure_slack_notifications' => 'boolean',
|
||||
'scheduled_task_success_slack_notifications' => 'boolean',
|
||||
'scheduled_task_failure_slack_notifications' => 'boolean',
|
||||
'docker_cleanup_slack_notifications' => 'boolean',
|
||||
'server_disk_usage_slack_notifications' => 'boolean',
|
||||
'server_reachable_slack_notifications' => 'boolean',
|
||||
'server_unreachable_slack_notifications' => 'boolean',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -16,10 +15,14 @@ class StandaloneClickhouse extends BaseModel
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
|
||||
|
||||
protected $casts = [
|
||||
'clickhouse_password' => 'encrypted',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($database) {
|
||||
LocalPersistentVolume::query()->create([
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'clickhouse-data-'.$database->uuid,
|
||||
'mount_path' => '/bitnami/clickhouse',
|
||||
'host_path' => null,
|
||||
@@ -66,13 +69,14 @@ class StandaloneClickhouse extends BaseModel
|
||||
}
|
||||
if ($oldConfigHash === $newConfigHash) {
|
||||
return false;
|
||||
}
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
} else {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function isRunning()
|
||||
@@ -105,8 +109,8 @@ class StandaloneClickhouse extends BaseModel
|
||||
return;
|
||||
}
|
||||
$server = data_get($this, 'destination.server');
|
||||
foreach ($persistentStorages as $persistentStorage) {
|
||||
instant_remote_process(["docker volume rm -f $persistentStorage->name"], $server, false);
|
||||
foreach ($persistentStorages as $storage) {
|
||||
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +283,7 @@ class StandaloneClickhouse extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -301,7 +305,7 @@ class StandaloneClickhouse extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -315,11 +319,4 @@ class StandaloneClickhouse extends BaseModel
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'clickhouse_password' => 'encrypted',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,10 +74,6 @@ class StandaloneDocker extends BaseModel
|
||||
|
||||
public function attachedTo()
|
||||
{
|
||||
if ($this->applications?->count() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->databases()->count() > 0;
|
||||
return $this->applications?->count() > 0 || $this->databases()->count() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -16,10 +15,14 @@ class StandaloneDragonfly extends BaseModel
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
|
||||
|
||||
protected $casts = [
|
||||
'dragonfly_password' => 'encrypted',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($database) {
|
||||
LocalPersistentVolume::query()->create([
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'dragonfly-data-'.$database->uuid,
|
||||
'mount_path' => '/data',
|
||||
'host_path' => null,
|
||||
@@ -66,13 +69,14 @@ class StandaloneDragonfly extends BaseModel
|
||||
}
|
||||
if ($oldConfigHash === $newConfigHash) {
|
||||
return false;
|
||||
}
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
} else {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function isRunning()
|
||||
@@ -105,8 +109,8 @@ class StandaloneDragonfly extends BaseModel
|
||||
return;
|
||||
}
|
||||
$server = data_get($this, 'destination.server');
|
||||
foreach ($persistentStorages as $persistentStorage) {
|
||||
instant_remote_process(["docker volume rm -f $persistentStorage->name"], $server, false);
|
||||
foreach ($persistentStorages as $storage) {
|
||||
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +277,7 @@ class StandaloneDragonfly extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -295,7 +299,7 @@ class StandaloneDragonfly extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -315,11 +319,4 @@ class StandaloneDragonfly extends BaseModel
|
||||
return $this->morphMany(EnvironmentVariable::class, 'resourceable')
|
||||
->orderBy('key', 'asc');
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'dragonfly_password' => 'encrypted',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -16,10 +15,14 @@ class StandaloneKeydb extends BaseModel
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'server_status'];
|
||||
|
||||
protected $casts = [
|
||||
'keydb_password' => 'encrypted',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($database) {
|
||||
LocalPersistentVolume::query()->create([
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'keydb-data-'.$database->uuid,
|
||||
'mount_path' => '/data',
|
||||
'host_path' => null,
|
||||
@@ -66,13 +69,14 @@ class StandaloneKeydb extends BaseModel
|
||||
}
|
||||
if ($oldConfigHash === $newConfigHash) {
|
||||
return false;
|
||||
}
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
} else {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function isRunning()
|
||||
@@ -105,8 +109,8 @@ class StandaloneKeydb extends BaseModel
|
||||
return;
|
||||
}
|
||||
$server = data_get($this, 'destination.server');
|
||||
foreach ($persistentStorages as $persistentStorage) {
|
||||
instant_remote_process(["docker volume rm -f $persistentStorage->name"], $server, false);
|
||||
foreach ($persistentStorages as $storage) {
|
||||
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +277,7 @@ class StandaloneKeydb extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -295,7 +299,7 @@ class StandaloneKeydb extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -315,11 +319,4 @@ class StandaloneKeydb extends BaseModel
|
||||
return $this->morphMany(EnvironmentVariable::class, 'resourceable')
|
||||
->orderBy('key', 'asc');
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'keydb_password' => 'encrypted',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -16,10 +15,14 @@ class StandaloneMariadb extends BaseModel
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
|
||||
|
||||
protected $casts = [
|
||||
'mariadb_password' => 'encrypted',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($database) {
|
||||
LocalPersistentVolume::query()->create([
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'mariadb-data-'.$database->uuid,
|
||||
'mount_path' => '/var/lib/mysql',
|
||||
'host_path' => null,
|
||||
@@ -66,13 +69,14 @@ class StandaloneMariadb extends BaseModel
|
||||
}
|
||||
if ($oldConfigHash === $newConfigHash) {
|
||||
return false;
|
||||
}
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
} else {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function isRunning()
|
||||
@@ -105,8 +109,8 @@ class StandaloneMariadb extends BaseModel
|
||||
return;
|
||||
}
|
||||
$server = data_get($this, 'destination.server');
|
||||
foreach ($persistentStorages as $persistentStorage) {
|
||||
instant_remote_process(["docker volume rm -f $persistentStorage->name"], $server, false);
|
||||
foreach ($persistentStorages as $storage) {
|
||||
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +283,7 @@ class StandaloneMariadb extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -301,7 +305,7 @@ class StandaloneMariadb extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -315,11 +319,4 @@ class StandaloneMariadb extends BaseModel
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'mariadb_password' => 'encrypted',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,10 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Throwable;
|
||||
|
||||
class StandaloneMongodb extends BaseModel
|
||||
{
|
||||
@@ -20,7 +18,7 @@ class StandaloneMongodb extends BaseModel
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($database) {
|
||||
LocalPersistentVolume::query()->create([
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'mongodb-configdb-'.$database->uuid,
|
||||
'mount_path' => '/data/configdb',
|
||||
'host_path' => null,
|
||||
@@ -28,7 +26,7 @@ class StandaloneMongodb extends BaseModel
|
||||
'resource_type' => $database->getMorphClass(),
|
||||
'is_readonly' => true,
|
||||
]);
|
||||
LocalPersistentVolume::query()->create([
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'mongodb-db-'.$database->uuid,
|
||||
'mount_path' => '/data/db',
|
||||
'host_path' => null,
|
||||
@@ -75,13 +73,14 @@ class StandaloneMongodb extends BaseModel
|
||||
}
|
||||
if ($oldConfigHash === $newConfigHash) {
|
||||
return false;
|
||||
}
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
} else {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function isRunning()
|
||||
@@ -114,8 +113,8 @@ class StandaloneMongodb extends BaseModel
|
||||
return;
|
||||
}
|
||||
$server = data_get($this, 'destination.server');
|
||||
foreach ($persistentStorages as $persistentStorage) {
|
||||
instant_remote_process(["docker volume rm -f $persistentStorage->name"], $server, false);
|
||||
foreach ($persistentStorages as $storage) {
|
||||
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +196,7 @@ class StandaloneMongodb extends BaseModel
|
||||
get: function ($value) {
|
||||
try {
|
||||
return decrypt($value);
|
||||
} catch (Throwable $th) {
|
||||
} catch (\Throwable $th) {
|
||||
$this->mongo_initdb_root_password = encrypt($value);
|
||||
$this->save();
|
||||
|
||||
@@ -298,7 +297,7 @@ class StandaloneMongodb extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -320,7 +319,7 @@ class StandaloneMongodb extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -16,10 +15,15 @@ class StandaloneMysql extends BaseModel
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
|
||||
|
||||
protected $casts = [
|
||||
'mysql_password' => 'encrypted',
|
||||
'mysql_root_password' => 'encrypted',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($database) {
|
||||
LocalPersistentVolume::query()->create([
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'mysql-data-'.$database->uuid,
|
||||
'mount_path' => '/var/lib/mysql',
|
||||
'host_path' => null,
|
||||
@@ -66,13 +70,14 @@ class StandaloneMysql extends BaseModel
|
||||
}
|
||||
if ($oldConfigHash === $newConfigHash) {
|
||||
return false;
|
||||
}
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
} else {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function isRunning()
|
||||
@@ -105,8 +110,8 @@ class StandaloneMysql extends BaseModel
|
||||
return;
|
||||
}
|
||||
$server = data_get($this, 'destination.server');
|
||||
foreach ($persistentStorages as $persistentStorage) {
|
||||
instant_remote_process(["docker volume rm -f $persistentStorage->name"], $server, false);
|
||||
foreach ($persistentStorages as $storage) {
|
||||
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +278,7 @@ class StandaloneMysql extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -295,7 +300,7 @@ class StandaloneMysql extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -315,12 +320,4 @@ class StandaloneMysql extends BaseModel
|
||||
return $this->morphMany(EnvironmentVariable::class, 'resourceable')
|
||||
->orderBy('key', 'asc');
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'mysql_password' => 'encrypted',
|
||||
'mysql_root_password' => 'encrypted',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -16,10 +15,15 @@ class StandalonePostgresql extends BaseModel
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
|
||||
|
||||
protected $casts = [
|
||||
'init_scripts' => 'array',
|
||||
'postgres_password' => 'encrypted',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($database) {
|
||||
LocalPersistentVolume::query()->create([
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'postgres-data-'.$database->uuid,
|
||||
'mount_path' => '/var/lib/postgresql/data',
|
||||
'host_path' => null,
|
||||
@@ -70,8 +74,8 @@ class StandalonePostgresql extends BaseModel
|
||||
return;
|
||||
}
|
||||
$server = data_get($this, 'destination.server');
|
||||
foreach ($persistentStorages as $persistentStorage) {
|
||||
instant_remote_process(["docker volume rm -f $persistentStorage->name"], $server, false);
|
||||
foreach ($persistentStorages as $storage) {
|
||||
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,13 +95,14 @@ class StandalonePostgresql extends BaseModel
|
||||
}
|
||||
if ($oldConfigHash === $newConfigHash) {
|
||||
return false;
|
||||
}
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
} else {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function isRunning()
|
||||
@@ -278,7 +283,7 @@ class StandalonePostgresql extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -300,7 +305,7 @@ class StandalonePostgresql extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -315,12 +320,4 @@ class StandalonePostgresql extends BaseModel
|
||||
return $this->morphMany(EnvironmentVariable::class, 'resourceable')
|
||||
->orderBy('key', 'asc');
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'init_scripts' => 'array',
|
||||
'postgres_password' => 'encrypted',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -19,7 +18,7 @@ class StandaloneRedis extends BaseModel
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($database) {
|
||||
LocalPersistentVolume::query()->create([
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'redis-data-'.$database->uuid,
|
||||
'mount_path' => '/data',
|
||||
'host_path' => null,
|
||||
@@ -66,13 +65,14 @@ class StandaloneRedis extends BaseModel
|
||||
}
|
||||
if ($oldConfigHash === $newConfigHash) {
|
||||
return false;
|
||||
}
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
} else {
|
||||
if ($save) {
|
||||
$this->config_hash = $newConfigHash;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function isRunning()
|
||||
@@ -105,8 +105,8 @@ class StandaloneRedis extends BaseModel
|
||||
return;
|
||||
}
|
||||
$server = data_get($this, 'destination.server');
|
||||
foreach ($persistentStorages as $persistentStorage) {
|
||||
instant_remote_process(["docker volume rm -f $persistentStorage->name"], $server, false);
|
||||
foreach ($persistentStorages as $storage) {
|
||||
instant_remote_process(["docker volume rm -f $storage->name"], $server, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ class StandaloneRedis extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
@@ -310,7 +310,7 @@ class StandaloneRedis extends BaseModel
|
||||
if ($error === 'Unauthorized') {
|
||||
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.';
|
||||
}
|
||||
throw new Exception($error);
|
||||
throw new \Exception($error);
|
||||
}
|
||||
$metrics = json_decode($metrics, true);
|
||||
$parsedCollection = collect($metrics)->map(function ($metric) {
|
||||
|
||||
@@ -19,7 +19,7 @@ class Subscription extends Model
|
||||
if (! $this->stripe_plan_id) {
|
||||
return 'zero';
|
||||
}
|
||||
$subscription = \App\Models\Subscription::query()->where('id', $this->id)->first();
|
||||
$subscription = Subscription::where('id', $this->id)->first();
|
||||
if (! $subscription) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -77,10 +77,6 @@ class SwarmDocker extends BaseModel
|
||||
|
||||
public function attachedTo()
|
||||
{
|
||||
if ($this->applications?->count() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->databases()->count() > 0;
|
||||
return $this->applications?->count() > 0 || $this->databases()->count() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use App\Notifications\Channels\SendsEmail;
|
||||
use App\Notifications\Channels\SendsPushover;
|
||||
use App\Notifications\Channels\SendsSlack;
|
||||
use App\Traits\HasNotificationSettings;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
@@ -41,6 +40,10 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $casts = [
|
||||
'personal_team' => 'boolean',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($team) {
|
||||
@@ -53,7 +56,7 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
|
||||
|
||||
static::saving(function ($team) {
|
||||
if (auth()->user()?->isMember()) {
|
||||
throw new Exception('You are not allowed to update this team.');
|
||||
throw new \Exception('You are not allowed to update this team.');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -92,7 +95,11 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
|
||||
|
||||
public function serverOverflow()
|
||||
{
|
||||
return $this->serverLimit() < $this->servers->count();
|
||||
if ($this->serverLimit() < $this->servers->count()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function serverLimit()
|
||||
@@ -100,7 +107,7 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
|
||||
if (currentTeam()->id === 0 && isDev()) {
|
||||
return 9999999;
|
||||
}
|
||||
$team = \App\Models\Team::query()->find(currentTeam()->id);
|
||||
$team = Team::find(currentTeam()->id);
|
||||
if (! $team) {
|
||||
return 0;
|
||||
}
|
||||
@@ -162,20 +169,12 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
|
||||
if (isCloud()) {
|
||||
return true;
|
||||
}
|
||||
if ($this->getNotificationSettings('email')?->isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
if ($this->getNotificationSettings('discord')?->isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
if ($this->getNotificationSettings('slack')?->isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
if ($this->getNotificationSettings('telegram')?->isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (bool) $this->getNotificationSettings('pushover')?->isEnabled();
|
||||
return $this->getNotificationSettings('email')?->isEnabled() ||
|
||||
$this->getNotificationSettings('discord')?->isEnabled() ||
|
||||
$this->getNotificationSettings('slack')?->isEnabled() ||
|
||||
$this->getNotificationSettings('telegram')?->isEnabled() ||
|
||||
$this->getNotificationSettings('pushover')?->isEnabled();
|
||||
}
|
||||
|
||||
public function subscriptionEnded()
|
||||
@@ -223,7 +222,11 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
|
||||
|
||||
public function isEmpty()
|
||||
{
|
||||
return $this->projects()->count() === 0 && $this->servers()->count() === 0 && $this->privateKeys()->count() === 0 && $this->sources()->count() === 0;
|
||||
if ($this->projects()->count() === 0 && $this->servers()->count() === 0 && $this->privateKeys()->count() === 0 && $this->sources()->count() === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function projects()
|
||||
@@ -279,11 +282,4 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
|
||||
{
|
||||
return $this->hasOne(PushoverNotificationSettings::class);
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'personal_team' => 'boolean',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,10 @@ class TeamInvitation extends Model
|
||||
$diff = $createdAt->diffInDays(now());
|
||||
if ($diff <= config('constants.invitation.link.expiration_days')) {
|
||||
return true;
|
||||
}
|
||||
$this->delete();
|
||||
} else {
|
||||
$this->delete();
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,36 @@ class TelegramNotificationSettings extends Model
|
||||
'telegram_notifications_server_unreachable_thread_id',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'telegram_enabled' => 'boolean',
|
||||
'telegram_token' => 'encrypted',
|
||||
'telegram_chat_id' => 'encrypted',
|
||||
|
||||
'deployment_success_telegram_notifications' => 'boolean',
|
||||
'deployment_failure_telegram_notifications' => 'boolean',
|
||||
'status_change_telegram_notifications' => 'boolean',
|
||||
'backup_success_telegram_notifications' => 'boolean',
|
||||
'backup_failure_telegram_notifications' => 'boolean',
|
||||
'scheduled_task_success_telegram_notifications' => 'boolean',
|
||||
'scheduled_task_failure_telegram_notifications' => 'boolean',
|
||||
'docker_cleanup_telegram_notifications' => 'boolean',
|
||||
'server_disk_usage_telegram_notifications' => 'boolean',
|
||||
'server_reachable_telegram_notifications' => 'boolean',
|
||||
'server_unreachable_telegram_notifications' => 'boolean',
|
||||
|
||||
'telegram_notifications_deployment_success_thread_id' => 'encrypted',
|
||||
'telegram_notifications_deployment_failure_thread_id' => 'encrypted',
|
||||
'telegram_notifications_status_change_thread_id' => 'encrypted',
|
||||
'telegram_notifications_backup_success_thread_id' => 'encrypted',
|
||||
'telegram_notifications_backup_failure_thread_id' => 'encrypted',
|
||||
'telegram_notifications_scheduled_task_success_thread_id' => 'encrypted',
|
||||
'telegram_notifications_scheduled_task_failure_thread_id' => 'encrypted',
|
||||
'telegram_notifications_docker_cleanup_thread_id' => 'encrypted',
|
||||
'telegram_notifications_server_disk_usage_thread_id' => 'encrypted',
|
||||
'telegram_notifications_server_reachable_thread_id' => 'encrypted',
|
||||
'telegram_notifications_server_unreachable_thread_id' => 'encrypted',
|
||||
];
|
||||
|
||||
public function team()
|
||||
{
|
||||
return $this->belongsTo(Team::class);
|
||||
@@ -52,37 +82,4 @@ class TelegramNotificationSettings extends Model
|
||||
{
|
||||
return $this->telegram_enabled;
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'telegram_enabled' => 'boolean',
|
||||
'telegram_token' => 'encrypted',
|
||||
'telegram_chat_id' => 'encrypted',
|
||||
|
||||
'deployment_success_telegram_notifications' => 'boolean',
|
||||
'deployment_failure_telegram_notifications' => 'boolean',
|
||||
'status_change_telegram_notifications' => 'boolean',
|
||||
'backup_success_telegram_notifications' => 'boolean',
|
||||
'backup_failure_telegram_notifications' => 'boolean',
|
||||
'scheduled_task_success_telegram_notifications' => 'boolean',
|
||||
'scheduled_task_failure_telegram_notifications' => 'boolean',
|
||||
'docker_cleanup_telegram_notifications' => 'boolean',
|
||||
'server_disk_usage_telegram_notifications' => 'boolean',
|
||||
'server_reachable_telegram_notifications' => 'boolean',
|
||||
'server_unreachable_telegram_notifications' => 'boolean',
|
||||
|
||||
'telegram_notifications_deployment_success_thread_id' => 'encrypted',
|
||||
'telegram_notifications_deployment_failure_thread_id' => 'encrypted',
|
||||
'telegram_notifications_status_change_thread_id' => 'encrypted',
|
||||
'telegram_notifications_backup_success_thread_id' => 'encrypted',
|
||||
'telegram_notifications_backup_failure_thread_id' => 'encrypted',
|
||||
'telegram_notifications_scheduled_task_success_thread_id' => 'encrypted',
|
||||
'telegram_notifications_scheduled_task_failure_thread_id' => 'encrypted',
|
||||
'telegram_notifications_docker_cleanup_thread_id' => 'encrypted',
|
||||
'telegram_notifications_server_disk_usage_thread_id' => 'encrypted',
|
||||
'telegram_notifications_server_reachable_thread_id' => 'encrypted',
|
||||
'telegram_notifications_server_unreachable_thread_id' => 'encrypted',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,12 @@ class User extends Authenticatable implements SendsEmail
|
||||
'two_factor_secret',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'email_verified_at' => 'datetime',
|
||||
'force_password_reset' => 'boolean',
|
||||
'show_boarding' => 'boolean',
|
||||
];
|
||||
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
@@ -61,7 +67,7 @@ class User extends Authenticatable implements SendsEmail
|
||||
$team['id'] = 0;
|
||||
$team['name'] = 'Root Team';
|
||||
}
|
||||
$new_team = Team::query()->create($team);
|
||||
$new_team = Team::create($team);
|
||||
$user->teams()->attach($new_team, ['role' => 'owner']);
|
||||
});
|
||||
}
|
||||
@@ -77,7 +83,7 @@ class User extends Authenticatable implements SendsEmail
|
||||
$team['id'] = 0;
|
||||
$team['name'] = 'Root Team';
|
||||
}
|
||||
$new_team = Team::query()->create($team);
|
||||
$new_team = Team::create($team);
|
||||
$this->teams()->attach($new_team, ['role' => 'owner']);
|
||||
|
||||
return $new_team;
|
||||
@@ -92,7 +98,7 @@ class User extends Authenticatable implements SendsEmail
|
||||
hash('crc32b', $tokenEntropy)
|
||||
);
|
||||
|
||||
$personalAccessToken = $this->tokens()->create([
|
||||
$token = $this->tokens()->create([
|
||||
'name' => $name,
|
||||
'token' => hash('sha256', $plainTextToken),
|
||||
'abilities' => $abilities,
|
||||
@@ -100,7 +106,7 @@ class User extends Authenticatable implements SendsEmail
|
||||
'team_id' => session('currentTeam')->id,
|
||||
]);
|
||||
|
||||
return new NewAccessToken($personalAccessToken, $personalAccessToken->getKey().'|'.$plainTextToken);
|
||||
return new NewAccessToken($token, $token->getKey().'|'.$plainTextToken);
|
||||
}
|
||||
|
||||
public function teams()
|
||||
@@ -115,7 +121,7 @@ class User extends Authenticatable implements SendsEmail
|
||||
|
||||
public function sendVerificationEmail()
|
||||
{
|
||||
$mailMessage = new MailMessage;
|
||||
$mail = new MailMessage;
|
||||
$url = Url::temporarySignedRoute(
|
||||
'verify.verify',
|
||||
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
|
||||
@@ -124,11 +130,11 @@ class User extends Authenticatable implements SendsEmail
|
||||
'hash' => sha1($this->getEmailForVerification()),
|
||||
]
|
||||
);
|
||||
$mailMessage->view('emails.email-verification', [
|
||||
$mail->view('emails.email-verification', [
|
||||
'url' => $url,
|
||||
]);
|
||||
$mailMessage->subject('Coolify: Verify your email.');
|
||||
send_user_an_email($mailMessage, $this->email);
|
||||
$mail->subject('Coolify: Verify your email.');
|
||||
send_user_an_email($mail, $this->email);
|
||||
}
|
||||
|
||||
public function sendPasswordResetNotification($token): void
|
||||
@@ -138,11 +144,7 @@ class User extends Authenticatable implements SendsEmail
|
||||
|
||||
public function isAdmin()
|
||||
{
|
||||
if ($this->role() === 'admin') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->role() === 'owner';
|
||||
return $this->role() === 'admin' || $this->role() === 'owner';
|
||||
}
|
||||
|
||||
public function isOwner()
|
||||
@@ -179,7 +181,11 @@ class User extends Authenticatable implements SendsEmail
|
||||
{
|
||||
$found_root_team = Auth::user()->teams->filter(function ($team) {
|
||||
if ($team->id == 0) {
|
||||
return (bool) Auth::user()->isAdmin();
|
||||
if (! Auth::user()->isAdmin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -195,7 +201,7 @@ class User extends Authenticatable implements SendsEmail
|
||||
return Auth::user()->teams[0];
|
||||
}
|
||||
|
||||
return Team::query()->find(session('currentTeam')->id);
|
||||
return Team::find(session('currentTeam')->id);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -215,13 +221,4 @@ class User extends Authenticatable implements SendsEmail
|
||||
|
||||
return data_get($user, 'pivot.role');
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'force_password_reset' => 'boolean',
|
||||
'show_boarding' => 'boolean',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,8 @@ class Webhook extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'type' => 'string',
|
||||
'payload' => 'encrypted',
|
||||
];
|
||||
}
|
||||
protected $casts = [
|
||||
'type' => 'string',
|
||||
'payload' => 'encrypted',
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user