diff --git a/app/Http/Livewire/Project/Service/StackForm.php b/app/Http/Livewire/Project/Service/StackForm.php
index 30a3e7380..ebdb2d481 100644
--- a/app/Http/Livewire/Project/Service/StackForm.php
+++ b/app/Http/Livewire/Project/Service/StackForm.php
@@ -7,17 +7,34 @@ use Livewire\Component;
class StackForm extends Component
{
public $service;
- public $isConfigurationRequired = false;
+ public $fields = [];
protected $listeners = ["saveCompose"];
- protected $rules = [
+ public $rules = [
'service.docker_compose_raw' => 'required',
'service.docker_compose' => 'required',
'service.name' => 'required',
'service.description' => 'nullable',
];
- public function mount () {
- if ($this->service->applications->filter(fn($app) => str($app->image)->contains('minio/minio'))->count() > 0) {
- $this->isConfigurationRequired = true;
+ public $validationAttributes = [];
+ public function mount()
+ {
+ $extraFields = $this->service->extraFields();
+ foreach ($extraFields as $serviceName => $fields) {
+ foreach ($fields as $fieldKey => $field) {
+ $key = data_get($field, 'key');
+ $value = data_get($field, 'value');
+ $rules = data_get($field, 'rules');
+ $isPassword = data_get($field, 'isPassword');
+ $this->fields[$key] = [
+ "serviceName" => $serviceName,
+ "key" => $key,
+ "name" => $fieldKey,
+ "value" => $value,
+ "isPassword" => $isPassword,
+ ];
+ $this->rules["fields.$key.value"] = $rules;
+ $this->validationAttributes["fields.$key.value"] = $fieldKey;
+ }
}
}
public function saveCompose($raw)
@@ -32,6 +49,7 @@ class StackForm extends Component
try {
$this->validate();
$this->service->save();
+ $this->service->saveExtraFields($this->fields);
$this->service->parse();
$this->service->refresh();
$this->service->saveComposeConfigs();
diff --git a/app/Models/Service.php b/app/Models/Service.php
index 14084f282..eabdc1d88 100644
--- a/app/Models/Service.php
+++ b/app/Models/Service.php
@@ -45,7 +45,168 @@ class Service extends BaseModel
{
return 'service';
}
+ public function extraFields()
+ {
+ $fields = collect([]);
+ $applications = $this->applications()->get();
+ foreach ($applications as $application) {
+ $image = str($application->image)->before(':')->value();
+ switch ($image) {
+ case str($image)->contains('minio'):
+ $console_url = $this->environment_variables()->where('key', 'MINIO_BROWSER_REDIRECT_URL')->first();
+ $s3_api_url = $this->environment_variables()->where('key', 'MINIO_SERVER_URL')->first();
+ $admin_user = $this->environment_variables()->where('key', 'SERVICE_USER_MINIO')->first();
+ $admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_MINIO')->first();
+ $fields->put('MinIO', [
+ 'Console URL' => [
+ 'key' => data_get($console_url, 'key'),
+ 'value' => data_get($console_url, 'value'),
+ 'rules' => 'required|url',
+ ],
+ 'S3 API URL' => [
+ 'key' => data_get($s3_api_url, 'key'),
+ 'value' => data_get($s3_api_url, 'value'),
+ 'rules' => 'required|url',
+ ],
+ 'Admin User' => [
+ 'key' => data_get($admin_user, 'key'),
+ 'value' => data_get($admin_user, 'value'),
+ 'rules' => 'required',
+ ],
+ 'Admin Password' => [
+ 'key' => data_get($admin_password, 'key'),
+ 'value' => data_get($admin_password, 'value'),
+ 'rules' => 'required',
+ 'isPassword' => true,
+ ],
+ ]);
+ break;
+ }
+ }
+ $databases = $this->databases()->get();
+ foreach ($databases as $database) {
+ $image = str($database->image)->before(':')->value();
+ switch ($image) {
+ case str($image)->contains('postgres'):
+ $userVariables = ['SERVICE_USER_POSTGRES', 'SERVICE_USER_POSTGRESQL'];
+ $passwordVariables = ['SERVICE_PASSWORD_POSTGRES', 'SERVICE_PASSWORD_POSTGRESQL'];
+ $dbNameVariables = ['POSTGRESQL_DATABASE', 'POSTGRES_DB'];
+ $postgres_user = $this->environment_variables()->whereIn('key', $userVariables)->first();
+ $postgres_password = $this->environment_variables()->whereIn('key', $passwordVariables)->first();
+ $postgres_db_name = $this->environment_variables()->whereIn('key', $dbNameVariables)->first();
+ $fields->put('PostgreSQL', [
+ 'User' => [
+ 'key' => data_get($postgres_user, 'key'),
+ 'value' => data_get($postgres_user, 'value'),
+ 'rules' => 'required',
+ ],
+ 'Password' => [
+ 'key' => data_get($postgres_password, 'key'),
+ 'value' => data_get($postgres_password, 'value'),
+ 'rules' => 'required',
+ 'isPassword' => true,
+ ],
+ 'Database Name' => [
+ 'key' => data_get($postgres_db_name, 'key'),
+ 'value' => data_get($postgres_db_name, 'value'),
+ 'rules' => 'required',
+ ],
+ ]);
+ break;
+ case str($image)->contains('mysql'):
+ $userVariables = ['SERVICE_USER_MYSQL', 'SERVICE_USER_WORDPRESS'];
+ $passwordVariables = ['SERVICE_PASSWORD_MYSQL', 'SERVICE_PASSWORD_WORDPRESS'];
+ $rootPasswordVariables = ['SERVICE_PASSWORD_MYSQLROOT', 'SERVICE_PASSWORD_ROOT'];
+ $dbNameVariables = ['MYSQL_DATABASE'];
+ $mysql_user = $this->environment_variables()->whereIn('key', $userVariables)->first();
+ $mysql_password = $this->environment_variables()->whereIn('key', $passwordVariables)->first();
+ $mysql_root_password = $this->environment_variables()->whereIn('key', $rootPasswordVariables)->first();
+ $mysql_db_name = $this->environment_variables()->whereIn('key', $dbNameVariables)->first();
+ $fields->put('MySQL', [
+ 'User' => [
+ 'key' => data_get($mysql_user, 'key'),
+ 'value' => data_get($mysql_user, 'value'),
+ 'rules' => 'required',
+ ],
+ 'Password' => [
+ 'key' => data_get($mysql_password, 'key'),
+ 'value' => data_get($mysql_password, 'value'),
+ 'rules' => 'required',
+ 'isPassword' => true,
+ ],
+ 'Root Password' => [
+ 'key' => data_get($mysql_root_password, 'key'),
+ 'value' => data_get($mysql_root_password, 'value'),
+ 'rules' => 'required',
+ 'isPassword' => true,
+ ],
+ 'Database Name' => [
+ 'key' => data_get($mysql_db_name, 'key'),
+ 'value' => data_get($mysql_db_name, 'value'),
+ 'rules' => 'required',
+ ],
+ ]);
+ break;
+ case str($image)->contains('mariadb'):
+ $userVariables = ['SERVICE_USER_MARIADB', 'SERVICE_USER_WORDPRESS', '_APP_DB_USER'];
+ $passwordVariables = ['SERVICE_PASSWORD_MARIADB', 'SERVICE_PASSWORD_WORDPRESS', '_APP_DB_PASS'];
+ $rootPasswordVariables = ['SERVICE_PASSWORD_MARIADBROOT', 'SERVICE_PASSWORD_ROOT', '_APP_DB_ROOT_PASS'];
+ $dbNameVariables = ['SERVICE_DATABASE_MARIADB', 'SERVICE_DATABASE_WORDPRESS', '_APP_DB_SCHEMA'];
+ $mariadb_user = $this->environment_variables()->whereIn('key', $userVariables)->first();
+ $mariadb_password = $this->environment_variables()->whereIn('key', $passwordVariables)->first();
+ $mariadb_root_password = $this->environment_variables()->whereIn('key', $rootPasswordVariables)->first();
+ $mariadb_db_name = $this->environment_variables()->whereIn('key', $dbNameVariables)->first();
+ $fields->put('MariaDB', [
+ 'User' => [
+ 'key' => data_get($mariadb_user, 'key'),
+ 'value' => data_get($mariadb_user, 'value'),
+ 'rules' => 'required',
+ ],
+ 'Password' => [
+ 'key' => data_get($mariadb_password, 'key'),
+ 'value' => data_get($mariadb_password, 'value'),
+ 'rules' => 'required',
+ 'isPassword' => true,
+ ],
+ 'Root Password' => [
+ 'key' => data_get($mariadb_root_password, 'key'),
+ 'value' => data_get($mariadb_root_password, 'value'),
+ 'rules' => 'required',
+ 'isPassword' => true,
+ ],
+ 'Database Name' => [
+ 'key' => data_get($mariadb_db_name, 'key'),
+ 'value' => data_get($mariadb_db_name, 'value'),
+ 'rules' => data_get($mariadb_db_name, 'value') && 'required',
+ ],
+ ]);
+
+ break;
+ }
+ }
+ return $fields;
+ }
+ public function saveExtraFields($fields)
+ {
+ foreach ($fields as $field) {
+ $key = data_get($field, 'key');
+ $value = data_get($field, 'value');
+ $found = $this->environment_variables()->where('key', $key)->first();
+ if ($found) {
+ $found->value = $value;
+ $found->save();
+ } else {
+ $this->environment_variables()->create([
+ 'key' => $key,
+ 'value' => $value,
+ 'is_build_time' => false,
+ 'service_id' => $this->id,
+ 'is_preview' => false,
+ ]);
+ }
+ }
+ }
public function documentation()
{
$services = getServiceTemplates();
@@ -257,7 +418,7 @@ class Service extends BaseModel
}
}
$networks = collect();
- foreach ($serviceNetworks as $key =>$serviceNetwork) {
+ foreach ($serviceNetworks as $key => $serviceNetwork) {
if (gettype($serviceNetwork) === 'string') {
// networks:
// - appwrite
@@ -268,7 +429,7 @@ class Service extends BaseModel
// ipv4_address: 192.168.203.254
// $networks->put($serviceNetwork, null);
ray($key);
- $networks->put($key,$serviceNetwork);
+ $networks->put($key, $serviceNetwork);
}
}
foreach ($definedNetwork as $key => $network) {
@@ -564,12 +725,18 @@ class Service extends BaseModel
}
// Add labels to the service
- $fqdns = collect(data_get($savedService, 'fqdns'));
- $defaultLabels = defaultLabels($this->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
- $serviceLabels = $serviceLabels->merge($defaultLabels);
- if (!$isDatabase && $fqdns->count() > 0) {
- if ($fqdns) {
- $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($this->uuid, $fqdns, true));
+ if (!$isDatabase) {
+ if ($savedService->serviceType()) {
+ $fqdns = generateServiceSpecificFqdns($savedService, forTraefik: true);
+ } else {
+ $fqdns = collect(data_get($savedService, 'fqdns'));
+ }
+ $defaultLabels = defaultLabels($this->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
+ $serviceLabels = $serviceLabels->merge($defaultLabels);
+ if ($fqdns->count() > 0) {
+ if ($fqdns) {
+ $serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($this->uuid, $fqdns, true));
+ }
}
}
data_set($service, 'labels', $serviceLabels->toArray());
diff --git a/app/Models/ServiceApplication.php b/app/Models/ServiceApplication.php
index c70ceeccf..b1db1c581 100644
--- a/app/Models/ServiceApplication.php
+++ b/app/Models/ServiceApplication.php
@@ -22,6 +22,16 @@ class ServiceApplication extends BaseModel
{
return 'service';
}
+ public function serviceType()
+ {
+ $found = str(collect(SPECIFIC_SERVICES)->filter(function ($service) {
+ return str($this->image)->before(':')->value() === $service;
+ })->first());
+ if ($found->isNotEmpty()) {
+ return $found;
+ }
+ return null;
+ }
public function service()
{
return $this->belongsTo(Service::class);
diff --git a/app/Models/ServiceDatabase.php b/app/Models/ServiceDatabase.php
index 0dbbf6196..e37821af2 100644
--- a/app/Models/ServiceDatabase.php
+++ b/app/Models/ServiceDatabase.php
@@ -29,7 +29,6 @@ class ServiceDatabase extends BaseModel
return "standalone-$image";
}
public function getServiceDatabaseUrl() {
- // $type = $this->databaseType();
$port = $this->public_port;
$realIp = $this->service->server->ip;
if ($realIp === 'host.docker.internal' || isDev()) {
diff --git a/app/View/Components/Services/Links.php b/app/View/Components/Services/Links.php
index b2cc8618d..b3953c174 100644
--- a/app/View/Components/Services/Links.php
+++ b/app/View/Components/Services/Links.php
@@ -16,22 +16,28 @@ class Links extends Component
{
$this->links = collect([]);
$service->applications()->get()->map(function ($application) {
- if ($application->fqdn) {
- $fqdns = collect(Str::of($application->fqdn)->explode(','));
- $fqdns->map(function ($fqdn) {
- $this->links->push(getFqdnWithoutPort($fqdn));
- });
- }
- if ($application->ports) {
- $portsCollection = collect(Str::of($application->ports)->explode(','));
- $portsCollection->map(function ($port) {
- if (Str::of($port)->contains(':')) {
- $hostPort = Str::of($port)->before(':');
- } else {
- $hostPort = $port;
- }
- $this->links->push(base_url(withPort:false) . ":{$hostPort}");
- });
+ $type = $application->serviceType();
+ if ($type) {
+ $links = generateServiceSpecificFqdns($application, false);
+ $this->links = $this->links->merge($links);
+ } else {
+ if ($application->fqdn) {
+ $fqdns = collect(Str::of($application->fqdn)->explode(','));
+ $fqdns->map(function ($fqdn) {
+ $this->links->push(getFqdnWithoutPort($fqdn));
+ });
+ }
+ if ($application->ports) {
+ $portsCollection = collect(Str::of($application->ports)->explode(','));
+ $portsCollection->map(function ($port) {
+ if (Str::of($port)->contains(':')) {
+ $hostPort = Str::of($port)->before(':');
+ } else {
+ $hostPort = $port;
+ }
+ $this->links->push(base_url(withPort: false) . ":{$hostPort}");
+ });
+ }
}
});
}
diff --git a/bootstrap/helpers/constants.php b/bootstrap/helpers/constants.php
index 10c1353d3..e844efea9 100644
--- a/bootstrap/helpers/constants.php
+++ b/bootstrap/helpers/constants.php
@@ -23,3 +23,6 @@ const DATABASE_DOCKER_IMAGES = [
'influxdb',
'clickhouse/clickhouse-server'
];
+const SPECIFIC_SERVICES = [
+ 'quay.io/minio/minio',
+];
diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php
index 90c4179a7..f932c95f3 100644
--- a/bootstrap/helpers/docker.php
+++ b/bootstrap/helpers/docker.php
@@ -144,6 +144,39 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica
}
return $labels;
}
+function generateServiceSpecificFqdns($service, $forTraefik = false)
+{
+ $variables = collect($service->service->environment_variables);
+ $type = $service->serviceType();
+ $payload = collect([]);
+ switch ($type) {
+ case $type->contains('minio'):
+ $MINIO_BROWSER_REDIRECT_URL = $variables->where('key', 'MINIO_BROWSER_REDIRECT_URL')->first();
+ if (is_null($MINIO_BROWSER_REDIRECT_URL->value)) {
+ $MINIO_BROWSER_REDIRECT_URL->update([
+ "value" => generateFqdn($service->service->server, 'console-' . $service->uuid)
+ ]);
+ }
+ $MINIO_SERVER_URL = $variables->where('key', 'MINIO_SERVER_URL')->first();
+ if (is_null($MINIO_SERVER_URL->value)) {
+ $MINIO_SERVER_URL->update([
+ "value" => generateFqdn($service->service->server, 'minio-' . $service->uuid)
+ ]);
+ }
+ if ($forTraefik) {
+ $payload = collect([
+ $MINIO_BROWSER_REDIRECT_URL->value . ':9001',
+ $MINIO_SERVER_URL->value . ':9000',
+ ]);
+ } else {
+ $payload = collect([
+ $MINIO_BROWSER_REDIRECT_URL->value,
+ $MINIO_SERVER_URL->value,
+ ]);
+ }
+ }
+ return $payload;
+}
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled, $onlyPort = null)
{
$labels = collect([]);
diff --git a/resources/views/components/services/navbar.blade.php b/resources/views/components/services/navbar.blade.php
index 1d0cf03a5..6c9af5f07 100644
--- a/resources/views/components/services/navbar.blade.php
+++ b/resources/views/components/services/navbar.blade.php
@@ -3,7 +3,7 @@
href="{{ route('project.service', $parameters) }}">
-