Merge pull request #3683 from coollabsio/next

v4.0.0-beta.351
This commit is contained in:
Andras Bacsai
2024-10-03 15:15:21 +02:00
committed by GitHub
32 changed files with 417 additions and 88 deletions

View File

@@ -744,8 +744,10 @@ class ApplicationsController extends Controller
$application->destination_id = $destination->id; $application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass(); $application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id; $application->environment_id = $environment->id;
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer; $application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save(); $application->settings->save();
}
$application->save(); $application->save();
$application->refresh(); $application->refresh();
if (! $application->settings->is_container_label_readonly_enabled) { if (! $application->settings->is_container_label_readonly_enabled) {
@@ -842,8 +844,10 @@ class ApplicationsController extends Controller
$application->environment_id = $environment->id; $application->environment_id = $environment->id;
$application->source_type = $githubApp->getMorphClass(); $application->source_type = $githubApp->getMorphClass();
$application->source_id = $githubApp->id; $application->source_id = $githubApp->id;
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer; $application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save(); $application->settings->save();
}
$application->save(); $application->save();
$application->refresh(); $application->refresh();
if (! $application->settings->is_container_label_readonly_enabled) { if (! $application->settings->is_container_label_readonly_enabled) {
@@ -936,8 +940,10 @@ class ApplicationsController extends Controller
$application->destination_id = $destination->id; $application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass(); $application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id; $application->environment_id = $environment->id;
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer; $application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save(); $application->settings->save();
}
$application->save(); $application->save();
$application->refresh(); $application->refresh();
if (! $application->settings->is_container_label_readonly_enabled) { if (! $application->settings->is_container_label_readonly_enabled) {
@@ -1017,8 +1023,10 @@ class ApplicationsController extends Controller
$application->destination_id = $destination->id; $application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass(); $application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id; $application->environment_id = $environment->id;
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer; $application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save(); $application->settings->save();
}
$application->git_repository = 'coollabsio/coolify'; $application->git_repository = 'coollabsio/coolify';
$application->git_branch = 'main'; $application->git_branch = 'main';
@@ -1077,8 +1085,10 @@ class ApplicationsController extends Controller
$application->destination_id = $destination->id; $application->destination_id = $destination->id;
$application->destination_type = $destination->getMorphClass(); $application->destination_type = $destination->getMorphClass();
$application->environment_id = $environment->id; $application->environment_id = $environment->id;
if (isset($useBuildServer)) {
$application->settings->is_build_server_enabled = $useBuildServer; $application->settings->is_build_server_enabled = $useBuildServer;
$application->settings->save(); $application->settings->save();
}
$application->git_repository = 'coollabsio/coolify'; $application->git_repository = 'coollabsio/coolify';
$application->git_branch = 'main'; $application->git_branch = 'main';
@@ -1555,8 +1565,11 @@ class ApplicationsController extends Controller
$instantDeploy = $request->instant_deploy; $instantDeploy = $request->instant_deploy;
$use_build_server = $request->use_build_server; $use_build_server = $request->use_build_server;
if (isset($use_build_server)) {
$application->settings->is_build_server_enabled = $use_build_server; $application->settings->is_build_server_enabled = $use_build_server;
$application->settings->save(); $application->settings->save();
}
removeUnnecessaryFieldsFromRequest($request); removeUnnecessaryFieldsFromRequest($request);

View File

@@ -243,6 +243,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
try { try {
if (str($databaseType)->contains('postgres')) { if (str($databaseType)->contains('postgres')) {
$this->backup_file = "/pg-dump-$database-".Carbon::now()->timestamp.'.dmp'; $this->backup_file = "/pg-dump-$database-".Carbon::now()->timestamp.'.dmp';
if ($this->backup->dump_all) {
$this->backup_file = '/pg-dump-all-'.Carbon::now()->timestamp.'.gz';
}
$this->backup_location = $this->backup_dir.$this->backup_file; $this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([ $this->backup_log = ScheduledDatabaseBackupExecution::create([
'database_name' => $database, 'database_name' => $database,
@@ -271,6 +274,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
$this->backup_standalone_mongodb($database); $this->backup_standalone_mongodb($database);
} elseif (str($databaseType)->contains('mysql')) { } elseif (str($databaseType)->contains('mysql')) {
$this->backup_file = "/mysql-dump-$database-".Carbon::now()->timestamp.'.dmp'; $this->backup_file = "/mysql-dump-$database-".Carbon::now()->timestamp.'.dmp';
if ($this->backup->dump_all) {
$this->backup_file = '/mysql-dump-all-'.Carbon::now()->timestamp.'.gz';
}
$this->backup_location = $this->backup_dir.$this->backup_file; $this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([ $this->backup_log = ScheduledDatabaseBackupExecution::create([
'database_name' => $database, 'database_name' => $database,
@@ -280,6 +286,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
$this->backup_standalone_mysql($database); $this->backup_standalone_mysql($database);
} elseif (str($databaseType)->contains('mariadb')) { } elseif (str($databaseType)->contains('mariadb')) {
$this->backup_file = "/mariadb-dump-$database-".Carbon::now()->timestamp.'.dmp'; $this->backup_file = "/mariadb-dump-$database-".Carbon::now()->timestamp.'.dmp';
if ($this->backup->dump_all) {
$this->backup_file = '/mariadb-dump-all-'.Carbon::now()->timestamp.'.gz';
}
$this->backup_location = $this->backup_dir.$this->backup_file; $this->backup_location = $this->backup_dir.$this->backup_file;
$this->backup_log = ScheduledDatabaseBackupExecution::create([ $this->backup_log = ScheduledDatabaseBackupExecution::create([
'database_name' => $database, 'database_name' => $database,
@@ -379,7 +388,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
if ($this->postgres_password) { if ($this->postgres_password) {
$backupCommand .= " -e PGPASSWORD=$this->postgres_password"; $backupCommand .= " -e PGPASSWORD=$this->postgres_password";
} }
if ($this->backup->dump_all) {
$backupCommand .= " $this->container_name pg_dumpall --username {$this->database->postgres_user} | gzip > $this->backup_location";
} else {
$backupCommand .= " $this->container_name pg_dump --format=custom --no-acl --no-owner --username {$this->database->postgres_user} $database > $this->backup_location"; $backupCommand .= " $this->container_name pg_dump --format=custom --no-acl --no-owner --username {$this->database->postgres_user} $database > $this->backup_location";
}
$commands[] = $backupCommand; $commands[] = $backupCommand;
ray($commands); ray($commands);
@@ -400,8 +413,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{ {
try { try {
$commands[] = 'mkdir -p '.$this->backup_dir; $commands[] = 'mkdir -p '.$this->backup_dir;
if ($this->backup->dump_all) {
$commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} --all-databases --single-transaction --quick --lock-tables=false --compress | gzip > $this->backup_location";
} else {
$commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location"; $commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location";
ray($commands); }
$this->backup_output = instant_remote_process($commands, $this->server); $this->backup_output = instant_remote_process($commands, $this->server);
$this->backup_output = trim($this->backup_output); $this->backup_output = trim($this->backup_output);
if ($this->backup_output === '') { if ($this->backup_output === '') {
@@ -419,7 +435,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{ {
try { try {
$commands[] = 'mkdir -p '.$this->backup_dir; $commands[] = 'mkdir -p '.$this->backup_dir;
if ($this->backup->dump_all) {
$commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} --all-databases --single-transaction --quick --lock-tables=false --compress > $this->backup_location";
} else {
$commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location"; $commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location";
}
ray($commands); ray($commands);
$this->backup_output = instant_remote_process($commands, $this->server); $this->backup_output = instant_remote_process($commands, $this->server);
$this->backup_output = trim($this->backup_output); $this->backup_output = trim($this->backup_output);

View File

@@ -31,6 +31,7 @@ class BackupEdit extends Component
'backup.save_s3' => 'required|boolean', 'backup.save_s3' => 'required|boolean',
'backup.s3_storage_id' => 'nullable|integer', 'backup.s3_storage_id' => 'nullable|integer',
'backup.databases_to_backup' => 'nullable', 'backup.databases_to_backup' => 'nullable',
'backup.dump_all' => 'required|boolean',
]; ];
protected $validationAttributes = [ protected $validationAttributes = [
@@ -40,6 +41,7 @@ class BackupEdit extends Component
'backup.save_s3' => 'Save to S3', 'backup.save_s3' => 'Save to S3',
'backup.s3_storage_id' => 'S3 Storage', 'backup.s3_storage_id' => 'S3 Storage',
'backup.databases_to_backup' => 'Databases to Backup', 'backup.databases_to_backup' => 'Databases to Backup',
'backup.dump_all' => 'Backup All Databases',
]; ];
protected $messages = [ protected $messages = [

View File

@@ -108,6 +108,21 @@ class Navbar extends Component
return; return;
} }
StopService::run(service: $this->service, dockerCleanup: false);
$this->service->parse();
$this->dispatch('imagePulled');
$activity = StartService::run($this->service);
$this->dispatch('activityMonitor', $activity->id);
}
public function pullAndRestartEvent()
{
$this->checkDeployments();
if ($this->isDeploymentProgress) {
$this->dispatch('error', 'There is a deployment in progress.');
return;
}
PullImage::run($this->service); PullImage::run($this->service);
StopService::run(service: $this->service, dockerCleanup: false); StopService::run(service: $this->service, dockerCleanup: false);
$this->service->parse(); $this->service->parse();

View File

@@ -770,9 +770,34 @@ class Service extends BaseModel
} }
$fields->put('Code Server', $data->toArray()); $fields->put('Code Server', $data->toArray());
break; break;
case str($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;
} }
} }
$databases = $this->databases()->get(); $databases = $this->databases()->get();
ray($databases);
foreach ($databases as $database) { foreach ($databases as $database) {
$image = str($database->image)->before(':')->value(); $image = str($database->image)->before(':')->value();
@@ -1108,7 +1133,6 @@ class Service extends BaseModel
$real_value = escapeEnvVariables($env->real_value); $real_value = escapeEnvVariables($env->real_value);
} }
} }
ray("echo \"{$env->key}={$real_value}\" >> .env");
$commands[] = "echo \"{$env->key}={$real_value}\" >> .env"; $commands[] = "echo \"{$env->key}={$real_value}\" >> .env";
} }
} }

View File

@@ -115,4 +115,12 @@ class ServiceDatabase extends BaseModel
{ {
return $this->morphMany(ScheduledDatabaseBackup::class, 'database'); return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
} }
public function isBackupSolutionAvailable()
{
return str($this->databaseType())->contains('mysql') ||
str($this->databaseType())->contains('postgres') ||
str($this->databaseType())->contains('mariadb') ||
str($this->databaseType())->contains('mongodb');
}
} }

View File

@@ -294,4 +294,9 @@ class StandaloneClickhouse extends BaseModel
return $parsedCollection->toArray(); return $parsedCollection->toArray();
} }
} }
public function isBackupSolutionAvailable()
{
return false;
}
} }

View File

@@ -294,4 +294,9 @@ class StandaloneDragonfly extends BaseModel
return $parsedCollection->toArray(); return $parsedCollection->toArray();
} }
} }
public function isBackupSolutionAvailable()
{
return false;
}
} }

