diff --git a/app/Actions/Proxy/CheckConfiguration.php b/app/Actions/Proxy/CheckConfiguration.php index f4fe650c50..bdeafd0611 100644 --- a/app/Actions/Proxy/CheckConfiguration.php +++ b/app/Actions/Proxy/CheckConfiguration.php @@ -22,7 +22,7 @@ public function handle(Server $server, bool $reset = false) ]; $proxy_configuration = instant_remote_process($payload, $server, false); if ($reset || ! $proxy_configuration || is_null($proxy_configuration)) { - $proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value; + $proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value(); } if (! $proxy_configuration || is_null($proxy_configuration)) { throw new \Exception('Could not generate proxy configuration'); diff --git a/app/Actions/Proxy/StartProxy.php b/app/Actions/Proxy/StartProxy.php index 4ef9618d0c..f20c10123e 100644 --- a/app/Actions/Proxy/StartProxy.php +++ b/app/Actions/Proxy/StartProxy.php @@ -26,7 +26,7 @@ public function handle(Server $server, bool $async = true, bool $force = false): } SaveConfiguration::run($server, $configuration); $docker_compose_yml_base64 = base64_encode($configuration); - $server->proxy->last_applied_settings = str($docker_compose_yml_base64)->pipe('md5')->value; + $server->proxy->last_applied_settings = str($docker_compose_yml_base64)->pipe('md5')->value(); $server->save(); if ($server->isSwarm()) { $commands = $commands->merge([ diff --git a/app/Console/Commands/ServicesGenerate.php b/app/Console/Commands/ServicesGenerate.php index de64afefa0..da04cefcea 100644 --- a/app/Console/Commands/ServicesGenerate.php +++ b/app/Console/Commands/ServicesGenerate.php @@ -78,7 +78,7 @@ private function process_file($file) if ($logo->count() > 0) { $logo = str($logo[0])->after('# logo:')->trim()->value(); } else { - $logo = 'svgs/unknown.svg'; + $logo = 'svgs/coolify.png'; } $minversion = collect(preg_grep('/^# minversion:/', explode("\n", $content)))->values(); if ($minversion->count() > 0) { diff --git a/app/Livewire/Help.php b/app/Livewire/Help.php index 68691c1cdc..934e816614 100644 --- a/app/Livewire/Help.php +++ b/app/Livewire/Help.php @@ -61,6 +61,7 @@ public function submit() send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io'); } $this->dispatch('success', 'Feedback sent.', 'We will get in touch with you as soon as possible.'); + $this->reset('description', 'subject'); } catch (\Throwable $e) { return handleError($e, $this); } diff --git a/app/Livewire/Project/New/Select.php b/app/Livewire/Project/New/Select.php index 3c5f3901b9..e2ae1161ea 100644 --- a/app/Livewire/Project/New/Select.php +++ b/app/Livewire/Project/New/Select.php @@ -49,11 +49,8 @@ class Select extends Component public ?string $existingPostgresqlUrl = null; - public ?string $search = null; - protected $queryString = [ 'server_id', - 'search', ]; public function mount() @@ -90,40 +87,119 @@ public function updatedSelectedEnvironment() // } // } - public function updatedSearch() + public function loadServices() { - $this->loadServices(); - } - - public function loadServices(bool $force = false) - { - try { - $this->loadingServices = true; - if (count($this->allServices) > 0 && ! $force) { - if (! $this->search) { - $this->services = $this->allServices; - - return; - } - $this->services = $this->allServices->filter(function ($service, $key) { - $tags = collect(data_get($service, 'tags', [])); - - return str_contains(strtolower($key), strtolower($this->search)) || $tags->contains(function ($tag) { - return str_contains(strtolower($tag), strtolower($this->search)); - }); - }); - } else { - $this->search = null; - $this->allServices = get_service_templates($force); - $this->services = $this->allServices->filter(function ($service, $key) { - return str_contains(strtolower($key), strtolower($this->search)); - }); - } - } catch (\Throwable $e) { - return handleError($e, $this); - } finally { - $this->loadingServices = false; - } + $services = get_service_templates(true); + $services = collect($services)->map(function ($service, $key) { + return [ + 'name' => str($key)->headline(), + 'logo' => asset(data_get($service, 'logo', 'svgs/coolify.png')), + ] + (array) $service; + })->all(); + $gitBasedApplications = [ + [ + 'id' => 'public', + 'name' => 'Public Repository', + 'description' => 'You can deploy any kind of public repositories from the supported git providers.', + 'logo' => asset('svgs/git.svg'), + ], + [ + 'id' => 'private-gh-app', + 'name' => 'Private Repository (with GitHub App)', + 'description' => 'You can deploy public & private repositories through your GitHub Apps.', + 'logo' => asset('svgs/github.svg'), + ], + [ + 'id' => 'private-deploy-key', + 'name' => 'Private Repository (with Deploy Key)', + 'description' => 'You can deploy private repositories with a deploy key.', + 'logo' => asset('svgs/git.svg'), + ], + ]; + $dockerBasedApplications = [ + [ + 'id' => 'dockerfile', + 'name' => 'Dockerfile', + 'description' => 'You can deploy a simple Dockerfile, without Git.', + 'logo' => asset('svgs/docker.svg'), + ], + [ + 'id' => 'docker-compose-empty', + 'name' => 'Docker Compose Empty', + 'description' => 'You can deploy complex application easily with Docker Compose, without Git.', + 'logo' => asset('svgs/docker.svg'), + ], + [ + 'id' => 'docker-image', + 'name' => 'Docker Image', + 'description' => 'You can deploy an existing Docker Image from any Registry, without Git.', + 'logo' => asset('svgs/docker.svg'), + ], + ]; + $databases = [ + [ + 'id' => 'postgresql', + 'name' => 'PostgreSQL', + 'description' => 'PostgreSQL is an object-relational database known for its robustness, advanced features, and strong standards compliance.', + 'logo' => ' +', + ], + [ + 'id' => 'mysql', + 'name' => 'MySQL', + 'description' => 'MySQL is an open-source relational database known for its simplicity and performance.', + 'logo' => ' + + + +', + + ], + [ + 'id' => 'mariadb', + 'name' => 'MariaDB', + 'description' => 'MariaDB is an open-source relational database known for its simplicity and performance.', + 'logo' => '', + ], + [ + 'id' => 'redis', + 'name' => 'Redis', + 'description' => 'Redis is an open-source in-memory data structure store known for its simplicity and performance.', + 'logo' => '', + ], + [ + 'id' => 'keydb', + 'name' => 'KeyDB', + 'description' => 'KeyDB is an open-source in-memory data structure store known for its simplicity and performance.', + 'logo' => '
', + ], + [ + 'id' => 'dragonfly', + 'name' => 'Dragonfly', + 'description' => 'Dragonfly is an open-source in-memory data structure store known for its simplicity and performance.', + 'logo' => '
', + ], + [ + 'id' => 'mongodb', + 'name' => 'MongoDB', + 'description' => 'MongoDB is an open-source document-oriented database known for its simplicity and performance.', + 'logo' => '', + ], + [ + 'id' => 'clickhouse', + 'name' => 'ClickHouse', + 'description' => 'ClickHouse is an open-source column-oriented database known for its simplicity and performance.', + 'logo' => '
', + ], + + ]; + + return [ + 'services' => $services, + 'gitBasedApplications' => $gitBasedApplications, + 'dockerBasedApplications' => $dockerBasedApplications, + 'databases' => $databases, + ]; } public function instantSave() @@ -141,6 +217,7 @@ public function instantSave() public function setType(string $type) { + $type = str($type)->lower()->slug()->value(); if ($this->loading) { return; } diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php index a859c90b02..0dbf0f957f 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php @@ -48,14 +48,6 @@ public function mount() public function submit() { $this->validate(); - // if (str($this->value)->startsWith('{{') && str($this->value)->endsWith('}}')) { - // $type = str($this->value)->after('{{')->before('.')->value; - // if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) { - // $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.'); - - // return; - // } - // } $this->dispatch('saveKey', [ 'key' => $this->key, 'value' => $this->value, diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php index 055788b576..5a711259b2 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php @@ -53,29 +53,15 @@ public function instantSave() public function sortEnvironmentVariables() { - if ($this->resource->type() === 'application') { - $this->resource->load(['environment_variables', 'environment_variables_preview']); - } else { - $this->resource->load(['environment_variables']); - } - - $sortBy = data_get($this->resource, 'settings.is_env_sorting_enabled') ? 'key' : 'order'; - - $sortFunction = function ($variables) use ($sortBy) { - if (! $variables) { - return $variables; + if (! data_get($this->resource, 'settings.is_env_sorting_enabled')) { + if ($this->resource->environment_variables) { + $this->resource->environment_variables = $this->resource->environment_variables->sortBy('order')->values(); } - if ($sortBy === 'key') { - return $variables->sortBy(function ($item) { - return strtolower($item->key); - }, SORT_NATURAL | SORT_FLAG_CASE)->values(); - } else { - return $variables->sortBy('order')->values(); - } - }; - $this->resource->environment_variables = $sortFunction($this->resource->environment_variables); - $this->resource->environment_variables_preview = $sortFunction($this->resource->environment_variables_preview); + if ($this->resource->environment_variables_preview) { + $this->resource->environment_variables_preview = $this->resource->environment_variables_preview->sortBy('order')->values(); + } + } $this->getDevView(); } @@ -121,6 +107,8 @@ public function submit($data = null) $this->sortEnvironmentVariables(); } catch (\Throwable $e) { return handleError($e, $this); + } finally { + $this->refreshEnvs(); } } diff --git a/app/Models/Project.php b/app/Models/Project.php index 18481751c8..5a9dd964a5 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -24,9 +24,11 @@ class Project extends BaseModel { protected $guarded = []; + protected $appends = ['default_environment']; + public static function ownedByCurrentTeam() { - return Project::whereTeamId(currentTeam()->id)->orderBy('name'); + return Project::whereTeamId(currentTeam()->id)->orderByRaw('LOWER(name)'); } protected static function booted() @@ -131,7 +133,7 @@ public function databases() return $this->postgresqls()->get()->merge($this->redis()->get())->merge($this->mongodbs()->get())->merge($this->mysqls()->get())->merge($this->mariadbs()->get())->merge($this->keydbs()->get())->merge($this->dragonflies()->get())->merge($this->clickhouses()->get()); } - public function default_environment() + public function getDefaultEnvironmentAttribute() { $default = $this->environments()->where('name', 'production')->first(); if ($default) { diff --git a/app/Models/Server.php b/app/Models/Server.php index 3329c97613..8864deef1f 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -448,11 +448,19 @@ public function proxyPath() // Should move everything except /caddy and /nginx to /traefik // The code needs to be modified as well, so maybe it does not worth it if ($proxyType === ProxyTypes::TRAEFIK->value) { - $proxy_path = $proxy_path; + // Do nothing } elseif ($proxyType === ProxyTypes::CADDY->value) { - $proxy_path = $proxy_path.'/caddy'; + if (isDev()) { + $proxy_path = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/caddy'; + } else { + $proxy_path = $proxy_path.'/caddy'; + } } elseif ($proxyType === ProxyTypes::NGINX->value) { - $proxy_path = $proxy_path.'/nginx'; + if (isDev()) { + $proxy_path = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/nginx'; + } else { + $proxy_path = $proxy_path.'/nginx'; + } } return $proxy_path; diff --git a/app/Models/Service.php b/app/Models/Service.php index 6329345240..bcdb74f8c9 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -283,9 +283,147 @@ public function extraFields() $fields = collect([]); $applications = $this->applications()->get(); foreach ($applications as $application) { - $image = str($application->image)->before(':')->value(); + $image = str($application->image)->before(':'); + if ($image->isEmpty()) { + continue; + } switch ($image) { - case str($image)?->contains('rabbitmq'): + case $image->contains('label-studio'): + $data = collect([]); + $username = $this->environment_variables()->where('key', 'LABEL_STUDIO_USERNAME')->first(); + $password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_LABELSTUDIO')->first(); + if ($username) { + $data = $data->merge([ + 'Username' => [ + 'key' => 'LABEL_STUDIO_USERNAME', + 'value' => data_get($username, 'value'), + 'rules' => 'required', + ], + ]); + } + if ($password) { + $data = $data->merge([ + 'Password' => [ + 'key' => 'LABEL_STUDIO_PASSWORD', + 'value' => data_get($password, 'value'), + 'rules' => 'required', + 'isPassword' => true, + ], + ]); + } + $fields->put('Label Studio', $data->toArray()); + break; + case $image->contains('litellm'): + $data = collect([]); + $username = $this->environment_variables()->where('key', 'SERVICE_USER_UI')->first(); + $password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_UI')->first(); + 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('Litellm', $data->toArray()); + break; + case $image->contains('langfuse'): + $data = collect([]); + $email = $this->environment_variables()->where('key', 'LANGFUSE_INIT_USER_EMAIL')->first(); + if ($email) { + $data = $data->merge([ + 'Admin Email' => [ + 'key' => 'LANGFUSE_INIT_USER_EMAIL', + 'value' => data_get($email, 'value'), + 'rules' => 'required|email', + ], + ]); + } + $password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_LANGFUSE')->first(); + ray('password', $password); + if ($password) { + $data = $data->merge([ + 'Admin Password' => [ + 'key' => 'LANGFUSE_INIT_USER_PASSWORD', + 'value' => data_get($password, 'value'), + 'rules' => 'required', + 'isPassword' => true, + ], + ]); + } + $fields->put('Langfuse', $data->toArray()); + break; + case $image->contains('invoiceninja'): + $data = collect([]); + $email = $this->environment_variables()->where('key', 'IN_USER_EMAIL')->first(); + $data = $data->merge([ + 'Email' => [ + 'key' => 'IN_USER_EMAIL', + 'value' => data_get($email, 'value'), + 'rules' => 'required|email', + ], + ]); + $password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_INVOICENINJAUSER')->first(); + $data = $data->merge([ + 'Password' => [ + 'key' => 'IN_PASSWORD', + 'value' => data_get($password, 'value'), + 'rules' => 'required', + 'isPassword' => true, + ], + ]); + $fields->put('Invoice Ninja', $data->toArray()); + break; + case $image->contains('argilla'): + $data = collect([]); + $api_key = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_APIKEY')->first(); + $data = $data->merge([ + 'API Key' => [ + 'key' => data_get($api_key, 'key'), + 'value' => data_get($api_key, 'value'), + 'isPassword' => true, + 'rules' => 'required', + ], + ]); + $data = $data->merge([ + 'API Key' => [ + 'key' => data_get($api_key, 'key'), + 'value' => data_get($api_key, 'value'), + 'isPassword' => true, + 'rules' => 'required', + ], + ]); + $username = $this->environment_variables()->where('key', 'ARGILLA_USERNAME')->first(); + $data = $data->merge([ + 'Username' => [ + 'key' => data_get($username, 'key'), + 'value' => data_get($username, 'value'), + 'rules' => 'required', + ], + ]); + $password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_ARGILLA')->first(); + $data = $data->merge([ + 'Password' => [ + 'key' => data_get($password, 'key'), + 'value' => data_get($password, 'value'), + 'rules' => 'required', + 'isPassword' => true, + ], + ]); + $fields->put('Argilla', $data->toArray()); + break; + case $image->contains('rabbitmq'): $data = collect([]); $host_port = $this->environment_variables()->where('key', 'PORT')->first(); $username = $this->environment_variables()->where('key', 'SERVICE_USER_RABBITMQ')->first(); @@ -320,7 +458,7 @@ public function extraFields() } $fields->put('RabbitMQ', $data->toArray()); break; - case str($image)?->contains('tolgee'): + case $image->contains('tolgee'): $data = collect([]); $admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_TOLGEE')->first(); $data = $data->merge([ @@ -343,7 +481,7 @@ public function extraFields() } $fields->put('Tolgee', $data->toArray()); break; - case str($image)?->contains('logto'): + case $image->contains('logto'): $data = collect([]); $logto_endpoint = $this->environment_variables()->where('key', 'LOGTO_ENDPOINT')->first(); $logto_admin_endpoint = $this->environment_variables()->where('key', 'LOGTO_ADMIN_ENDPOINT')->first(); @@ -367,7 +505,7 @@ public function extraFields() } $fields->put('Logto', $data->toArray()); break; - case str($image)?->contains('unleash-server'): + case $image->contains('unleash-server'): $data = collect([]); $admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_UNLEASH')->first(); $data = $data->merge([ @@ -390,7 +528,7 @@ public function extraFields() } $fields->put('Unleash', $data->toArray()); break; - case str($image)?->contains('grafana'): + case $image->contains('grafana'): $data = collect([]); $admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_GRAFANA')->first(); $data = $data->merge([ @@ -413,7 +551,7 @@ public function extraFields() } $fields->put('Grafana', $data->toArray()); break; - case str($image)?->contains('directus'): + case $image->contains('directus'): $data = collect([]); $admin_email = $this->environment_variables()->where('key', 'ADMIN_EMAIL')->first(); $admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_ADMIN')->first(); @@ -439,7 +577,7 @@ public function extraFields() } $fields->put('Directus', $data->toArray()); break; - case str($image)?->contains('kong'): + case $image->contains('kong'): $data = collect([]); $dashboard_user = $this->environment_variables()->where('key', 'SERVICE_USER_ADMIN')->first(); $dashboard_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_ADMIN')->first(); @@ -463,7 +601,7 @@ public function extraFields() ]); } $fields->put('Supabase', $data->toArray()); - case str($image)?->contains('minio'): + case $image->contains('minio'): $data = collect([]); $console_url = $this->environment_variables()->where('key', 'MINIO_BROWSER_REDIRECT_URL')->first(); $s3_api_url = $this->environment_variables()->where('key', 'MINIO_SERVER_URL')->first(); @@ -516,7 +654,7 @@ public function extraFields() $fields->put('MinIO', $data->toArray()); break; - case str($image)?->contains('weblate'): + case $image->contains('weblate'): $data = collect([]); $admin_email = $this->environment_variables()->where('key', 'WEBLATE_ADMIN_EMAIL')->first(); $admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_WEBLATE')->first(); @@ -542,7 +680,7 @@ public function extraFields() } $fields->put('Weblate', $data->toArray()); break; - case str($image)?->contains('meilisearch'): + case $image->contains('meilisearch'): $data = collect([]); $SERVICE_PASSWORD_MEILISEARCH = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_MEILISEARCH')->first(); if ($SERVICE_PASSWORD_MEILISEARCH) { @@ -556,7 +694,7 @@ public function extraFields() } $fields->put('Meilisearch', $data->toArray()); break; - case str($image)?->contains('ghost'): + case $image->contains('ghost'): $data = collect([]); $MAIL_OPTIONS_AUTH_PASS = $this->environment_variables()->where('key', 'MAIL_OPTIONS_AUTH_PASS')->first(); $MAIL_OPTIONS_AUTH_USER = $this->environment_variables()->where('key', 'MAIL_OPTIONS_AUTH_USER')->first(); @@ -616,45 +754,8 @@ public function extraFields() $fields->put('Ghost', $data->toArray()); break; - 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([ - 'User' => [ - 'key' => 'SERVICE_USER_ADMIN', - 'value' => data_get($admin_user, 'value', 'admin'), - 'readonly' => true, - 'rules' => 'required', - ], - ]); - } - if ($admin_password) { - $data = $data->merge([ - 'Password' => [ - 'key' => 'SERVICE_PASSWORD_ADMIN', - 'value' => data_get($admin_password, 'value'), - 'rules' => 'required', - 'isPassword' => true, - ], - ]); - } - 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'): + case $image->contains('vaultwarden'): $data = collect([]); $DATABASE_URL = $this->environment_variables()->where('key', 'DATABASE_URL')->first(); @@ -720,7 +821,7 @@ public function extraFields() $fields->put('Vaultwarden', $data); break; - case str($image)->contains('gitlab/gitlab'): + case $image->contains('gitlab/gitlab'): $password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_GITLAB')->first(); $data = collect([]); if ($password) { @@ -744,7 +845,7 @@ public function extraFields() $fields->put('GitLab', $data->toArray()); break; - case str($image)->contains('code-server'): + case $image->contains('code-server'): $data = collect([]); $password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_64_PASSWORDCODESERVER')->first(); if ($password) { @@ -770,7 +871,7 @@ public function extraFields() } $fields->put('Code Server', $data->toArray()); break; - case str($image)->contains('elestio/strapi'): + case $image->contains('elestio/strapi'): $data = collect([]); $license = $this->environment_variables()->where('key', 'STRAPI_LICENSE')->first(); if ($license) { @@ -793,15 +894,55 @@ public function extraFields() $fields->put('Strapi', $data->toArray()); break; + 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([ + 'User' => [ + 'key' => 'SERVICE_USER_ADMIN', + 'value' => data_get($admin_user, 'value', 'admin'), + 'readonly' => true, + 'rules' => 'required', + ], + ]); + } + if ($admin_password) { + $data = $data->merge([ + 'Password' => [ + 'key' => 'SERVICE_PASSWORD_ADMIN', + 'value' => data_get($admin_password, 'value'), + 'rules' => 'required', + 'isPassword' => true, + ], + ]); + } + 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; } } $databases = $this->databases()->get(); foreach ($databases as $database) { - $image = str($database->image)->before(':')->value(); + $image = str($database->image)->before(':'); + if ($image->isEmpty()) { + continue; + } switch ($image) { - case str($image)->contains('postgres'): + case $image->contains('postgres'): $userVariables = ['SERVICE_USER_POSTGRES', 'SERVICE_USER_POSTGRESQL']; $passwordVariables = ['SERVICE_PASSWORD_POSTGRES', 'SERVICE_PASSWORD_POSTGRESQL']; $dbNameVariables = ['POSTGRESQL_DATABASE', 'POSTGRES_DB']; @@ -839,7 +980,7 @@ public function extraFields() } $fields->put('PostgreSQL', $data->toArray()); break; - case str($image)->contains('mysql'): + case $image->contains('mysql'): $userVariables = ['SERVICE_USER_MYSQL', 'SERVICE_USER_WORDPRESS', 'MYSQL_USER']; $passwordVariables = ['SERVICE_PASSWORD_MYSQL', 'SERVICE_PASSWORD_WORDPRESS', 'MYSQL_PASSWORD']; $rootPasswordVariables = ['SERVICE_PASSWORD_MYSQLROOT', 'SERVICE_PASSWORD_ROOT']; @@ -889,7 +1030,7 @@ public function extraFields() } $fields->put('MySQL', $data->toArray()); break; - case str($image)->contains('mariadb'): + case $image->contains('mariadb'): $userVariables = ['SERVICE_USER_MARIADB', 'SERVICE_USER_WORDPRESS', '_APP_DB_USER', 'SERVICE_USER_MYSQL', 'MYSQL_USER']; $passwordVariables = ['SERVICE_PASSWORD_MARIADB', 'SERVICE_PASSWORD_WORDPRESS', '_APP_DB_PASS', 'MYSQL_PASSWORD']; $rootPasswordVariables = ['SERVICE_PASSWORD_MARIADBROOT', 'SERVICE_PASSWORD_ROOT', '_APP_DB_ROOT_PASS', 'MYSQL_ROOT_PASSWORD']; @@ -1076,12 +1217,12 @@ public function scheduled_tasks(): HasMany public function environment_variables(): HasMany { - return $this->hasMany(EnvironmentVariable::class)->orderByRaw("key LIKE 'SERVICE%' DESC, value ASC"); + return $this->hasMany(EnvironmentVariable::class)->orderByRaw("LOWER(key) LIKE LOWER('SERVICE%') DESC, LOWER(key) ASC"); } public function environment_variables_preview(): HasMany { - return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->orderBy('key', 'asc'); + return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->orderByRaw("LOWER(key) LIKE LOWER('SERVICE%') DESC, LOWER(key) ASC"); } public function workdir() diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 4d78021c41..65fe3322d6 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -522,6 +522,11 @@ function sslip(Server $server) function get_service_templates(bool $force = false): Collection { + if (isDev()) { + $services = File::get(base_path('templates/service-templates.json')); + + return collect(json_decode($services))->sortKeys(); + } if ($force) { try { $response = Http::retry(3, 1000)->get(config('constants.services.official')); diff --git a/config/sentry.php b/config/sentry.php index e9ccf0a321..14f8ff5f1a 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.355', + 'release' => '4.0.0-beta.356', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index 03fbf57efe..b33115d86f 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ = 0.6" } diff --git a/package.json b/package.json index 3633a7ed12..29f8f1a377 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0", "alpinejs": "3.14.0", - "cookie": "^0.6.0", + "cookie": "^0.7.0", "dotenv": "^16.4.5", "ioredis": "5.4.1", "node-pty": "^1.0.0", diff --git a/public/svgs/anythingllm.svg b/public/svgs/anythingllm.svg new file mode 100644 index 0000000000..1c25f87115 --- /dev/null +++ b/public/svgs/anythingllm.svg @@ -0,0 +1,166 @@ + + + + + + + diff --git a/public/svgs/argilla.png b/public/svgs/argilla.png new file mode 100644 index 0000000000..3ead327856 Binary files /dev/null and b/public/svgs/argilla.png differ diff --git a/public/svgs/clickhouse.svg b/public/svgs/clickhouse.svg new file mode 100644 index 0000000000..d536536de9 --- /dev/null +++ b/public/svgs/clickhouse.svg @@ -0,0 +1 @@ + diff --git a/public/svgs/coolify.png b/public/svgs/coolify.png new file mode 100644 index 0000000000..fa01fec05e Binary files /dev/null and b/public/svgs/coolify.png differ diff --git a/public/svgs/getoutline.jpeg b/public/svgs/getoutline.jpeg new file mode 100644 index 0000000000..422e402f7e Binary files /dev/null and b/public/svgs/getoutline.jpeg differ diff --git a/public/svgs/infisical.png b/public/svgs/infisical.png new file mode 100644 index 0000000000..48eddae78d Binary files /dev/null and b/public/svgs/infisical.png differ diff --git a/public/svgs/labelstudio.png b/public/svgs/labelstudio.png new file mode 100644 index 0000000000..afa5160b9a Binary files /dev/null and b/public/svgs/labelstudio.png differ diff --git a/public/svgs/langfuse.png b/public/svgs/langfuse.png new file mode 100644 index 0000000000..8dec0fe4ae Binary files /dev/null and b/public/svgs/langfuse.png differ diff --git a/public/svgs/litellm.svg b/public/svgs/litellm.svg new file mode 100644 index 0000000000..01830c3f66 --- /dev/null +++ b/public/svgs/litellm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/svgs/nitropage.svg b/public/svgs/nitropage.svg new file mode 100644 index 0000000000..67b2df17fe --- /dev/null +++ b/public/svgs/nitropage.svg @@ -0,0 +1,8 @@ + + + NP + + + + + diff --git a/public/svgs/ollama.svg b/public/svgs/ollama.svg new file mode 100644 index 0000000000..3df9a9fbab --- /dev/null +++ b/public/svgs/ollama.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/svgs/postgresql.svg b/public/svgs/postgresql.svg new file mode 100644 index 0000000000..1ff2238563 --- /dev/null +++ b/public/svgs/postgresql.svg @@ -0,0 +1 @@ + diff --git a/public/svgs/prefect.png b/public/svgs/prefect.png new file mode 100644 index 0000000000..2f87ec0d74 Binary files /dev/null and b/public/svgs/prefect.png differ diff --git a/public/svgs/qdrant.png b/public/svgs/qdrant.png new file mode 100644 index 0000000000..ecb2a56d55 Binary files /dev/null and b/public/svgs/qdrant.png differ diff --git a/public/svgs/searxng.svg b/public/svgs/searxng.svg new file mode 100644 index 0000000000..b94fe3728a --- /dev/null +++ b/public/svgs/searxng.svg @@ -0,0 +1,56 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/public/svgs/tolgee.svg b/public/svgs/tolgee.svg new file mode 100644 index 0000000000..0f216e0c64 --- /dev/null +++ b/public/svgs/tolgee.svg @@ -0,0 +1,5 @@ + + + + diff --git a/public/svgs/unstructured.png b/public/svgs/unstructured.png new file mode 100644 index 0000000000..a6ec855b65 Binary files /dev/null and b/public/svgs/unstructured.png differ diff --git a/public/svgs/weaviate.png b/public/svgs/weaviate.png new file mode 100644 index 0000000000..1342942532 Binary files /dev/null and b/public/svgs/weaviate.png differ diff --git a/resources/views/components/modal-confirmation.blade.php b/resources/views/components/modal-confirmation.blade.php index ad2ebe1965..fde75ab24d 100644 --- a/resources/views/components/modal-confirmation.blade.php +++ b/resources/views/components/modal-confirmation.blade.php @@ -272,7 +272,7 @@ class="p-2 mt-1 w-full text-black rounded input"> class="block text-sm font-medium text-gray-700 dark:text-gray-300"> Your Password -
+
diff --git a/resources/views/components/resource-view.blade.php b/resources/views/components/resource-view.blade.php index a01e5ab53d..d2c8947eef 100644 --- a/resources/views/components/resource-view.blade.php +++ b/resources/views/components/resource-view.blade.php @@ -2,7 +2,7 @@ 'transition-all duration-150 box-without-bg dark:bg-coolgray-100 bg-white group', 'hover:border-l-coollabs cursor-pointer' => !$upgrade, 'hover:border-l-red-500 cursor-not-allowed' => $upgrade, -]) @if (!$upgrade) wire:click={{ $wire }} @endif> +])>
{{ $logo }}
@@ -20,11 +20,6 @@
@isset($documentation)
-
- - Docs - -
+ {{ $documentation }} @endisset
diff --git a/resources/views/components/server/navbar.blade.php b/resources/views/components/server/navbar.blade.php index a73f6865ef..18f923289c 100644 --- a/resources/views/components/server/navbar.blade.php +++ b/resources/views/components/server/navbar.blade.php @@ -6,7 +6,7 @@ @endif -
{{ data_get($server, 'name') }}.
+
{{ data_get($server, 'name') }}