Merge pull request #1806 from coollabsio/next

v4.0.0-beta.233
This commit is contained in:
Andras Bacsai 2024-03-04 11:18:56 +01:00 committed by GitHub
commit 89c6563e00
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 305 additions and 52 deletions

View File

@ -15,6 +15,9 @@ public function handle(Server $server, bool $async = true): string|Activity
{
try {
$proxyType = $server->proxyType();
if ($proxyType === 'NONE') {
return 'OK';
}
$commands = collect([]);
$proxy_path = get_proxy_path();
$configuration = CheckConfiguration::run($server);

View File

@ -77,6 +77,9 @@ function (Scope $scope) {
);
}
);
if (str($e->getMessage())->contains('No space left on device')) {
return;
}
ray('reporting to sentry');
Integration::captureUnhandledException($e);
});

View File

@ -374,6 +374,7 @@ private function deploy_docker_compose_buildpack()
$this->cleanup_git();
$this->application->loadComposeFile(isInit: false);
if ($this->application->settings->is_raw_compose_deployment_enabled) {
$this->application->parseRawCompose();
$yaml = $composeFile = $this->application->docker_compose_raw;
} else {
$composeFile = $this->application->parseCompose(pull_request_id: $this->pull_request_id);
@ -413,16 +414,33 @@ private function deploy_docker_compose_buildpack()
]);
}
$this->write_deployment_configurations();
// Start compose file
if ($this->docker_compose_custom_start_command) {
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), "hidden" => true],
);
if ($this->application->settings->is_raw_compose_deployment_enabled) {
if ($this->docker_compose_custom_start_command) {
$this->execute_remote_command(
["cd {$this->basedir} && {$this->docker_compose_custom_start_command}", "hidden" => true],
);
} else {
$server_workdir = $this->application->workdir();
ray("SOURCE_COMMIT={$this->commit} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d");
$this->execute_remote_command(
["SOURCE_COMMIT={$this->commit} docker compose --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d", "hidden" => true],
);
}
} else {
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"), "hidden" => true],
);
if ($this->docker_compose_custom_start_command) {
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "cd {$this->basedir} && {$this->docker_compose_custom_start_command}"), "hidden" => true],
);
} else {
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "SOURCE_COMMIT={$this->commit} docker compose --project-directory {$this->workdir} -f {$this->workdir}{$this->docker_compose_location} up -d"), "hidden" => true],
);
}
}
$this->application_deployment_queue->addLogEntry("New container started.");
}
private function deploy_dockerfile_buildpack()

View File

