feat: make service databases public

This commit is contained in:
Andras Bacsai
2023-11-09 14:59:38 +01:00
parent 8f5b084931
commit 61e1fdede9
10 changed files with 126 additions and 20 deletions

View File

@@ -2,6 +2,7 @@
namespace App\Actions\Database; namespace App\Actions\Database;
use App\Models\ServiceDatabase;
use App\Models\StandaloneMariadb; use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb; use App\Models\StandaloneMongodb;
use App\Models\StandaloneMysql; use App\Models\StandaloneMysql;
@@ -14,18 +15,44 @@ class StartDatabaseProxy
{ {
use AsAction; use AsAction;
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database) public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
{ {
$internalPort = null; $internalPort = null;
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') { $type = $database->getMorphClass();
$network = data_get($database, 'destination.network');
$server = data_get($database, 'destination.server');
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
$databaseType = $database->databaseType();
$network = data_get($database, 'service.destination.network');
$server = data_get($database, 'service.destination.server');
ray($databaseType, $network);
switch ($databaseType) {
case 'standalone-mariadb':
$type = 'App\Models\StandaloneMariadb';
break;
case 'standalone-mongodb':
$type = 'App\Models\StandaloneMongodb';
break;
case 'standalone-mysql':
$type = 'App\Models\StandaloneMysql';
break;
case 'standalone-postgresql':
$type = 'App\Models\StandalonePostgresql';
break;
case 'standalone-redis':
$type = 'App\Models\StandaloneRedis';
break;
}
}
if ($type === 'App\Models\StandaloneRedis') {
$internalPort = 6379; $internalPort = 6379;
} else if ($database->getMorphClass() === 'App\Models\StandalonePostgresql') { } else if ($type === 'App\Models\StandalonePostgresql') {
$internalPort = 5432; $internalPort = 5432;
} else if ($database->getMorphClass() === 'App\Models\StandaloneMongodb') { } else if ($type === 'App\Models\StandaloneMongodb') {
$internalPort = 27017; $internalPort = 27017;
} else if ($database->getMorphClass() === 'App\Models\StandaloneMysql') { } else if ($type === 'App\Models\StandaloneMysql') {
$internalPort = 3306; $internalPort = 3306;
} else if ($database->getMorphClass() === 'App\Models\StandaloneMariadb') { } else if ($type === 'App\Models\StandaloneMariadb') {
$internalPort = 3306; $internalPort = 3306;
} }
$containerName = "{$database->uuid}-proxy"; $containerName = "{$database->uuid}-proxy";
@@ -66,7 +93,7 @@ class StartDatabaseProxy
"$database->public_port:$database->public_port", "$database->public_port:$database->public_port",
], ],
'networks' => [ 'networks' => [
$database->destination->network, $network,
], ],
'healthcheck' => [ 'healthcheck' => [
'test' => [ 'test' => [
@@ -81,9 +108,9 @@ class StartDatabaseProxy
] ]
], ],
'networks' => [ 'networks' => [
$database->destination->network => [ $network => [
'external' => true, 'external' => true,
'name' => $database->destination->network, 'name' => $network,
'attachable' => true, 'attachable' => true,
] ]
] ]
@@ -97,6 +124,6 @@ class StartDatabaseProxy
"echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf", "echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf",
"echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml", "echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml",
"docker compose --project-directory {$configuration_dir} up --build -d", "docker compose --project-directory {$configuration_dir} up --build -d",
], $database->destination->server); ], $server);
} }
} }

View File

@@ -2,6 +2,7 @@
namespace App\Actions\Database; namespace App\Actions\Database;
use App\Models\ServiceDatabase;
use App\Models\StandaloneMariadb; use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb; use App\Models\StandaloneMongodb;
use App\Models\StandaloneMysql; use App\Models\StandaloneMysql;
@@ -13,9 +14,13 @@ class StopDatabaseProxy
{ {
use AsAction; use AsAction;
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database) public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
{ {
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $database->destination->server); $server = data_get($database, 'destination.server');
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
$server = data_get($database, 'service.server');
}
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $server);
$database->is_public = false; $database->is_public = false;
$database->save(); $database->save();
} }

View File