View File

@@ -294,4 +294,9 @@ class StandaloneKeydb extends BaseModel
return $parsedCollection->toArray(); return $parsedCollection->toArray();
} }
} }
public function isBackupSolutionAvailable()
{
return false;
}
} }

View File

@@ -294,4 +294,9 @@ class StandaloneMariadb extends BaseModel
return $parsedCollection->toArray(); return $parsedCollection->toArray();
} }
} }
public function isBackupSolutionAvailable()
{
return true;
}
} }

View File

@@ -314,4 +314,9 @@ class StandaloneMongodb extends BaseModel
return $parsedCollection->toArray(); return $parsedCollection->toArray();
} }
} }
public function isBackupSolutionAvailable()
{
return true;
}
} }

View File

@@ -295,4 +295,9 @@ class StandaloneMysql extends BaseModel
return $parsedCollection->toArray(); return $parsedCollection->toArray();
} }
} }
public function isBackupSolutionAvailable()
{
return true;
}
} }

View File

@@ -296,4 +296,9 @@ class StandalonePostgresql extends BaseModel
return $parsedCollection->toArray(); return $parsedCollection->toArray();
} }
} }
public function isBackupSolutionAvailable()
{
return true;
}
} }

View File

@@ -290,4 +290,9 @@ class StandaloneRedis extends BaseModel
return $parsedCollection->toArray(); return $parsedCollection->toArray();
} }
} }
public function isBackupSolutionAvailable()
{
return false;
}
} }

