Merge pull request #3184 from peaklabs-dev/fix-redis-db-ui
Fix: Redis DB UI
This commit is contained in:
@@ -21,8 +21,6 @@ class StartRedis
|
||||
{
|
||||
$this->database = $database;
|
||||
|
||||
$startCommand = "redis-server --requirepass {$this->database->redis_password} --appendonly yes";
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
||||
|
||||
@@ -37,6 +35,8 @@ class StartRedis
|
||||
$environment_variables = $this->generate_environment_variables();
|
||||
$this->add_custom_redis();
|
||||
|
||||
$startCommand = $this->buildStartCommand();
|
||||
|
||||
$docker_compose = [
|
||||
'services' => [
|
||||
$container_name => [
|
||||
@@ -105,7 +105,6 @@ class StartRedis
|
||||
'target' => '/usr/local/etc/redis/redis.conf',
|
||||
'read_only' => true,
|
||||
];
|
||||
$docker_compose['services'][$container_name]['command'] = "redis-server /usr/local/etc/redis/redis.conf --requirepass {$this->database->redis_password} --appendonly yes";
|
||||
}
|
||||
|
||||
// Add custom docker run options
|
||||
@@ -160,12 +159,26 @@ class StartRedis
|
||||
private function generate_environment_variables()
|
||||
{
|
||||
$environment_variables = collect();
|
||||
foreach ($this->database->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("REDIS_PASSWORD={$this->database->redis_password}");
|
||||
foreach ($this->database->runtime_environment_variables as $env) {
|
||||
if ($env->is_shared) {
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
|
||||
if ($env->key === 'REDIS_PASSWORD') {
|
||||
$this->database->update(['redis_password' => $env->real_value]);
|
||||
}
|
||||
|
||||
if ($env->key === 'REDIS_USERNAME') {
|
||||
$this->database->update(['redis_username' => $env->real_value]);
|
||||
}
|
||||
} else {
|
||||
if ($env->key === 'REDIS_PASSWORD') {
|
||||
$env->update(['value' => $this->database->redis_password]);
|
||||
} elseif ($env->key === 'REDIS_USERNAME') {
|
||||
$env->update(['value' => $this->database->redis_username]);
|
||||
}
|
||||
$environment_variables->push("$env->key=$env->real_value");
|
||||
}
|
||||
}
|
||||
|
||||
add_coolify_default_environment_variables($this->database, $environment_variables, $environment_variables);
|
||||
@@ -173,6 +186,27 @@ class StartRedis
|
||||
return $environment_variables->all();
|
||||
}
|
||||
|
||||
private function buildStartCommand(): string
|
||||
{
|
||||
$hasRedisConf = ! is_null($this->database->redis_conf) && ! empty($this->database->redis_conf);
|
||||
$redisConfPath = '/usr/local/etc/redis/redis.conf';
|
||||
|
||||
if ($hasRedisConf) {
|
||||
$confContent = $this->database->redis_conf;
|
||||
$hasRequirePass = str_contains($confContent, 'requirepass');
|
||||
|
||||
if ($hasRequirePass) {
|
||||
$command = "redis-server $redisConfPath";
|
||||
} else {
|
||||
$command = "redis-server $redisConfPath --requirepass {$this->database->redis_password}";
|
||||
}
|
||||
} else {
|
||||
$command = "redis-server --requirepass {$this->database->redis_password} --appendonly yes";
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
private function add_custom_redis()
|
||||
{
|
||||
if (is_null($this->database->redis_conf) || empty($this->database->redis_conf)) {
|
||||
|
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Database\Redis;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Exception;
|
||||
@@ -25,6 +26,7 @@ class General extends Component
|
||||
'database.name' => 'required',
|
||||
'database.description' => 'nullable',
|
||||
'database.redis_conf' => 'nullable',
|
||||
'database.redis_username' => 'required',
|
||||
'database.redis_password' => 'required',
|
||||
'database.image' => 'required',
|
||||
'database.ports_mappings' => 'nullable',
|
||||
@@ -38,6 +40,7 @@ class General extends Component
|
||||
'database.name' => 'Name',
|
||||
'database.description' => 'Description',
|
||||
'database.redis_conf' => 'Redis Configuration',
|
||||
'database.redis_username' => 'Redis Username',
|
||||
'database.redis_password' => 'Redis Password',
|
||||
'database.image' => 'Image',
|
||||
'database.ports_mappings' => 'Port Mapping',
|
||||
@@ -75,16 +78,32 @@ class General extends Component
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
if ($this->database->redis_conf === '') {
|
||||
$this->database->redis_conf = null;
|
||||
|
||||
$redis_version = $this->get_redis_version();
|
||||
|
||||
if (version_compare($redis_version, '6.0', '>=') && $this->database->isDirty('redis_username')) {
|
||||
$this->updateEnvironmentVariable('REDIS_USERNAME', $this->database->redis_username);
|
||||
}
|
||||
|
||||
if ($this->database->isDirty('redis_password')) {
|
||||
$this->updateEnvironmentVariable('REDIS_PASSWORD', $this->database->redis_password);
|
||||
}
|
||||
|
||||
$this->database->save();
|
||||
|
||||
$this->dispatch('success', 'Database updated.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
private function get_redis_version()
|
||||
{
|
||||
$image_parts = explode(':', $this->database->image);
|
||||
|
||||
return $image_parts[1] ?? '0.0';
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
@@ -125,4 +144,31 @@ class General extends Component
|
||||
{
|
||||
return view('livewire.project.database.redis.general');
|
||||
}
|
||||
|
||||
public function isSharedVariable($name)
|
||||
{
|
||||
return EnvironmentVariable::where('key', $name)
|
||||
->where('standalone_redis_id', $this->database->id)
|
||||
->where('is_shared', true)
|
||||
->exists();
|
||||
}
|
||||
|
||||
private function updateEnvironmentVariable($key, $value)
|
||||
{
|
||||
$envVar = $this->database->runtime_environment_variables()
|
||||
->where('key', $key)
|
||||
->first();
|
||||
|
||||
if ($envVar) {
|
||||
if (! $envVar->is_shared) {
|
||||
$envVar->update(['value' => $value]);
|
||||
}
|
||||
} else {
|
||||
$this->database->runtime_environment_variables()->create([
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
'is_shared' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -74,6 +74,9 @@ class EnvironmentVariable extends Model
|
||||
'version' => config('version'),
|
||||
]);
|
||||
});
|
||||
static::saving(function (EnvironmentVariable $environmentVariable) {
|
||||
$environmentVariable->updateIsShared();
|
||||
});
|
||||
}
|
||||
|
||||
public function service()
|
||||
@@ -217,4 +220,11 @@ class EnvironmentVariable extends Model
|
||||
set: fn (string $value) => str($value)->trim()->replace(' ', '_')->value,
|
||||
);
|
||||
}
|
||||
|
||||
protected function updateIsShared(): void
|
||||
{
|
||||
$type = str($this->value)->after('{{')->before('.')->value;
|
||||
$isShared = str($this->value)->startsWith('{{'.$type) && str($this->value)->endsWith('}}');
|
||||
$this->is_shared = $isShared;
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,14 @@ class StandaloneRedis extends BaseModel
|
||||
|
||||
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
|
||||
|
||||
protected $casts = [
|
||||
'redis_password' => 'encrypted',
|
||||
];
|
||||
|
||||
protected $attributes = [
|
||||
'redis_username' => 'default',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($database) {
|
||||
@@ -210,7 +218,12 @@ class StandaloneRedis extends BaseModel
|
||||
protected function internalDbUrl(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
get: fn () => "redis://:{$this->redis_password}@{$this->uuid}:6379/0",
|
||||
get: function () {
|
||||
$redis_version = $this->get_redis_version();
|
||||
$username_part = version_compare($redis_version, '6.0', '>=') ? "{$this->redis_username}:" : '';
|
||||
|
||||
return "redis://{$username_part}{$this->redis_password}@{$this->uuid}:6379/0";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -219,7 +232,10 @@ class StandaloneRedis extends BaseModel
|
||||
return new Attribute(
|
||||
get: function () {
|
||||
if ($this->is_public && $this->public_port) {
|
||||
return "redis://:{$this->redis_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
|
||||
$redis_version = $this->get_redis_version();
|
||||
$username_part = version_compare($redis_version, '6.0', '>=') ? "{$this->redis_username}:" : '';
|
||||
|
||||
return "redis://{$username_part}{$this->redis_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -227,6 +243,13 @@ class StandaloneRedis extends BaseModel
|
||||
);
|
||||
}
|
||||
|
||||
private function get_redis_version()
|
||||
{
|
||||
$image_parts = explode(':', $this->image);
|
||||
|
||||
return $image_parts[1] ?? '0.0';
|
||||
}
|
||||
|
||||
public function environment()
|
||||
{
|
||||
return $this->belongsTo(Environment::class);
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDocker;
|
||||
@@ -49,6 +50,7 @@ function create_standalone_redis($environment_id, $destination_uuid, ?array $oth
|
||||
$database = new StandaloneRedis;
|
||||
$database->name = generate_database_name('redis');
|
||||
$database->redis_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
|
||||
$database->redis_username = 'default';
|
||||
$database->environment_id = $environment_id;
|
||||
$database->destination_id = $destination->id;
|
||||
$database->destination_type = $destination->getMorphClass();
|
||||
@@ -57,6 +59,20 @@ function create_standalone_redis($environment_id, $destination_uuid, ?array $oth
|
||||
}
|
||||
$database->save();
|
||||
|
||||
EnvironmentVariable::create([
|
||||
'key' => 'REDIS_PASSWORD',
|
||||
'value' => $database->redis_password,
|
||||
'standalone_redis_id' => $database->id,
|
||||
'is_shared' => false,
|
||||
]);
|
||||
|
||||
EnvironmentVariable::create([
|
||||
'key' => 'REDIS_USERNAME',
|
||||
'value' => $database->redis_username,
|
||||
'standalone_redis_id' => $database->id,
|
||||
'is_shared' => false,
|
||||
]);
|
||||
|
||||
return $database;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('standalone_redis', function (Blueprint $table) {
|
||||
$table->string('redis_username')->default('redis')->after('description');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('standalone_redis', function (Blueprint $table) {
|
||||
$table->dropColumn('redis_username');
|
||||
});
|
||||
}
|
||||
};
|
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddIsSharedToEnvironmentVariables extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::table('environment_variables', function (Blueprint $table) {
|
||||
$table->boolean('is_shared')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::table('environment_variables', function (Blueprint $table) {
|
||||
$table->dropColumn('is_shared');
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class EncryptExistingRedisPasswords extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
try {
|
||||
DB::table('standalone_redis')->chunkById(100, function ($redisInstances) {
|
||||
foreach ($redisInstances as $redis) {
|
||||
DB::table('standalone_redis')
|
||||
->where('id', $redis->id)
|
||||
->update(['redis_password' => Crypt::encryptString($redis->redis_password)]);
|
||||
}
|
||||
});
|
||||
} catch (\Exception $e) {
|
||||
echo 'Encrypting Redis passwords failed.';
|
||||
echo $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,8 +9,29 @@
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input label="Name" id="database.name" />
|
||||
<x-forms.input label="Description" id="database.description" />
|
||||
<x-forms.input label="Image" id="database.image" required
|
||||
helper="For all available images, check here:<br><br><a target='_blank' href='https://hub.docker.com/_/redis'>https://hub.docker.com/_/redis</a>" />
|
||||
<x-forms.input label="Image" id="database.image" required helper="For all available images, check here:<br><br><a target='_blank' href='https://hub.docker.com/_/redis'>https://hub.docker.com/_/redis</a>" />
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
@php
|
||||
$redis_version = explode(':', $database->image)[1] ?? '0.0';
|
||||
@endphp
|
||||
@if (version_compare($redis_version, '6.0', '>='))
|
||||
<x-forms.input label="Username" id="database.redis_username" required
|
||||
helper="You can change the Redis Username in the input field below or by editing the value of the REDIS_USERNAME environment variable.
|
||||
<br><br>
|
||||
If you change the Redis Username in the database, please sync it here, otherwise automations (like backups) won't work.
|
||||
<br><br>
|
||||
Note: If the environment variable REDIS_USERNAME is set as a shared variable (environment, project, or team-based), this input field will become read-only."
|
||||
:disabled="$this->isSharedVariable('REDIS_USERNAME')" />
|
||||
@endif
|
||||
<x-forms.input label="Password" id="database.redis_password" type="password" required
|
||||
helper="You can change the Redis Password in the input field below or by editing the value of the REDIS_PASSWORD environment variable.
|
||||
<br><br>
|
||||
If you change the Redis Password in the database, please sync it here, otherwise automations (like backups) won't work.
|
||||
<br><br>
|
||||
Note: If the environment variable REDIS_PASSWORD is set as a shared variable (environment, project, or team-based), this input field will become read-only."
|
||||
wire:model.defer="database.redis_password"
|
||||
:disabled="$this->isSharedVariable('REDIS_PASSWORD')" />
|
||||
</div>
|
||||
<x-forms.input
|
||||
helper="You can add custom docker run options that will be used when your container is started.<br>Note: Not all options are supported, as they could mess up Coolify's automation and could cause bad experience for users.<br><br>Check the <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/docker/custom-commands'>docs.</a>"
|
||||
@@ -19,42 +40,32 @@
|
||||
<div class="flex flex-col gap-2">
|
||||
<h3 class="py-2">Network</h3>
|
||||
<div class="flex items-end gap-2">
|
||||
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings"
|
||||
helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
|
||||
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings" helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold dark:text-warning'>Example</span>3000:5432,3002:5433" />
|
||||
</div>
|
||||
<x-forms.input label="Redis URL (internal)"
|
||||
helper="If you change the user/password/port, this could be different. This is with the default values."
|
||||
type="password" readonly wire:model="db_url" />
|
||||
<x-forms.input label="Redis URL (internal)" helper="If you change the user/password/port, this could be different. This is with the default values." type="password" readonly wire:model="db_url" />
|
||||
@if ($db_url_public)
|
||||
<x-forms.input label="Redis URL (public)"
|
||||
helper="If you change the user/password/port, this could be different. This is with the default values."
|
||||
type="password" readonly wire:model="db_url_public" />
|
||||
<x-forms.input label="Redis URL (public)" helper="If you change the user/password/port, this could be different. This is with the default values." type="password" readonly wire:model="db_url_public" />
|
||||
@endif
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="py-2">Proxy</h3>
|
||||
<div class="flex items-end gap-2">
|
||||
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}"
|
||||
id="database.public_port" label="Public Port" />
|
||||
<x-forms.input placeholder="5432" disabled="{{ data_get($database, 'is_public') }}" id="database.public_port" label="Public Port" />
|
||||
<x-slide-over fullScreen>
|
||||
<x-slot:title>Proxy Logs</x-slot:title>
|
||||
<x-slot:content>
|
||||
<livewire:project.shared.get-logs :server="$server" :resource="$database"
|
||||
container="{{ data_get($database, 'uuid') }}-proxy" lazy />
|
||||
<livewire:project.shared.get-logs :server="$server" :resource="$database" container="{{ data_get($database, 'uuid') }}-proxy" lazy />
|
||||
</x-slot:content>
|
||||
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true"
|
||||
class="w-28">Proxy Logs</x-forms.button>
|
||||
<x-forms.button disabled="{{ !data_get($database, 'is_public') }}" @click="slideOverOpen=true" class="w-28">Proxy Logs</x-forms.button>
|
||||
</x-slide-over>
|
||||
<x-forms.checkbox instantSave id="database.is_public" label="Make it publicly available" />
|
||||
</div>
|
||||
</div>
|
||||
<x-forms.textarea
|
||||
helper="<a target='_blank' class='underline dark:text-white' href='https://raw.githubusercontent.com/redis/redis/7.2/redis.conf'>Redis Default Configuration</a>"
|
||||
label="Custom Redis Configuration" rows="10" id="database.redis_conf" />
|
||||
<x-forms.textarea helper="<a target='_blank' class='underline dark:text-white' href='https://raw.githubusercontent.com/redis/redis/7.2/redis.conf'>Redis Default Configuration</a>" label="Custom Redis Configuration" rows="10" id="database.redis_conf" />
|
||||
<h3 class="pt-4">Advanced</h3>
|
||||
<div class="flex flex-col">
|
||||
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."
|
||||
instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" />
|
||||
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings." instantSave="instantSaveAdvanced" id="database.is_log_drain_enabled" label="Drain Logs" />
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user