@ -51,6 +51,9 @@ public function __construct($backup)
{
$this->backup = $backup;
$this->team = Team::find($backup->team_id);
if (is_null($this->team)) {
return;
}
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
$this->database = data_get($this->backup, 'database');
$this->server = $this->database->service->server;
@ -316,7 +319,7 @@ public function handle(): void
private function backup_standalone_mongodb(string $databaseWithCollections): void
{
try {
$url = $this->database->getDbUrl(useInternal: true);
$url = $this->database->get_db_url(useInternal: true);
if ($databaseWithCollections === 'all') {
$commands[] = "mkdir -p " . $this->backup_dir;
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location";

View File

@ -9,6 +9,8 @@ class Advanced extends Component
{
public Application $application;
public bool $is_force_https_enabled;
public bool $is_gzip_enabled;
public bool $is_stripprefix_enabled;
protected $rules = [
'application.settings.is_git_submodules_enabled' => 'boolean|required',
'application.settings.is_git_lfs_enabled' => 'boolean|required',
@ -19,13 +21,17 @@ class Advanced extends Component
'application.settings.is_gpu_enabled' => 'boolean|required',
'application.settings.is_build_server_enabled' => 'boolean|required',
'application.settings.is_consistent_container_name_enabled' => 'boolean|required',
'application.settings.is_gzip_enabled' => 'boolean|required',
'application.settings.is_stripprefix_enabled' => 'boolean|required',
'application.settings.gpu_driver' => 'string|required',
'application.settings.gpu_count' => 'string|required',
'application.settings.gpu_device_ids' => 'string|required',
'application.settings.gpu_options' => 'string|required',
];
public function mount() {
$this->is_force_https_enabled = $this->application->settings->is_force_https_enabled;
$this->is_force_https_enabled = $this->application->isForceHttpsEnabled();
$this->is_gzip_enabled = $this->application->isGzipEnabled();
$this->is_stripprefix_enabled = $this->application->isStripprefixEnabled();
}
public function instantSave()
{
@ -40,6 +46,14 @@ public function instantSave()
$this->application->settings->is_force_https_enabled = $this->is_force_https_enabled;
$this->dispatch('resetDefaultLabels', false);
}
if ($this->application->settings->is_gzip_enabled !== $this->is_gzip_enabled) {
$this->application->settings->is_gzip_enabled = $this->is_gzip_enabled;
$this->dispatch('resetDefaultLabels', false);
}
if ($this->application->settings->is_stripprefix_enabled !== $this->is_stripprefix_enabled) {
$this->application->settings->is_stripprefix_enabled = $this->is_stripprefix_enabled;
$this->dispatch('resetDefaultLabels', false);
}
$this->application->settings->save();
$this->dispatch('success', 'Settings saved.');
}

View File

@ -263,7 +263,11 @@ public function submit($showToaster = true)
}
if ($this->application->build_pack === 'dockercompose') {
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
$this->parsedServices = $this->application->parseCompose();
if ($this->application->settings->is_raw_compose_deployment_enabled) {
$this->application->parseRawCompose();
} else {
$this->parsedServices = $this->application->parseCompose();
}
}
$this->application->custom_labels = base64_encode($this->customLabels);
$this->application->save();

View File

@ -46,9 +46,9 @@ class General extends Component
public function mount()
{
$this->db_url = $this->database->getDbUrl(true);
$this->db_url = $this->database->get_db_url(true);
if ($this->database->is_public) {
$this->db_url_public = $this->database->getDbUrl();
$this->db_url_public = $this->database->get_db_url();
}
}
public function instantSaveAdvanced() {
@ -93,7 +93,7 @@ public function instantSave()
return;
}
StartDatabaseProxy::run($this->database);
$this->db_url_public = $this->database->getDbUrl();
$this->db_url_public = $this->database->get_db_url();
$this->dispatch('success', 'Database is now publicly accessible.');
} else {
StopDatabaseProxy::run($this->database);

View File

@ -44,9 +44,9 @@ class General extends Component
public function mount()
{
$this->db_url = $this->database->getDbUrl(true);
$this->db_url = $this->database->get_db_url(true);
if ($this->database->is_public) {
$this->db_url_public = $this->database->getDbUrl();
$this->db_url_public = $this->database->get_db_url();
}
}
public function instantSaveAdvanced()
@ -95,7 +95,7 @@ public function instantSave()
return;
}
StartDatabaseProxy::run($this->database);
$this->db_url_public = $this->database->getDbUrl();
$this->db_url_public = $this->database->get_db_url();
$this->dispatch('success', 'Database is now publicly accessible.');
} else {
StopDatabaseProxy::run($this->database);

View File

@ -46,9 +46,9 @@ class General extends Component
public function mount()
{
$this->db_url = $this->database->getDbUrl(true);
$this->db_url = $this->database->get_db_url(true);
if ($this->database->is_public) {
$this->db_url_public = $this->database->getDbUrl();
$this->db_url_public = $this->database->get_db_url();
}
}
public function instantSaveAdvanced()
@ -94,7 +94,7 @@ public function instantSave()
return;
}
StartDatabaseProxy::run($this->database);
$this->db_url_public = $this->database->getDbUrl();
$this->db_url_public = $this->database->get_db_url();
$this->dispatch('success', 'Database is now publicly accessible.');
} else {
StopDatabaseProxy::run($this->database);

View File

@ -53,9 +53,9 @@ class General extends Component
];
public function mount()
{
$this->db_url = $this->database->getDbUrl(true);
$this->db_url = $this->database->get_db_url(true);
if ($this->database->is_public) {
$this->db_url_public = $this->database->getDbUrl();
$this->db_url_public = $this->database->get_db_url();
}
}
public function instantSaveAdvanced() {
@ -87,7 +87,7 @@ public function instantSave()
return;
}
StartDatabaseProxy::run($this->database);
$this->db_url_public = $this->database->getDbUrl();
$this->db_url_public = $this->database->get_db_url();
$this->dispatch('success', 'Database is now publicly accessible.');
} else {
StopDatabaseProxy::run($this->database);

View File

@ -39,9 +39,9 @@ class General extends Component
];
public function mount()
{
$this->db_url = $this->database->getDbUrl(true);
$this->db_url = $this->database->get_db_url(true);
if ($this->database->is_public) {
$this->db_url_public = $this->database->getDbUrl();
$this->db_url_public = $this->database->get_db_url();
}
}
public function instantSaveAdvanced() {
@ -86,7 +86,7 @@ public function instantSave()
return;
}
StartDatabaseProxy::run($this->database);
$this->db_url_public = $this->database->getDbUrl();
$this->db_url_public = $this->database->get_db_url();
$this->dispatch('success', 'Database is now publicly accessible.');
} else {
StopDatabaseProxy::run($this->database);

View File

@ -18,6 +18,7 @@ public function getListeners()
$userId = auth()->user()->id;
return [
"echo-private:user.{$userId},ServiceStatusChanged" => 'check_status',
"check_status"
];
}
public function render()