View File

@@ -34,6 +34,7 @@ const DATABASE_DOCKER_IMAGES = [
'influxdb', 'influxdb',
'clickhouse/clickhouse-server', 'clickhouse/clickhouse-server',
'supabase/postgres', 'supabase/postgres',
'elestio/postgres',
]; ];
const SPECIFIC_SERVICES = [ const SPECIFIC_SERVICES = [
'quay.io/minio/minio', 'quay.io/minio/minio',

View File

@@ -708,10 +708,12 @@ function getTopLevelNetworks(Service|Application $resource)
return $value == $networkName || $key == $networkName; return $value == $networkName || $key == $networkName;
}); });
if (! $networkExists) { if (! $networkExists) {
if (is_string($networkDetails) || is_int($networkDetails)) {
$topLevelNetworks->put($networkDetails, null); $topLevelNetworks->put($networkDetails, null);
} }
} }
} }
}
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) { $definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
return $value == $definedNetwork; return $value == $definedNetwork;
@@ -758,10 +760,12 @@ function getTopLevelNetworks(Service|Application $resource)
return $value == $networkName || $key == $networkName; return $value == $networkName || $key == $networkName;
}); });
if (! $networkExists) { if (! $networkExists) {
if (is_string($networkDetails) || is_int($networkDetails)) {
$topLevelNetworks->put($networkDetails, null); $topLevelNetworks->put($networkDetails, null);
} }
} }
} }
}
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) { $definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
return $value == $definedNetwork; return $value == $definedNetwork;
}); });
@@ -1608,10 +1612,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
return $value == $networkName || $key == $networkName; return $value == $networkName || $key == $networkName;
}); });
if (! $networkExists) { if (! $networkExists) {
if (is_string($networkDetails) || is_int($networkDetails)) {
$topLevelNetworks->put($networkDetails, null); $topLevelNetworks->put($networkDetails, null);
} }
} }
} }
}
// Collect/create/update ports // Collect/create/update ports
$collectedPorts = collect([]); $collectedPorts = collect([]);
@@ -2523,10 +2529,12 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
return $value == $networkName || $key == $networkName; return $value == $networkName || $key == $networkName;
}); });
if (! $networkExists) { if (! $networkExists) {
if (is_string($networkDetails) || is_int($networkDetails)) {
$topLevelNetworks->put($networkDetails, null); $topLevelNetworks->put($networkDetails, null);
} }
} }
} }
}
// Collect/create/update ports // Collect/create/update ports
$collectedPorts = collect([]); $collectedPorts = collect([]);
if ($servicePorts->count() > 0) { if ($servicePorts->count() > 0) {
@@ -2984,11 +2992,22 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$predefinedPort = '8000'; $predefinedPort = '8000';
} }
if ($isDatabase) { if ($isDatabase) {
$applicationFound = ServiceApplication::where('name', $serviceName)->where('image', $image)->where('service_id', $resource->id)->first();
if ($applicationFound) {
$savedService = $applicationFound;
$savedService = ServiceDatabase::firstOrCreate([
'name' => $applicationFound->name,
'image' => $applicationFound->image,
'service_id' => $applicationFound->service_id,
]);
$applicationFound->delete();
} else {
$savedService = ServiceDatabase::firstOrCreate([ $savedService = ServiceDatabase::firstOrCreate([
'name' => $serviceName, 'name' => $serviceName,
'image' => $image, 'image' => $image,
'service_id' => $resource->id, 'service_id' => $resource->id,
]); ]);
}
} else { } else {
$savedService = ServiceApplication::firstOrCreate([ $savedService = ServiceApplication::firstOrCreate([
'name' => $serviceName, 'name' => $serviceName,
@@ -3209,12 +3228,24 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
if ($serviceName === 'plausible') { if ($serviceName === 'plausible') {
$predefinedPort = '8000'; $predefinedPort = '8000';
} }
if ($isDatabase) { if ($isDatabase) {
$applicationFound = ServiceApplication::where('name', $serviceName)->where('image', $image)->where('service_id', $resource->id)->first();
if ($applicationFound) {
$savedService = $applicationFound;
$savedService = ServiceDatabase::firstOrCreate([
'name' => $applicationFound->name,
'image' => $applicationFound->image,
'service_id' => $applicationFound->service_id,
]);
$applicationFound->delete();
} else {
$savedService = ServiceDatabase::firstOrCreate([ $savedService = ServiceDatabase::firstOrCreate([
'name' => $serviceName, 'name' => $serviceName,
'image' => $image, 'image' => $image,
'service_id' => $resource->id, 'service_id' => $resource->id,
]); ]);
}
} else { } else {
$savedService = ServiceApplication::firstOrCreate([ $savedService = ServiceApplication::firstOrCreate([
'name' => $serviceName, 'name' => $serviceName,
@@ -3889,6 +3920,7 @@ function convertComposeEnvironmentToArray($environment)
} else { } else {
// Example: $environment = ['FOO=bar', 'BAZ=qux']; // Example: $environment = ['FOO=bar', 'BAZ=qux'];
foreach ($environment as $value) { foreach ($environment as $value) {
if (is_string($value)) {
$parts = explode('=', $value, 2); $parts = explode('=', $value, 2);
$key = $parts[0]; $key = $parts[0];
$realValue = $parts[1] ?? ''; $realValue = $parts[1] ?? '';
@@ -3897,6 +3929,7 @@ function convertComposeEnvironmentToArray($environment)
} }
} }
} }
}
return $convertedServiceVariables; return $convertedServiceVariables;

View File

@@ -7,7 +7,7 @@ return [
// The release version of your application // The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
'release' => '4.0.0-beta.350', 'release' => '4.0.0-beta.351',
// When left empty or `null` the Laravel environment will be used // When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'), 'environment' => config('app.env'),

View File

@@ -1,3 +1,3 @@
<?php <?php
return '4.0.0-beta.350'; return '4.0.0-beta.351';

View File

@@ -0,0 +1,28 @@
<?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('scheduled_database_backups', function (Blueprint $table) {
$table->boolean('dump_all')->default(false);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('scheduled_database_backups', function (Blueprint $table) {
$table->dropColumn('dump_all');
});
}
};

View File

@@ -46,6 +46,9 @@ services:
- PUSHER_APP_ID - PUSHER_APP_ID
- PUSHER_APP_KEY - PUSHER_APP_KEY
- PUSHER_APP_SECRET - PUSHER_APP_SECRET
- TERMINAL_PROTOCOL
- TERMINAL_HOST
- TERMINAL_PORT
- AUTOUPDATE - AUTOUPDATE
- SELF_HOSTED - SELF_HOSTED
- SSH_MUX_ENABLED - SSH_MUX_ENABLED
@@ -110,7 +113,7 @@ services:
retries: 10 retries: 10
timeout: 2s timeout: 2s
soketi: soketi:
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.2' image: 'ghcr.io/coollabsio/coolify-realtime:1.0.3'
ports: ports:
- "${SOKETI_PORT:-6001}:6001" - "${SOKETI_PORT:-6001}:6001"
- "6002:6002" - "6002:6002"

View File

@@ -1,16 +1,16 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.349" "version": "4.0.0-beta.350"
}, },
"nightly": { "nightly": {
"version": "4.0.0-beta.350" "version": "4.0.0-beta.351"
}, },
"helper": { "helper": {
"version": "1.0.1" "version": "1.0.1"
}, },
"realtime": { "realtime": {
"version": "1.0.2" "version": "1.0.3"
} }
} }
} }

15
public/svgs/bitcoin.svg Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW 2019 (64-Bit) -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" height="100%" version="1.1" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd"
viewBox="0 0 4091.27 4091.73"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<g id="Layer_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<g id="_1421344023328">
<path fill="#F7931A" fill-rule="nonzero" d="M4030.06 2540.77c-273.24,1096.01 -1383.32,1763.02 -2479.46,1489.71 -1095.68,-273.24 -1762.69,-1383.39 -1489.33,-2479.31 273.12,-1096.13 1383.2,-1763.19 2479,-1489.95 1096.06,273.24 1763.03,1383.51 1489.76,2479.57l0.02 -0.02z"/>
<path fill="white" fill-rule="nonzero" d="M2947.77 1754.38c40.72,-272.26 -166.56,-418.61 -450,-516.24l91.95 -368.8 -224.5 -55.94 -89.51 359.09c-59.02,-14.72 -119.63,-28.59 -179.87,-42.34l90.16 -361.46 -224.36 -55.94 -92 368.68c-48.84,-11.12 -96.81,-22.11 -143.35,-33.69l0.26 -1.16 -309.59 -77.31 -59.72 239.78c0,0 166.56,38.18 163.05,40.53 90.91,22.69 107.35,82.87 104.62,130.57l-104.74 420.15c6.26,1.59 14.38,3.89 23.34,7.49 -7.49,-1.86 -15.46,-3.89 -23.73,-5.87l-146.81 588.57c-11.11,27.62 -39.31,69.07 -102.87,53.33 2.25,3.26 -163.17,-40.72 -163.17,-40.72l-111.46 256.98 292.15 72.83c54.35,13.63 107.61,27.89 160.06,41.3l-92.9 373.03 224.24 55.94 92 -369.07c61.26,16.63 120.71,31.97 178.91,46.43l-91.69 367.33 224.51 55.94 92.89 -372.33c382.82,72.45 670.67,43.24 791.83,-303.02 97.63,-278.78 -4.86,-439.58 -206.26,-544.44 146.69,-33.83 257.18,-130.31 286.64,-329.61l-0.07 -0.05zm-512.93 719.26c-69.38,278.78 -538.76,128.08 -690.94,90.29l123.28 -494.2c152.17,37.99 640.17,113.17 567.67,403.91zm69.43 -723.3c-63.29,253.58 -453.96,124.75 -580.69,93.16l111.77 -448.21c126.73,31.59 534.85,90.55 468.94,355.05l-0.02 0z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

8
public/svgs/strapi.svg Normal file
View File

@@ -0,0 +1,8 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 22.1867C0 11.7278 0 6.49832 3.24916 3.24916C6.49832 0 11.7278 0 22.1867 0H41.8133C52.2722 0 57.5017 0 60.7508 3.24916C64 6.49832 64 11.7278 64 22.1867V41.8133C64 52.2722 64 57.5017 60.7508 60.7508C57.5017 64 52.2722 64 41.8133 64H22.1867C11.7278 64 6.49832 64 3.24916 60.7508C0 57.5017 0 52.2722 0 41.8133V22.1867Z" fill="#4945FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M44.156 19.4131H22.6094V30.4004H33.596V41.3864H44.5827V19.8398C44.5827 19.6041 44.3917 19.4131 44.156 19.4131Z" fill="white"/>
<rect x="33.1719" y="30.4004" width="0.426667" height="0.426667" fill="white"/>
<path d="M22.6172 30.4004H33.1772C33.4128 30.4004 33.6039 30.5914 33.6039 30.8271V41.3871H23.0439C22.8082 41.3871 22.6172 41.196 22.6172 40.9604V30.4004Z" fill="#9593FF"/>
<path d="M33.6016 41.3867H44.5882L33.9657 52.0092C33.8314 52.1436 33.6016 52.0484 33.6016 51.8584V41.3867Z" fill="#9593FF"/>
<path d="M22.6151 30.3998H12.1434C11.9534 30.3998 11.8582 30.17 11.9926 30.0356L22.6151 19.4131V30.3998Z" fill="#9593FF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -8,17 +8,14 @@
<livewire:project.database.backup-now :backup="$backup" /> <livewire:project.database.backup-now :backup="$backup" />
@endif @endif
@if ($backup->database_id !== 0) @if ($backup->database_id !== 0)
<x-modal-confirmation <x-modal-confirmation title="Confirm Backup Schedule Deletion?" buttonTitle="Delete Backups and Schedule"
title="Confirm Backup Schedule Deletion?" isErrorButton submitAction="delete" :checkboxes="$checkboxes" :actions="[
buttonTitle="Delete Backups and Schedule" 'The selected backup schedule will be deleted.',
isErrorButton 'Scheduled backups for this database will be stopped (if this is the only backup schedule for this database).',
submitAction="delete" ]"
:checkboxes="$checkboxes"
:actions="['The selected backup schedule will be deleted.', 'Scheduled backups for this database will be stopped (if this is the only backup schedule for this database).']"
confirmationText="{{ $backup->database->name }}" confirmationText="{{ $backup->database->name }}"
confirmationLabel="Please confirm the execution of the actions by entering the Database Name of the scheduled backups below" confirmationLabel="Please confirm the execution of the actions by entering the Database Name of the scheduled backups below"
shortConfirmationLabel="Database Name" shortConfirmationLabel="Database Name" />
/>
@endif @endif
</div> </div>
<div class="w-48 pb-2"> <div class="w-48 pb-2">
@@ -36,24 +33,40 @@
</div> </div>
@endif @endif
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<div class="flex gap-2"> <h3>Settings</h3>
<div class="flex gap-2 flex-col ">
@if ($backup->database_type === 'App\Models\StandalonePostgresql') @if ($backup->database_type === 'App\Models\StandalonePostgresql')
<div class="w-48">
<x-forms.checkbox label="Backup All Databases" id="backup.dump_all" instantSave />
</div>
@if (!$backup->dump_all)
<x-forms.input label="Databases To Backup" <x-forms.input label="Databases To Backup"
helper="Comma separated list of databases to backup. Empty will include the default one." helper="Comma separated list of databases to backup. Empty will include the default one."
id="backup.databases_to_backup" /> id="backup.databases_to_backup" />
@endif
@elseif($backup->database_type === 'App\Models\StandaloneMongodb') @elseif($backup->database_type === 'App\Models\StandaloneMongodb')
<x-forms.input label="Databases To Include" <x-forms.input label="Databases To Include"
helper="A list of databases to backup. You can specify which collection(s) per database to exclude from the backup. Empty will include all databases and collections.<br><br>Example:<br><br>database1:collection1,collection2|database2:collection3,collection4<br><br> database1 will include all collections except collection1 and collection2. <br>database2 will include all collections except collection3 and collection4.<br><br>Another Example:<br><br>database1:collection1|database2<br><br> database1 will include all collections except collection1.<br>database2 will include ALL collections." helper="A list of databases to backup. You can specify which collection(s) per database to exclude from the backup. Empty will include all databases and collections.<br><br>Example:<br><br>database1:collection1,collection2|database2:collection3,collection4<br><br> database1 will include all collections except collection1 and collection2. <br>database2 will include all collections except collection3 and collection4.<br><br>Another Example:<br><br>database1:collection1|database2<br><br> database1 will include all collections except collection1.<br>database2 will include ALL collections."
id="backup.databases_to_backup" /> id="backup.databases_to_backup" />
@elseif($backup->database_type === 'App\Models\StandaloneMysql') @elseif($backup->database_type === 'App\Models\StandaloneMysql')
<div class="w-48">
<x-forms.checkbox label="Backup All Databases" id="backup.dump_all" instantSave />
</div>
@if (!$backup->dump_all)
<x-forms.input label="Databases To Backup" <x-forms.input label="Databases To Backup"
helper="Comma separated list of databases to backup. Empty will include the default one." helper="Comma separated list of databases to backup. Empty will include the default one."
id="backup.databases_to_backup" /> id="backup.databases_to_backup" />
@endif
@elseif($backup->database_type === 'App\Models\StandaloneMariadb') @elseif($backup->database_type === 'App\Models\StandaloneMariadb')
<div class="w-48">
<x-forms.checkbox label="Backup All Databases" id="backup.dump_all" instantSave />
</div>
@if (!$backup->dump_all)
<x-forms.input label="Databases To Backup" <x-forms.input label="Databases To Backup"
helper="Comma separated list of databases to backup. Empty will include the default one." helper="Comma separated list of databases to backup. Empty will include the default one."
id="backup.databases_to_backup" /> id="backup.databases_to_backup" />
@endif @endif
@endif
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<x-forms.input label="Frequency" id="backup.frequency" /> <x-forms.input label="Frequency" id="backup.frequency" />

View File

@@ -149,6 +149,12 @@
<div class="text-xs">{{ $database->status }}</div> <div class="text-xs">{{ $database->status }}</div>
</div> </div>
<div class="flex items-center px-4"> <div class="flex items-center px-4">
@if ($database->isBackupSolutionAvailable())
<a class="mx-4 text-xs font-bold hover:underline"
href="{{ route('project.service.index', [...$parameters, 'stack_service_uuid' => $database->uuid]) }}#backups">
Backups
</a>
@endif
<a class="mx-4 text-xs font-bold hover:underline" <a class="mx-4 text-xs font-bold hover:underline"
href="{{ route('project.service.index', [...$parameters, 'stack_service_uuid' => $database->uuid]) }}"> href="{{ route('project.service.index', [...$parameters, 'stack_service_uuid' => $database->uuid]) }}">
Settings Settings

View File

@@ -10,9 +10,7 @@
<a class="menu-item" :class="activeTab === 'general' && 'menu-item-active'" <a class="menu-item" :class="activeTab === 'general' && 'menu-item-active'"
@click.prevent="activeTab = 'general'; window.location.hash = 'general'; if(window.location.search) window.location.search = ''" @click.prevent="activeTab = 'general'; window.location.hash = 'general'; if(window.location.search) window.location.search = ''"
href="#">General</a> href="#">General</a>
@if (str($serviceDatabase?->databaseType())->contains('mysql') || @if ($serviceDatabase->isBackupSolutionAvailable())
str($serviceDatabase?->databaseType())->contains('postgres') ||
str($serviceDatabase?->databaseType())->contains('mariadb'))
<a :class="activeTab === 'backups' && 'menu-item-active'" class="menu-item" <a :class="activeTab === 'backups' && 'menu-item-active'" class="menu-item"
@click.prevent="activeTab = 'backups'; window.location.hash = 'backups'" href="#">Backups</a> @click.prevent="activeTab = 'backups'; window.location.hash = 'backups'" href="#">Backups</a>
@endif @endif
@@ -29,11 +27,13 @@
@endisset @endisset
@isset($serviceDatabase) @isset($serviceDatabase)
<x-slot:title> <x-slot:title>
{{ data_get_str($service, 'name')->limit(10) }} > {{ data_get_str($serviceDatabase, 'name')->limit(10) }} | Coolify {{ data_get_str($service, 'name')->limit(10) }} >
{{ data_get_str($serviceDatabase, 'name')->limit(10) }} | Coolify
</x-slot> </x-slot>
<div x-cloak x-show="activeTab === 'general'" class="h-full"> <div x-cloak x-show="activeTab === 'general'" class="h-full">
<livewire:project.service.database :database="$serviceDatabase" /> <livewire:project.service.database :database="$serviceDatabase" />
</div> </div>
@if ($serviceDatabase->isBackupSolutionAvailable())
<div x-cloak x-show="activeTab === 'backups'"> <div x-cloak x-show="activeTab === 'backups'">
<div class="flex gap-2 "> <div class="flex gap-2 ">
<h2 class="pb-4">Scheduled Backups</h2> <h2 class="pb-4">Scheduled Backups</h2>
@@ -42,8 +42,9 @@
</x-modal-input> </x-modal-input>
</div> </div>
<livewire:project.database.scheduled-backups :database="$serviceDatabase" /> <livewire:project.database.scheduled-backups :database="$serviceDatabase" />
@endif
</div> </div>
@endisset @endisset
</div> </div>
</div> </div>
</div> </div>

View File

@@ -22,7 +22,25 @@
</nav> </nav>
<div class="flex flex-wrap order-first gap-2 items-center sm:order-last"> <div class="flex flex-wrap order-first gap-2 items-center sm:order-last">
@if (str($service->status())->contains('running')) @if (str($service->status())->contains('running'))
<button @click="$wire.dispatch('restartEvent')" class="gap-2 button"> <x-dropdown>
<x-slot:title>
Advanced
</x-slot>
<div class="dropdown-item" @click="$wire.dispatch('pullAndRestartEvent')">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M12.983 8.978c3.955 -.182 7.017 -1.446 7.017 -2.978c0 -1.657 -3.582 -3 -8 -3c-1.661 0 -3.204 .19 -4.483 .515m-2.783 1.228c-.471 .382 -.734 .808 -.734 1.257c0 1.22 1.944 2.271 4.734 2.74" />
<path
d="M4 6v6c0 1.657 3.582 3 8 3c.986 0 1.93 -.067 2.802 -.19m3.187 -.82c1.251 -.53 2.011 -1.228 2.011 -1.99v-6" />
<path d="M4 12v6c0 1.657 3.582 3 8 3c3.217 0 5.991 -.712 7.261 -1.74m.739 -3.26v-4" />
<path d="M3 3l18 18" />
</svg>
Pull Latest Images & Restart
</div>
</x-dropdown>
<x-forms.button title="Restart" @click="$wire.dispatch('restartEvent')">
<svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg class="w-5 h-5 dark:text-warning" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2"> stroke-width="2">
@@ -30,8 +48,8 @@
<path d="M20 4v5h-5" /> <path d="M20 4v5h-5" />
</g> </g>
</svg> </svg>
Pull Latest Images & Restart Restart
</button> </x-forms.button>
<x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" submitAction="stop" <x-modal-confirmation title="Confirm Service Stopping?" buttonTitle="Stop" submitAction="stop"
:checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]" :confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue" :checkboxes="$checkboxes" :actions="[__('service.stop'), __('resource.non_persistent')]" :confirmWithText="false" :confirmWithPassword="false" step1ButtonText="Continue"
step2ButtonText="Stop Service" :dispatchEvent="true" dispatchEventType="stopEvent"> step2ButtonText="Stop Service" :dispatchEvent="true" dispatchEventType="stopEvent">
@@ -135,9 +153,13 @@
$wire.$call('start'); $wire.$call('start');
}); });
$wire.$on('restartEvent', () => { $wire.$on('restartEvent', () => {
$wire.$dispatch('info', 'Pulling new images.'); $wire.$dispatch('info', 'Service restart in progress.');
$wire.$call('restart'); $wire.$call('restart');
}); });
$wire.$on('pullAndRestartEvent', () => {
$wire.$dispatch('info', 'Pulling new images.');
$wire.$call('pullAndRestartEvent');
});
$wire.on('imagePulled', () => { $wire.on('imagePulled', () => {
window.dispatchEvent(new CustomEvent('startservice')); window.dispatchEvent(new CustomEvent('startservice'));
$wire.$dispatch('info', 'Restarting service.'); $wire.$dispatch('info', 'Restarting service.');

View File

@@ -0,0 +1,17 @@
# documentation: https://hub.docker.com/r/ruimarinho/bitcoin-core/
# slogan: A self-hosted Bitcoin Core full node.
# tags: cryptocurrency,node,blockchain,bitcoin
# logo: svgs/bitcoin.svg
services:
bitcoin-core:
image: ruimarinho/bitcoin-core:latest
environment:
- BITCOIN_RPCUSER=${BITCOIN_RPCUSER:-bitcoinuser}
- BITCOIN_RPCPASSWORD=${SERVICE_PASSWORD_PASSWORD64}
- BITCOIN_NETWORK=${BITCOIN_NETWORK:-mainnet}
- BITCOIN_PRINTTOCONSOLE=${BITCOIN_PRINTTOCONSOLE:-1}
- BITCOIN_TXINDEX=${BITCOIN_TXINDEX:-1}
volumes:
- bitcoin_data:/home/bitcoin/.bitcoin

View File

@@ -1,5 +1,5 @@
# documentation: https://docs.docker.com/registry/ # documentation: https://docs.docker.com/registry/
# slogan: The Docker Registry is lets you distribute Docker images. # slogan: The Docker Registry lets you distribute Docker images.
# tags: registry,images,docker # tags: registry,images,docker
# logo: svgs/docker-registry.png # logo: svgs/docker-registry.png
# port: 5000 # port: 5000

View File

@@ -0,0 +1,60 @@
# documentation: https://docs.strapi.io/
# slogan: Open-source headless CMS to build powerful APIs with built-in content management.
# tags: cms, headless, mysql, api
# logo: svgs/strapi.svg
# port: 1337
services:
strapi:
image: "elestio/strapi-development:latest"
environment:
- SERVICE_FQDN_STRAPI_1337
- DATABASE_CLIENT=postgres
- DATABASE_HOST=postgresql
- DATABASE_PORT=5432
- "DATABASE_NAME=${POSTGRESQL_DATABASE:-strapi}"
- DATABASE_USERNAME=$SERVICE_USER_POSTGRESQL
- DATABASE_PASSWORD=$SERVICE_PASSWORD_POSTGRESQL
- JWT_SECRET=$SERVICE_BASE64_64_SECRET
- ADMIN_JWT_SECRET=$SERVICE_BASE64_64_SECRET
- APP_KEYS=$SERVICE_BASE64_64_KEY
- STRAPI_TELEMETRY_DISABLED=${STRAPI_TELEMETRY_DISABLED:-true}
- STRAPI_LICENSE=${STRAPI_LICENSE}
- NODE_ENV=${NODE_ENV:-development}
- BROWSER=${BROWSER:-true}
- STRAPI_PLUGIN_I18N_INIT_LOCALE_CODE=${STRAPI_PLUGIN_I18N_INIT_LOCALE_CODE:-en}
- STRAPI_ENFORCE_SOURCEMAPS=${STRAPI_ENFORCE_SOURCEMAPS:-false}
- FAST_REFRESH=${FAST_REFRESH:-true}
volumes:
- "strapi-config:/opt/app/config"
- "strapi-src:/opt/app/src"
- "strapi-uploads:/opt/app/public/uploads"
healthcheck:
test:
- CMD
- wget
- "-q"
- "--spider"
- "http://127.0.0.1:1337/"
interval: 5s
timeout: 20s
retries: 10
depends_on:
postgresql:
condition: service_healthy
postgresql:
image: "elestio/postgres:latest"
environment:
- "POSTGRES_DB=${POSTGRESQL_DATABASE:-strapi}"
- POSTGRES_USER=$SERVICE_USER_POSTGRESQL
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRESQL
- PGDATA=/var/lib/postgresql/data
volumes:
- "strapi-postgresql-data:/var/lib/postgresql/data"
healthcheck:
test:
- CMD-SHELL
- "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"
interval: 5s
timeout: 20s
retries: 10

File diff suppressed because one or more lines are too long

View File

@@ -1,10 +1,10 @@
{ {
"coolify": { "coolify": {
"v4": { "v4": {
"version": "4.0.0-beta.350" "version": "4.0.0-beta.351"
}, },
"nightly": { "nightly": {
"version": "4.0.0-beta.351" "version": "4.0.0-beta.352"
}, },
"helper": { "helper": {
"version": "1.0.1" "version": "1.0.1"