Merge branch 'next' into main

This commit is contained in:
Andras Bacsai
2024-08-14 21:00:56 +02:00
committed by GitHub
33 changed files with 904 additions and 430 deletions

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Process;
class OpenApi extends Command
{
protected $signature = 'openapi';
protected $description = 'Generate OpenApi file.';
public function handle()
{
// Generate OpenAPI documentation
echo "Generating OpenAPI documentation.\n";
$process = Process::run(['/var/www/html/vendor/bin/openapi', 'app', '-o', 'openapi.yaml']);
$error = $process->errorOutput();
$error = preg_replace('/^.*an object literal,.*$/m', '', $error);
$error = preg_replace('/^\h*\v+/m', '', $error);
echo $error;
echo $process->output();
}
}

View File

@@ -201,7 +201,7 @@ class ApplicationsController extends Controller
#[OA\Post(
summary: 'Create (Private - GH App)',
description: 'Create new application based on a private repository through a Github App.',
path: '/applications/private-gh-app',
path: '/applications/private-github-app',
security: [
['bearerAuth' => []],
],

View File

@@ -1,35 +0,0 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\EnvironmentVariable;
use Illuminate\Http\Request;
class EnvironmentVariablesController extends Controller
{
public function delete_env_by_uuid(Request $request)
{
$teamId = getTeamIdFromToken();
if (is_null($teamId)) {
return invalidTokenResponse();
}
$env = EnvironmentVariable::where('uuid', $request->env_uuid)->first();
if (! $env) {
return response()->json([
'message' => 'Environment variable not found.',
], 404);
}
$found_app = $env->resource()->whereRelation('environment.project.team', 'id', $teamId)->first();
if (! $found_app) {
return response()->json([
'message' => 'Environment variable not found.',
], 404);
}
$env->delete();
return response()->json([
'message' => 'Environment variable deleted.',
]);
}
}

View File