View File

@ -6,7 +6,6 @@
use App\Actions\Service\StartService;
use App\Actions\Service\StopService;
use App\Events\ServiceStatusChanged;
use App\Jobs\ContainerStatusJob;
use App\Models\Service;
use Livewire\Component;
use Spatie\Activitylog\Models\Activity;
@ -27,6 +26,10 @@ public function serviceStatusChanged()
{
$this->dispatch('refresh')->self();
}
public function check_status() {
$this->dispatch('check_status');
$this->dispatch('success', 'Service status updated.');
}
public function render()
{
return view('livewire.project.service.navbar');

View File

@ -18,6 +18,7 @@ class ServiceApplicationView extends Component
'application.required_fqdn' => 'required|boolean',
'application.is_log_drain_enabled' => 'nullable|boolean',
'application.is_gzip_enabled' => 'nullable|boolean',
'application.is_stripprefix_enabled' => 'nullable|boolean',
];
public function render()
{

View File

@ -4,6 +4,7 @@
use App\Actions\Proxy\CheckConfiguration;
use App\Actions\Proxy\SaveConfiguration;
use App\Actions\Proxy\StartProxy;
use App\Models\Server;
use Livewire\Component;
use Illuminate\Support\Str;
@ -26,7 +27,7 @@ public function mount()
public function proxyStatusUpdated()
{
$this->server->refresh();
$this->dispatch('refresh')->self();
}
public function change_proxy()
@ -41,6 +42,9 @@ public function select_proxy($proxy_type)
$this->server->proxy->set('type', $proxy_type);
$this->server->save();
$this->selectedProxy = $this->server->proxy->type;
if ($this->selectedProxy !== 'NONE') {
StartProxy::run($this->server, false);
}
$this->dispatch('proxyStatusUpdated');
}

View File

