+
Supabase PostgreSQL (with extensions)
+
+ Supabase is a modern, open-source alternative to PostgreSQL with lots of extensions.
-
-
PostGIS
-
- PostGIS is a PostgreSQL extension for geographic objects.
-
-
-
-
+
+
+
PostGIS
+
+ PostGIS is a PostgreSQL extension for geographic objects.
-
-
-
PGVector (16)
-
- PGVector is a PostgreSQL extension for vector data types.
-
+
+
+
+
+
+
PGVector (16)
+
+ PGVector is a PostgreSQL extension for vector data types.
-
+
+
-
+
From 2a9d4992513d80b67fce23f819c12cea7796a9e7 Mon Sep 17 00:00:00 2001
From: Andras Bacsai
Date: Tue, 17 Dec 2024 10:38:32 +0100
Subject: [PATCH 066/136] feat: migrate env variables to polymorphic
relationship fix: proxy status query ui
---
.../Api/ApplicationsController.php | 36 +++-
.../Controllers/Api/ServicesController.php | 19 +-
.../Project/Application/DeploymentNavbar.php | 2 +-
app/Livewire/Project/CloneMe.php | 15 +-
app/Livewire/Project/New/DockerCompose.php | 3 +-
app/Livewire/Project/Resource/Create.php | 3 +-
.../Shared/EnvironmentVariable/All.php | 67 ++-----
.../Shared/EnvironmentVariable/Show.php | 100 +++++++----
.../Project/Shared/ResourceOperations.php | 3 +-
app/Models/Application.php | 52 ++++--
app/Models/EnvironmentVariable.php | 73 ++++----
app/Models/Service.php | 14 +-
app/Models/StandaloneClickhouse.php | 10 +-
app/Models/StandaloneDragonfly.php | 16 +-
app/Models/StandaloneKeydb.php | 16 +-
app/Models/StandaloneMariadb.php | 10 +-
app/Models/StandaloneMongodb.php | 16 +-
app/Models/StandaloneMysql.php | 16 +-
app/Models/StandalonePostgresql.php | 16 +-
app/Models/StandaloneRedis.php | 16 +-
app/View/Components/Forms/Button.php | 3 +-
bootstrap/helpers/services.php | 51 ++++--
bootstrap/helpers/shared.php | 85 +++++----
...columns_to_environment_variables_table.php | 165 ++++++++++++++++++
.../views/components/forms/button.blade.php | 14 +-
resources/views/components/loading.blade.php | 4 +-
.../views/components/server/navbar.blade.php | 1 +
.../views/components/status/running.blade.php | 54 +++---
.../project/database/heading.blade.php | 2 +-
.../shared/environment-variable/all.blade.php | 6 +-
.../environment-variable/show.blade.php | 61 +++----
.../livewire/server/proxy/deploy.blade.php | 35 ++--
.../livewire/server/proxy/status.blade.php | 13 +-
33 files changed, 624 insertions(+), 373 deletions(-)
create mode 100644 database/migrations/2024_12_16_134437_add_resourceable_columns_to_environment_variables_table.php
diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php
index f02c4255dd..6e7370bb37 100644
--- a/app/Http/Controllers/Api/ApplicationsController.php
+++ b/app/Http/Controllers/Api/ApplicationsController.php
@@ -27,6 +27,9 @@ private function removeSensitiveData($application)
{
$application->makeHidden([
'id',
+ 'resourceable',
+ 'resourceable_id',
+ 'resourceable_type',
]);
if (request()->attributes->get('can_read_sensitive', false) === false) {
$application->makeHidden([
@@ -1893,8 +1896,9 @@ public function update_env_by_uuid(Request $request)
$is_preview = $request->is_preview ?? false;
$is_build_time = $request->is_build_time ?? false;
$is_literal = $request->is_literal ?? false;
+ $key = str($request->key)->trim()->replace(' ', '_')->value;
if ($is_preview) {
- $env = $application->environment_variables_preview->where('key', $request->key)->first();
+ $env = $application->environment_variables_preview->where('key', $key)->first();
if ($env) {
$env->value = $request->value;
if ($env->is_build_time != $is_build_time) {
@@ -1921,7 +1925,7 @@ public function update_env_by_uuid(Request $request)
], 404);
}
} else {
- $env = $application->environment_variables->where('key', $request->key)->first();
+ $env = $application->environment_variables->where('key', $key)->first();
if ($env) {
$env->value = $request->value;
if ($env->is_build_time != $is_build_time) {
@@ -2064,6 +2068,7 @@ public function create_bulk_envs(Request $request)
$bulk_data = collect($bulk_data)->map(function ($item) {
return collect($item)->only(['key', 'value', 'is_preview', 'is_build_time', 'is_literal']);
});
+ $returnedEnvs = collect();
foreach ($bulk_data as $item) {
$validator = customApiValidator($item, [
'key' => 'string|required',
@@ -2085,8 +2090,9 @@ public function create_bulk_envs(Request $request)
$is_literal = $item->get('is_literal') ?? false;
$is_multi_line = $item->get('is_multiline') ?? false;
$is_shown_once = $item->get('is_shown_once') ?? false;
+ $key = str($item->get('key'))->trim()->replace(' ', '_')->value;
if ($is_preview) {
- $env = $application->environment_variables_preview->where('key', $item->get('key'))->first();
+ $env = $application->environment_variables_preview->where('key', $key)->first();
if ($env) {
$env->value = $item->get('value');
if ($env->is_build_time != $is_build_time) {
@@ -2111,10 +2117,12 @@ public function create_bulk_envs(Request $request)
'is_literal' => $is_literal,
'is_multiline' => $is_multi_line,
'is_shown_once' => $is_shown_once,
+ 'resourceable_type' => get_class($application),
+ 'resourceable_id' => $application->id,
]);
}
} else {
- $env = $application->environment_variables->where('key', $item->get('key'))->first();
+ $env = $application->environment_variables->where('key', $key)->first();
if ($env) {
$env->value = $item->get('value');
if ($env->is_build_time != $is_build_time) {
@@ -2139,12 +2147,15 @@ public function create_bulk_envs(Request $request)
'is_literal' => $is_literal,
'is_multiline' => $is_multi_line,
'is_shown_once' => $is_shown_once,
+ 'resourceable_type' => get_class($application),
+ 'resourceable_id' => $application->id,
]);
}
}
+ $returnedEnvs->push($this->removeSensitiveData($env));
}
- return response()->json($this->removeSensitiveData($env))->setStatusCode(201);
+ return response()->json($returnedEnvs)->setStatusCode(201);
}
#[OA\Post(
@@ -2257,8 +2268,10 @@ public function create_env(Request $request)
], 422);
}
$is_preview = $request->is_preview ?? false;
+ $key = str($request->key)->trim()->replace(' ', '_')->value;
+
if ($is_preview) {
- $env = $application->environment_variables_preview->where('key', $request->key)->first();
+ $env = $application->environment_variables_preview->where('key', $key)->first();
if ($env) {
return response()->json([
'message' => 'Environment variable already exists. Use PATCH request to update it.',
@@ -2272,6 +2285,8 @@ public function create_env(Request $request)
'is_literal' => $request->is_literal ?? false,
'is_multiline' => $request->is_multiline ?? false,
'is_shown_once' => $request->is_shown_once ?? false,
+ 'resourceable_type' => get_class($application),
+ 'resourceable_id' => $application->id,
]);
return response()->json([
@@ -2279,7 +2294,7 @@ public function create_env(Request $request)
])->setStatusCode(201);
}
} else {
- $env = $application->environment_variables->where('key', $request->key)->first();
+ $env = $application->environment_variables->where('key', $key)->first();
if ($env) {
return response()->json([
'message' => 'Environment variable already exists. Use PATCH request to update it.',
@@ -2293,6 +2308,8 @@ public function create_env(Request $request)
'is_literal' => $request->is_literal ?? false,
'is_multiline' => $request->is_multiline ?? false,
'is_shown_once' => $request->is_shown_once ?? false,
+ 'resourceable_type' => get_class($application),
+ 'resourceable_id' => $application->id,
]);
return response()->json([
@@ -2380,7 +2397,10 @@ public function delete_env_by_uuid(Request $request)
'message' => 'Application not found.',
], 404);
}
- $found_env = EnvironmentVariable::where('uuid', $request->env_uuid)->where('application_id', $application->id)->first();
+ $found_env = EnvironmentVariable::where('uuid', $request->env_uuid)
+ ->where('resourceable_type', Application::class)
+ ->where('resourceable_id', $application->id)
+ ->first();
if (! $found_env) {
return response()->json([
'message' => 'Environment variable not found.',
diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php
index bcaba71078..f5ecdcf286 100644
--- a/app/Http/Controllers/Api/ServicesController.php
+++ b/app/Http/Controllers/Api/ServicesController.php
@@ -20,6 +20,9 @@ private function removeSensitiveData($service)
{
$service->makeHidden([
'id',
+ 'resourceable',
+ 'resourceable_id',
+ 'resourceable_type',
]);
if (request()->attributes->get('can_read_sensitive', false) === false) {
$service->makeHidden([
@@ -333,7 +336,8 @@ public function create_service(Request $request)
EnvironmentVariable::create([
'key' => $key,
'value' => $generatedValue,
- 'service_id' => $service->id,
+ 'resourceable_id' => $service->id,
+ 'resourceable_type' => $service->getMorphClass(),
'is_build_time' => false,
'is_preview' => false,
]);
@@ -673,7 +677,8 @@ public function update_env_by_uuid(Request $request)
], 422);
}
- $env = $service->environment_variables()->where('key', $request->key)->first();
+ $key = str($request->key)->trim()->replace(' ', '_')->value;
+ $env = $service->environment_variables()->where('key', $key)->first();
if (! $env) {
return response()->json(['message' => 'Environment variable not found.'], 404);
}
@@ -799,9 +804,9 @@ public function create_bulk_envs(Request $request)
'errors' => $validator->errors(),
], 422);
}
-
+ $key = str($item['key'])->trim()->replace(' ', '_')->value;
$env = $service->environment_variables()->updateOrCreate(
- ['key' => $item['key']],
+ ['key' => $key],
$item
);
@@ -909,7 +914,8 @@ public function create_env(Request $request)
], 422);
}
- $existingEnv = $service->environment_variables()->where('key', $request->key)->first();
+ $key = str($request->key)->trim()->replace(' ', '_')->value;
+ $existingEnv = $service->environment_variables()->where('key', $key)->first();
if ($existingEnv) {
return response()->json([
'message' => 'Environment variable already exists. Use PATCH request to update it.',
@@ -995,7 +1001,8 @@ public function delete_env_by_uuid(Request $request)
}
$env = EnvironmentVariable::where('uuid', $request->env_uuid)
- ->where('service_id', $service->id)
+ ->where('resourceable_type', Service::class)
+ ->where('resourceable_id', $service->id)
->first();
if (! $env) {
diff --git a/app/Livewire/Project/Application/DeploymentNavbar.php b/app/Livewire/Project/Application/DeploymentNavbar.php
index 6a6fa24823..87b40d4dc1 100644
--- a/app/Livewire/Project/Application/DeploymentNavbar.php
+++ b/app/Livewire/Project/Application/DeploymentNavbar.php
@@ -23,7 +23,7 @@ class DeploymentNavbar extends Component
public function mount()
{
- $this->application = Application::find($this->application_deployment_queue->application_id);
+ $this->application = Application::ownedByCurrentTeam()->find($this->application_deployment_queue->application_id);
$this->server = $this->application->destination->server;
$this->is_debug_enabled = $this->application->settings->is_debug_enabled;
}
diff --git a/app/Livewire/Project/CloneMe.php b/app/Livewire/Project/CloneMe.php
index 4d2bc65891..a8e3e6cab3 100644
--- a/app/Livewire/Project/CloneMe.php
+++ b/app/Livewire/Project/CloneMe.php
@@ -119,7 +119,7 @@ public function clone(string $type)
$environmentVaribles = $application->environment_variables()->get();
foreach ($environmentVaribles as $environmentVarible) {
$newEnvironmentVariable = $environmentVarible->replicate()->fill([
- 'application_id' => $newApplication->id,
+ 'resourceable_id' => $newApplication->id,
]);
$newEnvironmentVariable->save();
}
@@ -145,17 +145,8 @@ public function clone(string $type)
$environmentVaribles = $database->environment_variables()->get();
foreach ($environmentVaribles as $environmentVarible) {
$payload = [];
- if ($database->type() === 'standalone-postgresql') {
- $payload['standalone_postgresql_id'] = $newDatabase->id;
- } elseif ($database->type() === 'standalone-redis') {
- $payload['standalone_redis_id'] = $newDatabase->id;
- } elseif ($database->type() === 'standalone-mongodb') {
- $payload['standalone_mongodb_id'] = $newDatabase->id;
- } elseif ($database->type() === 'standalone-mysql') {
- $payload['standalone_mysql_id'] = $newDatabase->id;
- } elseif ($database->type() === 'standalone-mariadb') {
- $payload['standalone_mariadb_id'] = $newDatabase->id;
- }
+ $payload['resourceable_id'] = $newDatabase->id;
+ $payload['resourceable_type'] = $newDatabase->getMorphClass();
$newEnvironmentVariable = $environmentVarible->replicate()->fill($payload);
$newEnvironmentVariable->save();
}
diff --git a/app/Livewire/Project/New/DockerCompose.php b/app/Livewire/Project/New/DockerCompose.php
index 199a20cf6e..e6667e6f3d 100644
--- a/app/Livewire/Project/New/DockerCompose.php
+++ b/app/Livewire/Project/New/DockerCompose.php
@@ -87,7 +87,8 @@ public function submit()
'value' => $variable,
'is_build_time' => false,
'is_preview' => false,
- 'service_id' => $service->id,
+ 'resourceable_id' => $service->id,
+ 'resourceable_type' => $service->getMorphClass(),
]);
}
$service->name = "service-$service->uuid";
diff --git a/app/Livewire/Project/Resource/Create.php b/app/Livewire/Project/Resource/Create.php
index 9266a57fc8..4fbda22118 100644
--- a/app/Livewire/Project/Resource/Create.php
+++ b/app/Livewire/Project/Resource/Create.php
@@ -95,7 +95,8 @@ public function mount()
EnvironmentVariable::create([
'key' => $key,
'value' => $value,
- 'service_id' => $service->id,
+ 'resourceable_id' => $service->id,
+ 'resourceable_type' => $service->getMorphClass(),
'is_build_time' => false,
'is_preview' => false,
]);
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php
index 787d33a690..80156bf65a 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php
@@ -4,7 +4,6 @@
use App\Models\EnvironmentVariable;
use Livewire\Component;
-use Visus\Cuid2\Cuid2;
class All extends Component
{
@@ -14,38 +13,35 @@ class All extends Component
public bool $showPreview = false;
- public ?string $modalId = null;
-
public ?string $variables = null;
public ?string $variablesPreview = null;
public string $view = 'normal';
+ public bool $is_env_sorting_enabled = false;
+
protected $listeners = [
'saveKey' => 'submit',
'refreshEnvs',
'environmentVariableDeleted' => 'refreshEnvs',
];
- protected $rules = [
- 'resource.settings.is_env_sorting_enabled' => 'required|boolean',
- ];
-
public function mount()
{
+ $this->is_env_sorting_enabled = data_get($this->resource, 'settings.is_env_sorting_enabled', false);
$this->resourceClass = get_class($this->resource);
$resourceWithPreviews = [\App\Models\Application::class];
- $simpleDockerfile = ! is_null(data_get($this->resource, 'dockerfile'));
+ $simpleDockerfile = filled(data_get($this->resource, 'dockerfile'));
if (str($this->resourceClass)->contains($resourceWithPreviews) && ! $simpleDockerfile) {
$this->showPreview = true;
}
- $this->modalId = new Cuid2;
$this->sortEnvironmentVariables();
}
public function instantSave()
{
+ $this->resource->settings->is_env_sorting_enabled = $this->is_env_sorting_enabled;
$this->resource->settings->save();
$this->sortEnvironmentVariables();
$this->dispatch('success', 'Environment variable settings updated.');
@@ -53,7 +49,7 @@ public function instantSave()
public function sortEnvironmentVariables()
{
- if (! data_get($this->resource, 'settings.is_env_sorting_enabled')) {
+ if ($this->is_env_sorting_enabled === false) {
if ($this->resource->environment_variables) {
$this->resource->environment_variables = $this->resource->environment_variables->sortBy('order')->values();
}
@@ -178,35 +174,12 @@ private function createEnvironmentVariable($data)
$environment->is_multiline = $data['is_multiline'] ?? false;
$environment->is_literal = $data['is_literal'] ?? false;
$environment->is_preview = $data['is_preview'] ?? false;
-
- $resourceType = $this->resource->type();
- $resourceIdField = $this->getResourceIdField($resourceType);
-
- if ($resourceIdField) {
- $environment->$resourceIdField = $this->resource->id;
- }
+ $environment->resourceable_id = $this->resource->id;
+ $environment->resourceable_type = $this->resource->getMorphClass();
return $environment;
}
- private function getResourceIdField($resourceType)
- {
- $resourceTypes = [
- 'application' => 'application_id',
- 'standalone-postgresql' => 'standalone_postgresql_id',
- 'standalone-redis' => 'standalone_redis_id',
- 'standalone-mongodb' => 'standalone_mongodb_id',
- 'standalone-mysql' => 'standalone_mysql_id',
- 'standalone-mariadb' => 'standalone_mariadb_id',
- 'standalone-keydb' => 'standalone_keydb_id',
- 'standalone-dragonfly' => 'standalone_dragonfly_id',
- 'standalone-clickhouse' => 'standalone_clickhouse_id',
- 'service' => 'service_id',
- ];
-
- return $resourceTypes[$resourceType] ?? null;
- }
-
private function deleteRemovedVariables($isPreview, $variables)
{
$method = $isPreview ? 'environment_variables_preview' : 'environment_variables';
@@ -231,34 +204,14 @@ private function updateOrCreateVariables($isPreview, $variables)
$environment->is_build_time = false;
$environment->is_multiline = false;
$environment->is_preview = $isPreview;
+ $environment->resourceable_id = $this->resource->id;
+ $environment->resourceable_type = $this->resource->getMorphClass();
- $this->setEnvironmentResourceId($environment);
$environment->save();
}
}
}
- private function setEnvironmentResourceId($environment)
- {
- $resourceTypes = [
- 'application' => 'application_id',
- 'standalone-postgresql' => 'standalone_postgresql_id',
- 'standalone-redis' => 'standalone_redis_id',
- 'standalone-mongodb' => 'standalone_mongodb_id',
- 'standalone-mysql' => 'standalone_mysql_id',
- 'standalone-mariadb' => 'standalone_mariadb_id',
- 'standalone-keydb' => 'standalone_keydb_id',
- 'standalone-dragonfly' => 'standalone_dragonfly_id',
- 'standalone-clickhouse' => 'standalone_clickhouse_id',
- 'service' => 'service_id',
- ];
-
- $resourceType = $this->resource->type();
- if (isset($resourceTypes[$resourceType])) {
- $environment->{$resourceTypes[$resourceType]} = $this->resource->id;
- }
- }
-
public function refreshEnvs()
{
$this->resource->refresh();
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
index 2bccde0e9c..4b66bfdcb6 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
@@ -20,6 +20,26 @@ class Show extends Component
public string $type;
+ public string $key;
+
+ public ?string $value = null;
+
+ public ?string $real_value = null;
+
+ public bool $is_shared = false;
+
+ public bool $is_build_time = false;
+
+ public bool $is_multiline = false;
+
+ public bool $is_literal = false;
+
+ public bool $is_shown_once = false;
+
+ public bool $is_required = false;
+
+ public bool $is_really_required = false;
+
protected $listeners = [
'refreshEnvs' => 'refresh',
'refresh',
@@ -27,39 +47,59 @@ class Show extends Component
];
protected $rules = [
- 'env.key' => 'required|string',
- 'env.value' => 'nullable',
- 'env.is_build_time' => 'required|boolean',
- 'env.is_multiline' => 'required|boolean',
- 'env.is_literal' => 'required|boolean',
- 'env.is_shown_once' => 'required|boolean',
- 'env.real_value' => 'nullable',
- 'env.is_required' => 'required|boolean',
+ 'key' => 'required|string',
+ 'value' => 'nullable',
+ 'is_build_time' => 'required|boolean',
+ 'is_multiline' => 'required|boolean',
+ 'is_literal' => 'required|boolean',
+ 'is_shown_once' => 'required|boolean',
+ 'real_value' => 'nullable',
+ 'is_required' => 'required|boolean',
];
- protected $validationAttributes = [
- 'env.key' => 'Key',
- 'env.value' => 'Value',
- 'env.is_build_time' => 'Build Time',
- 'env.is_multiline' => 'Multiline',
- 'env.is_literal' => 'Literal',
- 'env.is_shown_once' => 'Shown Once',
- 'env.is_required' => 'Required',
- ];
+ public function mount()
+ {
+ $this->syncData();
+ if ($this->env->getMorphClass() === \App\Models\SharedEnvironmentVariable::class) {
+ $this->isSharedVariable = true;
+ }
+ $this->parameters = get_route_parameters();
+ $this->checkEnvs();
+
+ }
public function refresh()
{
- $this->env->refresh();
+ $this->syncData();
$this->checkEnvs();
}
- public function mount()
+ public function syncData(bool $toModel = false)
{
- if ($this->env->getMorphClass() === \App\Models\SharedEnvironmentVariable::class) {
- $this->isSharedVariable = true;
+ if ($toModel) {
+ $this->validate();
+ $this->env->key = $this->key;
+ $this->env->value = $this->value;
+ $this->env->is_build_time = $this->is_build_time;
+ $this->env->is_multiline = $this->is_multiline;
+ $this->env->is_literal = $this->is_literal;
+ $this->env->is_shown_once = $this->is_shown_once;
+ $this->env->is_required = $this->is_required;
+ $this->env->is_shared = $this->is_shared;
+ $this->env->save();
+ } else {
+
+ $this->key = $this->env->key;
+ $this->value = $this->env->value;
+ $this->is_build_time = $this->env->is_build_time ?? false;
+ $this->is_multiline = $this->env->is_multiline;
+ $this->is_literal = $this->env->is_literal;
+ $this->is_shown_once = $this->env->is_shown_once;
+ $this->is_required = $this->env->is_required ?? false;
+ $this->is_really_required = $this->env->is_really_required ?? false;
+ $this->is_shared = $this->env->is_shared ?? false;
+ $this->real_value = $this->env->real_value;
}
- $this->parameters = get_route_parameters();
- $this->checkEnvs();
}
public function checkEnvs()
@@ -103,17 +143,17 @@ public function submit()
try {
if ($this->isSharedVariable) {
$this->validate([
- 'env.key' => 'required|string',
- 'env.value' => 'nullable',
- 'env.is_shown_once' => 'required|boolean',
+ 'key' => 'required|string',
+ 'value' => 'nullable',
+ 'is_shown_once' => 'required|boolean',
]);
} else {
$this->validate();
}
- if (! $this->isSharedVariable && $this->env->is_required && str($this->env->real_value)->isEmpty()) {
+ if (! $this->isSharedVariable && $this->is_required && str($this->value)->isEmpty()) {
$oldValue = $this->env->getOriginal('value');
- $this->env->value = $oldValue;
+ $this->value = $oldValue;
$this->dispatch('error', 'Required environment variable cannot be empty.');
return;
@@ -122,10 +162,10 @@ public function submit()
$this->serialize();
if ($this->isSharedVariable) {
- unset($this->env->is_required);
+ unset($this->is_required);
}
- $this->env->save();
+ $this->syncData(true);
$this->dispatch('success', 'Environment variable updated.');
$this->dispatch('envsUpdated');
} catch (\Exception $e) {
diff --git a/app/Livewire/Project/Shared/ResourceOperations.php b/app/Livewire/Project/Shared/ResourceOperations.php
index de7bb3c057..98b31641a0 100644
--- a/app/Livewire/Project/Shared/ResourceOperations.php
+++ b/app/Livewire/Project/Shared/ResourceOperations.php
@@ -60,7 +60,8 @@ public function cloneTo($destination_id)
$environmentVaribles = $this->resource->environment_variables()->get();
foreach ($environmentVaribles as $environmentVarible) {
$newEnvironmentVariable = $environmentVarible->replicate()->fill([
- 'application_id' => $new_resource->id,
+ 'resourceable_id' => $new_resource->id,
+ 'resourceable_type' => $new_resource->getMorphClass(),
]);
$newEnvironmentVariable->save();
}
diff --git a/app/Models/Application.php b/app/Models/Application.php
index bfb2a10418..6413c8fea4 100644
--- a/app/Models/Application.php
+++ b/app/Models/Application.php
@@ -695,46 +695,62 @@ public function main_port()
return $this->settings->is_static ? [80] : $this->ports_exposes_array;
}
- public function environment_variables(): HasMany
+ public function environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class)->where('is_preview', false)->orderBy('key', 'asc');
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->where('is_preview', false)
+ ->orderBy('key', 'asc');
}
- public function runtime_environment_variables(): HasMany
+ public function runtime_environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class)->where('is_preview', false)->where('key', 'not like', 'NIXPACKS_%');
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->where('is_preview', false)
+ ->where('key', 'not like', 'NIXPACKS_%');
}
- // Preview Deployments
-
- public function build_environment_variables(): HasMany
+ public function build_environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class)->where('is_preview', false)->where('is_build_time', true)->where('key', 'not like', 'NIXPACKS_%');
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->where('is_preview', false)
+ ->where('is_build_time', true)
+ ->where('key', 'not like', 'NIXPACKS_%');
}
- public function nixpacks_environment_variables(): HasMany
+ public function nixpacks_environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class)->where('is_preview', false)->where('key', 'like', 'NIXPACKS_%');
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->where('is_preview', false)
+ ->where('key', 'like', 'NIXPACKS_%');
}
- public function environment_variables_preview(): HasMany
+ public function environment_variables_preview()
{
- return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->orderBy('key', 'asc');
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->where('is_preview', true)
+ ->orderByRaw("LOWER(key) LIKE LOWER('SERVICE%') DESC, LOWER(key) ASC");
}
- public function runtime_environment_variables_preview(): HasMany
+ public function runtime_environment_variables_preview()
{
- return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->where('key', 'not like', 'NIXPACKS_%');
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->where('is_preview', true)
+ ->where('key', 'not like', 'NIXPACKS_%');
}
- public function build_environment_variables_preview(): HasMany
+ public function build_environment_variables_preview()
{
- return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->where('is_build_time', true)->where('key', 'not like', 'NIXPACKS_%');
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->where('is_preview', true)
+ ->where('is_build_time', true)
+ ->where('key', 'not like', 'NIXPACKS_%');
}
- public function nixpacks_environment_variables_preview(): HasMany
+ public function nixpacks_environment_variables_preview()
{
- return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->where('key', 'like', 'NIXPACKS_%');
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->where('is_preview', true)
+ ->where('key', 'like', 'NIXPACKS_%');
}
public function scheduled_tasks(): HasMany
diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php
index 96c57e63e8..507ff0d7e2 100644
--- a/app/Models/EnvironmentVariable.php
+++ b/app/Models/EnvironmentVariable.php
@@ -14,9 +14,8 @@
properties: [
'id' => ['type' => 'integer'],
'uuid' => ['type' => 'string'],
- 'application_id' => ['type' => 'integer'],
- 'service_id' => ['type' => 'integer'],
- 'database_id' => ['type' => 'integer'],
+ 'resourceable_type' => ['type' => 'string'],
+ 'resourceable_id' => ['type' => 'integer'],
'is_build_time' => ['type' => 'boolean'],
'is_literal' => ['type' => 'boolean'],
'is_multiline' => ['type' => 'boolean'],
@@ -42,6 +41,8 @@ class EnvironmentVariable extends Model
'is_multiline' => 'boolean',
'is_preview' => 'boolean',
'version' => 'string',
+ 'resourceable_type' => 'string',
+ 'resourceable_id' => 'integer',
];
protected $appends = ['real_value', 'is_shared', 'is_really_required'];
@@ -53,18 +54,25 @@ protected static function booted()
$model->uuid = (string) new Cuid2;
}
});
+
static::created(function (EnvironmentVariable $environment_variable) {
- if ($environment_variable->application_id && ! $environment_variable->is_preview) {
- $found = ModelsEnvironmentVariable::where('key', $environment_variable->key)->where('application_id', $environment_variable->application_id)->where('is_preview', true)->first();
+ 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', $environment_variable->resourceable_id)
+ ->where('is_preview', true)
+ ->first();
+
if (! $found) {
- $application = Application::find($environment_variable->application_id);
- if ($application->build_pack !== 'dockerfile') {
+ $application = Application::find($environment_variable->resourceable_id);
+ if ($application && $application->build_pack !== 'dockerfile') {
ModelsEnvironmentVariable::create([
'key' => $environment_variable->key,
'value' => $environment_variable->value,
'is_build_time' => $environment_variable->is_build_time,
'is_multiline' => $environment_variable->is_multiline ?? false,
- 'application_id' => $environment_variable->application_id,
+ 'resourceable_type' => Application::class,
+ 'resourceable_id' => $environment_variable->resourceable_id,
'is_preview' => true,
]);
}
@@ -74,6 +82,7 @@ protected static function booted()
'version' => config('constants.coolify.version'),
]);
});
+
static::saving(function (EnvironmentVariable $environmentVariable) {
$environmentVariable->updateIsShared();
});
@@ -92,43 +101,32 @@ protected function value(): Attribute
);
}
- public function resource()
+ /**
+ * Get the parent resourceable model.
+ */
+ public function resourceable()
{
- $resource = null;
- if ($this->application_id) {
- $resource = Application::find($this->application_id);
- } elseif ($this->service_id) {
- $resource = Service::find($this->service_id);
- } elseif ($this->standalone_postgresql_id) {
- $resource = StandalonePostgresql::find($this->standalone_postgresql_id);
- } elseif ($this->standalone_redis_id) {
- $resource = StandaloneRedis::find($this->standalone_redis_id);
- } elseif ($this->standalone_mongodb_id) {
- $resource = StandaloneMongodb::find($this->standalone_mongodb_id);
- } elseif ($this->standalone_mysql_id) {
- $resource = StandaloneMysql::find($this->standalone_mysql_id);
- } elseif ($this->standalone_mariadb_id) {
- $resource = StandaloneMariadb::find($this->standalone_mariadb_id);
- } elseif ($this->standalone_keydb_id) {
- $resource = StandaloneKeydb::find($this->standalone_keydb_id);
- } elseif ($this->standalone_dragonfly_id) {
- $resource = StandaloneDragonfly::find($this->standalone_dragonfly_id);
- } elseif ($this->standalone_clickhouse_id) {
- $resource = StandaloneClickhouse::find($this->standalone_clickhouse_id);
- }
+ return $this->morphTo();
+ }
- return $resource;
+ public function resource()
+ {
+ return $this->resourceable;
}
public function realValue(): Attribute
{
- $resource = $this->resource();
-
return Attribute::make(
- get: function () use ($resource) {
- $env = $this->get_real_environment_variables($this->value, $resource);
+ get: function () {
+ if (! $this->relationLoaded('resourceable')) {
+ $this->load('resourceable');
+ }
+ $resource = $this->resourceable;
+ if (! $resource) {
+ return null;
+ }
- return data_get($env, 'value', $env);
+ return $this->get_real_environment_variables($this->value, $resource);
}
);
}
@@ -164,7 +162,6 @@ private function get_real_environment_variables(?string $environment_variable =
if ($sharedEnvsFound->isEmpty()) {
return $environment_variable;
}
-
foreach ($sharedEnvsFound as $sharedEnv) {
$type = str($sharedEnv)->match('/(.*?)\./');
if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
diff --git a/app/Models/Service.php b/app/Models/Service.php
index 5a2690490a..c605f189be 100644
--- a/app/Models/Service.php
+++ b/app/Models/Service.php
@@ -1120,7 +1120,8 @@ public function saveExtraFields($fields)
'key' => $key,
'value' => $value,
'is_build_time' => false,
- 'service_id' => $this->id,
+ 'resourceable_id' => $this->id,
+ 'resourceable_type' => $this->getMorphClass(),
'is_preview' => false,
]);
}
@@ -1232,14 +1233,17 @@ public function scheduled_tasks(): HasMany
return $this->hasMany(ScheduledTask::class)->orderBy('name', 'asc');
}
- public function environment_variables(): HasMany
+ public function environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class)->orderByRaw("LOWER(key) LIKE LOWER('SERVICE%') DESC, LOWER(key) ASC");
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->orderBy('key', 'asc');
}
- public function environment_variables_preview(): HasMany
+ public function environment_variables_preview()
{
- return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->orderByRaw("LOWER(key) LIKE LOWER('SERVICE%') DESC, LOWER(key) ASC");
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->where('is_preview', true)
+ ->orderByRaw("LOWER(key) LIKE LOWER('SERVICE%') DESC, LOWER(key) ASC");
}
public function workdir()
diff --git a/app/Models/StandaloneClickhouse.php b/app/Models/StandaloneClickhouse.php
index 6d66c68546..ec56d0fddc 100644
--- a/app/Models/StandaloneClickhouse.php
+++ b/app/Models/StandaloneClickhouse.php
@@ -5,7 +5,6 @@
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
-use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class StandaloneClickhouse extends BaseModel
@@ -251,14 +250,15 @@ public function destination()
return $this->morphTo();
}
- public function environment_variables(): HasMany
+ public function environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class);
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->orderBy('key', 'asc');
}
- public function runtime_environment_variables(): HasMany
+ public function runtime_environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class);
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable');
}
public function persistentStorages()
diff --git a/app/Models/StandaloneDragonfly.php b/app/Models/StandaloneDragonfly.php
index f7d83f0a31..683c9acb4a 100644
--- a/app/Models/StandaloneDragonfly.php
+++ b/app/Models/StandaloneDragonfly.php
@@ -5,7 +5,6 @@
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
-use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class StandaloneDragonfly extends BaseModel
@@ -251,14 +250,9 @@ public function destination()
return $this->morphTo();
}
- public function environment_variables(): HasMany
+ public function runtime_environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class);
- }
-
- public function runtime_environment_variables(): HasMany
- {
- return $this->hasMany(EnvironmentVariable::class);
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable');
}
public function persistentStorages()
@@ -319,4 +313,10 @@ public function isBackupSolutionAvailable()
{
return false;
}
+
+ public function environment_variables()
+ {
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->orderBy('key', 'asc');
+ }
}
diff --git a/app/Models/StandaloneKeydb.php b/app/Models/StandaloneKeydb.php
index 083c743d95..e239020aca 100644
--- a/app/Models/StandaloneKeydb.php
+++ b/app/Models/StandaloneKeydb.php
@@ -5,7 +5,6 @@
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
-use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class StandaloneKeydb extends BaseModel
@@ -251,14 +250,9 @@ public function destination()
return $this->morphTo();
}
- public function environment_variables(): HasMany
+ public function runtime_environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class);
- }
-
- public function runtime_environment_variables(): HasMany
- {
- return $this->hasMany(EnvironmentVariable::class);
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable');
}
public function persistentStorages()
@@ -319,4 +313,10 @@ public function isBackupSolutionAvailable()
{
return false;
}
+
+ public function environment_variables()
+ {
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->orderBy('key', 'asc');
+ }
}
diff --git a/app/Models/StandaloneMariadb.php b/app/Models/StandaloneMariadb.php
index 833dad6c4f..c010cf4410 100644
--- a/app/Models/StandaloneMariadb.php
+++ b/app/Models/StandaloneMariadb.php
@@ -5,7 +5,6 @@
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
-use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class StandaloneMariadb extends BaseModel
@@ -251,14 +250,15 @@ public function destination()
return $this->morphTo();
}
- public function environment_variables(): HasMany
+ public function environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class);
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->orderBy('key', 'asc');
}
- public function runtime_environment_variables(): HasMany
+ public function runtime_environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class);
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable');
}
public function persistentStorages()
diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php
index dd8893180b..e18317088f 100644
--- a/app/Models/StandaloneMongodb.php
+++ b/app/Models/StandaloneMongodb.php
@@ -5,7 +5,6 @@
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
-use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class StandaloneMongodb extends BaseModel
@@ -271,14 +270,9 @@ public function destination()
return $this->morphTo();
}
- public function environment_variables(): HasMany
+ public function runtime_environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class);
- }
-
- public function runtime_environment_variables(): HasMany
- {
- return $this->hasMany(EnvironmentVariable::class);
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable');
}
public function persistentStorages()
@@ -339,4 +333,10 @@ public function isBackupSolutionAvailable()
{
return true;
}
+
+ public function environment_variables()
+ {
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->orderBy('key', 'asc');
+ }
}
diff --git a/app/Models/StandaloneMysql.php b/app/Models/StandaloneMysql.php
index 710fea1bc8..e6c976a8a7 100644
--- a/app/Models/StandaloneMysql.php
+++ b/app/Models/StandaloneMysql.php
@@ -5,7 +5,6 @@
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
-use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class StandaloneMysql extends BaseModel
@@ -252,14 +251,9 @@ public function destination()
return $this->morphTo();
}
- public function environment_variables(): HasMany
+ public function runtime_environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class);
- }
-
- public function runtime_environment_variables(): HasMany
- {
- return $this->hasMany(EnvironmentVariable::class);
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable');
}
public function persistentStorages()
@@ -320,4 +314,10 @@ public function isBackupSolutionAvailable()
{
return true;
}
+
+ public function environment_variables()
+ {
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->orderBy('key', 'asc');
+ }
}
diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php
index 4a457a6cf9..141768a7cf 100644
--- a/app/Models/StandalonePostgresql.php
+++ b/app/Models/StandalonePostgresql.php
@@ -5,7 +5,6 @@
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
-use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class StandalonePostgresql extends BaseModel
@@ -252,14 +251,9 @@ public function destination()
return $this->morphTo();
}
- public function environment_variables(): HasMany
+ public function runtime_environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class);
- }
-
- public function runtime_environment_variables(): HasMany
- {
- return $this->hasMany(EnvironmentVariable::class);
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable');
}
public function persistentStorages()
@@ -320,4 +314,10 @@ public function getMemoryMetrics(int $mins = 5)
return $parsedCollection->toArray();
}
+
+ public function environment_variables()
+ {
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->orderBy('key', 'asc');
+ }
}
diff --git a/app/Models/StandaloneRedis.php b/app/Models/StandaloneRedis.php
index 826bb951c5..df1681fc95 100644
--- a/app/Models/StandaloneRedis.php
+++ b/app/Models/StandaloneRedis.php
@@ -5,7 +5,6 @@
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
-use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class StandaloneRedis extends BaseModel
@@ -262,14 +261,9 @@ public function destination()
return $this->morphTo();
}
- public function environment_variables(): HasMany
+ public function runtime_environment_variables()
{
- return $this->hasMany(EnvironmentVariable::class);
- }
-
- public function runtime_environment_variables(): HasMany
- {
- return $this->hasMany(EnvironmentVariable::class);
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable');
}
public function persistentStorages()
@@ -359,4 +353,10 @@ public function redisUsername(): Attribute
}
);
}
+
+ public function environment_variables()
+ {
+ return $this->morphMany(EnvironmentVariable::class, 'resourceable')
+ ->orderBy('key', 'asc');
+ }
}
diff --git a/app/View/Components/Forms/Button.php b/app/View/Components/Forms/Button.php
index da8b46dec5..bf88d3f885 100644
--- a/app/View/Components/Forms/Button.php
+++ b/app/View/Components/Forms/Button.php
@@ -15,7 +15,8 @@ public function __construct(
public bool $disabled = false,
public bool $noStyle = false,
public ?string $modalId = null,
- public string $defaultClass = 'button'
+ public string $defaultClass = 'button',
+ public bool $showLoadingIndicator = true,
) {
if ($this->noStyle) {
$this->defaultClass = '';
diff --git a/bootstrap/helpers/services.php b/bootstrap/helpers/services.php
index fd2e1231fb..cd99713a27 100644
--- a/bootstrap/helpers/services.php
+++ b/bootstrap/helpers/services.php
@@ -2,6 +2,7 @@
use App\Models\Application;
use App\Models\EnvironmentVariable;
+use App\Models\Service;
use App\Models\ServiceApplication;
use App\Models\ServiceDatabase;
use Illuminate\Support\Stringable;
@@ -119,7 +120,10 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
if ($resourceFqdns->count() === 1) {
$resourceFqdns = $resourceFqdns->first();
$variableName = 'SERVICE_FQDN_'.str($resource->name)->upper()->replace('-', '');
- $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
+ $generatedEnv = EnvironmentVariable::where('resourceable_type', Service::class)
+ ->where('resourceable_id', $resource->service_id)
+ ->where('key', $variableName)
+ ->first();
$fqdn = Url::fromString($resourceFqdns);
$port = $fqdn->getPort();
$path = $fqdn->getPath();
@@ -134,7 +138,10 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
}
if ($port) {
$variableName = $variableName."_$port";
- $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
+ $generatedEnv = EnvironmentVariable::where('resourceable_type', Service::class)
+ ->where('resourceable_id', $resource->service_id)
+ ->where('key', $variableName)
+ ->first();
if ($generatedEnv) {
if ($path === '/') {
$generatedEnv->value = $fqdn;
@@ -145,7 +152,10 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
}
}
$variableName = 'SERVICE_URL_'.str($resource->name)->upper()->replace('-', '');
- $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
+ $generatedEnv = EnvironmentVariable::where('resourceable_type', Service::class)
+ ->where('resourceable_id', $resource->service_id)
+ ->where('key', $variableName)
+ ->first();
$url = Url::fromString($fqdn);
$port = $url->getPort();
$path = $url->getPath();
@@ -161,7 +171,10 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
}
if ($port) {
$variableName = $variableName."_$port";
- $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
+ $generatedEnv = EnvironmentVariable::where('resourceable_type', Service::class)
+ ->where('resourceable_id', $resource->service_id)
+ ->where('key', $variableName)
+ ->first();
if ($generatedEnv) {
if ($path === '/') {
$generatedEnv->value = $url;
@@ -179,10 +192,16 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
$path = $host->getPath();
$host = $host->getScheme().'://'.$host->getHost();
if ($port) {
- $port_envs = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'like', "SERVICE_FQDN_%_$port")->get();
+ $port_envs = EnvironmentVariable::where('resourceable_type', Service::class)
+ ->where('resourceable_id', $resource->service_id)
+ ->where('key', 'like', "SERVICE_FQDN_%_$port")
+ ->get();
foreach ($port_envs as $port_env) {
$service_fqdn = str($port_env->key)->beforeLast('_')->after('SERVICE_FQDN_');
- $env = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'SERVICE_FQDN_'.$service_fqdn)->first();
+ $env = EnvironmentVariable::where('resourceable_type', Service::class)
+ ->where('resourceable_id', $resource->service_id)
+ ->where('key', 'SERVICE_FQDN_'.$service_fqdn)
+ ->first();
if ($env) {
if ($path === '/') {
$env->value = $host;
@@ -198,10 +217,16 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
}
$port_env->save();
}
- $port_envs_url = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'like', "SERVICE_URL_%_$port")->get();
+ $port_envs_url = EnvironmentVariable::where('resourceable_type', Service::class)
+ ->where('resourceable_id', $resource->service_id)
+ ->where('key', 'like', "SERVICE_URL_%_$port")
+ ->get();
foreach ($port_envs_url as $port_env_url) {
$service_url = str($port_env_url->key)->beforeLast('_')->after('SERVICE_URL_');
- $env = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'SERVICE_URL_'.$service_url)->first();
+ $env = EnvironmentVariable::where('resourceable_type', Service::class)
+ ->where('resourceable_id', $resource->service_id)
+ ->where('key', 'SERVICE_URL_'.$service_url)
+ ->first();
if ($env) {
if ($path === '/') {
$env->value = $url;
@@ -219,7 +244,10 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
}
} else {
$variableName = 'SERVICE_FQDN_'.str($resource->name)->upper()->replace('-', '');
- $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
+ $generatedEnv = EnvironmentVariable::where('resourceable_type', Service::class)
+ ->where('resourceable_id', $resource->service_id)
+ ->where('key', $variableName)
+ ->first();
$fqdn = Url::fromString($fqdn);
$fqdn = $fqdn->getScheme().'://'.$fqdn->getHost().$fqdn->getPath();
if ($generatedEnv) {
@@ -227,7 +255,10 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
$generatedEnv->save();
}
$variableName = 'SERVICE_URL_'.str($resource->name)->upper()->replace('-', '');
- $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
+ $generatedEnv = EnvironmentVariable::where('resourceable_type', Service::class)
+ ->where('resourceable_id', $resource->service_id)
+ ->where('key', $variableName)
+ ->first();
$url = Url::fromString($fqdn);
$url = $url->getHost().$url->getPath();
if ($generatedEnv) {
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 7aa411cd10..b9fde2070c 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -1819,7 +1819,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'key' => $key,
'value' => $fqdn,
'is_build_time' => false,
- 'service_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
'is_preview' => false,
]);
}
@@ -1831,7 +1832,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
}
$env = EnvironmentVariable::where([
'key' => $key,
- 'service_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
])->first();
if ($env) {
$env_url = Url::fromString($savedService->fqdn);
@@ -1854,14 +1856,16 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
if ($value?->startsWith('$')) {
$foundEnv = EnvironmentVariable::where([
'key' => $key,
- 'service_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
])->first();
$value = replaceVariables($value);
$key = $value;
if ($value->startsWith('SERVICE_')) {
$foundEnv = EnvironmentVariable::where([
'key' => $key,
- 'service_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
])->first();
['command' => $command, 'forService' => $forService, 'generatedValue' => $generatedValue, 'port' => $port] = parseEnvVariable($value);
if (! is_null($command)) {
@@ -1895,7 +1899,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'key' => $key,
'value' => $fqdn,
'is_build_time' => false,
- 'service_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
'is_preview' => false,
]);
}
@@ -1912,7 +1917,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
}
$env = EnvironmentVariable::where([
'key' => $key,
- 'service_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
])->first();
if ($env) {
$env_url = Url::fromString($env->value);
@@ -1932,7 +1938,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
'key' => $key,
'value' => $generatedValue,
'is_build_time' => false,
- 'service_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
'is_preview' => false,
]);
}
@@ -1957,18 +1964,21 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
}
$foundEnv = EnvironmentVariable::where([
'key' => $key,
- 'service_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
])->first();
if ($foundEnv) {
$defaultValue = data_get($foundEnv, 'value');
}
EnvironmentVariable::updateOrCreate([
'key' => $key,
- 'service_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
], [
'value' => $defaultValue,
'is_build_time' => false,
- 'service_id' => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
'is_preview' => false,
]);
}
@@ -2831,6 +2841,10 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
data_set($service, 'container_name', $containerName);
data_forget($service, 'volumes.*.content');
data_forget($service, 'volumes.*.isDirectory');
+ data_forget($service, 'volumes.*.is_directory');
+ data_forget($service, 'exclude_from_hc');
+ data_set($service, 'environment', $serviceVariables->toArray());
+ updateCompose($savedService);
return $service;
});
@@ -2869,13 +2883,11 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
}
if ($isApplication) {
- $nameOfId = 'application_id';
$pullRequestId = $pull_request_id;
$isPullRequest = $pullRequestId == 0 ? false : true;
$server = data_get($resource, 'destination.server');
$fileStorages = $resource->fileStorages();
} elseif ($isService) {
- $nameOfId = 'service_id';
$server = data_get($resource, 'server');
$allServices = get_service_templates();
} else {
@@ -3042,9 +3054,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
}
if (substr_count(str($key)->value(), '_') === 2) {
- $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([
+ $resource->environment_variables()->firstOrCreate([
'key' => $key->value(),
- $nameOfId => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
], [
'value' => $fqdn,
'is_build_time' => false,
@@ -3053,9 +3066,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
}
if (substr_count(str($key)->value(), '_') === 3) {
$newKey = str($key)->beforeLast('_');
- $resource->environment_variables()->where('key', $newKey->value())->where($nameOfId, $resource->id)->firstOrCreate([
+ $resource->environment_variables()->firstOrCreate([
'key' => $newKey->value(),
- $nameOfId => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
], [
'value' => $fqdn,
'is_build_time' => false,
@@ -3071,7 +3085,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$key = str($key);
$value = replaceVariables($value);
$command = parseCommandFromMagicEnvVariable($key);
- $found = $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->first();
+ $found = $resource->environment_variables()->where('key', $key->value())->where('resourceable_type', get_class($resource))->where('resourceable_id', $resource->id)->first();
if ($found) {
continue;
}
@@ -3085,9 +3099,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
} elseif ($isService) {
$fqdn = generateFqdn($server, "$fqdnFor-$uuid");
}
- $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([
+ $resource->environment_variables()->firstOrCreate([
'key' => $key->value(),
- $nameOfId => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
], [
'value' => $fqdn,
'is_build_time' => false,
@@ -3104,9 +3119,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$fqdn = generateFqdn($server, "$fqdnFor-$uuid");
}
$fqdn = str($fqdn)->replace('http://', '')->replace('https://', '');
- $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([
+ $resource->environment_variables()->firstOrCreate([
'key' => $key->value(),
- $nameOfId => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
], [
'value' => $fqdn,
'is_build_time' => false,
@@ -3114,9 +3130,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
]);
} else {
$value = generateEnvValue($command, $resource);
- $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([
+ $resource->environment_variables()->firstOrCreate([
'key' => $key->value(),
- $nameOfId => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
], [
'value' => $value,
'is_build_time' => false,
@@ -3464,9 +3481,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$originalValue = $value;
$parsedValue = replaceVariables($value);
if ($value->startsWith('$SERVICE_')) {
- $resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
+ $resource->environment_variables()->firstOrCreate([
'key' => $key,
- $nameOfId => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
], [
'value' => $value,
'is_build_time' => false,
@@ -3480,9 +3498,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
}
if ($key->value() === $parsedValue->value()) {
$value = null;
- $resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
+ $resource->environment_variables()->firstOrCreate([
'key' => $key,
- $nameOfId => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
], [
'value' => $value,
'is_build_time' => false,
@@ -3516,22 +3535,24 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
if ($originalValue->value() === $value->value()) {
// This means the variable does not have a default value, so it needs to be created in Coolify
$parsedKeyValue = replaceVariables($value);
- $resource->environment_variables()->where('key', $parsedKeyValue)->where($nameOfId, $resource->id)->firstOrCreate([
+ $resource->environment_variables()->firstOrCreate([
'key' => $parsedKeyValue,
- $nameOfId => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
], [
'is_build_time' => false,
'is_preview' => false,
'is_required' => $isRequired,
]);
// Add the variable to the environment so it will be shown in the deployable compose file
- $environment[$parsedKeyValue->value()] = $resource->environment_variables()->where('key', $parsedKeyValue)->where($nameOfId, $resource->id)->first()->value;
+ $environment[$parsedKeyValue->value()] = $resource->environment_variables()->where('key', $parsedKeyValue)->where('resourceable_type', get_class($resource))->where('resourceable_id', $resource->id)->first()->value;
continue;
}
- $resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
+ $resource->environment_variables()->firstOrCreate([
'key' => $key,
- $nameOfId => $resource->id,
+ 'resourceable_type' => get_class($resource),
+ 'resourceable_id' => $resource->id,
], [
'value' => $value,
'is_build_time' => false,
diff --git a/database/migrations/2024_12_16_134437_add_resourceable_columns_to_environment_variables_table.php b/database/migrations/2024_12_16_134437_add_resourceable_columns_to_environment_variables_table.php
new file mode 100644
index 0000000000..c4b7186380
--- /dev/null
+++ b/database/migrations/2024_12_16_134437_add_resourceable_columns_to_environment_variables_table.php
@@ -0,0 +1,165 @@
+string('resourceable_type')->nullable();
+ $table->unsignedBigInteger('resourceable_id')->nullable();
+ $table->index(['resourceable_type', 'resourceable_id']);
+ });
+
+ // Populate the new columns
+ DB::table('environment_variables')->whereNotNull('application_id')
+ ->update([
+ 'resourceable_type' => 'App\\Models\\Application',
+ 'resourceable_id' => DB::raw('application_id'),
+ ]);
+
+ DB::table('environment_variables')->whereNotNull('service_id')
+ ->update([
+ 'resourceable_type' => 'App\\Models\\Service',
+ 'resourceable_id' => DB::raw('service_id'),
+ ]);
+
+ DB::table('environment_variables')->whereNotNull('standalone_postgresql_id')
+ ->update([
+ 'resourceable_type' => 'App\\Models\\StandalonePostgresql',
+ 'resourceable_id' => DB::raw('standalone_postgresql_id'),
+ ]);
+
+ DB::table('environment_variables')->whereNotNull('standalone_redis_id')
+ ->update([
+ 'resourceable_type' => 'App\\Models\\StandaloneRedis',
+ 'resourceable_id' => DB::raw('standalone_redis_id'),
+ ]);
+
+ DB::table('environment_variables')->whereNotNull('standalone_mongodb_id')
+ ->update([
+ 'resourceable_type' => 'App\\Models\\StandaloneMongodb',
+ 'resourceable_id' => DB::raw('standalone_mongodb_id'),
+ ]);
+
+ DB::table('environment_variables')->whereNotNull('standalone_mysql_id')
+ ->update([
+ 'resourceable_type' => 'App\\Models\\StandaloneMysql',
+ 'resourceable_id' => DB::raw('standalone_mysql_id'),
+ ]);
+
+ DB::table('environment_variables')->whereNotNull('standalone_mariadb_id')
+ ->update([
+ 'resourceable_type' => 'App\\Models\\StandaloneMariadb',
+ 'resourceable_id' => DB::raw('standalone_mariadb_id'),
+ ]);
+
+ DB::table('environment_variables')->whereNotNull('standalone_keydb_id')
+ ->update([
+ 'resourceable_type' => 'App\\Models\\StandaloneKeydb',
+ 'resourceable_id' => DB::raw('standalone_keydb_id'),
+ ]);
+
+ DB::table('environment_variables')->whereNotNull('standalone_dragonfly_id')
+ ->update([
+ 'resourceable_type' => 'App\\Models\\StandaloneDragonfly',
+ 'resourceable_id' => DB::raw('standalone_dragonfly_id'),
+ ]);
+
+ DB::table('environment_variables')->whereNotNull('standalone_clickhouse_id')
+ ->update([
+ 'resourceable_type' => 'App\\Models\\StandaloneClickhouse',
+ 'resourceable_id' => DB::raw('standalone_clickhouse_id'),
+ ]);
+
+ // After successful migration, we can drop the old foreign key columns
+ Schema::table('environment_variables', function (Blueprint $table) {
+ $table->dropColumn([
+ 'application_id',
+ 'service_id',
+ 'standalone_postgresql_id',
+ 'standalone_redis_id',
+ 'standalone_mongodb_id',
+ 'standalone_mysql_id',
+ 'standalone_mariadb_id',
+ 'standalone_keydb_id',
+ 'standalone_dragonfly_id',
+ 'standalone_clickhouse_id',
+ ]);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('environment_variables', function (Blueprint $table) {
+ // Restore the old columns
+ $table->unsignedBigInteger('application_id')->nullable();
+ $table->unsignedBigInteger('service_id')->nullable();
+ $table->unsignedBigInteger('standalone_postgresql_id')->nullable();
+ $table->unsignedBigInteger('standalone_redis_id')->nullable();
+ $table->unsignedBigInteger('standalone_mongodb_id')->nullable();
+ $table->unsignedBigInteger('standalone_mysql_id')->nullable();
+ $table->unsignedBigInteger('standalone_mariadb_id')->nullable();
+ $table->unsignedBigInteger('standalone_keydb_id')->nullable();
+ $table->unsignedBigInteger('standalone_dragonfly_id')->nullable();
+ $table->unsignedBigInteger('standalone_clickhouse_id')->nullable();
+ });
+
+ Schema::table('environment_variables', function (Blueprint $table) {
+ // Restore data from polymorphic relationship
+ DB::table('environment_variables')
+ ->where('resourceable_type', 'App\\Models\\Application')
+ ->update(['application_id' => DB::raw('resourceable_id')]);
+
+ DB::table('environment_variables')
+ ->where('resourceable_type', 'App\\Models\\Service')
+ ->update(['service_id' => DB::raw('resourceable_id')]);
+
+ DB::table('environment_variables')
+ ->where('resourceable_type', 'App\\Models\\StandalonePostgresql')
+ ->update(['standalone_postgresql_id' => DB::raw('resourceable_id')]);
+
+ DB::table('environment_variables')
+ ->where('resourceable_type', 'App\\Models\\StandaloneRedis')
+ ->update(['standalone_redis_id' => DB::raw('resourceable_id')]);
+
+ DB::table('environment_variables')
+ ->where('resourceable_type', 'App\\Models\\StandaloneMongodb')
+ ->update(['standalone_mongodb_id' => DB::raw('resourceable_id')]);
+
+ DB::table('environment_variables')
+ ->where('resourceable_type', 'App\\Models\\StandaloneMysql')
+ ->update(['standalone_mysql_id' => DB::raw('resourceable_id')]);
+
+ DB::table('environment_variables')
+ ->where('resourceable_type', 'App\\Models\\StandaloneMariadb')
+ ->update(['standalone_mariadb_id' => DB::raw('resourceable_id')]);
+
+ DB::table('environment_variables')
+ ->where('resourceable_type', 'App\\Models\\StandaloneKeydb')
+ ->update(['standalone_keydb_id' => DB::raw('resourceable_id')]);
+
+ DB::table('environment_variables')
+ ->where('resourceable_type', 'App\\Models\\StandaloneDragonfly')
+ ->update(['standalone_dragonfly_id' => DB::raw('resourceable_id')]);
+
+ DB::table('environment_variables')
+ ->where('resourceable_type', 'App\\Models\\StandaloneClickhouse')
+ ->update(['standalone_clickhouse_id' => DB::raw('resourceable_id')]);
+
+ // Drop the polymorphic columns
+ $table->dropIndex(['resourceable_type', 'resourceable_id']);
+ $table->dropColumn(['resourceable_type', 'resourceable_id']);
+ });
+ }
+};
diff --git a/resources/views/components/forms/button.blade.php b/resources/views/components/forms/button.blade.php
index a109e84aca..96cdb4420f 100644
--- a/resources/views/components/forms/button.blade.php
+++ b/resources/views/components/forms/button.blade.php
@@ -8,11 +8,13 @@
@endisset>
{{ $slot }}
- @if ($attributes->whereStartsWith('wire:click')->first())
-
- @elseif($attributes->whereStartsWith('wire:target')->first())
-
+ @if ($showLoadingIndicator)
+ @if ($attributes->whereStartsWith('wire:click')->first())
+
+ @elseif($attributes->whereStartsWith('wire:target')->first())
+
+ @endif
@endif
diff --git a/resources/views/components/loading.blade.php b/resources/views/components/loading.blade.php
index 7ee3b1d1ac..80a47375eb 100644
--- a/resources/views/components/loading.blade.php
+++ b/resources/views/components/loading.blade.php
@@ -3,8 +3,8 @@
@if (isset($text))
{{ $text }}
@endif
-
diff --git a/resources/views/components/status/running.blade.php b/resources/views/components/status/running.blade.php
index 0e0f08fa45..4e5f0c2757 100644
--- a/resources/views/components/status/running.blade.php
+++ b/resources/views/components/status/running.blade.php
@@ -5,30 +5,42 @@
'noLoading' => false,
])
diff --git a/resources/views/livewire/project/database/heading.blade.php b/resources/views/livewire/project/database/heading.blade.php
index e334b2ce99..237d679c64 100644
--- a/resources/views/livewire/project/database/heading.blade.php
+++ b/resources/views/livewire/project/database/heading.blade.php
@@ -3,7 +3,7 @@
diff --git a/resources/views/livewire/project/shared/environment-variable/all.blade.php b/resources/views/livewire/project/shared/environment-variable/all.blade.php
index 38ea192b55..3be31ba554 100644
--- a/resources/views/livewire/project/shared/environment-variable/all.blade.php
+++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php
@@ -13,7 +13,7 @@
Environment variables (secrets) for this resource.
@if ($this->resourceClass === 'App\Models\Application' && data_get($this->resource, 'build_pack') !== 'dockercompose')
-
@@ -55,10 +55,10 @@
Preview Deployments Environment Variables
Environment (secrets) variables for Preview Deployments.
- @foreach ($resource->environment_variables_preview as $env)
+ {{-- @foreach ($resource->environment_variables_preview as $env)
- @endforeach
+ @endforeach --}}
@endif
@else