Merge branch 'next' into alpine_support

This commit is contained in:
Loïc Tosser
2024-09-09 08:48:36 +04:00
committed by GitHub
12 changed files with 374 additions and 285 deletions

View File

@@ -38,6 +38,8 @@ jobs:
platforms: linux/amd64
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
labels: |
coolify.managed=true
aarch64:
runs-on: [ self-hosted, arm64 ]
permissions:
@@ -64,6 +66,8 @@ jobs:
platforms: linux/aarch64
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64
labels: |
coolify.managed=true
merge-manifest:
runs-on: ubuntu-latest
permissions:
@@ -94,3 +98,4 @@ jobs:
if: always()
with:
webhook: ${{ secrets.DISCORD_WEBHOOK_DEV_RELEASE_CHANNEL }}

View File

@@ -2,7 +2,7 @@ name: Coolify Helper Image (v4)
on:
push:
branches: [ "main" ]
branches: [ "main", "next" ]
paths:
- .github/workflows/coolify-helper.yml
- docker/coolify-helper/Dockerfile
@@ -38,6 +38,8 @@ jobs:
platforms: linux/amd64
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
labels: |
coolify.managed=true
aarch64:
runs-on: [ self-hosted, arm64 ]
permissions:
@@ -64,6 +66,8 @@ jobs:
platforms: linux/aarch64
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
labels: |
coolify.managed=true
merge-manifest:
runs-on: ubuntu-latest
permissions:

View File

@@ -23,7 +23,7 @@ class CleanupDocker
{
$commonCommands = [
'docker container prune -f --filter "label=coolify.managed=true"',
'docker image prune -af',
'docker image prune -af --filter "label!=coolify.managed=true"',
'docker builder prune -af',
];

View File

@@ -25,6 +25,7 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Str;
use App\Models\InstanceSettings;
class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{
@@ -493,12 +494,15 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
} else {
$network = $this->database->destination->network;
}
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper";
$this->ensureHelperImageAvailable();
$fullImageName = $this->getFullImageName();
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
instant_remote_process($commands, $this->server);
$this->add_to_backup_output('Uploaded to S3.');
ray('Uploaded to S3. '.$this->backup_location.' to s3://'.$bucket.$this->backup_dir);
} catch (\Throwable $e) {
$this->add_to_backup_output($e->getMessage());
throw $e;
@@ -507,4 +511,40 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
instant_remote_process([$command], $this->server);
}
}
private function ensureHelperImageAvailable(): void
{
$fullImageName = $this->getFullImageName();
$imageExists = $this->checkImageExists($fullImageName);
if (!$imageExists) {
$this->pullHelperImage($fullImageName);
}
}
private function checkImageExists(string $fullImageName): bool
{
$result = instant_remote_process(["docker image inspect {$fullImageName} >/dev/null 2>&1 && echo 'exists' || echo 'not exists'"], $this->server, false);
return trim($result) === 'exists';
}
private function pullHelperImage(string $fullImageName): void
{
try {
instant_remote_process(["docker pull {$fullImageName}"], $this->server);
} catch (\Exception $e) {
$errorMessage = "Failed to pull helper image: " . $e->getMessage();
$this->add_to_backup_output($errorMessage);
throw new \RuntimeException($errorMessage);
}
}
private function getFullImageName(): string
{
$settings = InstanceSettings::get();
$helperImage = config('coolify.helper_image');
$latestVersion = $settings->helper_version;
return "{$helperImage}:{$latestVersion}";
}
}

View File

