mirror of
https://github.com/ershisan99/coolify.git
synced 2026-01-01 20:59:24 +00:00
Compare commits
49 Commits
v4.0.0-bet
...
v4.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1d395ff0c | ||
|
|
4e9126887f | ||
|
|
5fa650122d | ||
|
|
ee7f8200ac | ||
|
|
d2a8f31a1c | ||
|
|
2468d0044b | ||
|
|
81b8a58415 | ||
|
|
7442d19611 | ||
|
|
8c024ddb57 | ||
|
|
d990a5691d | ||
|
|
2181ef381b | ||
|
|
c80f5be974 | ||
|
|
358f6575f8 | ||
|
|
de0f34734b | ||
|
|
33cb2d150d | ||
|
|
5f07b473e9 | ||
|
|
5bcd813792 | ||
|
|
a0bb523507 | ||
|
|
0b4fc38d6b | ||
|
|
82c834915d | ||
|
|
d637675ce3 | ||
|
|
e71c04a0c7 | ||
|
|
885b3bdea7 | ||
|
|
9d6757aeb7 | ||
|
|
d84d0a816b | ||
|
|
0da31c34b5 | ||
|
|
ccdaf59ecb | ||
|
|
3fc9cf90ab | ||
|
|
c4be720b20 | ||
|
|
aabe27efd1 | ||
|
|
a8982379c9 | ||
|
|
6dd0bd0742 | ||
|
|
1d3494a6ba | ||
|
|
ee5eb427c9 | ||
|
|
ad7b5e6e1c | ||
|
|
73bd344147 | ||
|
|
ef448280d8 | ||
|
|
5282248cb4 | ||
|
|
14c9f25c57 | ||
|
|
44b3d08d86 | ||
|
|
5da1f48ae1 | ||
|
|
2bc1b9027c | ||
|
|
1c7ca56756 | ||
|
|
d70faea845 | ||
|
|
fdb5cab875 | ||
|
|
bb6cb8edc9 | ||
|
|
a6ec2b92fb | ||
|
|
31df222798 | ||
|
|
1b0e2e1257 |
@@ -2,14 +2,17 @@
|
||||
|
||||
namespace App\Actions\Proxy;
|
||||
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Models\Server;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class CheckProxy
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(Server $server, $fromUI = false)
|
||||
// It should return if the proxy should be started (true) or not (false)
|
||||
public function handle(Server $server, $fromUI = false): bool
|
||||
{
|
||||
if (! $server->isFunctional()) {
|
||||
return false;
|
||||
@@ -62,22 +65,42 @@ class CheckProxy
|
||||
$ip = 'host.docker.internal';
|
||||
}
|
||||
|
||||
$connection80 = @fsockopen($ip, '80');
|
||||
$connection443 = @fsockopen($ip, '443');
|
||||
$port80 = is_resource($connection80) && fclose($connection80);
|
||||
$port443 = is_resource($connection443) && fclose($connection443);
|
||||
if ($port80) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
$portsToCheck = ['80', '443'];
|
||||
|
||||
try {
|
||||
if ($server->proxyType() !== ProxyTypes::NONE->value) {
|
||||
$proxyCompose = CheckConfiguration::run($server);
|
||||
if (isset($proxyCompose)) {
|
||||
$yaml = Yaml::parse($proxyCompose);
|
||||
$portsToCheck = [];
|
||||
if ($server->proxyType() === ProxyTypes::TRAEFIK->value) {
|
||||
$ports = data_get($yaml, 'services.traefik.ports');
|
||||
} elseif ($server->proxyType() === ProxyTypes::CADDY->value) {
|
||||
$ports = data_get($yaml, 'services.caddy.ports');
|
||||
}
|
||||
if (isset($ports)) {
|
||||
foreach ($ports as $port) {
|
||||
$portsToCheck[] = str($port)->before(':')->value();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
$portsToCheck = [];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
ray($e->getMessage());
|
||||
}
|
||||
if ($port443) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
} else {
|
||||
return false;
|
||||
if (count($portsToCheck) === 0) {
|
||||
return false;
|
||||
}
|
||||
foreach ($portsToCheck as $port) {
|
||||
$connection = @fsockopen($ip, $port);
|
||||
if (is_resource($connection) && fclose($connection)) {
|
||||
if ($fromUI) {
|
||||
throw new \Exception("Port $port is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class CheckApplicationDeploymentQueue extends Command
|
||||
$deployments = ApplicationDeploymentQueue::whereIn('status', [
|
||||
ApplicationDeploymentStatus::IN_PROGRESS,
|
||||
ApplicationDeploymentStatus::QUEUED,
|
||||
])->where('created_at', '>=', now()->subSeconds($seconds))->get();
|
||||
])->where('created_at', '<=', now()->subSeconds($seconds))->get();
|
||||
if ($deployments->isEmpty()) {
|
||||
$this->info('No deployments found in the last '.$seconds.' seconds.');
|
||||
|
||||
|
||||
@@ -744,8 +744,10 @@ class ApplicationsController extends Controller
|
||||
$application->destination_id = $destination->id;
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
$application->save();
|
||||
$application->refresh();
|
||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||
@@ -842,8 +844,10 @@ class ApplicationsController extends Controller
|
||||
$application->environment_id = $environment->id;
|
||||
$application->source_type = $githubApp->getMorphClass();
|
||||
$application->source_id = $githubApp->id;
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
$application->save();
|
||||
$application->refresh();
|
||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||
@@ -936,8 +940,10 @@ class ApplicationsController extends Controller
|
||||
$application->destination_id = $destination->id;
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
$application->save();
|
||||
$application->refresh();
|
||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||
@@ -1017,8 +1023,10 @@ class ApplicationsController extends Controller
|
||||
$application->destination_id = $destination->id;
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
|
||||
$application->git_repository = 'coollabsio/coolify';
|
||||
$application->git_branch = 'main';
|
||||
@@ -1077,8 +1085,10 @@ class ApplicationsController extends Controller
|
||||
$application->destination_id = $destination->id;
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
|
||||
$application->git_repository = 'coollabsio/coolify';
|
||||
$application->git_branch = 'main';
|
||||
@@ -1555,8 +1565,11 @@ class ApplicationsController extends Controller
|
||||
$instantDeploy = $request->instant_deploy;
|
||||
|
||||
$use_build_server = $request->use_build_server;
|
||||
$application->settings->is_build_server_enabled = $use_build_server;
|
||||
$application->settings->save();
|
||||
|
||||
if (isset($use_build_server)) {
|
||||
$application->settings->is_build_server_enabled = $use_build_server;
|
||||
$application->settings->save();
|
||||
}
|
||||
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
|
||||
@@ -66,7 +66,12 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
$this->team = Team::findOrFail($this->backup->team_id);
|
||||
$this->team = Team::find($this->backup->team_id);
|
||||
if (! $this->team) {
|
||||
$this->backup->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
|
||||
$this->database = data_get($this->backup, 'database');
|
||||
$this->server = $this->database->service->server;
|
||||
@@ -243,6 +248,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
try {
|
||||
if (str($databaseType)->contains('postgres')) {
|
||||
$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_log = ScheduledDatabaseBackupExecution::create([
|
||||
'database_name' => $database,
|
||||
@@ -271,6 +279,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->backup_standalone_mongodb($database);
|
||||
} elseif (str($databaseType)->contains('mysql')) {
|
||||
$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_log = ScheduledDatabaseBackupExecution::create([
|
||||
'database_name' => $database,
|
||||
@@ -280,6 +291,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->backup_standalone_mysql($database);
|
||||
} elseif (str($databaseType)->contains('mariadb')) {
|
||||
$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_log = ScheduledDatabaseBackupExecution::create([
|
||||
'database_name' => $database,
|
||||
@@ -379,7 +393,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if ($this->postgres_password) {
|
||||
$backupCommand .= " -e PGPASSWORD=$this->postgres_password";
|
||||
}
|
||||
$backupCommand .= " $this->container_name pg_dump --format=custom --no-acl --no-owner --username {$this->database->postgres_user} $database > $this->backup_location";
|
||||
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";
|
||||
}
|
||||
|
||||
$commands[] = $backupCommand;
|
||||
ray($commands);
|
||||
@@ -400,8 +418,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
try {
|
||||
$commands[] = 'mkdir -p '.$this->backup_dir;
|
||||
$commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location";
|
||||
ray($commands);
|
||||
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";
|
||||
}
|
||||
$this->backup_output = instant_remote_process($commands, $this->server);
|
||||
$this->backup_output = trim($this->backup_output);
|
||||
if ($this->backup_output === '') {
|
||||
@@ -419,7 +440,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
try {
|
||||
$commands[] = 'mkdir -p '.$this->backup_dir;
|
||||
$commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location";
|
||||
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";
|
||||
}
|
||||
ray($commands);
|
||||
$this->backup_output = instant_remote_process($commands, $this->server);
|
||||
$this->backup_output = trim($this->backup_output);
|
||||
|
||||
@@ -31,6 +31,7 @@ class BackupEdit extends Component
|
||||
'backup.save_s3' => 'required|boolean',
|
||||
'backup.s3_storage_id' => 'nullable|integer',
|
||||
'backup.databases_to_backup' => 'nullable',
|
||||
'backup.dump_all' => 'required|boolean',
|
||||
];
|
||||
|
||||
protected $validationAttributes = [
|
||||
@@ -40,6 +41,7 @@ class BackupEdit extends Component
|
||||
'backup.save_s3' => 'Save to S3',
|
||||
'backup.s3_storage_id' => 'S3 Storage',
|
||||
'backup.databases_to_backup' => 'Databases to Backup',
|
||||
'backup.dump_all' => 'Backup All Databases',
|
||||
];
|
||||
|
||||
protected $messages = [
|
||||
|
||||
@@ -26,7 +26,7 @@ class ScheduledBackups extends Component
|
||||
public function mount(): void
|
||||
{
|
||||
if ($this->selectedBackupId) {
|
||||
$this->setSelectedBackup($this->selectedBackupId);
|
||||
$this->setSelectedBackup($this->selectedBackupId, true);
|
||||
}
|
||||
$this->parameters = get_route_parameters();
|
||||
if ($this->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||
@@ -37,10 +37,13 @@ class ScheduledBackups extends Component
|
||||
$this->s3s = currentTeam()->s3s;
|
||||
}
|
||||
|
||||
public function setSelectedBackup($backupId)
|
||||
public function setSelectedBackup($backupId, $force = false)
|
||||
{
|
||||
if ($this->selectedBackupId === $backupId && ! $force) {
|
||||
return;
|
||||
}
|
||||
$this->selectedBackupId = $backupId;
|
||||
$this->selectedBackup = $this->database->scheduledBackups->find($this->selectedBackupId);
|
||||
$this->selectedBackup = $this->database->scheduledBackups->find($backupId);
|
||||
if (is_null($this->selectedBackup)) {
|
||||
$this->selectedBackupId = null;
|
||||
}
|
||||
|
||||
@@ -108,6 +108,21 @@ class Navbar extends Component
|
||||
|
||||
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);
|
||||
StopService::run(service: $this->service, dockerCleanup: false);
|
||||
$this->service->parse();
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace App\Livewire\Server\Proxy;
|
||||
|
||||
use App\Actions\Docker\GetContainersStatus;
|
||||
use App\Actions\Proxy\CheckProxy;
|
||||
use App\Jobs\ContainerStatusJob;
|
||||
use App\Actions\Proxy\StartProxy;
|
||||
use App\Models\Server;
|
||||
use Livewire\Component;
|
||||
|
||||
@@ -44,7 +44,10 @@ class Status extends Component
|
||||
}
|
||||
$this->numberOfPolls++;
|
||||
}
|
||||
CheckProxy::run($this->server, true);
|
||||
$shouldStart = CheckProxy::run($this->server, true);
|
||||
if ($shouldStart) {
|
||||
StartProxy::run($this->server, false);
|
||||
}
|
||||
$this->dispatch('proxyStatusUpdated');
|
||||
if ($this->server->proxy->status === 'running') {
|
||||
$this->polling = false;
|
||||
|
||||
@@ -460,15 +460,6 @@ $schema://$host {
|
||||
|
||||
public function proxyType()
|
||||
{
|
||||
// $proxyType = $this->proxy->get('type');
|
||||
// if ($proxyType === ProxyTypes::NONE->value) {
|
||||
// return $proxyType;
|
||||
// }
|
||||
// if (is_null($proxyType)) {
|
||||
// $this->proxy->type = ProxyTypes::TRAEFIK->value;
|
||||
// $this->proxy->status = ProxyStatus::EXITED->value;
|
||||
// $this->save();
|
||||
// }
|
||||
return data_get($this->proxy, 'type');
|
||||
}
|
||||
|
||||
|
||||
@@ -770,6 +770,30 @@ class Service extends BaseModel
|
||||
}
|
||||
$fields->put('Code Server', $data->toArray());
|
||||
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();
|
||||
@@ -1108,7 +1132,6 @@ class Service extends BaseModel
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
}
|
||||
ray("echo \"{$env->key}={$real_value}\" >> .env");
|
||||
$commands[] = "echo \"{$env->key}={$real_value}\" >> .env";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,4 +112,9 @@ class ServiceApplication extends BaseModel
|
||||
{
|
||||
getFilesystemVolumesFromServer($this, $isInit);
|
||||
}
|
||||
|
||||
public function isBackupSolutionAvailable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,4 +115,13 @@ class ServiceDatabase extends BaseModel
|
||||
{
|
||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||
}
|
||||
|
||||
public function isBackupSolutionAvailable()
|
||||
{
|
||||
return str($this->databaseType())->contains('mysql') ||
|
||||
str($this->databaseType())->contains('postgres') ||
|
||||
str($this->databaseType())->contains('postgis') ||
|
||||
str($this->databaseType())->contains('mariadb') ||
|
||||
str($this->databaseType())->contains('mongodb');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,4 +294,9 @@ class StandaloneClickhouse extends BaseModel
|
||||
return $parsedCollection->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
public function isBackupSolutionAvailable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,4 +294,9 @@ class StandaloneDragonfly extends BaseModel
|
||||
return $parsedCollection->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
public function isBackupSolutionAvailable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,4 +294,9 @@ class StandaloneKeydb extends BaseModel
|
||||
return $parsedCollection->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
public function isBackupSolutionAvailable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,4 +294,9 @@ class StandaloneMariadb extends BaseModel
|
||||
return $parsedCollection->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
public function isBackupSolutionAvailable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,4 +314,9 @@ class StandaloneMongodb extends BaseModel
|
||||
return $parsedCollection->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
public function isBackupSolutionAvailable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,4 +295,9 @@ class StandaloneMysql extends BaseModel
|
||||
return $parsedCollection->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
public function isBackupSolutionAvailable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,4 +296,9 @@ class StandalonePostgresql extends BaseModel
|
||||
return $parsedCollection->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
public function isBackupSolutionAvailable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,4 +290,9 @@ class StandaloneRedis extends BaseModel
|
||||
return $parsedCollection->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
public function isBackupSolutionAvailable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,16 @@ const RESTART_MODE = 'unless-stopped';
|
||||
const DATABASE_DOCKER_IMAGES = [
|
||||
'bitnami/mariadb',
|
||||
'bitnami/mongodb',
|
||||
'bitnami/mysql',
|
||||
'bitnami/postgresql',
|
||||
'bitnami/redis',
|
||||
'mysql',
|
||||
'bitnami/mysql',
|
||||
'mysql/mysql-server',
|
||||
'mariadb',
|
||||
'postgis/postgis',
|
||||
'postgres',
|
||||
'bitnami/postgresql',
|
||||
'supabase/postgres',
|
||||
'elestio/postgres',
|
||||
'mongo',
|
||||
'redis',
|
||||
'memcached',
|
||||
@@ -33,7 +37,6 @@ const DATABASE_DOCKER_IMAGES = [
|
||||
'neo4j',
|
||||
'influxdb',
|
||||
'clickhouse/clickhouse-server',
|
||||
'supabase/postgres',
|
||||
];
|
||||
const SPECIFIC_SERVICES = [
|
||||
'quay.io/minio/minio',
|
||||
|
||||
@@ -325,38 +325,16 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
$labels->push('traefik.http.middlewares.gzip.compress=true');
|
||||
$labels->push('traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https');
|
||||
|
||||
$basic_auth = false;
|
||||
$basic_auth_middleware = null;
|
||||
$redirect = false;
|
||||
$redirect_middleware = null;
|
||||
$middlewares_from_labels = collect([]);
|
||||
|
||||
if ($serviceLabels) {
|
||||
$basic_auth = $serviceLabels->contains(function ($value) {
|
||||
return str_contains($value, 'basicauth');
|
||||
});
|
||||
if ($basic_auth) {
|
||||
$basic_auth_middleware = $serviceLabels
|
||||
->map(function ($item) {
|
||||
if (preg_match('/traefik\.http\.middlewares\.(.*?)\.basicauth\.users/', $item, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
})
|
||||
->filter()
|
||||
->first();
|
||||
}
|
||||
$redirect = $serviceLabels->contains(function ($value) {
|
||||
return str_contains($value, 'redirectregex');
|
||||
});
|
||||
if ($redirect) {
|
||||
$redirect_middleware = $serviceLabels
|
||||
->map(function ($item) {
|
||||
if (preg_match('/traefik\.http\.middlewares\.(.*?)\.redirectregex\.regex/', $item, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
})
|
||||
->filter()
|
||||
->first();
|
||||
}
|
||||
$middlewares_from_labels = $serviceLabels->map(function ($item) {
|
||||
if (preg_match('/traefik\.http\.middlewares\.(.*?)(\.|$)/', $item, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
return null;
|
||||
})->filter()
|
||||
->unique();
|
||||
}
|
||||
foreach ($domains as $loop => $domain) {
|
||||
try {
|
||||
@@ -404,20 +382,15 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
$labels->push("traefik.http.services.{$https_label}.loadbalancer.server.port=$port");
|
||||
}
|
||||
if ($path !== '/') {
|
||||
// Middleware handling
|
||||
$middlewares = collect([]);
|
||||
if ($is_stripprefix_enabled && ! str($image)->contains('ghost')) {
|
||||
if ($is_stripprefix_enabled && !str($image)->contains('ghost')) {
|
||||
$labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}");
|
||||
$middlewares->push("{$https_label}-stripprefix");
|
||||
}
|
||||
if ($is_gzip_enabled) {
|
||||
$middlewares->push('gzip');
|
||||
}
|
||||
if ($basic_auth && $basic_auth_middleware) {
|
||||
$middlewares->push($basic_auth_middleware);
|
||||
}
|
||||
if ($redirect && $redirect_middleware) {
|
||||
$middlewares->push($redirect_middleware);
|
||||
}
|
||||
if (str($image)->contains('ghost')) {
|
||||
$middlewares->push('redir-ghost');
|
||||
}
|
||||
@@ -425,10 +398,13 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
$labels = $labels->merge($redirect_to_non_www);
|
||||
$middlewares->push($to_non_www_name);
|
||||
}
|
||||
if ($redirect_direction === 'www' && ! str($host)->startsWith('www.')) {
|
||||
if ($redirect_direction === 'www' && !str($host)->startsWith('www.')) {
|
||||
$labels = $labels->merge($redirect_to_www);
|
||||
$middlewares->push($to_www_name);
|
||||
}
|
||||
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
|
||||
$middlewares->push($middleware_name);
|
||||
});
|
||||
if ($middlewares->isNotEmpty()) {
|
||||
$middlewares = $middlewares->join(',');
|
||||
$labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}");
|
||||
@@ -437,13 +413,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
$middlewares = collect([]);
|
||||
if ($is_gzip_enabled) {
|
||||
$middlewares->push('gzip');
|
||||
}
|
||||
if ($basic_auth && $basic_auth_middleware) {
|
||||
$middlewares->push($basic_auth_middleware);
|
||||
}
|
||||
if ($redirect && $redirect_middleware) {
|
||||
$middlewares->push($redirect_middleware);
|
||||
}
|
||||
}
|
||||
if (str($image)->contains('ghost')) {
|
||||
$middlewares->push('redir-ghost');
|
||||
}
|
||||
@@ -455,6 +425,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
$labels = $labels->merge($redirect_to_www);
|
||||
$middlewares->push($to_www_name);
|
||||
}
|
||||
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
|
||||
$middlewares->push($middleware_name);
|
||||
});
|
||||
if ($middlewares->isNotEmpty()) {
|
||||
$middlewares = $middlewares->join(',');
|
||||
$labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}");
|
||||
@@ -490,12 +463,6 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
if ($is_gzip_enabled) {
|
||||
$middlewares->push('gzip');
|
||||
}
|
||||
if ($basic_auth && $basic_auth_middleware) {
|
||||
$middlewares->push($basic_auth_middleware);
|
||||
}
|
||||
if ($redirect && $redirect_middleware) {
|
||||
$middlewares->push($redirect_middleware);
|
||||
}
|
||||
if (str($image)->contains('ghost')) {
|
||||
$middlewares->push('redir-ghost');
|
||||
}
|
||||
@@ -507,6 +474,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
$labels = $labels->merge($redirect_to_www);
|
||||
$middlewares->push($to_www_name);
|
||||
}
|
||||
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
|
||||
$middlewares->push($middleware_name);
|
||||
});
|
||||
if ($middlewares->isNotEmpty()) {
|
||||
$middlewares = $middlewares->join(',');
|
||||
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
|
||||
@@ -516,12 +486,6 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
if ($is_gzip_enabled) {
|
||||
$middlewares->push('gzip');
|
||||
}
|
||||
if ($basic_auth && $basic_auth_middleware) {
|
||||
$middlewares->push($basic_auth_middleware);
|
||||
}
|
||||
if ($redirect && $redirect_middleware) {
|
||||
$middlewares->push($redirect_middleware);
|
||||
}
|
||||
if (str($image)->contains('ghost')) {
|
||||
$middlewares->push('redir-ghost');
|
||||
}
|
||||
@@ -533,6 +497,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
||||
$labels = $labels->merge($redirect_to_www);
|
||||
$middlewares->push($to_www_name);
|
||||
}
|
||||
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
|
||||
$middlewares->push($middleware_name);
|
||||
});
|
||||
if ($middlewares->isNotEmpty()) {
|
||||
$middlewares = $middlewares->join(',');
|
||||
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
|
||||
|
||||
@@ -708,7 +708,9 @@ function getTopLevelNetworks(Service|Application $resource)
|
||||
return $value == $networkName || $key == $networkName;
|
||||
});
|
||||
if (! $networkExists) {
|
||||
$topLevelNetworks->put($networkDetails, null);
|
||||
if (is_string($networkDetails) || is_int($networkDetails)) {
|
||||
$topLevelNetworks->put($networkDetails, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -758,7 +760,9 @@ function getTopLevelNetworks(Service|Application $resource)
|
||||
return $value == $networkName || $key == $networkName;
|
||||
});
|
||||
if (! $networkExists) {
|
||||
$topLevelNetworks->put($networkDetails, null);
|
||||
if (is_string($networkDetails) || is_int($networkDetails)) {
|
||||
$topLevelNetworks->put($networkDetails, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -824,6 +828,31 @@ function convertToArray($collection)
|
||||
return $collection;
|
||||
}
|
||||
|
||||
function parseCommandFromMagicEnvVariable(Str|string $key): Stringable
|
||||
{
|
||||
$value = str($key);
|
||||
$count = substr_count($value->value(), '_');
|
||||
if ($count === 2) {
|
||||
if ($value->startsWith('SERVICE_FQDN') || $value->startsWith('SERVICE_URL')) {
|
||||
// SERVICE_FQDN_UMAMI
|
||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||
} else {
|
||||
// SERVICE_BASE64_UMAMI
|
||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||
}
|
||||
}
|
||||
if ($count === 3) {
|
||||
if ($value->startsWith('SERVICE_FQDN') || $value->startsWith('SERVICE_URL')) {
|
||||
// SERVICE_FQDN_UMAMI_1000
|
||||
$command = $value->after('SERVICE_')->before('_');
|
||||
} else {
|
||||
// SERVICE_BASE64_64_UMAMI
|
||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||
}
|
||||
}
|
||||
|
||||
return str($command);
|
||||
}
|
||||
function parseEnvVariable(Str|string $value)
|
||||
{
|
||||
$value = str($value);
|
||||
@@ -855,6 +884,7 @@ function parseEnvVariable(Str|string $value)
|
||||
} else {
|
||||
// SERVICE_BASE64_64_UMAMI
|
||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||
ray($command);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1608,7 +1638,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
return $value == $networkName || $key == $networkName;
|
||||
});
|
||||
if (! $networkExists) {
|
||||
$topLevelNetworks->put($networkDetails, null);
|
||||
if (is_string($networkDetails) || is_int($networkDetails)) {
|
||||
$topLevelNetworks->put($networkDetails, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2523,7 +2555,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
return $value == $networkName || $key == $networkName;
|
||||
});
|
||||
if (! $networkExists) {
|
||||
$topLevelNetworks->put($networkDetails, null);
|
||||
if (is_string($networkDetails) || is_int($networkDetails)) {
|
||||
$topLevelNetworks->put($networkDetails, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2984,11 +3018,22 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
$predefinedPort = '8000';
|
||||
}
|
||||
if ($isDatabase) {
|
||||
$savedService = ServiceDatabase::firstOrCreate([
|
||||
'name' => $serviceName,
|
||||
'image' => $image,
|
||||
'service_id' => $resource->id,
|
||||
]);
|
||||
$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([
|
||||
'name' => $serviceName,
|
||||
'image' => $image,
|
||||
'service_id' => $resource->id,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$savedService = ServiceApplication::firstOrCreate([
|
||||
'name' => $serviceName,
|
||||
@@ -3098,7 +3143,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
foreach ($magicEnvironments as $key => $value) {
|
||||
$key = str($key);
|
||||
$value = replaceVariables($value);
|
||||
$command = $key->after('SERVICE_')->before('_');
|
||||
$command = parseCommandFromMagicEnvVariable($key);
|
||||
$found = $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->first();
|
||||
if ($found) {
|
||||
continue;
|
||||
@@ -3209,12 +3254,24 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
if ($serviceName === 'plausible') {
|
||||
$predefinedPort = '8000';
|
||||
}
|
||||
|
||||
if ($isDatabase) {
|
||||
$savedService = ServiceDatabase::firstOrCreate([
|
||||
'name' => $serviceName,
|
||||
'image' => $image,
|
||||
'service_id' => $resource->id,
|
||||
]);
|
||||
$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([
|
||||
'name' => $serviceName,
|
||||
'image' => $image,
|
||||
'service_id' => $resource->id,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$savedService = ServiceApplication::firstOrCreate([
|
||||
'name' => $serviceName,
|
||||
@@ -3645,6 +3702,18 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
});
|
||||
}
|
||||
$serviceLabels = $labels->merge($defaultLabels);
|
||||
if ($serviceLabels->count() > 0) {
|
||||
if ($isApplication) {
|
||||
$isContainerLabelEscapeEnabled = data_get($resource, 'settings.is_container_label_escape_enabled');
|
||||
} else {
|
||||
$isContainerLabelEscapeEnabled = data_get($resource, 'is_container_label_escape_enabled');
|
||||
}
|
||||
if ($isContainerLabelEscapeEnabled) {
|
||||
$serviceLabels = $serviceLabels->map(function ($value, $key) {
|
||||
return escapeDollarSign($value);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
|
||||
if ($isApplication) {
|
||||
$shouldGenerateLabelsExactly = $resource->destination->server->settings->generate_exact_labels;
|
||||
@@ -3889,11 +3958,13 @@ function convertComposeEnvironmentToArray($environment)
|
||||
} else {
|
||||
// Example: $environment = ['FOO=bar', 'BAZ=qux'];
|
||||
foreach ($environment as $value) {
|
||||
$parts = explode('=', $value, 2);
|
||||
$key = $parts[0];
|
||||
$realValue = $parts[1] ?? '';
|
||||
if ($key) {
|
||||
$convertedServiceVariables->put($key, $realValue);
|
||||
if (is_string($value)) {
|
||||
$parts = explode('=', $value, 2);
|
||||
$key = $parts[0];
|
||||
$realValue = $parts[1] ?? '';
|
||||
if ($key) {
|
||||
$convertedServiceVariables->put($key, $realValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.350',
|
||||
'release' => '4.0.0-beta.355',
|
||||
// When left empty or `null` the Laravel environment will be used
|
||||
'environment' => config('app.env'),
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<?php
|
||||
|
||||
return '4.0.0-beta.350';
|
||||
return '4.0.0-beta.355';
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -46,6 +46,9 @@ services:
|
||||
- PUSHER_APP_ID
|
||||
- PUSHER_APP_KEY
|
||||
- PUSHER_APP_SECRET
|
||||
- TERMINAL_PROTOCOL
|
||||
- TERMINAL_HOST
|
||||
- TERMINAL_PORT
|
||||
- AUTOUPDATE
|
||||
- SELF_HOSTED
|
||||
- SSH_MUX_ENABLED
|
||||
@@ -110,7 +113,7 @@ services:
|
||||
retries: 10
|
||||
timeout: 2s
|
||||
soketi:
|
||||
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.2'
|
||||
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.3'
|
||||
ports:
|
||||
- "${SOKETI_PORT:-6001}:6001"
|
||||
- "6002:6002"
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.349"
|
||||
"version": "4.0.0-beta.354"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0-beta.350"
|
||||
"version": "4.0.0-beta.355"
|
||||
},
|
||||
"helper": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"realtime": {
|
||||
"version": "1.0.2"
|
||||
"version": "1.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
public/svgs/bitcoin.svg
Normal file
15
public/svgs/bitcoin.svg
Normal 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 |
11
public/svgs/homarr.svg
Normal file
11
public/svgs/homarr.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?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">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="484px" height="329px" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g><path style="opacity:0.947" fill="#f95251" d="M 157.5,-0.5 C 158.833,-0.5 160.167,-0.5 161.5,-0.5C 200.102,3.59921 222.268,24.9325 228,63.5C 228.667,88.5 228.667,113.5 228,138.5C 222.962,132.265 216.629,130.265 209,132.5C 208.667,109.167 208.333,85.8333 208,62.5C 202.332,36.3319 186.165,21.9986 159.5,19.5C 141.783,20.273 127.949,27.9397 118,42.5C 112.924,38.3791 107.757,34.3791 102.5,30.5C 115.97,11.5969 134.303,1.26361 157.5,-0.5 Z"/></g>
|
||||
<g><path style="opacity:0.947" fill="#f95251" d="M 321.5,-0.5 C 322.833,-0.5 324.167,-0.5 325.5,-0.5C 348.697,1.26361 367.03,11.5969 380.5,30.5C 375.243,34.3791 370.076,38.3791 365,42.5C 349.005,21.3908 328.505,15.2242 303.5,24C 287.107,31.7273 277.607,44.5606 275,62.5C 274.667,85.8333 274.333,109.167 274,132.5C 266.371,130.265 260.038,132.265 255,138.5C 254.333,113.5 254.333,88.5 255,63.5C 260.732,24.9325 282.898,3.59921 321.5,-0.5 Z"/></g>
|
||||
<g><path style="opacity:0.984" fill="#f95251" d="M -0.5,139.5 C -0.5,137.167 -0.5,134.833 -0.5,132.5C 19.5,132.5 39.5,132.5 59.5,132.5C 51.7127,98.1844 43.3793,64.0177 34.5,30C 83.3876,36.5512 118.554,62.0512 140,106.5C 159.513,155.397 153.846,201.064 123,243.5C 114.899,253.77 105.399,262.437 94.5,269.5C 90.7735,258.059 87.6068,246.392 85,234.5C 39.0428,218.384 10.5428,186.717 -0.5,139.5 Z"/></g>
|
||||
<g><path style="opacity:0.984" fill="#f95251" d="M 483.5,132.5 C 483.5,134.833 483.5,137.167 483.5,139.5C 472.457,186.717 443.957,218.384 398,234.5C 395.393,246.392 392.226,258.059 388.5,269.5C 351.514,243.037 332.514,206.871 331.5,161C 333.865,105.236 359.865,64.9024 409.5,40C 421.97,34.3953 434.97,31.0619 448.5,30C 439.621,64.0177 431.287,98.1844 423.5,132.5C 443.5,132.5 463.5,132.5 483.5,132.5 Z"/></g>
|
||||
<g><path style="opacity:0.95" fill="#f95251" d="M 211.5,170.5 C 225.127,170.958 231.627,177.958 231,191.5C 226.507,203.825 218.007,207.659 205.5,203C 197.535,196.871 195.369,189.037 199,179.5C 201.917,174.637 206.083,171.637 211.5,170.5 Z"/></g>
|
||||
<g><path style="opacity:0.949" fill="#f95251" d="M 265.5,170.5 C 280.848,170.68 287.348,178.347 285,193.5C 279.06,204.76 270.227,207.593 258.5,202C 249.176,192.625 249.176,183.292 258.5,174C 260.925,172.787 263.259,171.621 265.5,170.5 Z"/></g>
|
||||
<g><path style="opacity:0.987" fill="#f95251" d="M 388.5,328.5 C 386.5,328.5 384.5,328.5 382.5,328.5C 285.992,327.966 189.325,326.966 92.5,325.5C 120.96,261.579 170.294,227.913 240.5,224.5C 299.804,226.887 345.304,252.887 377,302.5C 381.676,310.846 385.509,319.513 388.5,328.5 Z"/></g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
6
public/svgs/it-tools.svg
Normal file
6
public/svgs/it-tools.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?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">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="512px" height="512px" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g><path style="opacity:0.995" fill="#18a057" d="M 230.5,-0.5 C 247.5,-0.5 264.5,-0.5 281.5,-0.5C 290.12,3.28885 296.287,9.62218 300,18.5C 299.871,28.5889 300.704,38.4222 302.5,48C 326.717,53.6219 349.217,63.1219 370,76.5C 376.469,71.0323 382.636,65.199 388.5,59C 398.925,52.0844 409.591,51.7511 420.5,58C 432,68.1667 442.833,79 453,90.5C 459,100.5 459,110.5 453,120.5C 447.017,127.317 440.851,133.984 434.5,140.5C 447.998,161.821 457.498,184.821 463,209.5C 474.144,210.648 485.31,211.815 496.5,213C 503.496,216.822 508.496,222.322 511.5,229.5C 511.5,246.5 511.5,263.5 511.5,280.5C 508.841,288.664 503.508,294.497 495.5,298C 484.573,299.444 473.573,300.277 462.5,300.5C 457.369,325.4 448.036,348.566 434.5,370C 441.693,377.526 448.526,385.359 455,393.5C 458.667,402.825 458.001,411.825 453,420.5C 443.167,430.333 433.333,440.167 423.5,450C 414.895,457.565 405.229,459.232 394.5,455C 386.322,448.153 378.155,441.32 370,434.5C 348.501,447.828 325.334,457.161 300.5,462.5C 301.01,473.958 300.177,485.291 298,496.5C 294.006,504.342 287.839,509.342 279.5,511.5C 263.167,511.5 246.833,511.5 230.5,511.5C 222.336,508.841 216.503,503.508 213,495.5C 212,484.833 211,474.167 210,463.5C 198.505,459.749 187.005,455.915 175.5,452C 173.842,451.275 173.342,450.108 174,448.5C 192.195,429.971 210.695,411.805 229.5,394C 293.836,401.915 343.336,378.748 378,324.5C 408.025,263.569 400.691,207.569 356,156.5C 315.267,118.147 267.767,106.314 213.5,121C 164.374,138.458 132.874,172.291 119,222.5C 117.485,228.075 116.485,233.742 116,239.5C 115.261,253.906 115.594,268.239 117,282.5C 98.8333,300.667 80.6667,318.833 62.5,337C 61.552,337.483 60.552,337.649 59.5,337.5C 54.8294,325.486 51.1627,313.153 48.5,300.5C 37.4331,300.188 26.4331,299.355 15.5,298C 7.189,293.843 1.85567,287.343 -0.5,278.5C -0.5,262.833 -0.5,247.167 -0.5,231.5C 2.22646,223.578 7.22646,217.411 14.5,213C 25.7307,211.098 37.0641,210.265 48.5,210.5C 53.718,185.511 63.0513,162.178 76.5,140.5C 71.2189,133.716 65.3855,127.383 59,121.5C 51.8558,110.353 52.1892,99.353 60,88.5C 69.8333,78.6667 79.6667,68.8333 89.5,59C 98.3002,53.1199 107.633,52.1199 117.5,56C 125.629,62.4604 133.463,69.2938 141,76.5C 162.028,62.9274 184.861,53.4274 209.5,48C 210.93,37.2282 212.097,26.3949 213,15.5C 216.503,7.49214 222.336,2.15881 230.5,-0.5 Z"/></g>
|
||||
<g><path style="opacity:0.99" fill="#1d1d1d" d="M 52.5,511.5 C 46.5,511.5 40.5,511.5 34.5,511.5C 16.5,506.167 4.83333,494.5 -0.5,476.5C -0.5,470.167 -0.5,463.833 -0.5,457.5C 1.14258,451.876 3.64258,446.542 7,441.5C 55.9723,391.528 105.306,341.861 155,292.5C 141.187,245.876 152.02,206.042 187.5,173C 216.657,150.235 248.991,143.902 284.5,154C 289.202,158.922 290.702,164.755 289,171.5C 275,186.167 261,200.833 247,215.5C 234.564,236.922 238.73,254.422 259.5,268C 272.178,272.999 284.178,271.666 295.5,264C 309.596,248.899 324.596,234.899 340.5,222C 353.68,220.169 360.513,226.003 361,239.5C 365.554,290.573 345.387,328.073 300.5,352C 273.591,363.046 246.258,364.379 218.5,356C 169.472,405.361 120.139,454.361 70.5,503C 64.8398,506.712 58.8398,509.545 52.5,511.5 Z"/></g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
8
public/svgs/strapi.svg
Normal file
8
public/svgs/strapi.svg
Normal 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 |
@@ -151,9 +151,9 @@
|
||||
@endif
|
||||
@endif
|
||||
<template x-teleport="body">
|
||||
<div x-show="modalOpen" @click.away="modalOpen = false; resetModal()"
|
||||
<div x-show="modalOpen"
|
||||
class="fixed top-0 lg:pt-10 left-0 z-[99] flex items-start justify-center w-screen h-screen" x-cloak>
|
||||
<div x-show="modalOpen" @click="modalOpen = false; resetModal()"
|
||||
<div x-show="modalOpen"
|
||||
class="absolute inset-0 w-full h-full bg-black bg-opacity-20 backdrop-blur-sm"></div>
|
||||
<div x-show="modalOpen" x-trap.inert.noscroll="modalOpen" x-transition:enter="ease-out duration-100"
|
||||
x-transition:enter-start="opacity-0 -translate-y-2 sm:scale-95"
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
'status' => 'Restarting',
|
||||
'lastDeploymentInfo' => null,
|
||||
'lastDeploymentLink' => null,
|
||||
'noLoading' => false,
|
||||
])
|
||||
<div class="flex items-center">
|
||||
<x-loading wire:loading.delay.longer />
|
||||
@if (!$noLoading)
|
||||
<x-loading wire:loading.delay.longer />
|
||||
@endif
|
||||
<span wire:loading.remove.delay.longer class="flex items-center">
|
||||
<div class="badge badge-warning "></div>
|
||||
<div class="pl-2 pr-1 text-xs font-bold tracking-wider dark:text-warning" @if($lastDeploymentInfo) title="{{$lastDeploymentInfo}}" @endif>
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
'status' => 'Running',
|
||||
'lastDeploymentInfo' => null,
|
||||
'lastDeploymentLink' => null,
|
||||
'noLoading' => false,
|
||||
])
|
||||
<div class="flex items-center">
|
||||
<x-loading wire:loading.delay.longer />
|
||||
@if (!$noLoading)
|
||||
<x-loading wire:loading.delay.longer />
|
||||
@endif
|
||||
<span wire:loading.remove.delay.longer class="flex items-center">
|
||||
<div class="badge badge-success "></div>
|
||||
<div class="pl-2 pr-1 text-xs font-bold tracking-wider text-success" @if($lastDeploymentInfo) title="{{$lastDeploymentInfo}}" @endif>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
@props([
|
||||
'status' => 'Stopped',
|
||||
'noLoading' => false,
|
||||
])
|
||||
<div class="flex items-center">
|
||||
<x-loading wire:loading.delay.longer />
|
||||
@if (!$noLoading)
|
||||
<x-loading wire:loading.delay.longer />
|
||||
@endif
|
||||
<span wire:loading.remove.delay.longer class="flex items-center">
|
||||
<div class="badge badge-error "></div>
|
||||
<div class="pl-2 pr-1 text-xs font-bold tracking-wider text-error">{{ str($status)->before(':')->headline() }}</div>
|
||||
|
||||
@@ -8,17 +8,14 @@
|
||||
<livewire:project.database.backup-now :backup="$backup" />
|
||||
@endif
|
||||
@if ($backup->database_id !== 0)
|
||||
<x-modal-confirmation
|
||||
title="Confirm Backup Schedule Deletion?"
|
||||
buttonTitle="Delete Backups and Schedule"
|
||||
isErrorButton
|
||||
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 }}"
|
||||
confirmationLabel="Please confirm the execution of the actions by entering the Database Name of the scheduled backups below"
|
||||
shortConfirmationLabel="Database Name"
|
||||
/>
|
||||
<x-modal-confirmation title="Confirm Backup Schedule Deletion?" buttonTitle="Delete Backups and Schedule"
|
||||
isErrorButton 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 }}"
|
||||
confirmationLabel="Please confirm the execution of the actions by entering the Database Name of the scheduled backups below"
|
||||
shortConfirmationLabel="Database Name" />
|
||||
@endif
|
||||
</div>
|
||||
<div class="w-48 pb-2">
|
||||
@@ -36,23 +33,39 @@
|
||||
</div>
|
||||
@endif
|
||||
<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')
|
||||
<x-forms.input label="Databases To Backup"
|
||||
helper="Comma separated list of databases to backup. Empty will include the default one."
|
||||
id="backup.databases_to_backup" />
|
||||
<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"
|
||||
helper="Comma separated list of databases to backup. Empty will include the default one."
|
||||
id="backup.databases_to_backup" />
|
||||
@endif
|
||||
@elseif($backup->database_type === 'App\Models\StandaloneMongodb')
|
||||
<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."
|
||||
id="backup.databases_to_backup" />
|
||||
@elseif($backup->database_type === 'App\Models\StandaloneMysql')
|
||||
<x-forms.input label="Databases To Backup"
|
||||
helper="Comma separated list of databases to backup. Empty will include the default one."
|
||||
id="backup.databases_to_backup" />
|
||||
<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"
|
||||
helper="Comma separated list of databases to backup. Empty will include the default one."
|
||||
id="backup.databases_to_backup" />
|
||||
@endif
|
||||
@elseif($backup->database_type === 'App\Models\StandaloneMariadb')
|
||||
<x-forms.input label="Databases To Backup"
|
||||
helper="Comma separated list of databases to backup. Empty will include the default one."
|
||||
id="backup.databases_to_backup" />
|
||||
<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"
|
||||
helper="Comma separated list of databases to backup. Empty will include the default one."
|
||||
id="backup.databases_to_backup" />
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
|
||||
@@ -1,37 +1,38 @@
|
||||
<div>
|
||||
<div class="flex flex-col gap-2">
|
||||
@forelse($database->scheduledBackups as $backup)
|
||||
@if ($type == 'database')
|
||||
<a class="box"
|
||||
href="{{ route('project.database.backup.execution', [...$parameters, 'backup_uuid' => $backup->uuid]) }}">
|
||||
<div class="flex flex-col">
|
||||
<div>Frequency: {{ $backup->frequency }}</div>
|
||||
<div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
|
||||
<div>Number of backups to keep (locally): {{ $backup->number_of_backups_locally }}</div>
|
||||
</div>
|
||||
</a>
|
||||
@else
|
||||
<div class="box" wire:click="setSelectedBackup('{{ data_get($backup, 'id') }}')">
|
||||
<div @class([ 'border-coollabs'=>
|
||||
data_get($backup, 'id') === data_get($selectedBackup, 'id'),
|
||||
'flex flex-col border-l-2 border-transparent',
|
||||
])>
|
||||
<div>Frequency: {{ $backup->frequency }}</div>
|
||||
<div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
|
||||
<div>Number of backups to keep (locally): {{ $backup->number_of_backups_locally }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@if ($type == 'database')
|
||||
<a class="box"
|
||||
href="{{ route('project.database.backup.execution', [...$parameters, 'backup_uuid' => $backup->uuid]) }}">
|
||||
<div class="flex flex-col">
|
||||
<div>Frequency: {{ $backup->frequency }}</div>
|
||||
<div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
|
||||
<div>Number of backups to keep (locally): {{ $backup->number_of_backups_locally }}</div>
|
||||
</div>
|
||||
</a>
|
||||
@else
|
||||
<div class="box" wire:click="setSelectedBackup('{{ data_get($backup, 'id') }}')">
|
||||
<div @class([
|
||||
'border-coollabs' =>
|
||||
data_get($backup, 'id') === data_get($selectedBackup, 'id'),
|
||||
'flex flex-col border-l-2 border-transparent',
|
||||
])>
|
||||
<div>Frequency: {{ $backup->frequency }}</div>
|
||||
<div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
|
||||
<div>Number of backups to keep (locally): {{ $backup->number_of_backups_locally }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@empty
|
||||
<div>No scheduled backups configured.</div>
|
||||
<div>No scheduled backups configured.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
@if ($type === 'service-database' && $selectedBackup)
|
||||
<div class="pt-10">
|
||||
<livewire:project.database.backup-edit wire:key="{{ $selectedBackup->id }}" :backup="$selectedBackup"
|
||||
:s3s="$s3s" :status="data_get($database, 'status')" />
|
||||
<h3 class="py-4">Executions</h3>
|
||||
<livewire:project.database.backup-executions wire:key="{{ $selectedBackup->id }}" :backup="$selectedBackup" :database="$database" />
|
||||
</div>
|
||||
<div class="pt-10">
|
||||
<livewire:project.database.backup-edit wire:key="{{ $selectedBackup->id }}" :backup="$selectedBackup"
|
||||
:s3s="$s3s" :status="data_get($database, 'status')" />
|
||||
<livewire:project.database.backup-executions wire:key="{{ $selectedBackup->uuid }}" :backup="$selectedBackup"
|
||||
:database="$database" />
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -149,6 +149,12 @@
|
||||
<div class="text-xs">{{ $database->status }}</div>
|
||||
</div>
|
||||
<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"
|
||||
href="{{ route('project.service.index', [...$parameters, 'stack_service_uuid' => $database->uuid]) }}">
|
||||
Settings
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
<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 = ''"
|
||||
href="#">General</a>
|
||||
@if (str($serviceDatabase?->databaseType())->contains('mysql') ||
|
||||
str($serviceDatabase?->databaseType())->contains('postgres') ||
|
||||
str($serviceDatabase?->databaseType())->contains('mariadb'))
|
||||
@if ($serviceDatabase?->isBackupSolutionAvailable())
|
||||
<a :class="activeTab === 'backups' && 'menu-item-active'" class="menu-item"
|
||||
@click.prevent="activeTab = 'backups'; window.location.hash = 'backups'" href="#">Backups</a>
|
||||
@endif
|
||||
@@ -28,22 +26,25 @@
|
||||
</div>
|
||||
@endisset
|
||||
@isset($serviceDatabase)
|
||||
<x-slot:title>
|
||||
{{ data_get_str($service, 'name')->limit(10) }} > {{ data_get_str($serviceDatabase, 'name')->limit(10) }} | Coolify
|
||||
</x-slot>
|
||||
<x-slot:title>
|
||||
{{ data_get_str($service, 'name')->limit(10) }} >
|
||||
{{ data_get_str($serviceDatabase, 'name')->limit(10) }} | Coolify
|
||||
</x-slot>
|
||||
<div x-cloak x-show="activeTab === 'general'" class="h-full">
|
||||
<livewire:project.service.database :database="$serviceDatabase" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'backups'">
|
||||
<div class="flex gap-2 ">
|
||||
<h2 class="pb-4">Scheduled Backups</h2>
|
||||
<x-modal-input buttonTitle="+ Add" title="New Scheduled Backup">
|
||||
<livewire:project.database.create-scheduled-backup :database="$serviceDatabase" :s3s="$s3s" />
|
||||
</x-modal-input>
|
||||
</div>
|
||||
<livewire:project.database.scheduled-backups :database="$serviceDatabase" />
|
||||
</div>
|
||||
@endisset
|
||||
</div>
|
||||
@if ($serviceDatabase->isBackupSolutionAvailable())
|
||||
<div x-cloak x-show="activeTab === 'backups'">
|
||||
<div class="flex gap-2 ">
|
||||
<h2 class="pb-4">Scheduled Backups</h2>
|
||||
<x-modal-input buttonTitle="+ Add" title="New Scheduled Backup">
|
||||
<livewire:project.database.create-scheduled-backup :database="$serviceDatabase" :s3s="$s3s" />
|
||||
</x-modal-input>
|
||||
</div>
|
||||
<livewire:project.database.scheduled-backups :database="$serviceDatabase" />
|
||||
@endif
|
||||
</div>
|
||||
@endisset
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,25 @@
|
||||
</nav>
|
||||
<div class="flex flex-wrap order-first gap-2 items-center sm:order-last">
|
||||
@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">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||
stroke-width="2">
|
||||
@@ -30,8 +48,8 @@
|
||||
<path d="M20 4v5h-5" />
|
||||
</g>
|
||||
</svg>
|
||||
Pull Latest Images & Restart
|
||||
</button>
|
||||
Restart
|
||||
</x-forms.button>
|
||||
<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"
|
||||
step2ButtonText="Stop Service" :dispatchEvent="true" dispatchEventType="stopEvent">
|
||||
@@ -135,9 +153,13 @@
|
||||
$wire.$call('start');
|
||||
});
|
||||
$wire.$on('restartEvent', () => {
|
||||
$wire.$dispatch('info', 'Pulling new images.');
|
||||
$wire.$dispatch('info', 'Service restart in progress.');
|
||||
$wire.$call('restart');
|
||||
});
|
||||
$wire.$on('pullAndRestartEvent', () => {
|
||||
$wire.$dispatch('info', 'Pulling new images.');
|
||||
$wire.$call('pullAndRestartEvent');
|
||||
});
|
||||
$wire.on('imagePulled', () => {
|
||||
window.dispatchEvent(new CustomEvent('startservice'));
|
||||
$wire.$dispatch('info', 'Restarting service.');
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<div x-init="$wire.checkProxy()" class="flex gap-2">
|
||||
@if (data_get($server, 'proxy.status') === 'running')
|
||||
<x-status.running status="Proxy Running" />
|
||||
<x-status.running status="Proxy Running" noLoading />
|
||||
@elseif (data_get($server, 'proxy.status') === 'restarting')
|
||||
<x-status.restarting status="Proxy Restarting" />
|
||||
<x-status.restarting status="Proxy Restarting" noLoading />
|
||||
@elseif (data_get($server, 'proxy.force_stop'))
|
||||
<x-status.stopped status="Proxy Stopped" />
|
||||
<x-status.stopped status="Proxy Stopped" noLoading />
|
||||
@elseif (data_get($server, 'proxy.status') === 'exited')
|
||||
<x-status.stopped status="Proxy Exited" />
|
||||
<x-status.stopped status="Proxy Exited" noLoading />
|
||||
@else
|
||||
<x-status.stopped status="Proxy Not Running" />
|
||||
<x-status.stopped status="Proxy Not Running" noLoading />
|
||||
@endif
|
||||
<x-forms.button wire:click='checkProxy(true)'>Refresh</x-forms.button>
|
||||
</div>
|
||||
|
||||
17
templates/compose/bitcoin-core.yaml
Normal file
17
templates/compose/bitcoin-core.yaml
Normal 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 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
|
||||
# logo: svgs/docker-registry.png
|
||||
# port: 5000
|
||||
|
||||
21
templates/compose/homarr.yaml
Normal file
21
templates/compose/homarr.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
# documentation: https://homarr.dev
|
||||
# slogan: Homarr is a self-hosted homepage for your services.
|
||||
# tags: homarr,self-hosted,homepage
|
||||
# logo: svgs/homarr.svg
|
||||
# port: 7575
|
||||
|
||||
services:
|
||||
homarr:
|
||||
image: ghcr.io/ajnart/homarr:latest
|
||||
environment:
|
||||
- SERVICE_FQDN_HOMARR_7575
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./homarr/configs:/app/data/configs
|
||||
- ./homarr/icons:/app/public/icons
|
||||
- ./homarr/data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:7575"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
18
templates/compose/it-tools.yaml
Normal file
18
templates/compose/it-tools.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
# documentation: https://github.com/corentinth/it-tools
|
||||
# slogan: IT Tools is a self-hosted solution for managing various IT tasks.
|
||||
# tags: it-tools,management,self-hosted
|
||||
# logo: svgs/it-tools.svg
|
||||
# port: 80
|
||||
|
||||
services:
|
||||
it-tools:
|
||||
image: corentinth/it-tools:latest
|
||||
environment:
|
||||
- SERVICE_FQDN_ITTOOLS_80
|
||||
volumes:
|
||||
- it-tools-data:/app/data
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
60
templates/compose/strapi.yaml
Normal file
60
templates/compose/strapi.yaml
Normal 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
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.350"
|
||||
"version": "4.0.0-beta.355"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0-beta.351"
|
||||
"version": "4.0.0-beta.356"
|
||||
},
|
||||
"helper": {
|
||||
"version": "1.0.1"
|
||||
|
||||
Reference in New Issue
Block a user