@@ -157,7 +157,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private ?string $coolify_variables = null;
private bool $preserveRepository = true;
private bool $preserveRepository = false;
public $tries = 1;
@@ -480,6 +480,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
}
// Start compose file
$server_workdir = $this->application->workdir();
if ($this->application->settings->is_raw_compose_deployment_enabled) {
if ($this->docker_compose_custom_start_command) {
$this->write_deployment_configurations();
@@ -488,7 +489,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
);
} else {
$this->write_deployment_configurations();
$server_workdir = $this->application->workdir();
$this->docker_compose_location = '/docker-compose.yaml';
$command = "{$this->coolify_variables} docker compose";
@@ -508,15 +508,26 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
);
} else {
$command = "{$this->coolify_variables} docker compose";
if ($this->env_filename) {
$command .= " --env-file {$this->workdir}/{$this->env_filename}";
}
$command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d";
if ($this->preserveRepository) {
if ($this->env_filename) {
$command .= " --env-file {$server_workdir}/{$this->env_filename}";
}
$command .= " --project-name {$this->application->uuid} --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d";
$this->write_deployment_configurations();
$this->execute_remote_command(
['command' => $command, 'hidden' => true],
);
} else {
if ($this->env_filename) {
$command .= " --env-file {$this->workdir}/{$this->env_filename}";
}
$command .= " --project-name {$this->application->uuid} --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d";
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, $command), 'hidden' => true],
);
}
$this->write_deployment_configurations();
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, $command), 'hidden' => true],
);
}
}
@@ -619,6 +630,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
],
);
}
$this->application->fileStorages()->each(function ($fileStorage) {
if (! $fileStorage->is_based_on_git && ! $fileStorage->is_directory) {
$fileStorage->saveStorageOnServer();
}
});
if ($this->use_build_server) {
$this->server = $this->build_server;
}
@@ -1708,13 +1724,17 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
if (count($this->application->ports_mappings_array) > 0 && $this->pull_request_id === 0) {
$docker_compose['services'][$this->container_name]['ports'] = $this->application->ports_mappings_array;
}
if (! data_get($docker_compose, 'services.'.$this->container_name.'.volumes')) {
$docker_compose['services'][$this->container_name]['volumes'] = [];
}
if (count($persistent_storages) > 0) {
$docker_compose['services'][$this->container_name]['volumes'] = $persistent_storages;
$docker_compose['services'][$this->container_name]['volumes'] = array_merge($docker_compose['services'][$this->container_name]['volumes'], $persistent_storages);
}
if (count($persistent_file_volumes) > 0) {
$docker_compose['services'][$this->container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
$docker_compose['services'][$this->container_name]['volumes'] = array_merge($docker_compose['services'][$this->container_name]['volumes'], $persistent_file_volumes->map(function ($item) {
return "$item->fs_path:$item->mount_path";
})->toArray();
})->toArray());
}
if (count($volume_names) > 0) {
$docker_compose['volumes'] = $volume_names;

View File

@@ -336,7 +336,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
$url = $this->database->internal_db_url;
if ($databaseWithCollections === 'all') {
$commands[] = 'mkdir -p '.$this->backup_dir;
if (str($this->database->image)->startsWith('mongo:4.0')) {
if (str($this->database->image)->startsWith('mongo:4')) {
$commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --archive > $this->backup_location";
} else {
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location";
@@ -351,13 +351,13 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
}
$commands[] = 'mkdir -p '.$this->backup_dir;
if ($collectionsToExclude->count() === 0) {
if (str($this->database->image)->startsWith('mongo:4.0')) {
if (str($this->database->image)->startsWith('mongo:4')) {
$commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --archive > $this->backup_location";
} else {
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --archive > $this->backup_location";
}
} else {
if (str($this->database->image)->startsWith('mongo:4.0')) {
if (str($this->database->image)->startsWith('mongo:4')) {
$commands[] = "docker exec $this->container_name mongodump --uri=$url --gzip --excludeCollection ".$collectionsToExclude->implode(' --excludeCollection ')." --archive > $this->backup_location";
} else {
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection ".$collectionsToExclude->implode(' --excludeCollection ')." --archive > $this->backup_location";

View File

@@ -3,7 +3,6 @@
namespace App\Livewire\Project\Application;
use App\Models\Application;
use App\Models\LocalFileVolume;
use Illuminate\Support\Collection;
use Livewire\Component;
use Visus\Cuid2\Cuid2;
@@ -30,6 +29,8 @@ class General extends Component
public ?string $ports_exposes = null;
public bool $is_preserve_repository_enabled = false;
public bool $is_container_label_escape_enabled = true;
public $customLabels;
@@ -145,6 +146,7 @@ class General extends Component
}
$this->parsedServiceDomains = $this->application->docker_compose_domains ? json_decode($this->application->docker_compose_domains, true) : [];
$this->ports_exposes = $this->application->ports_exposes;
$this->is_preserve_repository_enabled = $this->application->settings->is_preserve_repository_enabled;
$this->is_container_label_escape_enabled = $this->application->settings->is_container_label_escape_enabled;
$this->customLabels = $this->application->parseContainerLabels();
if (! $this->customLabels && $this->application->destination->server->proxyType() !== 'NONE' && ! $this->application->settings->is_container_label_readonly_enabled) {
@@ -168,9 +170,21 @@ class General extends Component
$this->application->settings->save();
$this->dispatch('success', 'Settings saved.');
$this->application->refresh();
// If port_exposes changed, reset default labels
if ($this->ports_exposes !== $this->application->ports_exposes || $this->is_container_label_escape_enabled !== $this->application->settings->is_container_label_escape_enabled) {
$this->resetDefaultLabels(false);
}
if ($this->is_preserve_repository_enabled !== $this->application->settings->is_preserve_repository_enabled) {
if ($this->application->settings->is_preserve_repository_enabled === false) {
$this->application->fileStorages->each(function ($storage) {
$storage->is_based_on_git = $this->application->settings->is_preserve_repository_enabled;
$storage->save();
});
}
}
}
public function loadComposeFile($isInit = false)
@@ -179,6 +193,11 @@ class General extends Component
if ($isInit && $this->application->docker_compose_raw) {
return;
}
// Must reload the application to get the latest database changes
// Why? Not sure, but it works.
$this->application->refresh();
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation] = $this->application->loadComposeFile($isInit);
if (is_null($this->parsedServices)) {
$this->dispatch('error', 'Failed to parse your docker-compose file. Please check the syntax and try again.');
@@ -186,32 +205,6 @@ class General extends Component
return;
}
$compose = $this->application->parseCompose();
$services = data_get($compose, 'services');
if ($services) {
$volumes = collect($services)->map(function ($service) {
return data_get($service, 'volumes');
})->flatten()->filter(function ($volume) {
return str($volume)->startsWith('/data/coolify');
})->unique()->values();
foreach ($volumes as $volume) {
$source = str($volume)->before(':');
$target = str($volume)->after(':')->beforeLast(':');
LocalFileVolume::updateOrCreate(
[
'mount_path' => $target,
'resource_id' => $this->application->id,
'resource_type' => get_class($this->application),
],
[
'fs_path' => $source,
'mount_path' => $target,
'resource_id' => $this->application->id,
'resource_type' => get_class($this->application),
]
);
}
}
$this->dispatch('success', 'Docker compose file loaded.');
$this->dispatch('compose_loaded');
$this->dispatch('refreshStorages');

View File

@@ -5,6 +5,8 @@ namespace App\Livewire\Project\New;
use App\Models\EnvironmentVariable;
use App\Models\Project;
use App\Models\Service;
use App\Models\StandaloneDocker;
use App\Models\SwarmDocker;
use Illuminate\Support\Str;
use Livewire\Component;
use Symfony\Component\Yaml\Yaml;
@@ -58,12 +60,26 @@ class DockerCompose extends Component
$project = Project::where('uuid', $this->parameters['project_uuid'])->first();
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
$destination_uuid = $this->query['destination'];
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
if (! $destination) {
$destination = SwarmDocker::where('uuid', $destination_uuid)->first();
}
if (! $destination) {
throw new \Exception('Destination not found. What?!');
}
$destination_class = $destination->getMorphClass();
$service = Service::create([
'name' => 'service'.Str::random(10),
'docker_compose_raw' => $this->dockerComposeRaw,
'environment_id' => $environment->id,
'server_id' => (int) $server_id,
'destination_id' => $destination->id,
'destination_type' => $destination_class,
]);
$variables = parseEnvFormatToArray($this->envFile);
foreach ($variables as $key => $variable) {
EnvironmentVariable::create([

View File

@@ -33,6 +33,7 @@ class FileStorage extends Component
'fileStorage.fs_path' => 'required',
'fileStorage.mount_path' => 'required',
'fileStorage.content' => 'nullable',
'fileStorage.is_based_on_git' => 'required|boolean',
];
public function mount()
@@ -45,6 +46,7 @@ class FileStorage extends Component
$this->workdir = null;
$this->fs_path = $this->fileStorage->fs_path;
}
$this->fileStorage->loadStorageOnServer();
}
public function convertToDirectory()
@@ -68,6 +70,9 @@ class FileStorage extends Component
$this->fileStorage->deleteStorageOnServer();
$this->fileStorage->is_directory = false;
$this->fileStorage->content = null;
if (data_get($this->resource, 'settings.is_preserve_repository_enabled')) {
$this->fileStorage->is_based_on_git = true;
}
$this->fileStorage->save();
$this->fileStorage->saveStorageOnServer();
} catch (\Throwable $e) {

View File

@@ -24,7 +24,8 @@ class Show extends Component
public string $type;
protected $listeners = [
'refresh' => 'refresh',
'refreshEnvs' => 'refresh',
'refresh',
'compose_loaded' => '$refresh',
];

View File

@@ -16,7 +16,7 @@ class Show extends Component
public array $parameters;
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey' => 'saveKey'];
protected $listeners = ['refreshEnvs' => '$refresh', 'saveKey'];
public function saveKey($data)
{

View File

@@ -6,7 +6,6 @@ use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use OpenApi\Attributes as OA;
use Symfony\Component\Yaml\Yaml;
use Visus\Cuid2\Cuid2;
#[OA\Schema(
@@ -97,8 +96,22 @@ class EnvironmentVariable extends Model
$resource = Application::find($this->application_id);
} elseif ($this->service_id) {
$resource = Service::find($this->service_id);
} elseif ($this->database_id) {
$resource = getResourceByUuid($this->parameters['database_uuid'], data_get(auth()->user()->currentTeam(), 'id'));
} elseif ($this->standalone_postgresql_id) {
$resource = StandalonePostgresql::find($this->standalone_postgresql_id);
} elseif ($this->standalone_redis_id) {
$resource = StandaloneRedis::find($this->standalone_redis_id);
} elseif ($this->standalone_mongodb_id) {
$resource = StandaloneMongodb::find($this->standalone_mongodb_id);
} elseif ($this->standalone_mysql_id) {
$resource = StandaloneMysql::find($this->standalone_mysql_id);
} elseif ($this->standalone_mariadb_id) {
$resource = StandaloneMariadb::find($this->standalone_mariadb_id);
} elseif ($this->standalone_keydb_id) {
$resource = StandaloneKeydb::find($this->standalone_keydb_id);
} elseif ($this->standalone_dragonfly_id) {
$resource = StandaloneDragonfly::find($this->standalone_dragonfly_id);
} elseif ($this->standalone_clickhouse_id) {
$resource = StandaloneClickhouse::find($this->standalone_clickhouse_id);
}
return $resource;
@@ -122,63 +135,6 @@ class EnvironmentVariable extends Model
);
}
protected function isFoundInCompose(): Attribute
{
return Attribute::make(
get: function () {
if (! $this->application_id) {
return true;
}
$found_in_compose = false;
$found_in_args = false;
$resource = $this->resource();
$compose = data_get($resource, 'docker_compose_raw');
if (! $compose) {
return true;
}
$yaml = Yaml::parse($compose);
$services = collect(data_get($yaml, 'services'));
if ($services->isEmpty()) {
return false;
}
foreach ($services as $service) {
$environments = collect(data_get($service, 'environment'));
$args = collect(data_get($service, 'build.args'));
if ($environments->isEmpty() && $args->isEmpty()) {
$found_in_compose = false;
break;
}
$found_in_compose = $environments->contains(function ($item) {
if (str($item)->contains('=')) {
$item = str($item)->before('=');
}
return strpos($item, $this->key) !== false;
});
if ($found_in_compose) {
break;
}
$found_in_args = $args->contains(function ($item) {
if (str($item)->contains('=')) {
$item = str($item)->before('=');
}
return strpos($item, $this->key) !== false;
});
if ($found_in_args) {
break;
}
}
return $found_in_compose || $found_in_args;
}
);
}
protected function isShared(): Attribute
{
return Attribute::make(
@@ -201,8 +157,10 @@ class EnvironmentVariable extends Model
$environment_variable = trim($environment_variable);
$sharedEnvsFound = str($environment_variable)->matchAll('/{{(.*?)}}/');
if ($sharedEnvsFound->isEmpty()) {
return $environment_variable;
}
foreach ($sharedEnvsFound as $sharedEnv) {
$type = str($sharedEnv)->match('/(.*?)\./');
if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {

View File

@@ -24,6 +24,32 @@ class LocalFileVolume extends BaseModel
return $this->morphTo('resource');
}
public function loadStorageOnServer()
{
$this->load(['service']);
$isService = data_get($this->resource, 'service');
if ($isService) {
$workdir = $this->resource->service->workdir();
$server = $this->resource->service->server;
} else {
$workdir = $this->resource->workdir();
$server = $this->resource->destination->server;
}
$commands = collect([]);
$path = data_get_str($this, 'fs_path');
if ($path->startsWith('.')) {
$path = $path->after('.');
$path = $workdir.$path;
}
$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
if ($isFile === 'OK') {
$content = instant_remote_process(["cat $path"], $server, false);
$this->content = $content;
$this->is_directory = false;
$this->save();
}
}
public function deleteStorageOnServer()
{
$isService = data_get($this->resource, 'service');
@@ -35,17 +61,20 @@ class LocalFileVolume extends BaseModel
$server = $this->resource->destination->server;
}
$commands = collect([]);
$fs_path = data_get($this, 'fs_path');
$isFile = instant_remote_process(["test -f $fs_path && echo OK || echo NOK"], $server);
$isDir = instant_remote_process(["test -d $fs_path && echo OK || echo NOK"], $server);
if ($fs_path && $fs_path != '/' && $fs_path != '.' && $fs_path != '..') {
ray($isFile, $isDir);
$path = data_get_str($this, 'fs_path');
if ($path->startsWith('.')) {
$path = $path->after('.');
$path = $workdir.$path;
}
$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
$isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server);
if ($path && $path != '/' && $path != '.' && $path != '..') {
if ($isFile === 'OK') {
$commands->push("rm -rf $fs_path > /dev/null 2>&1 || true");
$commands->push("rm -rf $path > /dev/null 2>&1 || true");
} elseif ($isDir === 'OK') {
$commands->push("rm -rf $fs_path > /dev/null 2>&1 || true");
$commands->push("rmdir $fs_path > /dev/null 2>&1 || true");
$commands->push("rm -rf $path > /dev/null 2>&1 || true");
$commands->push("rmdir $path > /dev/null 2>&1 || true");
}
}
if ($commands->count() > 0) {
@@ -55,6 +84,7 @@ class LocalFileVolume extends BaseModel
public function saveStorageOnServer()
{
$this->load(['service']);
$isService = data_get($this->resource, 'service');
if ($isService) {
$workdir = $this->resource->service->workdir();
@@ -74,30 +104,36 @@ class LocalFileVolume extends BaseModel
$commands->push("mkdir -p $parent_dir > /dev/null 2>&1 || true");
}
}
$fileVolume = $this;
$path = str(data_get($fileVolume, 'fs_path'));
$content = data_get($fileVolume, 'content');
$path = data_get_str($this, 'fs_path');
$content = data_get($this, 'content');
if ($path->startsWith('.')) {
$path = $path->after('.');
$path = $workdir.$path;
}
$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
$isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server);
if ($isFile == 'OK' && $fileVolume->is_directory) {
if ($isFile == 'OK' && $this->is_directory) {
$content = instant_remote_process(["cat $path"], $server, false);
$fileVolume->is_directory = false;
$fileVolume->content = $content;
$fileVolume->save();
$this->is_directory = false;
$this->content = $content;
$this->save();
FileStorageChanged::dispatch(data_get($server, 'team_id'));
throw new \Exception('The following file is a file on the server, but you are trying to mark it as a directory. Please delete the file on the server or mark it as directory.');
} elseif ($isDir == 'OK' && ! $fileVolume->is_directory) {
$fileVolume->is_directory = true;
$fileVolume->save();
throw new \Exception('The following file is a directory on the server, but you are trying to mark it as a file. <br><br>Please delete the directory on the server or mark it as directory.');
} elseif ($isDir == 'OK' && ! $this->is_directory) {
if ($path == '/' || $path == '.' || $path == '..' || $path == '' || str($path)->isEmpty() || is_null($path)) {
$this->is_directory = true;
$this->save();
throw new \Exception('The following file is a directory on the server, but you are trying to mark it as a file. <br><br>Please delete the directory on the server or mark it as directory.');
}
instant_remote_process([
"rm -fr $path",
"touch $path",
], $server, false);
FileStorageChanged::dispatch(data_get($server, 'team_id'));
}
if ($isDir == 'NOK' && ! $fileVolume->is_directory) {
$chmod = data_get($fileVolume, 'chmod');
$chown = data_get($fileVolume, 'chown');
if ($isDir == 'NOK' && ! $this->is_directory) {
$chmod = data_get($this, 'chmod');
$chown = data_get($this, 'chown');
if ($content) {
$content = base64_encode($content);
$commands->push("echo '$content' | base64 -d | tee $path > /dev/null");
@@ -111,7 +147,7 @@ class LocalFileVolume extends BaseModel
if ($chmod) {
$commands->push("chmod $chmod $path");
}
} elseif ($isDir == 'NOK' && $fileVolume->is_directory) {
} elseif ($isDir == 'NOK' && $this->is_directory) {
$commands->push("mkdir -p $path > /dev/null 2>&1 || true");
}

View File

@@ -23,6 +23,7 @@ use Symfony\Component\Yaml\Yaml;
'description' => ['type' => 'string', 'description' => 'The description of the service.'],
'docker_compose_raw' => ['type' => 'string', 'description' => 'The raw docker-compose.yml file of the service.'],
'docker_compose' => ['type' => 'string', 'description' => 'The docker-compose.yml file that is parsed and modified by Coolify.'],
'destination_type' => ['type' => 'integer', 'description' => 'The unique identifier of the destination where the service is running.'],
'destination_id' => ['type' => 'integer', 'description' => 'The unique identifier of the destination where the service is running.'],
'connect_to_docker_network' => ['type' => 'boolean', 'description' => 'The flag to connect the service to the predefined Docker network.'],
'is_container_label_escape_enabled' => ['type' => 'boolean', 'description' => 'The flag to enable the container label escape.'],
@@ -205,6 +206,41 @@ class Service extends BaseModel
foreach ($applications as $application) {
$image = str($application->image)->before(':')->value();
switch ($image) {
case str($image)?->contains('rabbitmq'):
$data = collect([]);
$host_port = $this->environment_variables()->where('key', 'PORT')->first();
$username = $this->environment_variables()->where('key', 'SERVICE_USER_RABBITMQ')->first();
$password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_RABBITMQ')->first();
if ($host_port) {
$data = $data->merge([
'Host Port Binding' => [
'key' => data_get($host_port, 'key'),
'value' => data_get($host_port, 'value'),
'rules' => 'required',
],
]);
}
if ($username) {
$data = $data->merge([
'Username' => [
'key' => data_get($username, 'key'),
'value' => data_get($username, 'value'),
'rules' => 'required',
],
]);
}
if ($password) {
$data = $data->merge([
'Password' => [
'key' => data_get($password, 'key'),
'value' => data_get($password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$fields->put('RabbitMQ', $data->toArray());
break;
case str($image)?->contains('tolgee'):
$data = collect([]);
$admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_TOLGEE')->first();
@@ -504,6 +540,9 @@ class Service extends BaseModel
default:
$data = collect([]);
$admin_user = $this->environment_variables()->where('key', 'SERVICE_USER_ADMIN')->first();
// Chaskiq
$admin_email = $this->environment_variables()->where('key', 'ADMIN_EMAIL')->first();
$admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_ADMIN')->first();
if ($admin_user) {
$data = $data->merge([
@@ -525,6 +564,15 @@ class Service extends BaseModel
],
]);
}
if ($admin_email) {
$data = $data->merge([
'Email' => [
'key' => 'ADMIN_EMAIL',
'value' => data_get($admin_email, 'value'),
'rules' => 'required|email',
],
]);
}
$fields->put('Admin', $data->toArray());
break;
case str($image)?->contains('vaultwarden'):