@@ -2,28 +2,56 @@
namespace App\Http\Livewire\Project\Service; namespace App\Http\Livewire\Project\Service;
use App\Actions\Database\StartDatabaseProxy;
use App\Actions\Database\StopDatabaseProxy;
use App\Models\ServiceDatabase; use App\Models\ServiceDatabase;
use Livewire\Component; use Livewire\Component;
class Database extends Component class Database extends Component
{ {
public ServiceDatabase $database; public ServiceDatabase $database;
public ?string $db_url_public = null;
public $fileStorages; public $fileStorages;
protected $listeners = ["refreshFileStorages"]; protected $listeners = ["refreshFileStorages"];
protected $rules = [ protected $rules = [
'database.human_name' => 'nullable', 'database.human_name' => 'nullable',
'database.description' => 'nullable', 'database.description' => 'nullable',
'database.image' => 'required', 'database.image' => 'required',
'database.exclude_from_status' => 'required|boolean', 'database.exclude_from_status' => 'required|boolean',
'database.public_port' => 'nullable|integer',
'database.is_public' => 'required|boolean',
]; ];
public function render() public function render()
{ {
return view('livewire.project.service.database'); return view('livewire.project.service.database');
} }
public function mount() { public function mount() {
if ($this->database->is_public) {
$this->db_url_public = $this->database->getServiceDatabaseUrl();
}
$this->refreshFileStorages(); $this->refreshFileStorages();
} }
public function instantSave() { public function instantSave() {
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) {
if (!str($this->database->status)->startsWith('running')) {
$this->emit('error', 'Database must be started to be publicly accessible.');
$this->database->is_public = false;
return;
}
StartDatabaseProxy::run($this->database);
$this->db_url_public = $this->database->getServiceDatabaseUrl();
$this->emit('success', 'Database is now publicly accessible.');
} else {
StopDatabaseProxy::run($this->database);
$this->db_url_public = null;
$this->emit('success', 'Database is no longer publicly accessible.');
}
$this->submit(); $this->submit();
} }
public function refreshFileStorages() public function refreshFileStorages()

View File

@@ -28,6 +28,12 @@ class ServiceDatabase extends BaseModel
} }
return "standalone-$image"; return "standalone-$image";
} }
public function getServiceDatabaseUrl() {
// $type = $this->databaseType();
$port = $this->public_port;
$url = "{$this->service->server->ip}:{$port}";
return $url;
}
public function service() public function service()
{ {
return $this->belongsTo(Service::class); return $this->belongsTo(Service::class);

View File

@@ -58,8 +58,9 @@ class StandaloneRedis extends BaseModel
{ {
return 'standalone-redis'; return 'standalone-redis';
} }
public function getDbUrl(): string { public function getDbUrl(bool $useInternal = false): string
if ($this->is_public) { {
if ($this->is_public && !$useInternal) {
return "redis://:{$this->redis_password}@{$this->destination->server->getIp}:{$this->public_port}/0"; return "redis://:{$this->redis_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
} else { } else {
return "redis://:{$this->redis_password}@{$this->uuid}:6379/0"; return "redis://:{$this->redis_password}@{$this->uuid}:6379/0";

View File

@@ -7,7 +7,7 @@ return [
// The release version of your application // The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.118', 'release' => '4.0.0-beta.119',
// When left empty or `null` the Laravel environment will be used // When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'), 'environment' => config('app.env'),

View File

@@ -1,3 +1,3 @@
<?php <?php
return '4.0.0-beta.118'; return '4.0.0-beta.119';

View File

@@ -0,0 +1,30 @@
<?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('service_databases', function (Blueprint $table) {
$table->integer('public_port')->nullable();
$table->boolean('is_public')->default(false);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('service_databases', function (Blueprint $table) {
$table->dropColumn('public_port');
$table->dropColumn('is_public');
});
}
};

View File

@@ -12,10 +12,19 @@
<div class="flex gap-2"> <div class="flex gap-2">
<x-forms.input label="Name" id="database.human_name" placeholder="Name"></x-forms.input> <x-forms.input label="Name" id="database.human_name" placeholder="Name"></x-forms.input>
<x-forms.input label="Description" id="database.description"></x-forms.input> <x-forms.input label="Description" id="database.description"></x-forms.input>
<x-forms.input required
helper="You can change the image you would like to deploy.<br><br><span class='text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>"
label="Image Tag" id="database.image"></x-forms.input>
</div> </div>
<div class="flex gap-2"> <div class="flex items-end gap-2">
<x-forms.input required helper="You can change the image you would like to deploy.<br><br><span class='text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>" label="Image Tag" @if ($db_url_public)
id="database.image"></x-forms.input> <x-forms.input label="Database URL (public)"
helper="Your credentials are available in your environment variables." type="password" readonly
wire:model="db_url_public" />
@endif
<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> </div>
</div> </div>
<h3 class="pt-2">Advanced</h3> <h3 class="pt-2">Advanced</h3>

View File

@@ -4,7 +4,7 @@
"version": "3.12.36" "version": "3.12.36"
}, },
"v4": { "v4": {
"version": "4.0.0-beta.118" "version": "4.0.0-beta.119"
} }
} }
} }