init: redis
This commit is contained in:
parent
b8dd7704b3
commit
beae0b545f
144
app/Actions/Database/StartRedis.php
Normal file
144
app/Actions/Database/StartRedis.php
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
class StartRedis
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
public StandaloneRedis $database;
|
||||||
|
public array $commands = [];
|
||||||
|
public string $configuration_dir;
|
||||||
|
|
||||||
|
|
||||||
|
public function handle(Server $server, StandaloneRedis $database)
|
||||||
|
{
|
||||||
|
$this->database = $database;
|
||||||
|
$container_name = $this->database->uuid;
|
||||||
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
|
$this->commands = [
|
||||||
|
"echo '####### Starting {$database->name}.'",
|
||||||
|
"mkdir -p $this->configuration_dir",
|
||||||
|
];
|
||||||
|
|
||||||
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
|
$environment_variables = $this->generate_environment_variables();
|
||||||
|
$docker_compose = [
|
||||||
|
'version' => '3.8',
|
||||||
|
'services' => [
|
||||||
|
$container_name => [
|
||||||
|
'image' => $this->database->image,
|
||||||
|
'command' => "redis-server --requirepass {$this->database->redis_password} --appendonly yes",
|
||||||
|
'container_name' => $container_name,
|
||||||
|
'environment' => $environment_variables,
|
||||||
|
'restart' => RESTART_MODE,
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network,
|
||||||
|
],
|
||||||
|
'healthcheck' => [
|
||||||
|
'test' => [
|
||||||
|
'CMD-SHELL',
|
||||||
|
'redis-cli',
|
||||||
|
'ping'
|
||||||
|
],
|
||||||
|
'interval' => '5s',
|
||||||
|
'timeout' => '5s',
|
||||||
|
'retries' => 10,
|
||||||
|
'start_period' => '5s'
|
||||||
|
],
|
||||||
|
'mem_limit' => $this->database->limits_memory,
|
||||||
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
|
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||||
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
|
'cpus' => $this->database->limits_cpus,
|
||||||
|
'cpuset' => $this->database->limits_cpuset,
|
||||||
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'networks' => [
|
||||||
|
$this->database->destination->network => [
|
||||||
|
'external' => true,
|
||||||
|
'name' => $this->database->destination->network,
|
||||||
|
'attachable' => true,
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||||
|
}
|
||||||
|
if (count($persistent_storages) > 0) {
|
||||||
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
|
}
|
||||||
|
if (count($volume_names) > 0) {
|
||||||
|
$docker_compose['volumes'] = $volume_names;
|
||||||
|
}
|
||||||
|
// if (count($this->init_scripts) > 0) {
|
||||||
|
// foreach ($this->init_scripts as $init_script) {
|
||||||
|
// $docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
|
// 'type' => 'bind',
|
||||||
|
// 'source' => $init_script,
|
||||||
|
// 'target' => '/docker-entrypoint-initdb.d/' . basename($init_script),
|
||||||
|
// 'read_only' => true,
|
||||||
|
// ];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
|
$this->commands[] = "echo '####### {$database->name} started.'";
|
||||||
|
return remote_process($this->commands, $server);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||||
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
|
}
|
||||||
|
return $local_persistent_volumes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_local_persistent_volumes_only_volume_names()
|
||||||
|
{
|
||||||
|
$local_persistent_volumes_names = [];
|
||||||
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
|
if ($persistentStorage->host_path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = $persistentStorage->name;
|
||||||
|
$local_persistent_volumes_names[$name] = [
|
||||||
|
'name' => $name,
|
||||||
|
'external' => false,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $local_persistent_volumes_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generate_environment_variables()
|
||||||
|
{
|
||||||
|
$environment_variables = collect();
|
||||||
|
ray('Generate Environment Variables')->green();
|
||||||
|
ray($this->database->runtime_environment_variables)->green();
|
||||||
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
|
$environment_variables->push("$env->key=$env->value");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||||
|
$environment_variables->push("REDIS_PASSWORD={$this->database->redis_password}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $environment_variables->all();
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,7 @@ public function configuration()
|
|||||||
if (!$environment) {
|
if (!$environment) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$database = $environment->databases->where('uuid', request()->route('database_uuid'))->first();
|
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
||||||
if (!$database) {
|
if (!$database) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
@ -37,7 +37,7 @@ public function executions()
|
|||||||
if (!$environment) {
|
if (!$environment) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$database = $environment->databases->where('uuid', request()->route('database_uuid'))->first();
|
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
||||||
if (!$database) {
|
if (!$database) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
@ -64,7 +64,7 @@ public function backups()
|
|||||||
if (!$environment) {
|
if (!$environment) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
$database = $environment->databases->where('uuid', request()->route('database_uuid'))->first();
|
$database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first();
|
||||||
if (!$database) {
|
if (!$database) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
|
@ -59,11 +59,16 @@ public function new()
|
|||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
if (in_array($type, DATABASE_TYPES)) {
|
if (in_array($type, DATABASE_TYPES)) {
|
||||||
$standalone_postgresql = create_standalone_postgresql($environment->id, $destination_uuid);
|
if ($type->value() === "postgresql") {
|
||||||
|
$database = create_standalone_postgresql($environment->id, $destination_uuid);
|
||||||
|
} else if ($type->value() === 'redis') {
|
||||||
|
$database = create_standalone_redis($environment->id, $destination_uuid);
|
||||||
|
}
|
||||||
|
ray($database);
|
||||||
return redirect()->route('project.database.configuration', [
|
return redirect()->route('project.database.configuration', [
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
'environment_name' => $environment->name,
|
'environment_name' => $environment->name,
|
||||||
'database_uuid' => $standalone_postgresql->uuid,
|
'database_uuid' => $database->uuid,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
if ($type->startsWith('one-click-service-') && !is_null( (int)$server_id)) {
|
if ($type->startsWith('one-click-service-') && !is_null( (int)$server_id)) {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Livewire\Project\Database;
|
namespace App\Http\Livewire\Project\Database;
|
||||||
|
|
||||||
use App\Actions\Database\StartPostgresql;
|
use App\Actions\Database\StartPostgresql;
|
||||||
|
use App\Actions\Database\StartRedis;
|
||||||
use App\Jobs\ContainerStatusJob;
|
use App\Jobs\ContainerStatusJob;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ public function check_status()
|
|||||||
{
|
{
|
||||||
dispatch_sync(new ContainerStatusJob($this->database->destination->server));
|
dispatch_sync(new ContainerStatusJob($this->database->destination->server));
|
||||||
$this->database->refresh();
|
$this->database->refresh();
|
||||||
|
$this->emit('refresh');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
@ -40,7 +42,7 @@ public function stop()
|
|||||||
$this->database->destination->server
|
$this->database->destination->server
|
||||||
);
|
);
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
stopPostgresProxy($this->database);
|
stopDatabaseProxy($this->database);
|
||||||
$this->database->is_public = false;
|
$this->database->is_public = false;
|
||||||
}
|
}
|
||||||
$this->database->status = 'exited';
|
$this->database->status = 'exited';
|
||||||
@ -55,5 +57,9 @@ public function start()
|
|||||||
$activity = resolve(StartPostgresql::class)($this->database->destination->server, $this->database);
|
$activity = resolve(StartPostgresql::class)($this->database->destination->server, $this->database);
|
||||||
$this->emit('newMonitorActivity', $activity->id);
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
}
|
}
|
||||||
|
if ($this->database->type() === 'standalone-redis') {
|
||||||
|
$activity = StartRedis::run($this->database->destination->server, $this->database);
|
||||||
|
$this->emit('newMonitorActivity', $activity->id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,10 +67,10 @@ public function instantSave()
|
|||||||
}
|
}
|
||||||
if ($this->database->is_public) {
|
if ($this->database->is_public) {
|
||||||
$this->emit('success', 'Starting TCP proxy...');
|
$this->emit('success', 'Starting TCP proxy...');
|
||||||
startPostgresProxy($this->database);
|
startDatabaseProxy($this->database);
|
||||||
$this->emit('success', 'Database is now publicly accessible.');
|
$this->emit('success', 'Database is now publicly accessible.');
|
||||||
} else {
|
} else {
|
||||||
stopPostgresProxy($this->database);
|
stopDatabaseProxy($this->database);
|
||||||
$this->emit('success', 'Database is no longer publicly accessible.');
|
$this->emit('success', 'Database is no longer publicly accessible.');
|
||||||
}
|
}
|
||||||
$this->getDbUrl();
|
$this->getDbUrl();
|
||||||
|
87
app/Http/Livewire/Project/Database/Redis/General.php
Normal file
87
app/Http/Livewire/Project/Database/Redis/General.php
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Project\Database\Redis;
|
||||||
|
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
|
use Exception;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class General extends Component
|
||||||
|
{
|
||||||
|
protected $listeners = ['refresh'];
|
||||||
|
|
||||||
|
public StandaloneRedis $database;
|
||||||
|
public string $db_url;
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'database.name' => 'required',
|
||||||
|
'database.description' => 'nullable',
|
||||||
|
'database.redis_password' => 'required',
|
||||||
|
'database.image' => 'required',
|
||||||
|
'database.ports_mappings' => 'nullable',
|
||||||
|
'database.is_public' => 'nullable|boolean',
|
||||||
|
'database.public_port' => 'nullable|integer',
|
||||||
|
];
|
||||||
|
protected $validationAttributes = [
|
||||||
|
'database.name' => 'Name',
|
||||||
|
'database.description' => 'Description',
|
||||||
|
'database.redis_password' => 'Postgres User',
|
||||||
|
'database.image' => 'Image',
|
||||||
|
'database.ports_mappings' => 'Port Mapping',
|
||||||
|
'database.is_public' => 'Is Public',
|
||||||
|
'database.public_port' => 'Public Port',
|
||||||
|
];
|
||||||
|
public function submit() {
|
||||||
|
try {
|
||||||
|
$this->validate();
|
||||||
|
$this->database->save();
|
||||||
|
$this->emit('success', 'Database updated successfully.');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($this->database->is_public && !$this->database->public_port) {
|
||||||
|
$this->emit('error', 'Public port is required.');
|
||||||
|
$this->database->is_public = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($this->database->is_public) {
|
||||||
|
$this->emit('success', 'Starting TCP proxy...');
|
||||||
|
startDatabaseProxy($this->database);
|
||||||
|
$this->emit('success', 'Database is now publicly accessible.');
|
||||||
|
} else {
|
||||||
|
stopDatabaseProxy($this->database);
|
||||||
|
$this->emit('success', 'Database is no longer publicly accessible.');
|
||||||
|
}
|
||||||
|
$this->getDbUrl();
|
||||||
|
$this->database->save();
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
$this->database->is_public = !$this->database->is_public;
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function refresh(): void
|
||||||
|
{
|
||||||
|
$this->database->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->getDbUrl();
|
||||||
|
}
|
||||||
|
public function getDbUrl() {
|
||||||
|
|
||||||
|
if ($this->database->is_public) {
|
||||||
|
$this->db_url = "redis://{$this->database->redis_password}@{$this->database->destination->server->getIp}:{$this->database->public_port}/0";
|
||||||
|
} else {
|
||||||
|
$this->db_url = "redis://{$this->database->redis_password}@{$this->database->uuid}:5432/0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.database.redis.general');
|
||||||
|
}
|
||||||
|
}
|
@ -75,6 +75,9 @@ public function saveVariables($isPreview)
|
|||||||
case 'standalone-postgresql':
|
case 'standalone-postgresql':
|
||||||
$environment->standalone_postgresql_id = $this->resource->id;
|
$environment->standalone_postgresql_id = $this->resource->id;
|
||||||
break;
|
break;
|
||||||
|
case 'standalone-redis':
|
||||||
|
$environment->standalone_redis_id = $this->resource->id;
|
||||||
|
break;
|
||||||
case 'service':
|
case 'service':
|
||||||
$environment->service_id = $this->resource->id;
|
$environment->service_id = $this->resource->id;
|
||||||
break;
|
break;
|
||||||
|
@ -6,12 +6,13 @@
|
|||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Logs extends Component
|
class Logs extends Component
|
||||||
{
|
{
|
||||||
public ?string $type = null;
|
public ?string $type = null;
|
||||||
public Application|StandalonePostgresql|Service $resource;
|
public Application|StandalonePostgresql|Service|StandaloneRedis $resource;
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public ?string $container = null;
|
public ?string $container = null;
|
||||||
public $parameters;
|
public $parameters;
|
||||||
@ -33,7 +34,14 @@ public function mount()
|
|||||||
}
|
}
|
||||||
} else if (data_get($this->parameters, 'database_uuid')) {
|
} else if (data_get($this->parameters, 'database_uuid')) {
|
||||||
$this->type = 'database';
|
$this->type = 'database';
|
||||||
$this->resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->firstOrFail();
|
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||||
|
if (is_null($resource)) {
|
||||||
|
$resource = StandaloneRedis::where('uuid', $this->parameters['database_uuid'])->first();
|
||||||
|
if (is_null($resource)) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->resource = $resource;
|
||||||
$this->status = $this->resource->status;
|
$this->status = $this->resource->status;
|
||||||
$this->server = $this->resource->destination->server;
|
$this->server = $this->resource->destination->server;
|
||||||
$this->container = $this->resource->uuid;
|
$this->container = $this->resource->uuid;
|
||||||
|
@ -14,7 +14,7 @@ class Environment extends Model
|
|||||||
|
|
||||||
public function can_delete_environment()
|
public function can_delete_environment()
|
||||||
{
|
{
|
||||||
return $this->applications()->count() == 0 && $this->postgresqls()->count() == 0 && $this->services()->count() == 0;
|
return $this->applications()->count() == 0 && $this->redis()->count() == 0 && $this->postgresqls()->count() == 0 && $this->services()->count() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function applications()
|
public function applications()
|
||||||
@ -26,10 +26,16 @@ public function postgresqls()
|
|||||||
{
|
{
|
||||||
return $this->hasMany(StandalonePostgresql::class);
|
return $this->hasMany(StandalonePostgresql::class);
|
||||||
}
|
}
|
||||||
|
public function redis()
|
||||||
|
{
|
||||||
|
return $this->hasMany(StandaloneRedis::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function databases()
|
public function databases()
|
||||||
{
|
{
|
||||||
return $this->postgresqls();
|
$postgresqls = $this->postgresqls;
|
||||||
|
$redis = $this->redis;
|
||||||
|
return $postgresqls->concat($redis);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function project()
|
public function project()
|
||||||
|
@ -52,4 +52,8 @@ public function postgresqls()
|
|||||||
{
|
{
|
||||||
return $this->hasManyThrough(StandalonePostgresql::class, Environment::class);
|
return $this->hasManyThrough(StandalonePostgresql::class, Environment::class);
|
||||||
}
|
}
|
||||||
|
public function redis()
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(StandaloneRedis::class, Environment::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,9 @@ public function databases()
|
|||||||
{
|
{
|
||||||
return $this->destinations()->map(function ($standaloneDocker) {
|
return $this->destinations()->map(function ($standaloneDocker) {
|
||||||
$postgresqls = $standaloneDocker->postgresqls;
|
$postgresqls = $standaloneDocker->postgresqls;
|
||||||
return $postgresqls?->concat([]) ?? collect([]);
|
$redis = $standaloneDocker->redis;
|
||||||
|
return $postgresqls->merge($redis);
|
||||||
|
// return $postgresqls?->concat([]) ?? collect([]);
|
||||||
})->flatten();
|
})->flatten();
|
||||||
}
|
}
|
||||||
public function applications()
|
public function applications()
|
||||||
|
@ -15,6 +15,10 @@ public function postgresqls()
|
|||||||
{
|
{
|
||||||
return $this->morphMany(StandalonePostgresql::class, 'destination');
|
return $this->morphMany(StandalonePostgresql::class, 'destination');
|
||||||
}
|
}
|
||||||
|
public function redis()
|
||||||
|
{
|
||||||
|
return $this->morphMany(StandaloneRedis::class, 'destination');
|
||||||
|
}
|
||||||
|
|
||||||
public function server()
|
public function server()
|
||||||
{
|
{
|
||||||
|
103
app/Models/StandaloneRedis.php
Normal file
103
app/Models/StandaloneRedis.php
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
|
class StandaloneRedis extends BaseModel
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
protected $guarded = [];
|
||||||
|
|
||||||
|
protected static function booted()
|
||||||
|
{
|
||||||
|
static::created(function ($database) {
|
||||||
|
LocalPersistentVolume::create([
|
||||||
|
'name' => 'redis-data-' . $database->uuid,
|
||||||
|
'mount_path' => '/data',
|
||||||
|
'host_path' => null,
|
||||||
|
'resource_id' => $database->id,
|
||||||
|
'resource_type' => $database->getMorphClass(),
|
||||||
|
'is_readonly' => true
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
static::deleting(function ($database) {
|
||||||
|
// Stop Container
|
||||||
|
instant_remote_process(
|
||||||
|
["docker rm -f {$database->uuid}"],
|
||||||
|
$database->destination->server,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
// Stop TCP Proxy
|
||||||
|
if ($database->is_public) {
|
||||||
|
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $database->destination->server, false);
|
||||||
|
}
|
||||||
|
$database->scheduledBackups()->delete();
|
||||||
|
$database->persistentStorages()->delete();
|
||||||
|
$database->environment_variables()->delete();
|
||||||
|
// Remove Volume
|
||||||
|
instant_remote_process(['docker volume rm postgres-data-' . $database->uuid], $database->destination->server, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function portsMappings(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
set: fn ($value) => $value === "" ? null : $value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal Deployments
|
||||||
|
|
||||||
|
public function portsMappingsArray(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: fn () => is_null($this->ports_mappings)
|
||||||
|
? []
|
||||||
|
: explode(',', $this->ports_mappings),
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function type(): string
|
||||||
|
{
|
||||||
|
return 'standalone-redis';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function environment()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Environment::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fileStorages()
|
||||||
|
{
|
||||||
|
return $this->morphMany(LocalFileVolume::class, 'resource');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destination()
|
||||||
|
{
|
||||||
|
return $this->morphTo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function environment_variables(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(EnvironmentVariable::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function runtime_environment_variables(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(EnvironmentVariable::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function persistentStorages()
|
||||||
|
{
|
||||||
|
return $this->morphMany(LocalPersistentVolume::class, 'resource');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scheduledBackups()
|
||||||
|
{
|
||||||
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
const DATABASE_TYPES = ['postgresql'];
|
const DATABASE_TYPES = ['postgresql','redis'];
|
||||||
const VALID_CRON_STRINGS = [
|
const VALID_CRON_STRINGS = [
|
||||||
'every_minute' => '* * * * *',
|
'every_minute' => '* * * * *',
|
||||||
'hourly' => '0 * * * *',
|
'hourly' => '0 * * * *',
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
function generate_database_name(string $type): string
|
function generate_database_name(string $type): string
|
||||||
@ -27,6 +28,21 @@ function create_standalone_postgresql($environment_id, $destination_uuid): Stand
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function create_standalone_redis($environment_id, $destination_uuid): StandaloneRedis
|
||||||
|
{
|
||||||
|
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
||||||
|
if (!$destination) {
|
||||||
|
throw new Exception('Destination not found');
|
||||||
|
}
|
||||||
|
return StandaloneRedis::create([
|
||||||
|
'name' => generate_database_name('redis'),
|
||||||
|
'redis_password' => \Illuminate\Support\Str::password(symbols: false),
|
||||||
|
'environment_id' => $environment_id,
|
||||||
|
'destination_id' => $destination->id,
|
||||||
|
'destination_type' => $destination->getMorphClass(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete file locally on the filesystem.
|
* Delete file locally on the filesystem.
|
||||||
* @param string $filename
|
* @param string $filename
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
use App\Actions\Proxy\SaveConfiguration;
|
use App\Actions\Proxy\SaveConfiguration;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
function get_proxy_path()
|
function get_proxy_path()
|
||||||
@ -187,8 +188,14 @@ function setup_default_redirect_404(string|null $redirect_url, Server $server)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function startPostgresProxy(StandalonePostgresql $database)
|
function startDatabaseProxy(StandalonePostgresql|StandaloneRedis $database)
|
||||||
{
|
{
|
||||||
|
$internalPort = null;
|
||||||
|
if ($database->getMorphClass()=== 'App\Models\StandaloneRedis') {
|
||||||
|
$internalPort = 6379;
|
||||||
|
} else if ($database->getMorphClass()=== 'App\Models\StandalonePostgresql') {
|
||||||
|
$internalPort = 5432;
|
||||||
|
}
|
||||||
$containerName = "{$database->uuid}-proxy";
|
$containerName = "{$database->uuid}-proxy";
|
||||||
$configuration_dir = database_proxy_dir($database->uuid);
|
$configuration_dir = database_proxy_dir($database->uuid);
|
||||||
$nginxconf = <<<EOF
|
$nginxconf = <<<EOF
|
||||||
@ -203,7 +210,7 @@ function startPostgresProxy(StandalonePostgresql $database)
|
|||||||
stream {
|
stream {
|
||||||
server {
|
server {
|
||||||
listen $database->public_port;
|
listen $database->public_port;
|
||||||
proxy_pass $database->uuid:5432;
|
proxy_pass $database->uuid:$internalPort;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EOF;
|
EOF;
|
||||||
@ -260,7 +267,7 @@ function startPostgresProxy(StandalonePostgresql $database)
|
|||||||
"docker compose --project-directory {$configuration_dir} up --build -d >/dev/null",
|
"docker compose --project-directory {$configuration_dir} up --build -d >/dev/null",
|
||||||
], $database->destination->server);
|
], $database->destination->server);
|
||||||
}
|
}
|
||||||
function stopPostgresProxy(StandalonePostgresql $database)
|
function stopDatabaseProxy(StandalonePostgresql|StandaloneRedis $database)
|
||||||
{
|
{
|
||||||
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $database->destination->server);
|
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $database->destination->server);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('standalone_redis', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('uuid')->unique();
|
||||||
|
$table->string('name');
|
||||||
|
$table->string('description')->nullable();
|
||||||
|
|
||||||
|
$table->text('redis_password');
|
||||||
|
$table->longText('redis_conf')->nullable();
|
||||||
|
|
||||||
|
$table->string('status')->default('exited');
|
||||||
|
|
||||||
|
$table->string('image')->default('redis:7.2');
|
||||||
|
|
||||||
|
$table->boolean('is_public')->default(false);
|
||||||
|
$table->integer('public_port')->nullable();
|
||||||
|
$table->text('ports_mappings')->nullable();
|
||||||
|
|
||||||
|
$table->string('limits_memory')->default("0");
|
||||||
|
$table->string('limits_memory_swap')->default("0");
|
||||||
|
$table->integer('limits_memory_swappiness')->default(60);
|
||||||
|
$table->string('limits_memory_reservation')->default("0");
|
||||||
|
|
||||||
|
$table->string('limits_cpus')->default("0");
|
||||||
|
$table->string('limits_cpuset')->nullable()->default("0");
|
||||||
|
$table->integer('limits_cpu_shares')->default(1024);
|
||||||
|
|
||||||
|
$table->timestamp('started_at')->nullable();
|
||||||
|
$table->morphs('destination');
|
||||||
|
$table->foreignId('environment_id')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('standalone_redis');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('environment_variables', function (Blueprint $table) {
|
||||||
|
$table->foreignId('standalone_redis_id')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('environment_variables', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('standalone_redis_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
23
database/seeders/StandaloneRedisSeeder.php
Normal file
23
database/seeders/StandaloneRedisSeeder.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\StandaloneDocker;
|
||||||
|
use App\Models\StandalonePostgresql;
|
||||||
|
use App\Models\StandaloneRedis;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class StandaloneRedisSeeder extends Seeder
|
||||||
|
{
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
StandaloneRedis::create([
|
||||||
|
'name' => 'Local PostgreSQL',
|
||||||
|
'description' => 'Local PostgreSQL for testing',
|
||||||
|
'redis_password' => 'redis',
|
||||||
|
'environment_id' => 1,
|
||||||
|
'destination_id' => 0,
|
||||||
|
'destination_type' => StandaloneDocker::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
<div>
|
||||||
|
<form wire:submit.prevent="submit" class="flex flex-col gap-2">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<h2>General</h2>
|
||||||
|
<x-forms.button type="submit">
|
||||||
|
Save
|
||||||
|
</x-forms.button>
|
||||||
|
</div>
|
||||||
|
<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/_/postgres'>https://hub.docker.com/_/postgres</a>" />
|
||||||
|
</div>
|
||||||
|
<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 text-warning'>Example</span>3000:5432,3002:5433" />
|
||||||
|
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
|
||||||
|
label="Public Port" />
|
||||||
|
<x-forms.checkbox instantSave id="database.is_public" label="Accessible over the internet" />
|
||||||
|
</div>
|
||||||
|
<x-forms.input label="Redis URL" readonly wire:model="db_url" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
@ -94,6 +94,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="box group" wire:click="setType('redis')">
|
||||||
|
<div class="flex flex-col mx-6">
|
||||||
|
<div class="font-bold text-white group-hover:text-white">
|
||||||
|
New Redis
|
||||||
|
</div>
|
||||||
|
<div class="text-xs group-hover:text-white">
|
||||||
|
The open source, in-memory data store for cache, streaming engine, and message broker.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{{-- <div class="box group" wire:click="setType('existing-postgresql')">
|
{{-- <div class="box group" wire:click="setType('existing-postgresql')">
|
||||||
<div class="flex flex-col mx-6">
|
<div class="flex flex-col mx-6">
|
||||||
<div class="group-hover:text-white">
|
<div class="group-hover:text-white">
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
<div>
|
<div>
|
||||||
@if (
|
@if (
|
||||||
$resource->getMorphClass() == 'App\Models\Application' ||
|
$resource->getMorphClass() == 'App\Models\Application' ||
|
||||||
$resource->getMorphClass() == 'App\Models\StandalonePostgresql')
|
$resource->getMorphClass() == 'App\Models\StandalonePostgresql' ||
|
||||||
|
$resource->getMorphClass() == 'App\Models\StandaloneRedis')
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<h2>Storages</h2>
|
<h2>Storages</h2>
|
||||||
<x-helper
|
<x-helper
|
||||||
|
@ -13,25 +13,23 @@
|
|||||||
</x-modal>
|
</x-modal>
|
||||||
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex h-full pt-6">
|
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex h-full pt-6">
|
||||||
<div class="flex flex-col gap-4 min-w-fit">
|
<div class="flex flex-col gap-4 min-w-fit">
|
||||||
<a :class="activeTab === 'general' && 'text-white'"
|
<a :class="activeTab === 'general' && 'text-white'" @click.prevent="activeTab = 'general';
|
||||||
@click.prevent="activeTab = 'general'; window.location.hash = 'general'" href="#">General</a>
|
window.location.hash = 'general'" href="#">General</a>
|
||||||
<a :class="activeTab === 'environment-variables' && 'text-white'"
|
<a :class="activeTab === 'environment-variables' && 'text-white'"
|
||||||
@click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'"
|
@click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'"
|
||||||
href="#">Environment
|
href="#">Environment
|
||||||
Variables</a>
|
Variables</a>
|
||||||
<a :class="activeTab === 'server' && 'text-white'"
|
<a :class="activeTab === 'server' && 'text-white'" @click.prevent="activeTab = 'server';
|
||||||
@click.prevent="activeTab = 'server'; window.location.hash = 'server'"
|
window.location.hash = 'server'" href="#">Server
|
||||||
href="#">Server
|
|
||||||
</a>
|
</a>
|
||||||
<a :class="activeTab === 'storages' && 'text-white'"
|
<a :class="activeTab === 'storages' && 'text-white'" @click.prevent="activeTab = 'storages';
|
||||||
@click.prevent="activeTab = 'storages'; window.location.hash = 'storages'" href="#">Storages
|
window.location.hash = 'storages'" href="#">Storages
|
||||||
</a>
|
</a>
|
||||||
<a :class="activeTab === 'resource-limits' && 'text-white'"
|
<a :class="activeTab === 'resource-limits' && 'text-white'" @click.prevent="activeTab = 'resource-limits';
|
||||||
@click.prevent="activeTab = 'resource-limits'; window.location.hash = 'resource-limits'"
|
window.location.hash = 'resource-limits'" href="#">Resource Limits
|
||||||
href="#">Resource Limits
|
|
||||||
</a>
|
</a>
|
||||||
<a :class="activeTab === 'danger' && 'text-white'"
|
<a :class="activeTab === 'danger' && 'text-white'" @click.prevent="activeTab = 'danger';
|
||||||
@click.prevent="activeTab = 'danger'; window.location.hash = 'danger'" href="#">Danger Zone
|
window.location.hash = 'danger'" href="#">Danger Zone
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full pl-8">
|
<div class="w-full pl-8">
|
||||||
@ -39,6 +37,9 @@
|
|||||||
@if ($database->type() === 'standalone-postgresql')
|
@if ($database->type() === 'standalone-postgresql')
|
||||||
<livewire:project.database.postgresql.general :database="$database" />
|
<livewire:project.database.postgresql.general :database="$database" />
|
||||||
@endif
|
@endif
|
||||||
|
@if ($database->type() === 'standalone-redis')
|
||||||
|
<livewire:project.database.redis.general :database="$database" />
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div x-cloak x-show="activeTab === 'environment-variables'">
|
<div x-cloak x-show="activeTab === 'environment-variables'">
|
||||||
<livewire:project.shared.environment-variable.all :resource="$database" />
|
<livewire:project.shared.environment-variable.all :resource="$database" />
|
||||||
|
@ -46,7 +46,7 @@ class="items-center justify-center box">+ Add New Resource</a>
|
|||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
@endforeach
|
@endforeach
|
||||||
@foreach ($environment->databases->sortBy('name') as $databases)
|
@foreach ($environment->databases()->sortBy('name') as $databases)
|
||||||
<a class="box group"
|
<a class="box group"
|
||||||
href="{{ route('project.database.configuration', [$project->uuid, $environment->name, $databases->uuid]) }}">
|
href="{{ route('project.database.configuration', [$project->uuid, $environment->name, $databases->uuid]) }}">
|
||||||
<div class="flex flex-col mx-6">
|
<div class="flex flex-col mx-6">
|
||||||
|
Loading…
Reference in New Issue
Block a user