@ -9,6 +9,7 @@
use Spatie\Activitylog\Models\Activity;
use Illuminate\Support\Str;
use RuntimeException;
use Symfony\Component\Yaml\Yaml;
use Visus\Cuid2\Cuid2;
class Application extends BaseModel
@ -79,6 +80,18 @@ public function is_github_based(): bool
}
return false;
}
public function isForceHttpsEnabled()
{
return data_get($this, 'settings.is_force_https_enabled', false);
}
public function isStripprefixEnabled()
{
return data_get($this, 'settings.is_stripprefix_enabled', true);
}
public function isGzipEnabled()
{
return data_get($this, 'settings.is_gzip_enabled', true);
}
public function link()
{
if (data_get($this, 'environment.project.uuid')) {
@ -476,6 +489,10 @@ public function isHealthcheckDisabled(): bool
}
return false;
}
public function workdir()
{
return application_configuration_dir() . "/{$this->uuid}";
}
public function isLogDrainEnabled()
{
return data_get($this, 'settings.is_log_drain_enabled', false);
@ -710,6 +727,64 @@ function generateGitImportCommands(string $deployment_uuid, int $pull_request_id
];
}
}
function parseRawCompose()
{
try {
$yaml = Yaml::parse($this->docker_compose_raw);
} catch (\Exception $e) {
throw new \Exception($e->getMessage());
}
$services = data_get($yaml, 'services');
$commands = collect([]);
$services = collect($services)->map(function ($service) use ($commands) {
$serviceVolumes = collect(data_get($service, 'volumes', []));
if ($serviceVolumes->count() > 0) {
foreach ($serviceVolumes as $volume) {
$workdir = $this->workdir();
$type = null;
$source = null;
if (is_string($volume)) {
$source = Str::of($volume)->before(':');
if ($source->startsWith('./') || $source->startsWith('/') || $source->startsWith('~')) {
$type = Str::of('bind');
}
} else if (is_array($volume)) {
$type = data_get_str($volume, 'type');
$source = data_get_str($volume, 'source');
}
if ($type->value() === 'bind') {
if ($source->value() === "/var/run/docker.sock") {
continue;
}
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
continue;
}
if ($source->startsWith('.')) {
$source = $source->after('.');
$source = $workdir . $source;
}
$commands->push("mkdir -p $source > /dev/null 2>&1 || true");
}
}
}
$labels = collect(data_get($service, 'labels', []));
if (!$labels->contains('coolify.managed')) {
$labels->push('coolify.managed=true');
}
if (!$labels->contains('coolify.applicationId')) {
$labels->push('coolify.applicationId=' . $this->id);
}
if (!$labels->contains('coolify.type')) {
$labels->push('coolify.type=application');
}
data_set($service, 'labels', $labels->toArray());
return $service;
});
data_set($yaml, 'services', $services->toArray());
$this->docker_compose_raw = Yaml::dump($yaml, 10, 2);
instant_remote_process($commands, $this->destination->server, false);
}
function parseCompose(int $pull_request_id = 0)
{
if ($this->docker_compose_raw) {

View File

@ -3,7 +3,6 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Str;
class LocalFileVolume extends BaseModel
{

View File

@ -23,6 +23,10 @@ public function isLogDrainEnabled()
{
return data_get($this, 'is_log_drain_enabled', false);
}
public function isStripprefixEnabled()
{
return data_get($this, 'is_stripprefix_enabled', true);
}
public function isGzipEnabled()
{
return data_get($this, 'is_gzip_enabled', true);

View File

@ -21,9 +21,13 @@ public function isLogDrainEnabled()
{
return data_get($this, 'is_log_drain_enabled', false);
}
public function isStripprefixEnabled()
{
return data_get($this, 'is_stripprefix_enabled', true);
}
public function isGzipEnabled()
{
return true;
return data_get($this, 'is_gzip_enabled', true);
}
public function type()
{

View File

@ -126,7 +126,7 @@ public function portsMappingsArray(): Attribute
);
}
public function getDbUrl(bool $useInternal = false): string
public function get_db_url(bool $useInternal = false): string
{
if ($this->is_public && !$useInternal) {
return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mariadb_database}";

View File

@ -142,7 +142,7 @@ public function type(): string
{
return 'standalone-mongodb';
}
public function getDbUrl(bool $useInternal = false)
public function get_db_url(bool $useInternal = false)
{
if ($this->is_public && !$useInternal) {
return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true";

View File

@ -127,7 +127,7 @@ public function portsMappingsArray(): Attribute
);
}
public function getDbUrl(bool $useInternal = false): string
public function get_db_url(bool $useInternal = false): string
{
if ($this->is_public && !$useInternal) {
return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mysql_database}";

View File

@ -126,7 +126,7 @@ public function type(): string
{
return 'standalone-postgresql';
}
public function getDbUrl(bool $useInternal = false): string
public function get_db_url(bool $useInternal = false): string
{
if ($this->is_public && !$useInternal) {
return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->postgres_db}";

View File

@ -122,7 +122,7 @@ public function type(): string
{
return 'standalone-redis';
}
public function getDbUrl(bool $useInternal = false): string
public function get_db_url(bool $useInternal = false): string
{
if ($this->is_public && !$useInternal) {
return "redis://:{$this->redis_password}@{$this->destination->server->getIp}:{$this->public_port}/0";

View File

@ -215,7 +215,7 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource,
}
return $payload;
}
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?string $service_name = null)
function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null)
{
$labels = collect([]);
$labels->push('traefik.enable=true');
@ -281,8 +281,10 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$labels->push("traefik.http.services.{$https_label}.loadbalancer.server.port=$port");
}
if ($path !== '/') {
$labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}");
$middlewares = collect(["{$https_label}-stripprefix"]);
if ($is_stripprefix_enabled) {
$labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}");
$middlewares = collect(["{$https_label}-stripprefix"]);
}
if ($is_gzip_enabled) {
$middlewares->push('gzip');
}
@ -334,8 +336,10 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
$labels->push("traefik.http.routers.{$http_label}.service={$http_label}");
}
if ($path !== '/') {
$labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}");
$middlewares = collect(["{$http_label}-stripprefix"]);
if ($is_stripprefix_enabled) {
$labels->push("traefik.http.middlewares.{$http_label}-stripprefix.stripprefix.prefixes={$path}");
$middlewares = collect(["{$http_label}-stripprefix"]);
}
if ($is_gzip_enabled) {
$middlewares->push('gzip');
}
@ -392,7 +396,14 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
}
// Add Traefik labels no matter which proxy is selected
$labels = $labels->merge(fqdnLabelsForTraefik($appUuid, $domains, $application->settings->is_force_https_enabled, $onlyPort));
$labels = $labels->merge(fqdnLabelsForTraefik(
uuid: $appUuid,
domains: $domains,
onlyPort: $onlyPort,
is_force_https_enabled: $application->isForceHttpsEnabled(),
is_gzip_enabled: $application->isGzipEnabled(),
is_stripprefix_enabled: $application->isStripprefixEnabled()
));
}
return $labels->all();
}

