coolify/app/Models/Service.php

1621 lines
73 KiB
PHP
Raw Normal View History

2023-09-20 13:42:41 +00:00
<?php
namespace App\Models;
use App\Enums\ProcessStatus;
use App\Services\ContainerStatusAggregator;
use App\Traits\ClearsGlobalSearchCache;
use App\Traits\HasSafeStringAttribute;
use Illuminate\Database\Eloquent\Casts\Attribute;
2023-09-20 13:42:41 +00:00
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
2023-12-08 17:32:08 +00:00
use Illuminate\Database\Eloquent\SoftDeletes;
2023-09-20 13:42:41 +00:00
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
2024-07-09 11:30:13 +00:00
use OpenApi\Attributes as OA;
use Spatie\Activitylog\Models\Activity;
use Spatie\Url\Url;
use Visus\Cuid2\Cuid2;
2023-09-20 13:42:41 +00:00
2024-07-09 11:30:13 +00:00
#[OA\Schema(
description: 'Service model',
type: 'object',
properties: [
2024-07-09 11:59:54 +00:00
'id' => ['type' => 'integer', 'description' => 'The unique identifier of the service. Only used for database identification.'],
'uuid' => ['type' => 'string', 'description' => 'The unique identifier of the service.'],
'name' => ['type' => 'string', 'description' => 'The name of the service.'],
'environment_id' => ['type' => 'integer', 'description' => 'The unique identifier of the environment where the service is attached to.'],
'server_id' => ['type' => 'integer', 'description' => 'The unique identifier of the server where the service is running.'],
'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.'],
2024-08-26 10:23:03 +00:00
'destination_type' => ['type' => 'string', 'description' => 'Destination type.'],
2024-07-09 11:59:54 +00:00
'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.'],
2024-07-17 12:52:40 +00:00
'is_container_label_readonly_enabled' => ['type' => 'boolean', 'description' => 'The flag to enable the container label readonly.'],
2024-07-09 11:59:54 +00:00
'config_hash' => ['type' => 'string', 'description' => 'The hash of the service configuration.'],
'service_type' => ['type' => 'string', 'description' => 'The type of the service.'],
'created_at' => ['type' => 'string', 'description' => 'The date and time when the service was created.'],
'updated_at' => ['type' => 'string', 'description' => 'The date and time when the service was last updated.'],
'deleted_at' => ['type' => 'string', 'description' => 'The date and time when the service was deleted.'],
2024-07-09 11:30:13 +00:00
],
)]
2023-09-20 13:42:41 +00:00
class Service extends BaseModel
{
use ClearsGlobalSearchCache, HasFactory, HasSafeStringAttribute, SoftDeletes;
2024-06-10 20:43:34 +00:00
private static $parserVersion = '5';
2023-09-20 13:42:41 +00:00
protected $guarded = [];
protected $appends = ['server_status', 'status'];
2024-08-27 14:02:52 +00:00
protected static function booted()
{
static::creating(function ($service) {
if (blank($service->name)) {
$service->name = 'service-'.(new Cuid2);
}
});
2024-08-27 14:02:52 +00:00
static::created(function ($service) {
$service->compose_parsing_version = self::$parserVersion;
2024-08-27 14:02:52 +00:00
$service->save();
});
}
public function isConfigurationChanged(bool $save = false)
{
$domains = $this->applications()->get()->pluck('fqdn')->sort()->toArray();
$domains = implode(',', $domains);
$applicationImages = $this->applications()->get()->pluck('image')->sort();
$databaseImages = $this->databases()->get()->pluck('image')->sort();
$images = $applicationImages->merge($databaseImages);
$images = implode(',', $images->toArray());
$applicationStorages = $this->applications()->get()->pluck('persistentStorages')->flatten()->sortBy('id');
$databaseStorages = $this->databases()->get()->pluck('persistentStorages')->flatten()->sortBy('id');
$storages = $applicationStorages->merge($databaseStorages)->implode('updated_at');
2024-09-23 17:51:31 +00:00
$newConfigHash = $images.$domains.$images.$storages;
$newConfigHash .= json_encode($this->environment_variables()->get('value')->sort());
$newConfigHash = md5($newConfigHash);
$oldConfigHash = data_get($this, 'config_hash');
if ($oldConfigHash === null) {
if ($save) {
$this->config_hash = $newConfigHash;
$this->save();
}
2024-06-10 20:43:34 +00:00
return true;
}
if ($oldConfigHash === $newConfigHash) {
return false;
} else {
if ($save) {
$this->config_hash = $newConfigHash;
$this->save();
}
2025-01-07 13:52:08 +00:00
return true;
}
}
2024-06-10 20:43:34 +00:00
protected function serverStatus(): Attribute
{
return Attribute::make(
get: function () {
return $this->server->isFunctional();
}
);
}
public function isRunning()
{
return (bool) str($this->status)->contains('running');
}
public function isExited()
{
return (bool) str($this->status)->contains('exited');
}
2024-06-10 20:43:34 +00:00
public function isStarting(): bool
{
try {
$activity = Activity::where('properties->type_uuid', $this->uuid)->latest()->first();
$status = data_get($activity, 'properties.status');
return $status === ProcessStatus::QUEUED->value || $status === ProcessStatus::IN_PROGRESS->value;
} catch (\Throwable) {
return false;
}
}
2023-09-22 09:23:49 +00:00
public function type()
2023-09-20 13:42:41 +00:00
{
2023-09-22 09:23:49 +00:00
return 'service';
2023-09-20 13:42:41 +00:00
}
2024-06-10 20:43:34 +00:00
2024-02-03 11:39:07 +00:00
public function project()
{
return data_get($this, 'environment.project');
}
2024-06-10 20:43:34 +00:00
2024-01-23 16:13:23 +00:00
public function team()
{
return data_get($this, 'environment.project.team');
}
2024-06-10 20:43:34 +00:00
2024-02-02 10:50:28 +00:00
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
2024-06-10 20:43:34 +00:00
/**
* Get query builder for services owned by current team.
* If you need all services without further query chaining, use ownedByCurrentTeamCached() instead.
*/
public static function ownedByCurrentTeam()
{
return Service::whereRelation('environment.project.team', 'id', currentTeam()->id)->orderBy('name');
}
/**
* Get all services owned by current team (cached for request duration).
*/
public static function ownedByCurrentTeamCached()
{
return once(function () {
return Service::ownedByCurrentTeam()->get();
});
}
public function deleteConfigurations()
{
$server = data_get($this, 'destination.server');
$workdir = $this->workdir();
if (str($workdir)->endsWith($this->uuid)) {
2024-09-23 17:51:31 +00:00
instant_remote_process(['rm -rf '.$this->workdir()], $server, false);
}
}
2024-06-10 20:43:34 +00:00
public function deleteConnectedNetworks()
{
$server = data_get($this, 'destination.server');
instant_remote_process(["docker network disconnect {$this->uuid} coolify-proxy"], $server, false);
instant_remote_process(["docker network rm {$this->uuid}"], $server, false);
}
/**
* Calculate the service's aggregate status from its applications and databases.
*
* This method aggregates status from Eloquent model relationships (not Docker containers).
* It differs from the CalculatesExcludedStatus trait which works with Docker container objects
* during container inspection. This accessor runs on-demand for UI display and works with
* already-stored status strings from the database.
*
* Status format: "{status}:{health}" or "{status}:{health}:excluded"
* - Status values: running, exited, degraded, starting, paused, restarting
* - Health values: healthy, unhealthy, unknown
* - :excluded suffix: Indicates all containers are excluded from health monitoring
*
* @return string The aggregate status in format "status:health" or "status:health:excluded"
*/
public function getStatusAttribute()
{
if ($this->isStarting()) {
return 'starting:unhealthy';
}
$applications = $this->applications;
$databases = $this->databases;
[$complexStatus, $complexHealth, $hasNonExcluded] = $this->aggregateResourceStatuses(
$applications,
$databases,
excludedOnly: false
);
// If all services are excluded from status checks, calculate status from excluded containers
// but mark it with :excluded to indicate monitoring is disabled
if (! $hasNonExcluded && ($complexStatus === null && $complexHealth === null)) {
[$excludedStatus, $excludedHealth] = $this->aggregateResourceStatuses(
$applications,
$databases,
excludedOnly: true
);
// Return status with :excluded suffix to indicate monitoring is disabled
if ($excludedStatus && $excludedHealth) {
return "{$excludedStatus}:{$excludedHealth}:excluded";
}
// If no status was calculated at all (no containers exist), return unknown
if ($excludedStatus === null && $excludedHealth === null) {
return 'unknown:unknown:excluded';
}
return 'exited';
}
// If health is null/empty, return just the status without trailing colon
if ($complexHealth === null || $complexHealth === '') {
return $complexStatus;
}
return "{$complexStatus}:{$complexHealth}";
}
/**
* Aggregate status and health from collections of applications and databases.
*
* This helper method consolidates status aggregation logic using ContainerStatusAggregator.
* It processes container status strings stored in the database (not live Docker data).
*
* @param \Illuminate\Database\Eloquent\Collection $applications Collection of Application models
* @param \Illuminate\Database\Eloquent\Collection $databases Collection of Database models
* @param bool $excludedOnly If true, only process excluded containers; if false, only process non-excluded
* @return array{0: string|null, 1: string|null, 2?: bool} [status, health, hasNonExcluded (only when excludedOnly=false)]
*/
private function aggregateResourceStatuses($applications, $databases, bool $excludedOnly = false): array
{
$hasNonExcluded = false;
$statusStrings = collect();
// Process both applications and databases using the same logic
$resources = $applications->concat($databases);
foreach ($resources as $resource) {
$isExcluded = $resource->exclude_from_status || str($resource->status)->contains(':excluded');
// Filter based on excludedOnly flag
if ($excludedOnly && ! $isExcluded) {
continue;
}
if (! $excludedOnly && $isExcluded) {
continue;
}
2024-06-10 20:43:34 +00:00
if (! $excludedOnly) {
$hasNonExcluded = true;
}
// Strip :excluded suffix before aggregation (it's in the 3rd part of "status:health:excluded")
$status = str($resource->status)->before(':excluded')->toString();
$statusStrings->push($status);
}
// If no status strings collected, return nulls
if ($statusStrings->isEmpty()) {
return $excludedOnly ? [null, null] : [null, null, $hasNonExcluded];
}
// Use ContainerStatusAggregator service for state machine logic
$aggregator = new ContainerStatusAggregator;
$aggregatedStatus = $aggregator->aggregateFromStrings($statusStrings);
// Parse the aggregated "status:health" string
$parts = explode(':', $aggregatedStatus);
$status = $parts[0] ?? null;
$health = $parts[1] ?? null;
if ($excludedOnly) {
return [$status, $health];
}
return [$status, $health, $hasNonExcluded];
}
2024-06-10 20:43:34 +00:00
public function extraFields()
{
$fields = collect([]);
$applications = $this->applications()->get();
foreach ($applications as $application) {
$image = str($application->image)->before(':');
if ($image->isEmpty()) {
continue;
}
switch ($image) {
case $image->contains('drizzle-team/gateway'):
$data = collect([]);
$masterpass = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_DRIZZLE')->first();
$data = $data->merge([
'Master Password' => [
'key' => data_get($masterpass, 'key'),
'value' => data_get($masterpass, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
$fields->put('Drizzle', $data->toArray());
break;
2024-10-10 11:28:42 +00:00
case $image->contains('castopod'):
$data = collect([]);
$disable_https = $this->environment_variables()->where('key', 'CP_DISABLE_HTTPS')->first();
if ($disable_https) {
$data = $data->merge([
'Disable HTTPS' => [
'key' => 'CP_DISABLE_HTTPS',
'value' => data_get($disable_https, 'value'),
'rules' => 'required',
2024-10-17 20:08:23 +00:00
'customHelper' => 'If you want to use https, set this to 0. Variable name: CP_DISABLE_HTTPS',
2024-10-10 11:28:42 +00:00
],
]);
}
$fields->put('Castopod', $data->toArray());
break;
2024-10-07 09:54:54 +00:00
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' => data_get($password, 'key'),
2024-10-07 09:54:54 +00:00
'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' => data_get($email, 'key'),
'value' => data_get($email, 'value'),
'rules' => 'required|email',
],
]);
}
$password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_LANGFUSE')->first();
if ($password) {
$data = $data->merge([
'Admin Password' => [
'key' => data_get($password, 'key'),
'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' => data_get($email, 'key'),
'value' => data_get($email, 'value'),
'rules' => 'required|email',
],
]);
$password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_INVOICENINJAUSER')->first();
$data = $data->merge([
'Password' => [
'key' => data_get($password, 'key'),
'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();
$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 $image->is('registry'):
$data = collect([]);
$registry_user = $this->environment_variables()->where('key', 'SERVICE_USER_REGISTRY')->first();
$registry_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_REGISTRY')->first();
if ($registry_user) {
$data = $data->merge([
'Registry User' => [
'key' => data_get($registry_user, 'key'),
'value' => data_get($registry_user, 'value'),
'rules' => 'required',
],
]);
}
if ($registry_password) {
$data = $data->merge([
'Registry Password' => [
'key' => data_get($registry_password, 'key'),
'value' => data_get($registry_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$fields->put('Docker Registry', $data->toArray());
break;
case $image->contains('tolgee'):
$data = collect([]);
$admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_TOLGEE')->first();
$data = $data->merge([
'Admin User' => [
'key' => 'TOLGEE_AUTHENTICATION_INITIAL_USERNAME',
'value' => 'admin',
'readonly' => true,
'rules' => 'required',
],
]);
if ($admin_password) {
$data = $data->merge([
'Admin Password' => [
'key' => data_get($admin_password, 'key'),
'value' => data_get($admin_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$fields->put('Tolgee', $data->toArray());
break;
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();
if ($logto_endpoint) {
$data = $data->merge([
'Endpoint' => [
'key' => data_get($logto_endpoint, 'key'),
'value' => data_get($logto_endpoint, 'value'),
'rules' => 'required|url',
],
]);
}
if ($logto_admin_endpoint) {
$data = $data->merge([
'Admin Endpoint' => [
'key' => data_get($logto_admin_endpoint, 'key'),
'value' => data_get($logto_admin_endpoint, 'value'),
'rules' => 'required|url',
],
]);
}
$fields->put('Logto', $data->toArray());
break;
case $image->contains('unleash-server'):
$data = collect([]);
$admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_UNLEASH')->first();
$data = $data->merge([
'Admin User' => [
'key' => 'SERVICE_USER_UNLEASH',
'value' => 'admin',
'readonly' => true,
'rules' => 'required',
],
]);
if ($admin_password) {
$data = $data->merge([
'Admin Password' => [
'key' => data_get($admin_password, 'key'),
'value' => data_get($admin_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$fields->put('Unleash', $data->toArray());
break;
case $image->contains('grafana'):
$data = collect([]);
$admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_GRAFANA')->first();
$data = $data->merge([
'Admin User' => [
'key' => 'GF_SECURITY_ADMIN_USER',
'value' => 'admin',
'readonly' => true,
'rules' => 'required',
],
]);
if ($admin_password) {
$data = $data->merge([
'Admin Password' => [
'key' => data_get($admin_password, 'key'),
'value' => data_get($admin_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$fields->put('Grafana', $data->toArray());
break;
case $image->contains('elasticsearch'):
$data = collect([]);
$elastic_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_ELASTICSEARCH')->first();
if ($elastic_password) {
$data = $data->merge([
'Password (default user: elastic)' => [
'key' => data_get($elastic_password, 'key'),
'value' => data_get($elastic_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$fields->put('Elasticsearch', $data->toArray());
break;
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();
if ($admin_email) {
$data = $data->merge([
'Admin Email' => [
'key' => data_get($admin_email, 'key'),
'value' => data_get($admin_email, 'value'),
'rules' => 'required|email',
],
]);
}
if ($admin_password) {
$data = $data->merge([
'Admin Password' => [
'key' => data_get($admin_password, 'key'),
'value' => data_get($admin_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$fields->put('Directus', $data->toArray());
break;
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();
if ($dashboard_user) {
$data = $data->merge([
'Dashboard User' => [
'key' => data_get($dashboard_user, 'key'),
'value' => data_get($dashboard_user, 'value'),
'rules' => 'required',
],
]);
}
if ($dashboard_password) {
$data = $data->merge([
'Dashboard Password' => [
'key' => data_get($dashboard_password, 'key'),
'value' => data_get($dashboard_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$fields->put('Supabase', $data->toArray());
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();
$admin_user = $this->environment_variables()->where('key', 'SERVICE_USER_MINIO')->first();
if (is_null($admin_user)) {
$admin_user = $this->environment_variables()->where('key', 'MINIO_ROOT_USER')->first();
}
$admin_password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_MINIO')->first();
if (is_null($admin_password)) {
$admin_password = $this->environment_variables()->where('key', 'MINIO_ROOT_PASSWORD')->first();
}
if ($console_url) {
$data = $data->merge([
'Console URL' => [
'key' => data_get($console_url, 'key'),
'value' => data_get($console_url, 'value'),
'rules' => 'required|url',
],
]);
}
if ($s3_api_url) {
$data = $data->merge([
'S3 API URL' => [
'key' => data_get($s3_api_url, 'key'),
'value' => data_get($s3_api_url, 'value'),
'rules' => 'required|url',
],
]);
}
if ($admin_user) {
$data = $data->merge([
'Admin User' => [
'key' => data_get($admin_user, 'key'),
'value' => data_get($admin_user, 'value'),
'rules' => 'required',
],
]);
}
if ($admin_password) {
$data = $data->merge([
'Admin Password' => [
'key' => data_get($admin_password, 'key'),
'value' => data_get($admin_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$fields->put('MinIO', $data->toArray());
break;
case $image->contains('garage'):
$data = collect([]);
$s3_api_url = $this->environment_variables()->where('key', 'GARAGE_S3_API_URL')->first();
$web_url = $this->environment_variables()->where('key', 'GARAGE_WEB_URL')->first();
$admin_url = $this->environment_variables()->where('key', 'GARAGE_ADMIN_URL')->first();
$admin_token = $this->environment_variables()->where('key', 'GARAGE_ADMIN_TOKEN')->first();
if (is_null($admin_token)) {
$admin_token = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_GARAGE')->first();
}
$rpc_secret = $this->environment_variables()->where('key', 'GARAGE_RPC_SECRET')->first();
if (is_null($rpc_secret)) {
$rpc_secret = $this->environment_variables()->where('key', 'SERVICE_HEX_32_RPCSECRET')->first();
}
$metrics_token = $this->environment_variables()->where('key', 'GARAGE_METRICS_TOKEN')->first();
if (is_null($metrics_token)) {
$metrics_token = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_GARAGEMETRICS')->first();
}
if ($s3_api_url) {
$data = $data->merge([
'S3 API URL' => [
'key' => data_get($s3_api_url, 'key'),
'value' => data_get($s3_api_url, 'value'),
'rules' => 'required|url',
],
]);
}
if ($web_url) {
$data = $data->merge([
'Web URL' => [
'key' => data_get($web_url, 'key'),
'value' => data_get($web_url, 'value'),
'rules' => 'required|url',
],
]);
}
if ($admin_url) {
$data = $data->merge([
'Admin URL' => [
'key' => data_get($admin_url, 'key'),
'value' => data_get($admin_url, 'value'),
'rules' => 'required|url',
],
]);
}
if ($admin_token) {
$data = $data->merge([
'Admin Token' => [
'key' => data_get($admin_token, 'key'),
'value' => data_get($admin_token, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
if ($rpc_secret) {
$data = $data->merge([
'RPC Secret' => [
'key' => data_get($rpc_secret, 'key'),
'value' => data_get($rpc_secret, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
if ($metrics_token) {
$data = $data->merge([
'Metrics Token' => [
'key' => data_get($metrics_token, 'key'),
'value' => data_get($metrics_token, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$fields->put('Garage', $data->toArray());
break;
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();
if ($admin_email) {
$data = $data->merge([
'Admin Email' => [
'key' => data_get($admin_email, 'key'),
'value' => data_get($admin_email, 'value'),
'rules' => 'required|email',
],
]);
}
if ($admin_password) {
$data = $data->merge([
'Admin Password' => [
'key' => data_get($admin_password, 'key'),
'value' => data_get($admin_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$fields->put('Weblate', $data->toArray());
break;
case $image->contains('meilisearch'):
2023-12-03 11:16:33 +00:00
$data = collect([]);
$SERVICE_PASSWORD_MEILISEARCH = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_MEILISEARCH')->first();
if ($SERVICE_PASSWORD_MEILISEARCH) {
$data = $data->merge([
'API Key' => [
'key' => data_get($SERVICE_PASSWORD_MEILISEARCH, 'key'),
'value' => data_get($SERVICE_PASSWORD_MEILISEARCH, 'value'),
'isPassword' => true,
],
]);
}
$fields->put('Meilisearch', $data->toArray());
2023-12-03 11:16:33 +00:00
break;
case $image->contains('linkding'):
$data = collect([]);
$SERVICE_USER_LINKDING = $this->environment_variables()->where('key', 'SERVICE_USER_LINKDING')->first();
$SERVICE_PASSWORD_LINKDING = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_LINKDING')->first();
if ($SERVICE_USER_LINKDING) {
$data = $data->merge([
'Superuser Name' => [
'key' => data_get($SERVICE_USER_LINKDING, 'key'),
'value' => data_get($SERVICE_USER_LINKDING, 'value'),
],
]);
}
if ($SERVICE_PASSWORD_LINKDING) {
$data = $data->merge([
'Superuser Password' => [
'key' => data_get($SERVICE_PASSWORD_LINKDING, 'key'),
'value' => data_get($SERVICE_PASSWORD_LINKDING, 'value'),
'isPassword' => true,
],
]);
}
$fields->put('Linkding', $data->toArray());
break;
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();
$MAIL_OPTIONS_SECURE = $this->environment_variables()->where('key', 'MAIL_OPTIONS_SECURE')->first();
$MAIL_OPTIONS_PORT = $this->environment_variables()->where('key', 'MAIL_OPTIONS_PORT')->first();
$MAIL_OPTIONS_SERVICE = $this->environment_variables()->where('key', 'MAIL_OPTIONS_SERVICE')->first();
$MAIL_OPTIONS_HOST = $this->environment_variables()->where('key', 'MAIL_OPTIONS_HOST')->first();
if ($MAIL_OPTIONS_AUTH_PASS) {
$data = $data->merge([
'Mail Password' => [
'key' => data_get($MAIL_OPTIONS_AUTH_PASS, 'key'),
'value' => data_get($MAIL_OPTIONS_AUTH_PASS, 'value'),
'isPassword' => true,
],
]);
}
if ($MAIL_OPTIONS_AUTH_USER) {
$data = $data->merge([
'Mail User' => [
'key' => data_get($MAIL_OPTIONS_AUTH_USER, 'key'),
'value' => data_get($MAIL_OPTIONS_AUTH_USER, 'value'),
],
]);
}
if ($MAIL_OPTIONS_SECURE) {
$data = $data->merge([
'Mail Secure' => [
'key' => data_get($MAIL_OPTIONS_SECURE, 'key'),
'value' => data_get($MAIL_OPTIONS_SECURE, 'value'),
],
]);
}
if ($MAIL_OPTIONS_PORT) {
$data = $data->merge([
'Mail Port' => [
'key' => data_get($MAIL_OPTIONS_PORT, 'key'),
'value' => data_get($MAIL_OPTIONS_PORT, 'value'),
],
]);
}
if ($MAIL_OPTIONS_SERVICE) {
$data = $data->merge([
'Mail Service' => [
'key' => data_get($MAIL_OPTIONS_SERVICE, 'key'),
'value' => data_get($MAIL_OPTIONS_SERVICE, 'value'),
],
]);
}
if ($MAIL_OPTIONS_HOST) {
$data = $data->merge([
'Mail Host' => [
'key' => data_get($MAIL_OPTIONS_HOST, 'key'),
'value' => data_get($MAIL_OPTIONS_HOST, 'value'),
],
]);
}
$fields->put('Ghost', $data->toArray());
break;
case $image->contains('vaultwarden'):
2024-05-27 07:51:32 +00:00
$data = collect([]);
$DATABASE_URL = $this->environment_variables()->where('key', 'DATABASE_URL')->first();
$ADMIN_TOKEN = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_64_ADMIN')->first();
$SIGNUP_ALLOWED = $this->environment_variables()->where('key', 'SIGNUP_ALLOWED')->first();
2024-05-27 22:40:35 +00:00
$PUSH_ENABLED = $this->environment_variables()->where('key', 'PUSH_ENABLED')->first();
$PUSH_INSTALLATION_ID = $this->environment_variables()->where('key', 'PUSH_SERVICE_ID')->first();
$PUSH_INSTALLATION_KEY = $this->environment_variables()->where('key', 'PUSH_SERVICE_KEY')->first();
2024-05-27 07:51:32 +00:00
if ($DATABASE_URL) {
$data = $data->merge([
'Database URL' => [
'key' => data_get($DATABASE_URL, 'key'),
'value' => data_get($DATABASE_URL, 'value'),
],
]);
}
if ($ADMIN_TOKEN) {
$data = $data->merge([
'Admin Password' => [
'key' => data_get($ADMIN_TOKEN, 'key'),
'value' => data_get($ADMIN_TOKEN, 'value'),
'isPassword' => true,
],
]);
}
if ($SIGNUP_ALLOWED) {
$data = $data->merge([
'Signup Allowed' => [
'key' => data_get($SIGNUP_ALLOWED, 'key'),
'value' => data_get($SIGNUP_ALLOWED, 'value'),
'rules' => 'required|string|in:true,false',
],
]);
}
2024-05-27 07:51:32 +00:00
2024-05-27 22:40:35 +00:00
if ($PUSH_ENABLED) {
$data = $data->merge([
'Push Enabled' => [
'key' => data_get($PUSH_ENABLED, 'key'),
'value' => data_get($PUSH_ENABLED, 'value'),
'rules' => 'required|string|in:true,false',
],
]);
}
if ($PUSH_INSTALLATION_ID) {
$data = $data->merge([
'Push Installation ID' => [
'key' => data_get($PUSH_INSTALLATION_ID, 'key'),
'value' => data_get($PUSH_INSTALLATION_ID, 'value'),
],
]);
}
if ($PUSH_INSTALLATION_KEY) {
$data = $data->merge([
'Push Installation Key' => [
'key' => data_get($PUSH_INSTALLATION_KEY, 'key'),
'value' => data_get($PUSH_INSTALLATION_KEY, 'value'),
'isPassword' => true,
],
]);
}
2024-05-27 07:51:32 +00:00
$fields->put('Vaultwarden', $data);
break;
case $image->contains('gitlab/gitlab'):
2024-07-25 12:14:00 +00:00
$password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_GITLAB')->first();
$data = collect([]);
if ($password) {
$data = $data->merge([
'Root Password' => [
'key' => data_get($password, 'key'),
'value' => data_get($password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$data = $data->merge([
'Root User' => [
'key' => 'GITLAB_ROOT_USER',
2024-07-25 12:14:00 +00:00
'value' => 'root',
'rules' => 'required',
'isPassword' => true,
],
]);
$fields->put('GitLab', $data->toArray());
break;
case $image->contains('code-server'):
$data = collect([]);
$password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_64_PASSWORDCODESERVER')->first();
if ($password) {
$data = $data->merge([
'Password' => [
'key' => data_get($password, 'key'),
'value' => data_get($password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$sudoPassword = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_SUDOCODESERVER')->first();
if ($sudoPassword) {
$data = $data->merge([
'Sudo Password' => [
'key' => data_get($sudoPassword, 'key'),
'value' => data_get($sudoPassword, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$fields->put('Code Server', $data->toArray());
break;
case $image->contains('elestio/strapi'):
$data = collect([]);
$license = $this->environment_variables()->where('key', 'STRAPI_LICENSE')->first();
if ($license) {
$data = $data->merge([
'License' => [
'key' => data_get($license, 'key'),
'value' => data_get($license, 'value'),
],
]);
}
$nodeEnv = $this->environment_variables()->where('key', 'NODE_ENV')->first();
if ($nodeEnv) {
$data = $data->merge([
'Node Environment' => [
'key' => data_get($nodeEnv, 'key'),
'value' => data_get($nodeEnv, 'value'),
],
]);
}
$fields->put('Strapi', $data->toArray());
break;
case $image->contains('marckohlbrugge/sessy'):
$data = collect([]);
$username = $this->environment_variables()->where('key', 'SERVICE_USER_SESSY')->first();
$password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_SESSY')->first();
if ($username) {
$data = $data->merge([
'HTTP Auth Username' => [
'key' => data_get($username, 'key'),
'value' => data_get($username, 'value'),
'rules' => 'required',
],
]);
}
if ($password) {
$data = $data->merge([
'HTTP Auth Password' => [
'key' => data_get($password, 'key'),
'value' => data_get($password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
$fields->put('Sessy', $data->toArray());
break;
case $image->contains('coollabsio/openclaw'):
$data = collect([]);
$username = $this->environment_variables()->where('key', 'AUTH_USERNAME')->first();
$password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_OPENCLAW')->first();
$gateway_token = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_64_GATEWAYTOKEN')->first();
if ($username) {
$data = $data->merge([
'Username' => [
'key' => data_get($username, 'key'),
'value' => data_get($username, 'value'),
'readonly' => true,
],
]);
}
if ($password) {
$data = $data->merge([
'Password' => [
'key' => data_get($password, 'key'),
'value' => data_get($password, 'value'),
'isPassword' => true,
],
]);
}
if ($gateway_token) {
$data = $data->merge([
'Gateway Token' => [
'key' => data_get($gateway_token, 'key'),
'value' => data_get($gateway_token, 'value'),
'isPassword' => true,
],
]);
}
$fields->put('Openclaw', $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' => data_get($admin_user, 'key'),
'value' => data_get($admin_user, 'value', 'admin'),
'readonly' => true,
'rules' => 'required',
],
]);
}
if ($admin_password) {
$data = $data->merge([
'Password' => [
'key' => data_get($admin_password, 'key'),
'value' => data_get($admin_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
if ($admin_email) {
$data = $data->merge([
'Email' => [
'key' => data_get($admin_email, 'key'),
'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(':');
if ($image->isEmpty()) {
continue;
}
switch ($image) {
case $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();
2023-11-13 16:06:43 +00:00
$data = collect([]);
if ($postgres_user) {
$data = $data->merge([
'User' => [
'key' => data_get($postgres_user, 'key'),
'value' => data_get($postgres_user, 'value'),
'rules' => 'required',
],
]);
}
if ($postgres_password) {
$data = $data->merge([
'Password' => [
'key' => data_get($postgres_password, 'key'),
'value' => data_get($postgres_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
if ($postgres_db_name) {
$data = $data->merge([
'Database Name' => [
'key' => data_get($postgres_db_name, 'key'),
'value' => data_get($postgres_db_name, 'value'),
'rules' => 'required',
],
]);
}
$fields->put('PostgreSQL', $data->toArray());
break;
case $image->contains('mysql'):
$userVariables = ['SERVICE_USER_MYSQL', 'SERVICE_USER_WORDPRESS', 'MYSQL_USER'];
2024-10-17 20:08:23 +00:00
$passwordVariables = ['SERVICE_PASSWORD_MYSQL', 'SERVICE_PASSWORD_WORDPRESS', 'MYSQL_PASSWORD', 'SERVICE_PASSWORD_64_MYSQL'];
$rootPasswordVariables = ['SERVICE_PASSWORD_MYSQLROOT', 'SERVICE_PASSWORD_ROOT', 'SERVICE_PASSWORD_64_MYSQLROOT'];
$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();
2023-11-13 16:06:43 +00:00
$data = collect([]);
if ($mysql_user) {
$data = $data->merge([
'User' => [
'key' => data_get($mysql_user, 'key'),
'value' => data_get($mysql_user, 'value'),
'rules' => 'required',
],
]);
}
if ($mysql_password) {
$data = $data->merge([
'Password' => [
'key' => data_get($mysql_password, 'key'),
'value' => data_get($mysql_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
if ($mysql_root_password) {
$data = $data->merge([
'Root Password' => [
'key' => data_get($mysql_root_password, 'key'),
'value' => data_get($mysql_root_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
if ($mysql_db_name) {
$data = $data->merge([
'Database Name' => [
'key' => data_get($mysql_db_name, 'key'),
'value' => data_get($mysql_db_name, 'value'),
'rules' => 'required',
],
]);
}
$fields->put('MySQL', $data->toArray());
break;
case $image->contains('mariadb'):
$userVariables = ['SERVICE_USER_MARIADB', 'SERVICE_USER_WORDPRESS', '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'];
$dbNameVariables = ['SERVICE_DATABASE_MARIADB', 'SERVICE_DATABASE_WORDPRESS', '_APP_DB_SCHEMA', 'MYSQL_DATABASE'];
$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();
2023-11-13 16:06:43 +00:00
$data = collect([]);
2023-09-22 09:23:49 +00:00
2023-11-13 16:06:43 +00:00
if ($mariadb_user) {
$data = $data->merge([
'User' => [
'key' => data_get($mariadb_user, 'key'),
'value' => data_get($mariadb_user, 'value'),
'rules' => 'required',
],
]);
}
if ($mariadb_password) {
$data = $data->merge([
'Password' => [
'key' => data_get($mariadb_password, 'key'),
'value' => data_get($mariadb_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
if ($mariadb_root_password) {
$data = $data->merge([
'Root Password' => [
'key' => data_get($mariadb_root_password, 'key'),
'value' => data_get($mariadb_root_password, 'value'),
'rules' => 'required',
'isPassword' => true,
],
]);
}
if ($mariadb_db_name) {
$data = $data->merge([
'Database Name' => [
'key' => data_get($mariadb_db_name, 'key'),
'value' => data_get($mariadb_db_name, 'value'),
'rules' => 'required',
],
]);
}
$fields->put('MariaDB', $data->toArray());
break;
}
}
$fields = collect($fields)->map(function ($extraFields) {
if (is_array($extraFields)) {
$extraFields = collect($extraFields)->map(function ($field) {
if (filled($field['value']) && str($field['value'])->startsWith('$SERVICE_')) {
$searchValue = str($field['value'])->after('$')->value;
$newValue = $this->environment_variables()->where('key', $searchValue)->first();
if ($newValue) {
$field['value'] = $newValue->value;
}
}
return $field;
});
}
return $extraFields;
});
2024-06-10 20:43:34 +00:00
return $fields;
}
2024-06-10 20:43:34 +00:00
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,
'resourceable_id' => $this->id,
'resourceable_type' => $this->getMorphClass(),
'is_preview' => false,
]);
}
}
}
2024-06-10 20:43:34 +00:00
public function link()
{
2023-11-30 11:21:53 +00:00
if (data_get($this, 'environment.project.uuid')) {
return route('project.service.configuration', [
'project_uuid' => data_get($this, 'environment.project.uuid'),
2024-11-22 15:03:20 +00:00
'environment_uuid' => data_get($this, 'environment.uuid'),
2024-06-10 20:43:34 +00:00
'service_uuid' => data_get($this, 'uuid'),
2023-11-30 11:21:53 +00:00
]);
}
2024-06-10 20:43:34 +00:00
2023-11-30 11:21:53 +00:00
return null;
}
2024-06-10 20:43:34 +00:00
public function taskLink($task_uuid)
{
if (data_get($this, 'environment.project.uuid')) {
$route = route('project.service.scheduled-tasks', [
'project_uuid' => data_get($this, 'environment.project.uuid'),
2024-11-22 15:03:20 +00:00
'environment_uuid' => data_get($this, 'environment.uuid'),
2024-05-24 15:05:18 +00:00
'service_uuid' => data_get($this, 'uuid'),
2024-06-10 20:43:34 +00:00
'task_uuid' => $task_uuid,
]);
$settings = InstanceSettings::get();
if (data_get($settings, 'fqdn')) {
$url = Url::fromString($route);
$url = $url->withPort(null);
$fqdn = data_get($settings, 'fqdn');
$fqdn = str_replace(['http://', 'https://'], '', $fqdn);
$url = $url->withHost($fqdn);
return $url->__toString();
}
return $route;
}
2024-06-10 20:43:34 +00:00
return null;
}
2024-06-10 20:43:34 +00:00
2023-09-26 13:07:33 +00:00
public function documentation()
{
$services = get_service_templates();
$service = data_get($services, str($this->name)->beforeLast('-')->value, []);
2024-06-10 20:43:34 +00:00
2024-11-12 14:18:48 +00:00
return data_get($service, 'documentation', config('constants.urls.docs'));
2023-09-26 13:07:33 +00:00
}
2024-06-10 20:43:34 +00:00
/**
* Get the required port for this service from the template definition.
*/
public function getRequiredPort(): ?int
{
try {
$services = get_service_templates();
$serviceName = str($this->name)->beforeLast('-')->value();
$service = data_get($services, $serviceName, []);
$port = data_get($service, 'port');
return $port ? (int) $port : null;
} catch (\Throwable) {
return null;
}
}
/**
* Check if this service requires a port to function correctly.
*/
public function requiresPort(): bool
{
return $this->getRequiredPort() !== null;
}
2023-09-20 13:42:41 +00:00
public function applications()
{
return $this->hasMany(ServiceApplication::class);
}
2024-06-10 20:43:34 +00:00
2023-09-20 13:42:41 +00:00
public function databases()
{
return $this->hasMany(ServiceDatabase::class);
}
2024-06-10 20:43:34 +00:00
2023-10-02 13:51:06 +00:00
public function destination()
{
return $this->morphTo();
}
2024-06-10 20:43:34 +00:00
2023-09-21 15:48:31 +00:00
public function environment()
{
return $this->belongsTo(Environment::class);
}
2024-06-10 20:43:34 +00:00
2023-09-22 09:23:49 +00:00
public function server()
{
2023-09-21 15:48:31 +00:00
return $this->belongsTo(Server::class);
}
2024-06-10 20:43:34 +00:00
public function byUuid(string $uuid)
{
$app = $this->applications()->whereUuid($uuid)->first();
if ($app) {
return $app;
}
$db = $this->databases()->whereUuid($uuid)->first();
if ($db) {
return $db;
}
2024-06-10 20:43:34 +00:00
return null;
}
2024-06-10 20:43:34 +00:00
2023-09-21 15:48:31 +00:00
public function byName(string $name)
{
$app = $this->applications()->whereName($name)->first();
if ($app) {
return $app;
}
$db = $this->databases()->whereName($name)->first();
if ($db) {
return $db;
}
2024-06-10 20:43:34 +00:00
2023-09-21 15:48:31 +00:00
return null;
}
2024-06-10 20:43:34 +00:00
public function scheduled_tasks(): HasMany
{
return $this->hasMany(ScheduledTask::class)->orderBy('name', 'asc');
}
2024-06-10 20:43:34 +00:00
public function environment_variables()
2023-09-20 13:42:41 +00:00
{
return $this->morphMany(EnvironmentVariable::class, 'resourceable');
}
2024-06-10 20:43:34 +00:00
2023-09-25 13:48:43 +00:00
public function workdir()
{
2024-09-23 17:51:31 +00:00
return service_configuration_dir()."/{$this->uuid}";
}
2024-06-10 20:43:34 +00:00
public function saveComposeConfigs()
{
// Guard against null or empty docker_compose
if (! $this->docker_compose) {
return;
}
$workdir = $this->workdir();
instant_remote_process([
"mkdir -p $workdir",
"cd $workdir",
], $this->server);
$filename = new Cuid2.'-docker-compose.yml';
Storage::disk('local')->put("tmp/{$filename}", $this->docker_compose);
$path = Storage::path("tmp/{$filename}");
instant_scp($path, "{$workdir}/docker-compose.yml", $this->server);
Storage::disk('local')->delete("tmp/{$filename}");
$commands[] = "cd $workdir";
2024-06-10 20:43:34 +00:00
$commands[] = 'rm -f .env || true';
$envs = collect([]);
// Generate SERVICE_NAME_* environment variables from docker-compose services
if ($this->docker_compose) {
try {
$dockerCompose = \Symfony\Component\Yaml\Yaml::parse($this->docker_compose);
$services = data_get($dockerCompose, 'services', []);
foreach ($services as $serviceName => $_) {
$envs->push('SERVICE_NAME_'.str($serviceName)->replace('-', '_')->replace('.', '_')->upper().'='.$serviceName);
}
} catch (\Exception $e) {
ray($e->getMessage());
}
}
$envs_from_coolify = $this->environment_variables()->get();
2024-09-09 13:04:51 +00:00
$sorted = $envs_from_coolify->sortBy(function ($env) {
if (str($env->key)->startsWith('SERVICE_')) {
return 1;
}
if (str($env->value)->startsWith('$SERVICE_') || str($env->value)->startsWith('${SERVICE_')) {
return 2;
}
return 3;
});
foreach ($sorted as $env) {
$envs->push("{$env->key}={$env->real_value}");
}
if ($envs->count() === 0) {
2024-06-10 20:43:34 +00:00
$commands[] = 'touch .env';
} else {
$envs_base64 = base64_encode($envs->implode("\n"));
$commands[] = "echo '$envs_base64' | base64 -d | tee .env > /dev/null";
2023-09-26 12:45:52 +00:00
}
instant_remote_process($commands, $this->server);
}
2023-09-28 08:53:00 +00:00
public function parse(bool $isNew = false): Collection
2023-09-20 13:42:41 +00:00
{
if ((int) $this->compose_parsing_version >= 3) {
return serviceParser($this);
} elseif ($this->docker_compose_raw) {
2024-08-27 14:02:52 +00:00
return parseDockerComposeFile($this, $isNew);
} else {
return collect([]);
2024-08-27 14:02:52 +00:00
}
2023-09-20 13:42:41 +00:00
}
2024-06-10 20:43:34 +00:00
public function networks()
{
2024-10-31 17:20:11 +00:00
return getTopLevelNetworks($this);
}
2024-10-11 12:38:22 +00:00
protected function isDeployable(): Attribute
{
return Attribute::make(
get: function () {
$envs = $this->environment_variables()->where('is_required', true)->get();
foreach ($envs as $env) {
if ($env->is_really_required) {
return false;
}
}
2024-10-17 20:08:23 +00:00
2024-10-11 12:38:22 +00:00
return true;
}
);
}
2023-09-20 13:42:41 +00:00
}