mirror of
https://github.com/coollabsio/coolify.git
synced 2026-03-11 08:55:47 +00:00
Merge e412b57df7 into 6b2a669cb9
This commit is contained in:
commit
e399f16e7c
23 changed files with 674 additions and 44 deletions
|
|
@ -1237,7 +1237,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||
});
|
||||
|
||||
foreach ($runtime_environment_variables as $env) {
|
||||
$envs->push($env->key.'='.$env->real_value);
|
||||
$envs->push($env->key.'='.$env->getResolvedValueWithServer($this->server));
|
||||
}
|
||||
|
||||
// Check for PORT environment variable mismatch with ports_exposes
|
||||
|
|
@ -1303,7 +1303,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||
});
|
||||
|
||||
foreach ($runtime_environment_variables_preview as $env) {
|
||||
$envs->push($env->key.'='.$env->real_value);
|
||||
$envs->push($env->key.'='.$env->getResolvedValueWithServer($this->server));
|
||||
}
|
||||
// Add PORT if not exists, use the first port as default
|
||||
if ($this->build_pack !== 'dockercompose') {
|
||||
|
|
@ -2324,14 +2324,16 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||
$this->env_nixpacks_args = collect([]);
|
||||
if ($this->pull_request_id === 0) {
|
||||
foreach ($this->application->nixpacks_environment_variables as $env) {
|
||||
if (! is_null($env->real_value) && $env->real_value !== '') {
|
||||
$this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}");
|
||||
$resolvedValue = $env->getResolvedValueWithServer($this->server);
|
||||
if (! is_null($resolvedValue) && $resolvedValue !== '') {
|
||||
$this->env_nixpacks_args->push("--env {$env->key}={$resolvedValue}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($this->application->nixpacks_environment_variables_preview as $env) {
|
||||
if (! is_null($env->real_value) && $env->real_value !== '') {
|
||||
$this->env_nixpacks_args->push("--env {$env->key}={$env->real_value}");
|
||||
$resolvedValue = $env->getResolvedValueWithServer($this->server);
|
||||
if (! is_null($resolvedValue) && $resolvedValue !== '') {
|
||||
$this->env_nixpacks_args->push("--env {$env->key}={$resolvedValue}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2467,8 +2469,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||
->get();
|
||||
|
||||
foreach ($envs as $env) {
|
||||
if (! is_null($env->real_value)) {
|
||||
$this->env_args->put($env->key, $env->real_value);
|
||||
$resolvedValue = $env->getResolvedValueWithServer($this->server);
|
||||
if (! is_null($resolvedValue)) {
|
||||
$this->env_args->put($env->key, $resolvedValue);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -2478,8 +2481,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||
->get();
|
||||
|
||||
foreach ($envs as $env) {
|
||||
if (! is_null($env->real_value)) {
|
||||
$this->env_args->put($env->key, $env->real_value);
|
||||
$resolvedValue = $env->getResolvedValueWithServer($this->server);
|
||||
if (! is_null($resolvedValue)) {
|
||||
$this->env_args->put($env->key, $resolvedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ class Add extends Component
|
|||
'team' => [],
|
||||
'project' => [],
|
||||
'environment' => [],
|
||||
'server' => [],
|
||||
];
|
||||
|
||||
// Early return if no team
|
||||
|
|
@ -122,6 +123,66 @@ class Add extends Component
|
|||
}
|
||||
}
|
||||
|
||||
// Get server variables
|
||||
$serverUuid = data_get($this->parameters, 'server_uuid');
|
||||
if ($serverUuid) {
|
||||
// If we have a specific server_uuid, show variables for that server
|
||||
$server = \App\Models\Server::where('team_id', $team->id)
|
||||
->where('uuid', $serverUuid)
|
||||
->first();
|
||||
|
||||
if ($server) {
|
||||
try {
|
||||
$this->authorize('view', $server);
|
||||
$result['server'] = $server->environment_variables()
|
||||
->pluck('key')
|
||||
->toArray();
|
||||
} catch (\Illuminate\Auth\Access\AuthorizationException $e) {
|
||||
// User not authorized to view server variables
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For application environment variables, try to use the application's destination server
|
||||
$applicationUuid = data_get($this->parameters, 'application_uuid');
|
||||
if ($applicationUuid) {
|
||||
$application = \App\Models\Application::whereRelation('environment.project.team', 'id', $team->id)
|
||||
->where('uuid', $applicationUuid)
|
||||
->with('destination.server')
|
||||
->first();
|
||||
|
||||
if ($application && $application->destination && $application->destination->server) {
|
||||
try {
|
||||
$this->authorize('view', $application->destination->server);
|
||||
$result['server'] = $application->destination->server->environment_variables()
|
||||
->pluck('key')
|
||||
->toArray();
|
||||
} catch (\Illuminate\Auth\Access\AuthorizationException $e) {
|
||||
// User not authorized to view server variables
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For service environment variables, try to use the service's server
|
||||
$serviceUuid = data_get($this->parameters, 'service_uuid');
|
||||
if ($serviceUuid) {
|
||||
$service = \App\Models\Service::whereRelation('environment.project.team', 'id', $team->id)
|
||||
->where('uuid', $serviceUuid)
|
||||
->with('server')
|
||||
->first();
|
||||
|
||||
if ($service && $service->server) {
|
||||
try {
|
||||
$this->authorize('view', $service->server);
|
||||
$result['server'] = $service->server->environment_variables()
|
||||
->pluck('key')
|
||||
->toArray();
|
||||
} catch (\Illuminate\Auth\Access\AuthorizationException $e) {
|
||||
// User not authorized to view server variables
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -204,6 +204,7 @@ class Show extends Component
|
|||
'team' => [],
|
||||
'project' => [],
|
||||
'environment' => [],
|
||||
'server' => [],
|
||||
];
|
||||
|
||||
// Early return if no team
|
||||
|
|
@ -259,6 +260,66 @@ class Show extends Component
|
|||
}
|
||||
}
|
||||
|
||||
// Get server variables
|
||||
$serverUuid = data_get($this->parameters, 'server_uuid');
|
||||
if ($serverUuid) {
|
||||
// If we have a specific server_uuid, show variables for that server
|
||||
$server = \App\Models\Server::where('team_id', $team->id)
|
||||
->where('uuid', $serverUuid)
|
||||
->first();
|
||||
|
||||
if ($server) {
|
||||
try {
|
||||
$this->authorize('view', $server);
|
||||
$result['server'] = $server->environment_variables()
|
||||
->pluck('key')
|
||||
->toArray();
|
||||
} catch (\Illuminate\Auth\Access\AuthorizationException $e) {
|
||||
// User not authorized to view server variables
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For application environment variables, try to use the application's destination server
|
||||
$applicationUuid = data_get($this->parameters, 'application_uuid');
|
||||
if ($applicationUuid) {
|
||||
$application = \App\Models\Application::whereRelation('environment.project.team', 'id', $team->id)
|
||||
->where('uuid', $applicationUuid)
|
||||
->with('destination.server')
|
||||
->first();
|
||||
|
||||
if ($application && $application->destination && $application->destination->server) {
|
||||
try {
|
||||
$this->authorize('view', $application->destination->server);
|
||||
$result['server'] = $application->destination->server->environment_variables()
|
||||
->pluck('key')
|
||||
->toArray();
|
||||
} catch (\Illuminate\Auth\Access\AuthorizationException $e) {
|
||||
// User not authorized to view server variables
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For service environment variables, try to use the service's server
|
||||
$serviceUuid = data_get($this->parameters, 'service_uuid');
|
||||
if ($serviceUuid) {
|
||||
$service = \App\Models\Service::whereRelation('environment.project.team', 'id', $team->id)
|
||||
->where('uuid', $serviceUuid)
|
||||
->with('server')
|
||||
->first();
|
||||
|
||||
if ($service && $service->server) {
|
||||
try {
|
||||
$this->authorize('view', $service->server);
|
||||
$result['server'] = $service->server->environment_variables()
|
||||
->pluck('key')
|
||||
->toArray();
|
||||
} catch (\Illuminate\Auth\Access\AuthorizationException $e) {
|
||||
// User not authorized to view server variables
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
|
|
|||
22
app/Livewire/SharedVariables/Server/Index.php
Normal file
22
app/Livewire/SharedVariables/Server/Index.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App\Livewire\SharedVariables\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Component;
|
||||
|
||||
class Index extends Component
|
||||
{
|
||||
public Collection $servers;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->servers = Server::ownedByCurrentTeamCached();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.shared-variables.server.index');
|
||||
}
|
||||
}
|
||||
184
app/Livewire/SharedVariables/Server/Show.php
Normal file
184
app/Livewire/SharedVariables/Server/Show.php
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
|
||||
namespace App\Livewire\SharedVariables\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public Server $server;
|
||||
|
||||
public string $view = 'normal';
|
||||
|
||||
public ?string $variables = null;
|
||||
|
||||
protected $listeners = ['refreshEnvs' => 'refreshEnvs', 'saveKey' => 'saveKey', 'environmentVariableDeleted' => 'refreshEnvs'];
|
||||
|
||||
public function saveKey($data)
|
||||
{
|
||||
try {
|
||||
$this->authorize('update', $this->server);
|
||||
|
||||
if (in_array($data['key'], ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])) {
|
||||
throw new \Exception('Cannot create predefined variable.');
|
||||
}
|
||||
|
||||
$found = $this->server->environment_variables()->where('key', $data['key'])->first();
|
||||
if ($found) {
|
||||
throw new \Exception('Variable already exists.');
|
||||
}
|
||||
$this->server->environment_variables()->create([
|
||||
'key' => $data['key'],
|
||||
'value' => $data['value'],
|
||||
'is_multiline' => $data['is_multiline'],
|
||||
'is_literal' => $data['is_literal'],
|
||||
'type' => 'server',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$this->server->refresh();
|
||||
$this->getDevView();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$serverUuid = request()->route('server_uuid');
|
||||
$teamId = currentTeam()->id;
|
||||
$server = Server::where('team_id', $teamId)->where('uuid', $serverUuid)->first();
|
||||
if (! $server) {
|
||||
return redirect()->route('dashboard');
|
||||
}
|
||||
$this->authorize('view', $server);
|
||||
$this->server = $server;
|
||||
$this->getDevView();
|
||||
}
|
||||
|
||||
public function switch()
|
||||
{
|
||||
$this->authorize('view', $this->server);
|
||||
$this->view = $this->view === 'normal' ? 'dev' : 'normal';
|
||||
$this->getDevView();
|
||||
}
|
||||
|
||||
public function getDevView()
|
||||
{
|
||||
$this->variables = $this->formatEnvironmentVariables($this->server->environment_variables->whereNotIn('key', ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])->sortBy('key'));
|
||||
}
|
||||
|
||||
private function formatEnvironmentVariables($variables)
|
||||
{
|
||||
return $variables->map(function ($item) {
|
||||
if ($item->is_shown_once) {
|
||||
return "$item->key=(Locked Secret, delete and add again to change)";
|
||||
}
|
||||
if ($item->is_multiline) {
|
||||
return "$item->key=(Multiline environment variable, edit in normal view)";
|
||||
}
|
||||
|
||||
return "$item->key=$item->value";
|
||||
})->join("\n");
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->authorize('update', $this->server);
|
||||
$this->handleBulkSubmit();
|
||||
$this->getDevView();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->refreshEnvs();
|
||||
}
|
||||
}
|
||||
|
||||
private function handleBulkSubmit()
|
||||
{
|
||||
$variables = parseEnvFormatToArray($this->variables);
|
||||
|
||||
$changesMade = DB::transaction(function () use ($variables) {
|
||||
// Delete removed variables
|
||||
$deletedCount = $this->deleteRemovedVariables($variables);
|
||||
|
||||
// Update or create variables
|
||||
$updatedCount = $this->updateOrCreateVariables($variables);
|
||||
|
||||
return $deletedCount > 0 || $updatedCount > 0;
|
||||
});
|
||||
|
||||
if ($changesMade) {
|
||||
$this->dispatch('success', 'Environment variables updated.');
|
||||
}
|
||||
}
|
||||
|
||||
private function deleteRemovedVariables($variables)
|
||||
{
|
||||
$variablesToDelete = $this->server->environment_variables()
|
||||
->whereNotIn('key', array_keys($variables))
|
||||
->whereNotIn('key', ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])
|
||||
->get();
|
||||
|
||||
if ($variablesToDelete->isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->server->environment_variables()
|
||||
->whereNotIn('key', array_keys($variables))
|
||||
->whereNotIn('key', ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])
|
||||
->delete();
|
||||
|
||||
return $variablesToDelete->count();
|
||||
}
|
||||
|
||||
private function updateOrCreateVariables($variables)
|
||||
{
|
||||
$count = 0;
|
||||
foreach ($variables as $key => $value) {
|
||||
// Skip predefined variables
|
||||
if (in_array($key, ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])) {
|
||||
continue;
|
||||
}
|
||||
$found = $this->server->environment_variables()->where('key', $key)->first();
|
||||
|
||||
if ($found) {
|
||||
if (! $found->is_shown_once && ! $found->is_multiline) {
|
||||
if ($found->value !== $value) {
|
||||
$found->value = $value;
|
||||
$found->save();
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->server->environment_variables()->create([
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
'is_multiline' => false,
|
||||
'is_literal' => false,
|
||||
'type' => 'server',
|
||||
'team_id' => currentTeam()->id,
|
||||
]);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function refreshEnvs()
|
||||
{
|
||||
$this->server->refresh();
|
||||
$this->getDevView();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.shared-variables.server.show');
|
||||
}
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ class Environment extends BaseModel
|
|||
|
||||
public function environment_variables()
|
||||
{
|
||||
return $this->hasMany(SharedEnvironmentVariable::class);
|
||||
return $this->hasMany(SharedEnvironmentVariable::class)->where('type', 'environment');
|
||||
}
|
||||
|
||||
public function applications()
|
||||
|
|
|
|||
|
|
@ -122,6 +122,17 @@ class EnvironmentVariable extends BaseModel
|
|||
return null;
|
||||
}
|
||||
|
||||
// Load relationships needed for shared variable resolution
|
||||
if (! $resource->relationLoaded('environment')) {
|
||||
$resource->load('environment');
|
||||
}
|
||||
if (! $resource->relationLoaded('server') && method_exists($resource, 'server')) {
|
||||
$resource->load('server');
|
||||
}
|
||||
if (! $resource->relationLoaded('destination') && method_exists($resource, 'destination')) {
|
||||
$resource->load('destination.server');
|
||||
}
|
||||
|
||||
$real_value = $this->get_real_environment_variables($this->value, $resource);
|
||||
|
||||
// Skip escaping for valid JSON objects/arrays to prevent quote corruption (see #6160)
|
||||
|
|
@ -187,7 +198,43 @@ class EnvironmentVariable extends BaseModel
|
|||
);
|
||||
}
|
||||
|
||||
private function get_real_environment_variables(?string $environment_variable = null, $resource = null)
|
||||
public function get_real_environment_variables_with_server(?string $environment_variable = null, $resource = null, $server = null)
|
||||
{
|
||||
return $this->get_real_environment_variables_internal($environment_variable, $resource, $server);
|
||||
}
|
||||
|
||||
public function getResolvedValueWithServer($server = null)
|
||||
{
|
||||
if (! $this->relationLoaded('resourceable')) {
|
||||
$this->load('resourceable');
|
||||
}
|
||||
$resource = $this->resourceable;
|
||||
if (! $resource) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Load relationships needed for shared variable resolution
|
||||
if (! $resource->relationLoaded('environment')) {
|
||||
$resource->load('environment');
|
||||
}
|
||||
if (! $resource->relationLoaded('server') && method_exists($resource, 'server')) {
|
||||
$resource->load('server');
|
||||
}
|
||||
if (! $resource->relationLoaded('destination') && method_exists($resource, 'destination')) {
|
||||
$resource->load('destination.server');
|
||||
}
|
||||
|
||||
$real_value = $this->get_real_environment_variables_internal($this->value, $resource, $server);
|
||||
if ($this->is_literal || $this->is_multiline) {
|
||||
$real_value = '\''.$real_value.'\'';
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($real_value);
|
||||
}
|
||||
|
||||
return $real_value;
|
||||
}
|
||||
|
||||
private function get_real_environment_variables_internal(?string $environment_variable = null, $resource = null, $serverOverride = null)
|
||||
{
|
||||
if ((is_null($environment_variable) && $environment_variable === '') || is_null($resource)) {
|
||||
return null;
|
||||
|
|
@ -209,6 +256,17 @@ class EnvironmentVariable extends BaseModel
|
|||
$id = $resource->environment->project->id;
|
||||
} elseif ($type->value() === 'team') {
|
||||
$id = $resource->team()->id;
|
||||
} elseif ($type->value() === 'server') {
|
||||
// Use server override if provided (for deployment context), otherwise use resource's server
|
||||
if ($serverOverride) {
|
||||
$id = $serverOverride->id;
|
||||
} elseif (isset($resource->server) && $resource->server) {
|
||||
$id = $resource->server->id;
|
||||
} elseif (isset($resource->destination) && $resource->destination && isset($resource->destination->server)) {
|
||||
$id = $resource->destination->server->id;
|
||||
} else {
|
||||
$id = null;
|
||||
}
|
||||
}
|
||||
if (is_null($id)) {
|
||||
continue;
|
||||
|
|
@ -222,6 +280,11 @@ class EnvironmentVariable extends BaseModel
|
|||
return str($environment_variable)->value();
|
||||
}
|
||||
|
||||
private function get_real_environment_variables(?string $environment_variable = null, $resource = null)
|
||||
{
|
||||
return $this->get_real_environment_variables_internal($environment_variable, $resource);
|
||||
}
|
||||
|
||||
private function get_environment_variables(?string $environment_variable = null): ?string
|
||||
{
|
||||
if (! $environment_variable) {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ class Project extends BaseModel
|
|||
|
||||
public function environment_variables()
|
||||
{
|
||||
return $this->hasMany(SharedEnvironmentVariable::class);
|
||||
return $this->hasMany(SharedEnvironmentVariable::class)->where('type', 'project');
|
||||
}
|
||||
|
||||
public function environments()
|
||||
|
|
|
|||
|
|
@ -178,6 +178,24 @@ class Server extends BaseModel
|
|||
if (! isset($server->proxy->redirect_enabled)) {
|
||||
$server->proxy->redirect_enabled = true;
|
||||
}
|
||||
|
||||
// Create predefined server shared variables
|
||||
SharedEnvironmentVariable::create([
|
||||
'key' => 'COOLIFY_SERVER_UUID',
|
||||
'value' => $server->uuid,
|
||||
'type' => 'server',
|
||||
'server_id' => $server->id,
|
||||
'team_id' => $server->team_id,
|
||||
'is_literal' => true,
|
||||
]);
|
||||
SharedEnvironmentVariable::create([
|
||||
'key' => 'COOLIFY_SERVER_NAME',
|
||||
'value' => $server->name,
|
||||
'type' => 'server',
|
||||
'server_id' => $server->id,
|
||||
'team_id' => $server->team_id,
|
||||
'is_literal' => true,
|
||||
]);
|
||||
});
|
||||
static::retrieved(function ($server) {
|
||||
if (! isset($server->proxy->redirect_enabled)) {
|
||||
|
|
@ -1003,6 +1021,11 @@ $schema://$host {
|
|||
return $this->belongsTo(Team::class);
|
||||
}
|
||||
|
||||
public function environment_variables()
|
||||
{
|
||||
return $this->hasMany(SharedEnvironmentVariable::class)->where('type', 'server');
|
||||
}
|
||||
|
||||
public function isProxyShouldRun()
|
||||
{
|
||||
// TODO: Do we need "|| $this->proxy->force_stop" here?
|
||||
|
|
|
|||
|
|
@ -27,4 +27,9 @@ class SharedEnvironmentVariable extends Model
|
|||
{
|
||||
return $this->belongsTo(Environment::class);
|
||||
}
|
||||
|
||||
public function server()
|
||||
{
|
||||
return $this->belongsTo(Server::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, Sen
|
|||
|
||||
public function environment_variables()
|
||||
{
|
||||
return $this->hasMany(SharedEnvironmentVariable::class)->whereNull('project_id')->whereNull('environment_id');
|
||||
return $this->hasMany(SharedEnvironmentVariable::class)->where('type', 'team');
|
||||
}
|
||||
|
||||
public function members()
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class EnvVarInput extends Component
|
|||
public array $availableVars = [],
|
||||
public ?string $projectUuid = null,
|
||||
public ?string $environmentUuid = null,
|
||||
public ?string $serverUuid = null,
|
||||
) {
|
||||
// Handle authorization-based disabling
|
||||
if ($this->canGate && $this->canResource && $this->autoDisable) {
|
||||
|
|
@ -86,6 +87,9 @@ class EnvVarInput extends Component
|
|||
'environment_uuid' => $this->environmentUuid,
|
||||
])
|
||||
: route('shared-variables.environment.index'),
|
||||
'server' => $this->serverUuid
|
||||
? route('shared-variables.server.show', ['server_uuid' => $this->serverUuid])
|
||||
: route('shared-variables.server.index'),
|
||||
'default' => route('shared-variables.index'),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -81,4 +81,4 @@ const NEEDS_TO_DISABLE_GZIP = [
|
|||
const NEEDS_TO_DISABLE_STRIPPREFIX = [
|
||||
'appwrite' => ['appwrite', 'appwrite-console', 'appwrite-realtime'],
|
||||
];
|
||||
const SHARED_VARIABLE_TYPES = ['team', 'project', 'environment'];
|
||||
const SHARED_VARIABLE_TYPES = ['team', 'project', 'environment', 'server'];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
DB::statement("ALTER TABLE shared_environment_variables DROP CONSTRAINT shared_environment_variables_type_check");
|
||||
DB::statement("ALTER TABLE shared_environment_variables ADD CONSTRAINT shared_environment_variables_type_check CHECK (type IN ('team', 'project', 'environment', 'server'))");
|
||||
Schema::table('shared_environment_variables', function (Blueprint $table) {
|
||||
$table->foreignId('server_id')->nullable()->constrained()->onDelete('cascade');
|
||||
$table->unique(['key', 'server_id', 'team_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('shared_environment_variables', function (Blueprint $table) {
|
||||
$table->dropUnique(['key', 'server_id', 'team_id']);
|
||||
$table->dropForeign(['server_id']);
|
||||
$table->dropColumn('server_id');
|
||||
});
|
||||
DB::statement("ALTER TABLE shared_environment_variables DROP CONSTRAINT shared_environment_variables_type_check");
|
||||
DB::statement("ALTER TABLE shared_environment_variables ADD CONSTRAINT shared_environment_variables_type_check CHECK (type IN ('team', 'project', 'environment'))");
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\SharedEnvironmentVariable;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Server::query()->chunk(100, function ($servers) {
|
||||
foreach ($servers as $server) {
|
||||
$existingKeys = SharedEnvironmentVariable::where('type', 'server')
|
||||
->where('server_id', $server->id)
|
||||
->whereIn('key', ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])
|
||||
->pluck('key')
|
||||
->toArray();
|
||||
|
||||
if (! in_array('COOLIFY_SERVER_UUID', $existingKeys)) {
|
||||
SharedEnvironmentVariable::create([
|
||||
'key' => 'COOLIFY_SERVER_UUID',
|
||||
'value' => $server->uuid,
|
||||
'type' => 'server',
|
||||
'server_id' => $server->id,
|
||||
'team_id' => $server->team_id,
|
||||
'is_literal' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
if (! in_array('COOLIFY_SERVER_NAME', $existingKeys)) {
|
||||
SharedEnvironmentVariable::create([
|
||||
'key' => 'COOLIFY_SERVER_NAME',
|
||||
'value' => $server->name,
|
||||
'type' => 'server',
|
||||
'server_id' => $server->id,
|
||||
'team_id' => $server->team_id,
|
||||
'is_literal' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
SharedEnvironmentVariable::where('type', 'server')
|
||||
->whereIn('key', ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])
|
||||
->delete();
|
||||
}
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\SharedEnvironmentVariable;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
|
|
@ -32,5 +33,29 @@ class SharedEnvironmentVariableSeeder extends Seeder
|
|||
'project_id' => 1,
|
||||
'team_id' => 0,
|
||||
]);
|
||||
|
||||
// Add predefined server variables to all existing servers
|
||||
$servers = \App\Models\Server::all();
|
||||
foreach ($servers as $server) {
|
||||
SharedEnvironmentVariable::firstOrCreate([
|
||||
'key' => 'COOLIFY_SERVER_UUID',
|
||||
'type' => 'server',
|
||||
'server_id' => $server->id,
|
||||
'team_id' => $server->team_id,
|
||||
], [
|
||||
'value' => $server->uuid,
|
||||
'is_literal' => true,
|
||||
]);
|
||||
|
||||
SharedEnvironmentVariable::firstOrCreate([
|
||||
'key' => 'COOLIFY_SERVER_NAME',
|
||||
'type' => 'server',
|
||||
'server_id' => $server->id,
|
||||
'team_id' => $server->team_id,
|
||||
], [
|
||||
'value' => $server->name,
|
||||
'is_literal' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,15 @@
|
|||
selectedIndex: 0,
|
||||
cursorPosition: 0,
|
||||
currentScope: null,
|
||||
availableScopes: ['team', 'project', 'environment'],
|
||||
availableVars: @js($availableVars),
|
||||
get availableScopes() {
|
||||
// Only include scopes that have at least one variable
|
||||
const allScopes = ['team', 'project', 'environment', 'server'];
|
||||
return allScopes.filter(scope => {
|
||||
const vars = this.availableVars[scope];
|
||||
return vars && vars.length > 0;
|
||||
});
|
||||
},
|
||||
scopeUrls: @js($scopeUrls),
|
||||
|
||||
handleInput() {
|
||||
|
|
@ -54,6 +61,11 @@
|
|||
|
||||
if (content === '') {
|
||||
this.currentScope = null;
|
||||
// Only show dropdown if there are available scopes with variables
|
||||
if (this.availableScopes.length === 0) {
|
||||
this.showDropdown = false;
|
||||
return;
|
||||
}
|
||||
this.suggestions = this.availableScopes.map(scope => ({
|
||||
type: 'scope',
|
||||
value: scope,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
<x-forms.env-var-input placeholder="production" id="value" label="Value" required
|
||||
:availableVars="$shared ? [] : $this->availableSharedVariables"
|
||||
:projectUuid="data_get($parameters, 'project_uuid')"
|
||||
:environmentUuid="data_get($parameters, 'environment_uuid')" />
|
||||
:environmentUuid="data_get($parameters, 'environment_uuid')"
|
||||
:serverUuid="data_get($parameters, 'server_uuid')" />
|
||||
@endif
|
||||
|
||||
@if (!$shared && !$is_multiline)
|
||||
|
|
|
|||
|
|
@ -109,9 +109,10 @@
|
|||
disabled
|
||||
type="password"
|
||||
id="value"
|
||||
:availableVars="$this->availableSharedVariables"
|
||||
:availableVars="$isSharedVariable ? [] : $this->availableSharedVariables"
|
||||
:projectUuid="data_get($parameters, 'project_uuid')"
|
||||
:environmentUuid="data_get($parameters, 'environment_uuid')" />
|
||||
:environmentUuid="data_get($parameters, 'environment_uuid')"
|
||||
:serverUuid="data_get($parameters, 'server_uuid')" />
|
||||
@if ($is_shared)
|
||||
<x-forms.input disabled type="password" id="real_value" />
|
||||
@endif
|
||||
|
|
@ -127,9 +128,10 @@
|
|||
:required="$is_redis_credential"
|
||||
type="password"
|
||||
id="value"
|
||||
:availableVars="$this->availableSharedVariables"
|
||||
:availableVars="$isSharedVariable ? [] : $this->availableSharedVariables"
|
||||
:projectUuid="data_get($parameters, 'project_uuid')"
|
||||
:environmentUuid="data_get($parameters, 'environment_uuid')" />
|
||||
:environmentUuid="data_get($parameters, 'environment_uuid')"
|
||||
:serverUuid="data_get($parameters, 'server_uuid')" />
|
||||
@endif
|
||||
@if ($is_shared)
|
||||
<x-forms.input :disabled="$is_redis_credential" :required="$is_redis_credential" disabled type="password" id="real_value" />
|
||||
|
|
@ -143,9 +145,10 @@
|
|||
disabled
|
||||
type="password"
|
||||
id="value"
|
||||
:availableVars="$this->availableSharedVariables"
|
||||
:availableVars="$isSharedVariable ? [] : $this->availableSharedVariables"
|
||||
:projectUuid="data_get($parameters, 'project_uuid')"
|
||||
:environmentUuid="data_get($parameters, 'environment_uuid')" />
|
||||
:environmentUuid="data_get($parameters, 'environment_uuid')"
|
||||
:serverUuid="data_get($parameters, 'server_uuid')" />
|
||||
@if ($is_shared)
|
||||
<x-forms.input disabled type="password" id="real_value" />
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -5,27 +5,33 @@
|
|||
<div class="flex items-start gap-2">
|
||||
<h1>Shared Variables</h1>
|
||||
</div>
|
||||
<div class="subtitle">Set Team / Project / Environment wide variables.</div>
|
||||
<div class="subtitle">Set Team / Project / Environment / Server wide variables.</div>
|
||||
|
||||
<div class="flex flex-col gap-2 -mt-1">
|
||||
<a class="coolbox group" href="{{ route('shared-variables.team.index') }}" {{ wireNavigate() }}>
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">Team wide</div>
|
||||
<div class="box-description">Usable for all resources in a team.</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="coolbox group" href="{{ route('shared-variables.project.index') }}" {{ wireNavigate() }}>
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">Project wide</div>
|
||||
<div class="box-description">Usable for all resources in a project.</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="coolbox group" href="{{ route('shared-variables.environment.index') }}" {{ wireNavigate() }}>
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">Environment wide</div>
|
||||
<div class="box-description">Usable for all resources in an environment.</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="flex flex-col gap-2 -mt-1">
|
||||
<a class="coolbox group" href="{{ route('shared-variables.team.index') }}" {{ wireNavigate() }}>
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">Team wide</div>
|
||||
<div class="box-description">Usable for all resources in a team.</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="coolbox group" href="{{ route('shared-variables.project.index') }}" {{ wireNavigate() }}>
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">Project wide</div>
|
||||
<div class="box-description">Usable for all resources in a project.</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="coolbox group" href="{{ route('shared-variables.environment.index') }}" {{ wireNavigate() }}>
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">Environment wide</div>
|
||||
<div class="box-description">Usable for all resources in an environment.</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="coolbox group" href="{{ route('shared-variables.server.index') }}" {{ wireNavigate() }}>
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">Server wide</div>
|
||||
<div class="box-description">Usable for all resources in a server.</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Server Variables | Coolify
|
||||
</x-slot>
|
||||
<div class="flex gap-2">
|
||||
<h1>Servers</h1>
|
||||
</div>
|
||||
<div class="subtitle">List of your servers.</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
@forelse ($servers as $server)
|
||||
<a class="coolbox group"
|
||||
href="{{ route('shared-variables.server.show', ['server_uuid' => data_get($server, 'uuid')]) }}" {{ wireNavigate() }}>
|
||||
<div class="flex flex-col justify-center mx-6 ">
|
||||
<div class="box-title">{{ $server->name }}</div>
|
||||
<div class="box-description ">
|
||||
{{ $server->description }}</div>
|
||||
</div>
|
||||
</a>
|
||||
@empty
|
||||
<div>
|
||||
<div>No server found.</div>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Server Variable | Coolify
|
||||
</x-slot>
|
||||
<div class="flex gap-2 items-center">
|
||||
<h1>Shared Variables for {{ data_get($server, 'name') }}</h1>
|
||||
@can('update', $server)
|
||||
<x-modal-input buttonTitle="+ Add" title="New Shared Variable">
|
||||
<livewire:project.shared.environment-variable.add :shared="true" />
|
||||
</x-modal-input>
|
||||
@endcan
|
||||
<x-forms.button canGate="update" :canResource="$server" wire:click='switch'>{{ $view === 'normal' ? 'Developer view' : 'Normal view' }}</x-forms.button>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1 subtitle">
|
||||
<div>You can use these variables anywhere with</div>
|
||||
<div class="dark:text-warning text-coollabs">@{{ server.VARIABLENAME }} </div>
|
||||
<x-helper
|
||||
helper="More info <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/environment-variables#shared-variables' target='_blank'>here</a>."></x-helper>
|
||||
</div>
|
||||
@if ($view === 'normal')
|
||||
<div class="flex flex-col gap-2">
|
||||
@forelse ($server->environment_variables->whereNotIn('key', ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])->sortBy('key') as $env)
|
||||
<livewire:project.shared.environment-variable.show wire:key="environment-{{ $env->id }}"
|
||||
:env="$env" type="server" />
|
||||
@empty
|
||||
<div>No environment variables found.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
@else
|
||||
<form wire:submit='submit' class="flex flex-col gap-2">
|
||||
<x-forms.textarea canGate="update" :canResource="$server" rows="20" class="whitespace-pre-wrap" id="variables" wire:model="variables"
|
||||
label="Server Shared Variables"></x-forms.textarea>
|
||||
<x-forms.button canGate="update" :canResource="$server" type="submit" class="btn btn-primary">Save All Environment Variables</x-forms.button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
|
|
@ -72,6 +72,8 @@ use App\Livewire\SharedVariables\Environment\Show as EnvironmentSharedVariablesS
|
|||
use App\Livewire\SharedVariables\Index as SharedVariablesIndex;
|
||||
use App\Livewire\SharedVariables\Project\Index as ProjectSharedVariablesIndex;
|
||||
use App\Livewire\SharedVariables\Project\Show as ProjectSharedVariablesShow;
|
||||
use App\Livewire\SharedVariables\Server\Index as ServerSharedVariablesIndex;
|
||||
use App\Livewire\SharedVariables\Server\Show as ServerSharedVariablesShow;
|
||||
use App\Livewire\SharedVariables\Team\Index as TeamSharedVariablesIndex;
|
||||
use App\Livewire\Source\Github\Change as GitHubChange;
|
||||
use App\Livewire\Storage\Index as StorageIndex;
|
||||
|
|
@ -148,6 +150,8 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
|||
Route::get('/project/{project_uuid}', ProjectSharedVariablesShow::class)->name('shared-variables.project.show');
|
||||
Route::get('/environments', EnvironmentSharedVariablesIndex::class)->name('shared-variables.environment.index');
|
||||
Route::get('/environments/project/{project_uuid}/environment/{environment_uuid}', EnvironmentSharedVariablesShow::class)->name('shared-variables.environment.show');
|
||||
Route::get('/servers', ServerSharedVariablesIndex::class)->name('shared-variables.server.index');
|
||||
Route::get('/server/{server_uuid}', ServerSharedVariablesShow::class)->name('shared-variables.server.show');
|
||||
});
|
||||
|
||||
Route::prefix('team')->group(function () {
|
||||
|
|
|
|||
Loading…
Reference in a new issue