View File

@ -1047,7 +1047,15 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$serviceLabels = $serviceLabels->merge($defaultLabels);
if (!$isDatabase && $fqdns->count() > 0) {
if ($fqdns) {
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik($resource->uuid, $fqdns, true, serviceLabels: $serviceLabels, is_gzip_enabled: $savedService->isGzipEnabled(), service_name: $serviceName));
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
uuid: $resource->uuid,
domains: $fqdns,
is_force_https_enabled: true,
serviceLabels: $serviceLabels,
is_gzip_enabled: $savedService->isGzipEnabled(),
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
service_name: $serviceName));
}
}
if ($resource->server->isLogDrainEnabled() && $savedService->isLogDrainEnabled()) {
@ -1249,7 +1257,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
// Collect/create/update networks
if ($serviceNetworks->count() > 0) {
foreach ($serviceNetworks as $networkName => $networkDetails) {
ray($networkDetails);
$networkExists = $topLevelNetworks->contains(function ($value, $key) use ($networkName) {
return $value == $networkName || $key == $networkName;
});
@ -1405,7 +1412,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
]);
}
} else {
$generatedValue = generateEnvValue($command, $service);
$generatedValue = generateEnvValue($command);
if (!$foundEnv) {
EnvironmentVariable::create([
'key' => $key,
@ -1581,7 +1588,7 @@ function parseEnvVariable(Str|string $value)
'port' => $port,
];
}
function generateEnvValue(string $command, Service $service)
function generateEnvValue(string $command, ?Service $service = null)
{
switch ($command) {
case 'PASSWORD':

View File

@ -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.232',
'release' => '4.0.0-beta.233',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),

View File

@ -1,3 +1,3 @@
<?php
return '4.0.0-beta.232';
return '4.0.0-beta.233';

View File

@ -0,0 +1,44 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('application_settings', function (Blueprint $table) {
$table->boolean('is_gzip_enabled')->default(true);
$table->boolean('is_stripprefix_enabled')->default(true);
});
Schema::table('service_applications', function (Blueprint $table) {
$table->boolean('is_stripprefix_enabled')->default(true);
});
Schema::table('service_databases', function (Blueprint $table) {
$table->boolean('is_gzip_enabled')->default(true);
$table->boolean('is_stripprefix_enabled')->default(true);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('application_settings', function (Blueprint $table) {
$table->dropColumn('is_gzip_enabled');
$table->dropColumn('is_stripprefix_enabled');
});
Schema::table('service_applications', function (Blueprint $table) {
$table->dropColumn('is_stripprefix_enabled');
});
Schema::table('service_databases', function (Blueprint $table) {
$table->dropColumn('is_gzip_enabled');
$table->dropColumn('is_stripprefix_enabled');
});
}
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -1,8 +1,10 @@
<div class="group">
@if (data_get($application, 'fqdn') ||
@if (
(data_get($application, 'fqdn') ||
collect(json_decode($this->application->docker_compose_domains))->count() > 0 ||
data_get($application, 'previews', collect([]))->count() > 0 ||
data_get($application, 'ports_mappings_array'))
data_get($application, 'ports_mappings_array')) &&
data_get($application, 'settings.is_raw_compose_deployment_enabled') !== true)
<label tabindex="0" class="flex items-center gap-2 cursor-pointer hover:text-white"> Open Application
<x-chevron-down />
</label>

View File

@ -20,6 +20,11 @@
helper="The deployed container will have the same name ({{ $application->uuid }}). <span class='font-bold text-warning'>You will lose the rolling update feature!</span>"
instantSave id="application.settings.is_consistent_container_name_enabled"
label="Consistent Container Names" />
<x-forms.checkbox label="Enable gzip compression"
helper="You can disable gzip compression if you want. Some services are compressing data by default. In this case, you do not need this."
instantSave id="is_gzip_enabled" />
<x-forms.checkbox helper="Strip Prefix is used to remove prefixes from paths. Like /api/ to /api."
instantSave id="is_stripprefix_enabled" label="Strip Prefixes" />
<h3>Logs</h3>
@if (!$application->settings->is_raw_compose_deployment_enabled)
<x-forms.checkbox helper="Drain logs to your configured log drain endpoint in your Server settings."

View File

@ -45,9 +45,11 @@
</div>
@endif
@if ($application->build_pack === 'dockercompose')
<div class="w-96">
<x-forms.checkbox instantSave id="application.settings.is_raw_compose_deployment_enabled"
label="Raw Compose Deployment"
helper="WARNING: Advanced use cases only. Your docker compose file will be deployed as-is. Nothing is modified by Coolify. You need to configure the proxy parts. More info in the <a href='https://coolify.io/docs/docker/compose#raw-docker-compose-deployment'>documentation.</a>" />
</div>
@if (count($parsedServices) > 0 && !$application->settings->is_raw_compose_deployment_enabled)
@foreach (data_get($parsedServices, 'services') as $serviceName => $service)
@if (!isDatabaseImage(data_get($service, 'image')))

View File

@ -34,7 +34,9 @@
<h3 class="pt-2">Advanced</h3>
<div class="w-96">
<x-forms.checkbox instantSave id="application.is_gzip_enabled" label="Enable gzip compression"
helper="You can disable gzip compression if you want. Some services are compressing data by default. In this case, you do not need this." />
helper="You can disable gzip compression if you want. Some services are compressing data by default. In this case, you do not need this." />
<x-forms.checkbox instantSave id="application.is_stripprefix_enabled" label="Strip Prefixes"
helper="Strip Prefix is used to remove prefixes from paths. Like /api/ to /api." />
<x-forms.checkbox instantSave label="Exclude from service status"
helper="If you do not need to monitor this resource, enable. Useful if this service is optional."
id="application.exclude_from_status"></x-forms.checkbox>

View File

@ -1,7 +1,7 @@
<div>
<div x-init="$wire.getLogs" id="screen" x-data="{ fullscreen: false, alwaysScroll: false, intervalId: null }">
<div class="flex items-center gap-2">
@if ($resource->type() === 'application')
@if ($resource?->type() === 'application')
<h3>{{ $container }}</h3>
@else
<h3>{{ str($container)->beforeLast('-')->headline() }}</h3>

View File

@ -25,7 +25,7 @@
</div>
<div wire:loading.remove wire:target="loadProxyConfiguration">
@if ($proxy_settings)
<div class="flex flex-col gap-2 pt-2">
<div class="flex flex-col gap-2 pt-4">
<x-forms.textarea label="Configuration file: traefik.conf" name="proxy_settings"
wire:model="proxy_settings" rows="30" />
<x-forms.button wire:click.prevent="reset_proxy_configuration">

View File

@ -0,0 +1,32 @@
# documentation: https://github.com/dgtlmoon/changedetection.io/
# slogan: Website change detection monitor and notifications.
# tags: web, alert, monitor
# logo: svgs/changedetection.png
services:
changedetection:
image: ghcr.io/dgtlmoon/changedetection.io
volumes:
- changedetection-data:/datastore
environment:
- SERVICE_FQDN_CHANGEDETECTION
- PUID=1000
- PGID=1000
- BASE_URL=$SERVICE_FQDN_CHANGEDETECTION
- PLAYWRIGHT_DRIVER_URL=ws://playwright-chrome:3000/?stealth=1&--disable-web-security=true
# Hides the `Referer` header so that monitored websites can't see the changedetection.io hostname.
- HIDE_REFERER=true
depends_on:
playwright-chrome:
condition: service_started
playwright-chrome:
image: dgtlmoon/sockpuppetbrowser:latest
# cap_add:
# - SYS_ADMIN
# SYS_ADMIN might be too much, but it can be needed on your platform https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#running-puppeteer-on-gitlabci
restart: unless-stopped
environment:
- SCREEN_WIDTH=1920
- SCREEN_HEIGHT=1024
- SCREEN_DEPTH=16
- MAX_CONCURRENT_CHROME_PROCESSES=10

View File

@ -51,6 +51,18 @@
"logo": "svgs\/unknown.svg",
"minversion": "0.0.0"
},
"changedetection": {
"documentation": "https:\/\/github.com\/dgtlmoon\/changedetection.io\/",
"slogan": "Website change detection monitor and notifications.",
"compose": "c2VydmljZXM6CiAgY2hhbmdlZGV0ZWN0aW9uOgogICAgaW1hZ2U6IGdoY3IuaW8vZGd0bG1vb24vY2hhbmdlZGV0ZWN0aW9uLmlvCiAgICB2b2x1bWVzOgogICAgICAtICdjaGFuZ2VkZXRlY3Rpb24tZGF0YTovZGF0YXN0b3JlJwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0NIQU5HRURFVEVDVElPTgogICAgICAtIFBVSUQ9MTAwMAogICAgICAtIFBHSUQ9MTAwMAogICAgICAtIEJBU0VfVVJMPSRTRVJWSUNFX0ZRRE5fQ0hBTkdFREVURUNUSU9OCiAgICAgIC0gJ1BMQVlXUklHSFRfRFJJVkVSX1VSTD13czovL3BsYXl3cmlnaHQtY2hyb21lOjMwMDAvP3N0ZWFsdGg9MSYtLWRpc2FibGUtd2ViLXNlY3VyaXR5PXRydWUnCiAgICAgIC0gSElERV9SRUZFUkVSPXRydWUKICAgIGRlcGVuZHNfb246CiAgICAgIHBsYXl3cmlnaHQtY2hyb21lOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9zdGFydGVkCiAgcGxheXdyaWdodC1jaHJvbWU6CiAgICBpbWFnZTogJ2RndGxtb29uL3NvY2twdXBwZXRicm93c2VyOmxhdGVzdCcKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTQ1JFRU5fV0lEVEg9MTkyMAogICAgICAtIFNDUkVFTl9IRUlHSFQ9MTAyNAogICAgICAtIFNDUkVFTl9ERVBUSD0xNgogICAgICAtIE1BWF9DT05DVVJSRU5UX0NIUk9NRV9QUk9DRVNTRVM9MTAK",
"tags": [
"web",
"alert",
"monitor"
],
"logo": "svgs\/changedetection.png",
"minversion": "0.0.0"
},
"code-server": {
"documentation": "https:\/\/coder.com\/docs\/code-server\/latest",
"slogan": "Code-Server is a web-based code editor that enables remote coding and collaboration from any device, anywhere.",

View File

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