@@ -2864,6 +2864,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
return collect($finalServices);
}
}
function newParser(Application|Service $resource, int $pull_request_id = 0, ?int $preview_id = null): Collection
{
$isApplication = $resource instanceof Application;
@@ -2920,6 +2921,178 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
}
$parsedServices = collect([]);
// parse environments first
$allMagicEnvironments = collect([]);
foreach ($services as $serviceName => $service) {
$magicEnvironments = collect([]);
$image = data_get_str($service, 'image');
$environment = collect(data_get($service, 'environment', []));
$buildArgs = collect(data_get($service, 'build.args', []));
$environment = $environment->merge($buildArgs);
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
if ($isService) {
if ($isDatabase) {
$savedService = ServiceDatabase::firstOrCreate([
'name' => $serviceName,
'image' => $image,
'service_id' => $resource->id,
]);
} else {
$savedService = ServiceApplication::firstOrCreate([
'name' => $serviceName,
'image' => $image,
'service_id' => $resource->id,
]);
}
$environment = collect(data_get($service, 'environment', []));
$buildArgs = collect(data_get($service, 'build.args', []));
$environment = $environment->merge($buildArgs);
// convert environment variables to one format
$environment = convertComposeEnvironmentToArray($environment);
// Add Coolify defined environments
$allEnvironments = $resource->environment_variables()->get(['key', 'value']);
$allEnvironments = $allEnvironments->mapWithKeys(function ($item) {
return [$item['key'] => $item['value']];
});
// filter magic environments
foreach ($environment as $key => $value) {
$regex = '/\$(\{?([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\}?)/';
preg_match_all($regex, $value, $valueMatches);
preg_match_all($regex, $key, $keyMatches);
if (count($valueMatches[1]) > 0) {
foreach ($valueMatches[1] as $match) {
if (str($match)->startsWith('SERVICE_')) {
if ($magicEnvironments->has($match)) {
continue;
}
$magicEnvironments->put($match, '');
}
}
}
if (count($keyMatches[1]) > 0) {
foreach ($keyMatches[1] as $match) {
if (str($match)->startsWith('SERVICE_')) {
if ($magicEnvironments->has($match)) {
continue;
}
$magicEnvironments->put($match, '');
}
}
}
if (str($key)->startsWith('SERVICE_')) {
$magicEnvironments->put($key, $value);
if (substr_count(str($key)->value(), '_') === 3) {
$newKey = str($key)->beforeLast('_')->value();
if ($newKey) {
$magicEnvironments->put($newKey, $value);
}
}
}
}
$allMagicEnvironments = $allMagicEnvironments->merge($magicEnvironments);
if ($magicEnvironments->count() > 0) {
foreach ($magicEnvironments as $key => $value) {
$key = str($key);
$value = str(replaceVariables(str($value)));
$originalValue = $value;
$keyCommand = $key->after('SERVICE_')->before('_');
$valueCommand = $value->after('SERVICE_')->before('_');
if ($key->startsWith('SERVICE_FQDN_')) {
$fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value();
if (str($fqdnFor)->contains('_')) {
$fqdnFor = str($fqdnFor)->before('_');
}
} elseif ($value->startsWith('SERVICE_FQDN_')) {
$fqdnFor = $value->after('SERVICE_FQDN_')->lower()->value();
if (str($fqdnFor)->contains('_')) {
$fqdnFor = str($fqdnFor)->before('_');
}
} else {
$fqdnFor = null;
}
if ($keyCommand->value() === 'FQDN' || $valueCommand->value() === 'FQDN') {
if ($isApplication) {
$fqdn = generateFqdn($server, "{$resource->name}-$uuid");
} elseif ($isService) {
if ($fqdnFor) {
$fqdn = generateFqdn($server, "$fqdnFor-$uuid");
} else {
$fqdn = generateFqdn($server, "{$savedService->name}-$uuid");
}
}
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
$path = $value->value();
if ($value === '/') {
$value = "$fqdn";
} else {
$value = "$fqdn$path";
}
} else {
$value = $fqdn;
}
if (! $isDatabase) {
if ($key->startsWith('SERVICE_FQDN_') && ($originalValue->value() === '' || $originalValue->startsWith('/'))) {
if ($isApplication && is_null($resource->fqdn)) {
data_forget($resource, 'environment_variables');
data_forget($resource, 'environment_variables_preview');
$resource->fqdn = $value;
$resource->save();
} elseif ($isService && is_null($savedService->fqdn)) {
if ($key->startsWith('SERVICE_FQDN_')) {
$savedService->fqdn = $value;
$savedService->save();
}
}
}
}
} elseif ($keyCommand->value() === 'URL' || $valueCommand->value() === 'URL') {
if ($isApplication) {
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
} elseif ($isService) {
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
}
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
$path = $value->value();
$value = "$fqdn$path";
} else {
$value = $fqdn;
}
$value = str($fqdn)->replace('http://', '')->replace('https://', '');
} else {
$generatedValue = generateEnvValue($keyCommand, $resource);
if ($generatedValue) {
$value = $generatedValue;
}
}
if (str($fqdnFor)->startsWith('/')) {
$fqdnFor = null;
}
// Lets save the magic value to the environment variables
if (! $originalValue->startsWith('/')) {
if ($key->startsWith('SERVICE_')) {
$originalValue = $key;
}
$resource->environment_variables()->where('key', $originalValue->value())->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $originalValue->value(),
$nameOfId => $resource->id,
], [
'value' => $value,
'is_build_time' => false,
'is_preview' => false,
]);
}
}
}
}
}
// Parse the rest of the services
foreach ($services as $serviceName => $service) {
$image = data_get_str($service, 'image');
$restart = data_get_str($service, 'restart', RESTART_MODE);
@@ -2938,6 +3111,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$ports = collect(data_get($service, 'ports', []));
$buildArgs = collect(data_get($service, 'build.args', []));
$environment = $environment->merge($buildArgs);
$environment = convertComposeEnvironmentToArray($environment);
$coolifyEnvironments = collect([]);
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
$volumesParsed = collect([]);
@@ -3069,10 +3246,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
if ($topLevel->get('volumes')->has($source->value())) {
$temp = $topLevel->get('volumes')->get($source->value());
if (data_get($temp, 'driver_opts.type') === 'cifs') {
return $volume;
continue;
}
if (data_get($temp, 'driver_opts.type') === 'nfs') {
return $volume;
continue;
}
}
$slugWithoutUuid = Str::slug($source, '-');
@@ -3204,176 +3381,48 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
]);
}
}
// convert environment variables to one format
$environment = convertComposeEnvironmentToArray($environment);
// Add Coolify defined environments
$allEnvironments = $resource->environment_variables()->get(['key', 'value']);
$allEnvironments = $allEnvironments->mapWithKeys(function ($item) {
return [$item['key'] => $item['value']];
$normalEnvironments = $environment->diffKeys($allMagicEnvironments);
$normalEnvironments = $normalEnvironments->filter(function ($value, $key) {
return ! str($value)->startsWith('SERVICE_');
});
// remove $environment from $allEnvironments
$coolifyDefinedEnvironments = $allEnvironments->diffKeys($environment);
// filter magic environments
$magicEnvironments = $environment->filter(function ($value, $key) {
$regex = '/\$\{(.*?)\}/';
preg_match_all($regex, $value, $matches);
if (count($matches[1]) > 0) {
foreach ($matches[1] as $match) {
if (str($match)->startsWith('SERVICE_') || str($match)->startsWith('SERVICE_')) {
return $match;
}
}
}
$value = str(replaceVariables(str($value)));
return str($key)->startsWith('SERVICE_') || str($value)->startsWith('SERVICE_');
});
foreach ($environment as $key => $value) {
$regex = '/\$\{(.*?)\}/';
preg_match_all($regex, $value, $matches);
if (count($matches[1]) > 0) {
foreach ($matches[1] as $match) {
if (str($match)->startsWith('SERVICE_') || str($match)->startsWith('SERVICE_')) {
$magicEnvironments->put($match, '$'.$match);
}
}
$magicEnvironments->forget($key);
}
}
$normalEnvironments = $environment->diffKeys($magicEnvironments);
if ($magicEnvironments->count() > 0) {
foreach ($magicEnvironments as $key => $value) {
foreach ($normalEnvironments as $key => $value) {
$key = str($key);
$value = str(replaceVariables(str($value)));
$originalValue = $value;
$keyCommand = $key->after('SERVICE_')->before('_');
$valueCommand = $value->after('SERVICE_')->before('_');
if ($key->startsWith('SERVICE_FQDN_')) {
$fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value();
if (str($fqdnFor)->contains('_')) {
$fqdnFor = str($fqdnFor)->before('_');
}
} elseif ($value->startsWith('SERVICE_FQDN_')) {
$fqdnFor = $value->after('SERVICE_FQDN_')->lower()->value();
if (str($fqdnFor)->contains('_')) {
$fqdnFor = str($fqdnFor)->before('_');
}
} else {
$fqdnFor = null;
}
if ($keyCommand->value() === 'FQDN' || $valueCommand->value() === 'FQDN') {
if ($isApplication) {
$fqdn = generateFqdn($server, "{$resource->name}-$uuid");
} elseif ($isService) {
if ($fqdnFor) {
$fqdn = generateFqdn($server, "$fqdnFor-$uuid");
} else {
$fqdn = generateFqdn($server, "{$savedService->name}-$uuid");
}
}
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
$path = $value->value();
if ($value === '/') {
$value = "$fqdn";
} else {
$value = "$fqdn$path";
}
} else {
$value = $fqdn;
}
if (! $isDatabase) {
if ($key->startsWith('SERVICE_FQDN_') && ($originalValue->value() === '' || $originalValue->startsWith('/'))) {
if ($isApplication && is_null($resource->fqdn)) {
data_forget($resource, 'environment_variables');
data_forget($resource, 'environment_variables_preview');
$resource->fqdn = $value;
$resource->save();
} elseif ($isService && is_null($savedService->fqdn)) {
if ($key->startsWith('SERVICE_FQDN_')) {
$savedService->fqdn = $value;
$savedService->save();
}
}
}
}
$value = str($value);
$parsedValue = str(replaceVariables(str($value)));
} elseif ($keyCommand->value() === 'URL' || $valueCommand->value() === 'URL') {
if ($isApplication) {
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
} elseif ($isService) {
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
}
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
$path = $value->value();
$value = "$fqdn$path";
} else {
$value = $fqdn;
}
$value = str($fqdn)->replace('http://', '')->replace('https://', '');
} else {
$generatedValue = generateEnvValue($valueCommand, $resource);
if ($generatedValue) {
$value = $generatedValue;
}
}
if (str($fqdnFor)->startsWith('/')) {
$fqdnFor = null;
}
// Lets save the magic value to the environment variables
if (! $originalValue->startsWith('/')) {
if ($key->startsWith('SERVICE_')) {
$originalValue = $key;
}
$resource->environment_variables()->where('key', $originalValue->value())->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $originalValue->value(),
if ($key->value() === $parsedValue->value()) {
$value = null;
$resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $key,
$nameOfId => $resource->id,
], [
'value' => $value,
'is_build_time' => false,
'is_preview' => false,
]);
}
// Save the original value to the environment variables
if ($originalValue->startsWith('SERVICE_')) {
$value = "$$originalValue";
}
$resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $key->value(),
$nameOfId => $resource->id,
], [
'value' => "$value",
'is_build_time' => false,
'is_preview' => false,
]);
}
}
foreach ($normalEnvironments as $key => $value) {
$key = str($key);
$value = str($value);
if ($value->startsWith('$') || $value->contains('${')) {
if ($value->contains('${')) {
$value = $value->after('${')->before('}');
}
$value = str(replaceVariables(str($value)));
} else {
if ($value->startsWith('$')) {
if ($value->contains(':-')) {
$value = str(replaceVariables(str($value)));
$key = $value->before(':');
$value = $value->after(':-');
} elseif ($value->contains('-')) {
$value = str(replaceVariables(str($value)));
$key = $value->before('-');
$value = $value->after('-');
} elseif ($value->contains(':?')) {
$value = str(replaceVariables(str($value)));
$key = $value->before(':');
$value = $value->after(':?');
} elseif ($value->contains('?')) {
$value = str(replaceVariables(str($value)));
$key = $value->before('?');
$value = $value->after('?');
} else {
$key = $value;
$value = null;
}
$resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
'key' => $key,
@@ -3384,6 +3433,8 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
'is_preview' => false,
]);
}
}
}
if ($isApplication) {
$branch = $originalResource->git_branch;
@@ -3391,13 +3442,13 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$branch = "pull/{$pullRequestId}/head";
}
if ($originalResource->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
$environment->put('COOLIFY_BRANCH', $branch);
$coolifyEnvironments->put('COOLIFY_BRANCH', $branch);
}
}
// Add COOLIFY_CONTAINER_NAME to environment
if ($resource->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
$environment->put('COOLIFY_CONTAINER_NAME', $containerName);
$coolifyEnvironments->put('COOLIFY_CONTAINER_NAME', $containerName);
}
if ($isApplication) {
@@ -3451,15 +3502,20 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
}
// Add COOLIFY_FQDN & COOLIFY_URL to environment
if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
$environment->put('COOLIFY_URL', $fqdns->implode(','));
$coolifyEnvironments->put('COOLIFY_URL', $fqdns->implode(','));
$urls = $fqdns->map(function ($fqdn) {
return str($fqdn)->replace('http://', '')->replace('https://', '');
});
$environment->put('COOLIFY_FQDN', $urls->implode(','));
$coolifyEnvironments->put('COOLIFY_FQDN', $urls->implode(','));
}
add_coolify_default_environment_variables($resource, $environment, $resource->environment_variables);
add_coolify_default_environment_variables($resource, $coolifyEnvironments, $resource->environment_variables);
if ($environment->count() > 0) {
$environment = $environment->filter(function ($value, $key) {
return ! str($key)->startsWith('SERVICE_FQDN_');
});
}
$serviceLabels = $labels->merge($defaultLabels);
if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
if ($isApplication) {
@@ -3554,8 +3610,8 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
if ($volumesParsed->count() > 0) {
$payload['volumes'] = $volumesParsed;
}
if ($environment->count() > 0 || $coolifyDefinedEnvironments->count() > 0) {
$payload['environment'] = $environment->merge($coolifyDefinedEnvironments);
if ($environment->count() > 0 || $coolifyEnvironments->count() > 0) {
$payload['environment'] = $environment->merge($coolifyEnvironments);
}
if ($logging) {
$payload['logging'] = $logging;

View File

@@ -7,7 +7,7 @@ return [
// 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.331',
'release' => '4.0.0-beta.332',
// 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.331';
return '4.0.0-beta.332';

View File

@@ -0,0 +1,37 @@
<?php
use App\Models\ServerSetting;
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('server_settings', function (Blueprint $table) {
$table->string('docker_cleanup_frequency')->default('0 0 * * *')->change();
});
$serverSettings = ServerSetting::all();
foreach ($serverSettings as $serverSetting) {
if ($serverSetting->force_docker_cleanup && $serverSetting->docker_cleanup_frequency === '*/10 * * * *') {
$serverSetting->docker_cleanup_frequency = '0 0 * * *';
$serverSetting->save();
}
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('server_settings', function (Blueprint $table) {
$table->string('docker_cleanup_frequency')->default('*/10 * * * *')->change();
});
}
};

View File

@@ -16,7 +16,7 @@ services:
environment:
- SERVICE_FQDN_PLUNK_3000
- REDIS_URL=redis://redis:6379
- DATABASE_URL=postgresql://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@postgresql/docmost?schema=public
- DATABASE_URL=postgresql://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@postgresql/plunk?schema=public
- JWT_SECRET=${SERVICE_PASSWORD_JWT_SECRET}
- AWS_REGION=${AWS_REGION}
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}

File diff suppressed because one or more lines are too long

View File

@@ -88,114 +88,61 @@ networks:
]);
$this->serviceYaml = '
services:
chatwoot:
image: chatwoot/chatwoot:latest
depends_on:
- postgres
- redis
activepieces:
image: "ghcr.io/activepieces/activepieces:latest"
environment:
- SERVICE_FQDN_CHATWOOT_3000
- SECRET_KEY_BASE=$SERVICE_PASSWORD_CHATWOOT
- FRONTEND_URL=${SERVICE_FQDN_CHATWOOT}
- DEFAULT_LOCALE=${CHATWOOT_DEFAULT_LOCALE}
- FORCE_SSL=false
- ENABLE_ACCOUNT_SIGNUP=false
- REDIS_URL=redis://default@redis:6379
- REDIS_PASSWORD=$SERVICE_PASSWORD_REDIS
- REDIS_OPENSSL_VERIFY_MODE=none
- POSTGRES_DATABASE=chatwoot
- POSTGRES_HOST=postgres
- POSTGRES_USERNAME=$SERVICE_USER_POSTGRES_USER
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
- RAILS_MAX_THREADS=5
- NODE_ENV=production
- RAILS_ENV=production
- INSTALLATION_ENV=docker
- MAILER_SENDER_EMAIL=${CHATWOOT_MAILER_SENDER_EMAIL}
- SMTP_ADDRESS=${CHATWOOT_SMTP_ADDRESS}
- SMTP_AUTHENTICATION=${CHATWOOT_SMTP_AUTHENTICATION}
- SMTP_DOMAIN=${CHATWOOT_SMTP_DOMAIN}
- SMTP_ENABLE_STARTTLS_AUTO=${CHATWOOT_SMTP_ENABLE_STARTTLS_AUTO}
- SMTP_PORT=${CHATWOOT_SMTP_PORT}
- SMTP_USERNAME=${CHATWOOT_SMTP_USERNAME}
- SMTP_PASSWORD=${CHATWOOT_SMTP_PASSWORD}
- ACTIVE_STORAGE_SERVICE=local
entrypoint: docker/entrypoints/rails.sh
command: sh -c "bundle exec rails db:chatwoot_prepare && bundle exec rails s -p 3000 -b 0.0.0.0"
volumes:
- rails-data:/app/storage
- SERVICE_FQDN_ACTIVEPIECES
- AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY
- AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY
- AP_ENGINE_EXECUTABLE_PATH=dist/packages/engine/main.js
- AP_ENVIRONMENT=prod
- AP_EXECUTION_MODE=UNSANDBOXED
- AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES
- AP_JWT_SECRET=$SERVICE_PASSWORD_64_JWT
- AP_POSTGRES_DATABASE=activepieces
- AP_POSTGRES_HOST=postgres
- AP_POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
- AP_POSTGRES_PORT=5432
- AP_POSTGRES_USERNAME=$SERVICE_USER_POSTGRES
- AP_REDIS_HOST=redis
- AP_REDIS_PORT=6379
- AP_SANDBOX_RUN_TIME_SECONDS=600
- AP_TELEMETRY_ENABLED=true
- "AP_TEMPLATES_SOURCE_URL=https://cloud.activepieces.com/api/v1/flow-templates"
- AP_TRIGGER_DEFAULT_POLL_INTERVAL=5
- AP_WEBHOOK_TIMEOUT_SECONDS=30
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:3000"]
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
interval: 5s
timeout: 20s
retries: 10
sidekiq:
image: chatwoot/chatwoot:latest
depends_on:
- postgres
- redis
environment:
- SECRET_KEY_BASE=$SERVICE_PASSWORD_CHATWOOT
- FRONTEND_URL=${SERVICE_FQDN_CHATWOOT}
- DEFAULT_LOCALE=${CHATWOOT_DEFAULT_LOCALE}
- FORCE_SSL=false
- ENABLE_ACCOUNT_SIGNUP=false
- REDIS_URL=redis://default@redis:6379
- REDIS_PASSWORD=$SERVICE_PASSWORD_REDIS
- REDIS_OPENSSL_VERIFY_MODE=none
- POSTGRES_DATABASE=chatwoot
- POSTGRES_HOST=postgres
- POSTGRES_USERNAME=$SERVICE_USER_POSTGRES_USER
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
- RAILS_MAX_THREADS=5
- NODE_ENV=production
- RAILS_ENV=production
- INSTALLATION_ENV=docker
- MAILER_SENDER_EMAIL=${CHATWOOT_MAILER_SENDER_EMAIL}
- SMTP_ADDRESS=${CHATWOOT_SMTP_ADDRESS}
- SMTP_AUTHENTICATION=${CHATWOOT_SMTP_AUTHENTICATION}
- SMTP_DOMAIN=${CHATWOOT_SMTP_DOMAIN}
- SMTP_ENABLE_STARTTLS_AUTO=${CHATWOOT_SMTP_ENABLE_STARTTLS_AUTO}
- SMTP_PORT=${CHATWOOT_SMTP_PORT}
- SMTP_USERNAME=${CHATWOOT_SMTP_USERNAME}
- SMTP_PASSWORD=${CHATWOOT_SMTP_PASSWORD}
- ACTIVE_STORAGE_SERVICE=local
command: ["bundle", "exec", "sidekiq", "-C", "config/sidekiq.yml"]
volumes:
- sidekiq-data:/app/storage
healthcheck:
test: ["CMD-SHELL", "bundle exec rails runner \'puts Sidekiq.redis(&:info)\' > /dev/null 2>&1"]
interval: 30s
timeout: 10s
retries: 3
postgres:
image: postgres:12
restart: always
volumes:
- postgres-data:/var/lib/postgresql/data
image: "postgres:latest"
environment:
- POSTGRES_DB=chatwoot
- POSTGRES_USER=$SERVICE_USER_POSTGRES_USER
- POSTGRES_DB=activepieces
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $SERVICE_USER_POSTGRES_USER -d chatwoot -h 127.0.0.1"]
interval: 30s
timeout: 10s
retries: 5
redis:
image: redis:alpine
restart: always
command: ["sh", "-c", "redis-server --requirepass \"$SERVICE_PASSWORD_REDIS\""]
- POSTGRES_USER=$SERVICE_USER_POSTGRES
volumes:
- redis-data:/data
- "pg-data:/var/lib/postgresql/data"
healthcheck:
test: ["CMD", "redis-cli", "-a", "$SERVICE_PASSWORD_REDIS", "PING"]
interval: 30s
timeout: 10s
retries: 5
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 20s
retries: 10
redis:
image: "redis:latest"
volumes:
- "redis_data:/data"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 20s
retries: 10
';

View File

@@ -1,13 +1,13 @@
{
"coolify": {
"v4": {
"version": "4.0.0-beta.331"
},
"nightly": {
"version": "4.0.0-beta.332"
},
"nightly": {
"version": "4.0.0-beta.333"
},
"helper": {
"version": "1.0.0"
"version": "1.0.1"
}
}
}