Merge branch 'next' into next
This commit is contained in:
103
.github/workflows/coolify-realtime-next.yml
vendored
Normal file
103
.github/workflows/coolify-realtime-next.yml
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
name: Coolify Realtime Development (v4)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "next" ]
|
||||
paths:
|
||||
- .github/workflows/coolify-realtime.yml
|
||||
- docker/coolify-realtime/Dockerfile
|
||||
- docker/coolify-realtime/terminal-server.js
|
||||
- docker/coolify-realtime/package.json
|
||||
- docker/coolify-realtime/soketi-entrypoint.sh
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: "coollabsio/coolify-realtime"
|
||||
|
||||
jobs:
|
||||
amd64:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Get Version
|
||||
id: version
|
||||
run: |
|
||||
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
file: docker/coolify-realtime/Dockerfile
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
|
||||
labels: |
|
||||
coolify.managed=true
|
||||
aarch64:
|
||||
runs-on: [ self-hosted, arm64 ]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Get Version
|
||||
id: version
|
||||
run: |
|
||||
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
|
||||
- name: Build image and push to registry
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
no-cache: true
|
||||
context: .
|
||||
file: docker/coolify-realtime/Dockerfile
|
||||
platforms: linux/aarch64
|
||||
push: true
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64
|
||||
labels: |
|
||||
coolify.managed=true
|
||||
merge-manifest:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
needs: [ amd64, aarch64 ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Get Version
|
||||
id: version
|
||||
run: |
|
||||
echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.realtime.version' versions.json)"|xargs >> $GITHUB_OUTPUT
|
||||
- name: Create & publish manifest
|
||||
run: |
|
||||
docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
|
||||
- uses: sarisia/actions-status-discord@v1
|
||||
if: always()
|
||||
with:
|
||||
webhook: ${{ secrets.DISCORD_WEBHOOK_PROD_RELEASE_CHANNEL }}
|
2
.github/workflows/coolify-realtime.yml
vendored
2
.github/workflows/coolify-realtime.yml
vendored
@@ -2,7 +2,7 @@ name: Coolify Realtime (v4)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "next" ]
|
||||
branches: [ "main" ]
|
||||
paths:
|
||||
- .github/workflows/coolify-realtime.yml
|
||||
- docker/coolify-realtime/Dockerfile
|
||||
|
@@ -46,9 +46,6 @@ class StartDragonfly
|
||||
'networks' => [
|
||||
$this->database->destination->network,
|
||||
],
|
||||
'ulimits' => [
|
||||
'memlock' => '-1',
|
||||
],
|
||||
'labels' => [
|
||||
'coolify.managed' => 'true',
|
||||
],
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
@@ -20,7 +19,7 @@ class CreateNewUser implements CreatesNewUsers
|
||||
*/
|
||||
public function create(array $input): User
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (! $settings->is_registration_enabled) {
|
||||
abort(403);
|
||||
}
|
||||
@@ -48,7 +47,7 @@ class CreateNewUser implements CreatesNewUsers
|
||||
$team = $user->teams()->first();
|
||||
|
||||
// Disable registration after first user is created
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$settings->is_registration_enabled = false;
|
||||
$settings->save();
|
||||
} else {
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Actions\License;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
@@ -13,7 +12,7 @@ class CheckResaleLicense
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (isDev()) {
|
||||
$settings->update([
|
||||
'is_resale_license_active' => true,
|
||||
|
@@ -22,7 +22,7 @@ class CheckConfiguration
|
||||
];
|
||||
$proxy_configuration = instant_remote_process($payload, $server, false);
|
||||
if ($reset || ! $proxy_configuration || is_null($proxy_configuration)) {
|
||||
$proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value;
|
||||
$proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value();
|
||||
}
|
||||
if (! $proxy_configuration || is_null($proxy_configuration)) {
|
||||
throw new \Exception('Could not generate proxy configuration');
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -26,7 +26,7 @@ class StartProxy
|
||||
}
|
||||
SaveConfiguration::run($server, $configuration);
|
||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||
$server->proxy->last_applied_settings = str($docker_compose_yml_base64)->pipe('md5')->value;
|
||||
$server->proxy->last_applied_settings = str($docker_compose_yml_base64)->pipe('md5')->value();
|
||||
$server->save();
|
||||
if ($server->isSwarm()) {
|
||||
$commands = $commands->merge([
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
@@ -12,7 +11,7 @@ class CleanupDocker
|
||||
|
||||
public function handle(Server $server)
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$helperImageVersion = data_get($settings, 'helper_version');
|
||||
$helperImage = config('coolify.helper_image');
|
||||
$helperImageWithVersion = "$helperImage:$helperImageVersion";
|
||||
|
@@ -3,7 +3,6 @@
|
||||
namespace App\Actions\Server;
|
||||
|
||||
use App\Jobs\PullHelperImageJob;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
@@ -20,7 +19,7 @@ class UpdateCoolify
|
||||
public function handle($manual_update = false)
|
||||
{
|
||||
try {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$this->server = Server::find(0);
|
||||
if (! $this->server) {
|
||||
return;
|
||||
|
50
app/Console/Commands/CheckApplicationDeploymentQueue.php
Normal file
50
app/Console/Commands/CheckApplicationDeploymentQueue.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class CheckApplicationDeploymentQueue extends Command
|
||||
{
|
||||
protected $signature = 'check:deployment-queue {--force} {--seconds=3600}';
|
||||
|
||||
protected $description = 'Check application deployment queue.';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$seconds = $this->option('seconds');
|
||||
$deployments = ApplicationDeploymentQueue::whereIn('status', [
|
||||
ApplicationDeploymentStatus::IN_PROGRESS,
|
||||
ApplicationDeploymentStatus::QUEUED,
|
||||
])->where('created_at', '<=', now()->subSeconds($seconds))->get();
|
||||
if ($deployments->isEmpty()) {
|
||||
$this->info('No deployments found in the last '.$seconds.' seconds.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info('Found '.$deployments->count().' deployments created in the last '.$seconds.' seconds.');
|
||||
|
||||
foreach ($deployments as $deployment) {
|
||||
if ($this->option('force')) {
|
||||
$this->info('Deployment '.$deployment->id.' created at '.$deployment->created_at.' is older than '.$seconds.' seconds. Setting status to failed.');
|
||||
$this->cancelDeployment($deployment);
|
||||
} else {
|
||||
$this->info('Deployment '.$deployment->id.' created at '.$deployment->created_at.' is older than '.$seconds.' seconds. Setting status to failed.');
|
||||
if ($this->confirm('Do you want to cancel this deployment?', true)) {
|
||||
$this->cancelDeployment($deployment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function cancelDeployment(ApplicationDeploymentQueue $deployment)
|
||||
{
|
||||
$deployment->update(['status' => ApplicationDeploymentStatus::FAILED]);
|
||||
if ($deployment->server?->isFunctional()) {
|
||||
remote_process(['docker rm -f '.$deployment->deployment_uuid], $deployment->server, false);
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,9 +7,9 @@ use Illuminate\Console\Command;
|
||||
|
||||
class CleanupApplicationDeploymentQueue extends Command
|
||||
{
|
||||
protected $signature = 'cleanup:application-deployment-queue {--team-id=}';
|
||||
protected $signature = 'cleanup:deployment-queue {--team-id=}';
|
||||
|
||||
protected $description = 'CleanupApplicationDeploymentQueue';
|
||||
protected $description = 'Cleanup application deployment queue.';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
|
@@ -4,6 +4,7 @@ namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\CleanupHelperContainersJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\ScheduledTask;
|
||||
@@ -47,6 +48,17 @@ class CleanupStuckedResources extends Command
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in cleaning stucked resources: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$applicationsDeploymentQueue = ApplicationDeploymentQueue::get();
|
||||
foreach ($applicationsDeploymentQueue as $applicationDeploymentQueue) {
|
||||
if (is_null($applicationDeploymentQueue->application)) {
|
||||
echo "Deleting stuck application deployment queue: {$applicationDeploymentQueue->id}\n";
|
||||
$applicationDeploymentQueue->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in cleaning stuck application deployment queue: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
|
||||
foreach ($applications as $application) {
|
||||
|
@@ -48,6 +48,13 @@ class Dev extends Command
|
||||
echo "Generating APP_KEY.\n";
|
||||
Artisan::call('key:generate');
|
||||
}
|
||||
|
||||
// Generate STORAGE link if not exists
|
||||
if (! file_exists(public_path('storage'))) {
|
||||
echo "Generating STORAGE link.\n";
|
||||
Artisan::call('storage:link');
|
||||
}
|
||||
|
||||
// Seed database if it's empty
|
||||
$settings = InstanceSettings::find(0);
|
||||
if (! $settings) {
|
||||
|
@@ -7,7 +7,6 @@ use App\Enums\ActivityTypes;
|
||||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\Environment;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandalonePostgresql;
|
||||
@@ -69,7 +68,7 @@ class Init extends Command
|
||||
} catch (\Throwable $e) {
|
||||
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||
}
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (! is_null(env('AUTOUPDATE', null))) {
|
||||
if (env('AUTOUPDATE') == true) {
|
||||
$settings->update(['is_auto_update_enabled' => true]);
|
||||
@@ -196,7 +195,7 @@ class Init extends Command
|
||||
{
|
||||
$id = config('app.id');
|
||||
$version = config('version');
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$do_not_track = data_get($settings, 'do_not_track');
|
||||
if ($do_not_track == true) {
|
||||
echo "Skipping alive as do_not_track is enabled\n";
|
||||
|
@@ -12,8 +12,8 @@ use App\Jobs\PullSentinelImageJob;
|
||||
use App\Jobs\PullTemplatesFromCDN;
|
||||
use App\Jobs\ScheduledTaskJob;
|
||||
use App\Jobs\ServerCheckJob;
|
||||
use App\Jobs\ServerStorageCheckJob;
|
||||
use App\Jobs\UpdateCoolifyJob;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\ScheduledTask;
|
||||
use App\Models\Server;
|
||||
@@ -28,7 +28,7 @@ class Kernel extends ConsoleKernel
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
$this->all_servers = Server::all();
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
|
||||
$schedule->job(new CleanupStaleMultiplexedConnections)->hourly();
|
||||
|
||||
@@ -66,7 +66,7 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
private function pull_images($schedule)
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
||||
foreach ($servers as $server) {
|
||||
if ($server->isSentinelEnabled()) {
|
||||
@@ -88,7 +88,7 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
private function schedule_updates($schedule)
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
|
||||
$updateCheckFrequency = $settings->update_check_frequency;
|
||||
$schedule->job(new CheckForUpdatesJob)
|
||||
@@ -116,6 +116,7 @@ class Kernel extends ConsoleKernel
|
||||
}
|
||||
foreach ($servers as $server) {
|
||||
$schedule->job(new ServerCheckJob($server))->everyMinute()->onOneServer();
|
||||
// $schedule->job(new ServerStorageCheckJob($server))->everyMinute()->onOneServer();
|
||||
$serverTimezone = $server->settings->server_timezone;
|
||||
if ($server->settings->force_docker_cleanup) {
|
||||
$schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer();
|
||||
|
@@ -65,7 +65,7 @@ class Handler extends ExceptionHandler
|
||||
if ($e instanceof RuntimeException) {
|
||||
return;
|
||||
}
|
||||
$this->settings = \App\Models\InstanceSettings::get();
|
||||
$this->settings = instanceSettings();
|
||||
if ($this->settings->do_not_track) {
|
||||
return;
|
||||
}
|
||||
|
@@ -94,7 +94,9 @@ class SshMultiplexingHelper
|
||||
$muxPersistTime = config('constants.ssh.mux_persist_time');
|
||||
|
||||
$scp_command = "timeout $timeout scp ";
|
||||
|
||||
if ($server->isIpv6()) {
|
||||
$scp_command .= '-6 ';
|
||||
}
|
||||
if (self::isMultiplexingEnabled()) {
|
||||
$scp_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
|
||||
self::ensureMultiplexedConnection($server);
|
||||
@@ -136,8 +138,8 @@ class SshMultiplexingHelper
|
||||
|
||||
$ssh_command .= self::getCommonSshOptions($server, $sshKeyLocation, config('constants.ssh.connection_timeout'), config('constants.ssh.server_interval'));
|
||||
|
||||
$command = "PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/host/usr/local/sbin:/host/usr/local/bin:/host/usr/sbin:/host/usr/bin:/host/sbin:/host/bin && $command";
|
||||
$delimiter = Hash::make($command);
|
||||
$delimiter = base64_encode($delimiter);
|
||||
$command = str_replace($delimiter, '', $command);
|
||||
|
||||
$ssh_command .= "{$server->user}@{$server->ip} 'bash -se' << \\$delimiter".PHP_EOL
|
||||
|
@@ -177,6 +177,7 @@ class ApplicationsController extends Controller
|
||||
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
@@ -279,6 +280,7 @@ class ApplicationsController extends Controller
|
||||
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
@@ -381,6 +383,7 @@ class ApplicationsController extends Controller
|
||||
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
@@ -468,6 +471,7 @@ class ApplicationsController extends Controller
|
||||
'manual_webhook_secret_gitea' => ['type' => 'string', 'description' => 'Manual webhook secret for Gitea.'],
|
||||
'redirect' => ['type' => 'string', 'nullable' => true, 'description' => 'How to set redirect with Traefik / Caddy. www<->non-www.', 'enum' => ['www', 'non-www', 'both']],
|
||||
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
@@ -552,6 +556,7 @@ class ApplicationsController extends Controller
|
||||
'manual_webhook_secret_gitea' => ['type' => 'string', 'description' => 'Manual webhook secret for Gitea.'],
|
||||
'redirect' => ['type' => 'string', 'nullable' => true, 'description' => 'How to set redirect with Traefik / Caddy. www<->non-www.', 'enum' => ['www', 'non-www', 'both']],
|
||||
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
@@ -602,6 +607,7 @@ class ApplicationsController extends Controller
|
||||
'name' => ['type' => 'string', 'description' => 'The application name.'],
|
||||
'description' => ['type' => 'string', 'description' => 'The application description.'],
|
||||
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
@@ -627,7 +633,7 @@ class ApplicationsController extends Controller
|
||||
|
||||
private function create_application(Request $request, $type)
|
||||
{
|
||||
$allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths'];
|
||||
$allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths', 'use_build_server'];
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
@@ -665,6 +671,7 @@ class ApplicationsController extends Controller
|
||||
$fqdn = $request->domains;
|
||||
$instantDeploy = $request->instant_deploy;
|
||||
$githubAppUuid = $request->github_app_uuid;
|
||||
$useBuildServer = $request->use_build_server;
|
||||
|
||||
$project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first();
|
||||
if (! $project) {
|
||||
@@ -737,6 +744,10 @@ class ApplicationsController extends Controller
|
||||
$application->destination_id = $destination->id;
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
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) {
|
||||
@@ -833,6 +844,10 @@ class ApplicationsController extends Controller
|
||||
$application->environment_id = $environment->id;
|
||||
$application->source_type = $githubApp->getMorphClass();
|
||||
$application->source_id = $githubApp->id;
|
||||
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) {
|
||||
@@ -925,6 +940,10 @@ class ApplicationsController extends Controller
|
||||
$application->destination_id = $destination->id;
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
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) {
|
||||
@@ -1004,6 +1023,10 @@ class ApplicationsController extends Controller
|
||||
$application->destination_id = $destination->id;
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
|
||||
$application->git_repository = 'coollabsio/coolify';
|
||||
$application->git_branch = 'main';
|
||||
@@ -1062,6 +1085,10 @@ class ApplicationsController extends Controller
|
||||
$application->destination_id = $destination->id;
|
||||
$application->destination_type = $destination->getMorphClass();
|
||||
$application->environment_id = $environment->id;
|
||||
if (isset($useBuildServer)) {
|
||||
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||
$application->settings->save();
|
||||
}
|
||||
|
||||
$application->git_repository = 'coollabsio/coolify';
|
||||
$application->git_branch = 'main';
|
||||
@@ -1259,16 +1286,10 @@ class ApplicationsController extends Controller
|
||||
format: 'uuid',
|
||||
)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'cleanup',
|
||||
in: 'query',
|
||||
description: 'Delete configurations and volumes.',
|
||||
required: false,
|
||||
schema: new OA\Schema(
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
)
|
||||
),
|
||||
new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
@@ -1316,10 +1337,14 @@ class ApplicationsController extends Controller
|
||||
'message' => 'Application not found',
|
||||
], 404);
|
||||
}
|
||||
|
||||
DeleteResourceJob::dispatch(
|
||||
resource: $application,
|
||||
deleteConfigurations: $cleanup,
|
||||
deleteVolumes: $cleanup);
|
||||
deleteConfigurations: $request->query->get('delete_configurations', true),
|
||||
deleteVolumes: $request->query->get('delete_volumes', true),
|
||||
dockerCleanup: $request->query->get('docker_cleanup', true),
|
||||
deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Application deletion request queued.',
|
||||
@@ -1404,6 +1429,7 @@ class ApplicationsController extends Controller
|
||||
'docker_compose_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
||||
'use_build_server' => ['type' => 'boolean', 'nullable' => true, 'description' => 'Use build server.'],
|
||||
],
|
||||
)),
|
||||
]),
|
||||
@@ -1460,7 +1486,7 @@ class ApplicationsController extends Controller
|
||||
], 404);
|
||||
}
|
||||
$server = $application->destination->server;
|
||||
$allowedFields = ['name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'redirect', 'instant_deploy'];
|
||||
$allowedFields = ['name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'redirect', 'instant_deploy', 'use_build_server'];
|
||||
|
||||
$validator = customApiValidator($request->all(), [
|
||||
sharedDataApplications(),
|
||||
@@ -1538,6 +1564,13 @@ class ApplicationsController extends Controller
|
||||
}
|
||||
$instantDeploy = $request->instant_deploy;
|
||||
|
||||
$use_build_server = $request->use_build_server;
|
||||
|
||||
if (isset($use_build_server)) {
|
||||
$application->settings->is_build_server_enabled = $use_build_server;
|
||||
$application->settings->save();
|
||||
}
|
||||
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
|
||||
$data = $request->all();
|
||||
|
@@ -1541,16 +1541,10 @@ class DatabasesController extends Controller
|
||||
format: 'uuid',
|
||||
)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'cleanup',
|
||||
in: 'query',
|
||||
description: 'Delete configurations and volumes.',
|
||||
required: false,
|
||||
schema: new OA\Schema(
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
)
|
||||
),
|
||||
new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
@@ -1595,10 +1589,14 @@ class DatabasesController extends Controller
|
||||
if (! $database) {
|
||||
return response()->json(['message' => 'Database not found.'], 404);
|
||||
}
|
||||
|
||||
DeleteResourceJob::dispatch(
|
||||
resource: $database,
|
||||
deleteConfigurations: $cleanup,
|
||||
deleteVolumes: $cleanup);
|
||||
deleteConfigurations: $request->query->get('delete_configurations', true),
|
||||
deleteVolumes: $request->query->get('delete_volumes', true),
|
||||
dockerCleanup: $request->query->get('docker_cleanup', true),
|
||||
deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Database deletion request queued.',
|
||||
|
@@ -86,7 +86,7 @@ class OtherController extends Controller
|
||||
if ($teamId !== '0') {
|
||||
return response()->json(['message' => 'You are not allowed to enable the API.'], 403);
|
||||
}
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$settings->update(['is_api_enabled' => true]);
|
||||
|
||||
return response()->json(['message' => 'API enabled.'], 200);
|
||||
@@ -138,7 +138,7 @@ class OtherController extends Controller
|
||||
if ($teamId !== '0') {
|
||||
return response()->json(['message' => 'You are not allowed to disable the API.'], 403);
|
||||
}
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$settings->update(['is_api_enabled' => false]);
|
||||
|
||||
return response()->json(['message' => 'API disabled.'], 200);
|
||||
|
@@ -308,7 +308,7 @@ class ServersController extends Controller
|
||||
$projects = Project::where('team_id', $teamId)->get();
|
||||
$domains = collect();
|
||||
$applications = $projects->pluck('applications')->flatten();
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if ($applications->count() > 0) {
|
||||
foreach ($applications as $application) {
|
||||
$ip = $application->destination->server->ip;
|
||||
|
@@ -432,6 +432,10 @@ class ServicesController extends Controller
|
||||
tags: ['Services'],
|
||||
parameters: [
|
||||
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Service UUID', schema: new OA\Schema(type: 'string')),
|
||||
new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
@@ -476,7 +480,14 @@ class ServicesController extends Controller
|
||||
if (! $service) {
|
||||
return response()->json(['message' => 'Service not found.'], 404);
|
||||
}
|
||||
DeleteResourceJob::dispatch($service);
|
||||
|
||||
DeleteResourceJob::dispatch(
|
||||
resource: $service,
|
||||
deleteConfigurations: $request->query->get('delete_configurations', true),
|
||||
deleteVolumes: $request->query->get('delete_volumes', true),
|
||||
dockerCleanup: $request->query->get('docker_cleanup', true),
|
||||
deleteConnectedNetworks: $request->query->get('delete_connected_networks', true)
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Service deletion request queued.',
|
||||
@@ -516,7 +527,8 @@ class ServicesController extends Controller
|
||||
items: new OA\Items(ref: '#/components/schemas/EnvironmentVariable')
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
@@ -619,7 +631,8 @@ class ServicesController extends Controller
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
@@ -738,7 +751,8 @@ class ServicesController extends Controller
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
@@ -853,7 +867,8 @@ class ServicesController extends Controller
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
@@ -953,7 +968,8 @@ class ServicesController extends Controller
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
@@ -1025,9 +1041,11 @@ class ServicesController extends Controller
|
||||
type: 'object',
|
||||
properties: [
|
||||
'message' => ['type' => 'string', 'example' => 'Service starting request queued.'],
|
||||
])
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
@@ -1101,9 +1119,11 @@ class ServicesController extends Controller
|
||||
type: 'object',
|
||||
properties: [
|
||||
'message' => ['type' => 'string', 'example' => 'Service stopping request queued.'],
|
||||
])
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
@@ -1177,9 +1197,11 @@ class ServicesController extends Controller
|
||||
type: 'object',
|
||||
properties: [
|
||||
'message' => ['type' => 'string', 'example' => 'Service restaring request queued.'],
|
||||
])
|
||||
]
|
||||
)
|
||||
),
|
||||
]),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
@@ -22,7 +21,7 @@ class OauthController extends Controller
|
||||
$oauthUser = get_socialite_provider($provider)->user();
|
||||
$user = User::whereEmail($oauthUser->email)->first();
|
||||
if (! $user) {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (! $settings->is_registration_enabled) {
|
||||
abort(403, 'Registration is disabled');
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ class ApiAllowed
|
||||
if (isCloud()) {
|
||||
return $next($request);
|
||||
}
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if ($settings->is_api_enabled === false) {
|
||||
return response()->json(['success' => true, 'message' => 'API is disabled.'], 403);
|
||||
}
|
||||
|
@@ -12,7 +12,6 @@ use App\Models\ApplicationPreview;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\GithubApp;
|
||||
use App\Models\GitlabApp;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\SwarmDocker;
|
||||
@@ -962,7 +961,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
}
|
||||
if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||
if ($this->application->compose_parsing_version === '3') {
|
||||
if ((int) $this->application->compose_parsing_version >= 3) {
|
||||
$envs->push("COOLIFY_URL={$this->application->fqdn}");
|
||||
} else {
|
||||
$envs->push("COOLIFY_FQDN={$this->application->fqdn}");
|
||||
@@ -970,7 +969,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||
$url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
|
||||
if ($this->application->compose_parsing_version === '3') {
|
||||
if ((int) $this->application->compose_parsing_version >= 3) {
|
||||
$envs->push("COOLIFY_FQDN={$url}");
|
||||
} else {
|
||||
$envs->push("COOLIFY_URL={$url}");
|
||||
@@ -1334,7 +1333,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
private function prepare_builder_image()
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$helperImage = config('coolify.helper_image');
|
||||
$helperImage = "{$helperImage}:{$settings->helper_version}";
|
||||
// Get user home directory
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@@ -22,7 +21,7 @@ class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
|
||||
if (isDev() || isCloud()) {
|
||||
return;
|
||||
}
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
|
||||
if ($response->successful()) {
|
||||
$versions = $response->json();
|
||||
|
@@ -2,9 +2,7 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Actions\Database\StopDatabase;
|
||||
use App\Events\BackupCreated;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\S3Storage;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\ScheduledDatabaseBackupExecution;
|
||||
@@ -25,7 +23,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Str;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
@@ -64,32 +61,32 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
public function __construct($backup)
|
||||
{
|
||||
$this->backup = $backup;
|
||||
$this->team = Team::find($backup->team_id);
|
||||
if (is_null($this->team)) {
|
||||
return;
|
||||
}
|
||||
if (data_get($this->backup, 'database_type') === 'App\Models\ServiceDatabase') {
|
||||
$this->database = data_get($this->backup, 'database');
|
||||
$this->server = $this->database->service->server;
|
||||
$this->s3 = $this->backup->s3;
|
||||
} else {
|
||||
$this->database = data_get($this->backup, 'database');
|
||||
$this->server = $this->database->destination->server;
|
||||
$this->s3 = $this->backup->s3;
|
||||
}
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
// Check if team is exists
|
||||
if (is_null($this->team)) {
|
||||
$this->backup->update(['status' => 'failed']);
|
||||
StopDatabase::run($this->database);
|
||||
$this->database->delete();
|
||||
$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;
|
||||
$this->s3 = $this->backup->s3;
|
||||
} else {
|
||||
$this->database = data_get($this->backup, 'database');
|
||||
$this->server = $this->database->destination->server;
|
||||
$this->s3 = $this->backup->s3;
|
||||
}
|
||||
if (is_null($this->server)) {
|
||||
throw new \Exception('Server not found?!');
|
||||
}
|
||||
if (is_null($this->database)) {
|
||||
throw new \Exception('Database not found?!');
|
||||
}
|
||||
|
||||
BackupCreated::dispatch($this->team->id);
|
||||
|
||||
@@ -239,7 +236,6 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
}
|
||||
$this->backup_dir = backup_dir().'/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name;
|
||||
|
||||
if ($this->database->name === 'coolify-db') {
|
||||
$databasesToBackup = ['coolify'];
|
||||
$this->directory_name = $this->container_name = 'coolify-db';
|
||||
@@ -252,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,
|
||||
@@ -280,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,
|
||||
@@ -289,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,
|
||||
@@ -327,7 +332,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
send_internal_notification('DatabaseBackupJob failed with: '.$e->getMessage());
|
||||
throw $e;
|
||||
} finally {
|
||||
BackupCreated::dispatch($this->team->id);
|
||||
if ($this->team) {
|
||||
BackupCreated::dispatch($this->team->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,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);
|
||||
@@ -407,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 === '') {
|
||||
@@ -426,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);
|
||||
@@ -468,34 +486,6 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
}
|
||||
|
||||
// private function upload_to_s3(): void
|
||||
// {
|
||||
// try {
|
||||
// if (is_null($this->s3)) {
|
||||
// return;
|
||||
// }
|
||||
// $key = $this->s3->key;
|
||||
// $secret = $this->s3->secret;
|
||||
// // $region = $this->s3->region;
|
||||
// $bucket = $this->s3->bucket;
|
||||
// $endpoint = $this->s3->endpoint;
|
||||
// $this->s3->testConnection(shouldSave: true);
|
||||
// $configName = new Cuid2;
|
||||
|
||||
// $s3_copy_dir = str($this->backup_location)->replace(backup_dir(), '/var/www/html/storage/app/backups/');
|
||||
// $commands[] = "docker exec coolify bash -c 'mc config host add {$configName} {$endpoint} $key $secret'";
|
||||
// $commands[] = "docker exec coolify bash -c 'mc cp $s3_copy_dir {$configName}/{$bucket}{$this->backup_dir}/'";
|
||||
// instant_remote_process($commands, $this->server);
|
||||
// $this->add_to_backup_output('Uploaded to S3.');
|
||||
// } catch (\Throwable $e) {
|
||||
// $this->add_to_backup_output($e->getMessage());
|
||||
// throw $e;
|
||||
// } finally {
|
||||
// $removeConfigCommands[] = "docker exec coolify bash -c 'mc config remove {$configName}'";
|
||||
// $removeConfigCommands[] = "docker exec coolify bash -c 'mc alias rm {$configName}'";
|
||||
// instant_remote_process($removeConfigCommands, $this->server, false);
|
||||
// }
|
||||
// }
|
||||
private function upload_to_s3(): void
|
||||
{
|
||||
try {
|
||||
@@ -517,10 +507,27 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->ensureHelperImageAvailable();
|
||||
|
||||
$fullImageName = $this->getFullImageName();
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
|
||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
|
||||
|
||||
if (isDev()) {
|
||||
if ($this->database->name === 'coolify-db') {
|
||||
$backup_location_from = '/var/lib/docker/volumes/coolify_dev_backups_data/_data/coolify/coolify-db-'.$this->server->ip.$this->backup_file;
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
|
||||
} else {
|
||||
$backup_location_from = '/var/lib/docker/volumes/coolify_dev_backups_data/_data/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name.$this->backup_file;
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $backup_location_from:$this->backup_location:ro {$fullImageName}";
|
||||
}
|
||||
} else {
|
||||
$commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
|
||||
}
|
||||
if ($this->s3->isHetzner()) {
|
||||
$endpointWithoutBucket = 'https://'.str($endpoint)->after('https://')->after('.')->value();
|
||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc alias set --path=off --api=S3v4 temporary {$endpointWithoutBucket} $key $secret";
|
||||
} else {
|
||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
|
||||
}
|
||||
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
|
||||
instant_remote_process($commands, $this->server);
|
||||
|
||||
$this->add_to_backup_output('Uploaded to S3.');
|
||||
} catch (\Throwable $e) {
|
||||
$this->add_to_backup_output($e->getMessage());
|
||||
@@ -562,7 +569,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
private function getFullImageName(): string
|
||||
{
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$helperImage = config('coolify.helper_image');
|
||||
$latestVersion = $settings->helper_version;
|
||||
|
||||
|
@@ -31,10 +31,10 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
public function __construct(
|
||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource,
|
||||
public bool $deleteConfigurations,
|
||||
public bool $deleteVolumes,
|
||||
public bool $dockerCleanup,
|
||||
public bool $deleteConnectedNetworks
|
||||
public bool $deleteConfigurations = true,
|
||||
public bool $deleteVolumes = true,
|
||||
public bool $dockerCleanup = true,
|
||||
public bool $deleteConnectedNetworks = true
|
||||
) {}
|
||||
|
||||
public function handle()
|
||||
|
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
@@ -26,7 +25,7 @@ class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
|
||||
if ($response->successful()) {
|
||||
$versions = $response->json();
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$latest_version = data_get($versions, 'coolify.helper.version');
|
||||
$current_version = $settings->helper_version;
|
||||
if (version_compare($latest_version, $current_version, '>')) {
|
||||
|
59
app/Jobs/ServerStorageCheckJob.php
Normal file
59
app/Jobs/ServerStorageCheckJob.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ServerStorageCheckJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
public $timeout = 60;
|
||||
|
||||
public $containers;
|
||||
|
||||
public $applications;
|
||||
|
||||
public $databases;
|
||||
|
||||
public $services;
|
||||
|
||||
public $previews;
|
||||
|
||||
public function backoff(): int
|
||||
{
|
||||
return isDev() ? 1 : 3;
|
||||
}
|
||||
|
||||
public function __construct(public Server $server) {}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
if (! $this->server->isFunctional()) {
|
||||
ray('Server is not ready.');
|
||||
|
||||
return 'Server is not ready.';
|
||||
}
|
||||
$team = $this->server->team;
|
||||
$percentage = $this->server->storageCheck();
|
||||
if ($percentage > 1) {
|
||||
ray('Server storage is at '.$percentage.'%');
|
||||
}
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
|
||||
return handleError($e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -3,7 +3,6 @@
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Actions\Server\UpdateCoolify;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
@@ -23,7 +22,7 @@ class UpdateCoolifyJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
try {
|
||||
CheckForUpdatesJob::dispatchSync();
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (! $settings->new_version_available) {
|
||||
Log::info('No new version available. Skipping update.');
|
||||
|
||||
|
@@ -30,7 +30,7 @@ class Dashboard extends Component
|
||||
|
||||
public function cleanup_queue()
|
||||
{
|
||||
Artisan::queue('cleanup:application-deployment-queue', [
|
||||
Artisan::queue('cleanup:deployment-queue', [
|
||||
'--team-id' => currentTeam()->id,
|
||||
]);
|
||||
}
|
||||
|
@@ -47,7 +47,7 @@ class Help extends Component
|
||||
]
|
||||
);
|
||||
$mail->subject("[HELP]: {$this->subject}");
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$type = set_transanctional_email_settings($settings);
|
||||
if (! $type) {
|
||||
$url = 'https://app.coolify.io/api/feedback';
|
||||
@@ -61,6 +61,7 @@ class Help extends Component
|
||||
send_user_an_email($mail, auth()->user()?->email, 'hi@coollabs.io');
|
||||
}
|
||||
$this->dispatch('success', 'Feedback sent.', 'We will get in touch with you as soon as possible.');
|
||||
$this->reset('description', 'subject');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
@@ -172,7 +172,7 @@ class Email extends Component
|
||||
|
||||
public function copyFromInstanceSettings()
|
||||
{
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if ($settings->smtp_enabled) {
|
||||
$team = currentTeam();
|
||||
$team->update([
|
||||
|
@@ -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;
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@@ -108,8 +108,23 @@ 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($this->service);
|
||||
StopService::run(service: $this->service, dockerCleanup: false);
|
||||
$this->service->parse();
|
||||
$this->dispatch('imagePulled');
|
||||
$activity = StartService::run($this->service);
|
||||
|
@@ -91,10 +91,12 @@ class Danger extends Component
|
||||
|
||||
public function delete($password)
|
||||
{
|
||||
if (! Hash::check($password, Auth::user()->password)) {
|
||||
$this->addError('password', 'The provided password is incorrect.');
|
||||
if (isProduction()) {
|
||||
if (! Hash::check($password, Auth::user()->password)) {
|
||||
$this->addError('password', 'The provided password is incorrect.');
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (! $this->resource) {
|
||||
|
@@ -48,14 +48,6 @@ class Add extends Component
|
||||
public function submit()
|
||||
{
|
||||
$this->validate();
|
||||
// if (str($this->value)->startsWith('{{') && str($this->value)->endsWith('}}')) {
|
||||
// $type = str($this->value)->after('{{')->before('.')->value;
|
||||
// if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
|
||||
// $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
|
||||
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
$this->dispatch('saveKey', [
|
||||
'key' => $this->key,
|
||||
'value' => $this->value,
|
||||
|
@@ -53,30 +53,16 @@ class All extends Component
|
||||
|
||||
public function sortEnvironmentVariables()
|
||||
{
|
||||
if ($this->resource->type() === 'application') {
|
||||
$this->resource->load(['environment_variables', 'environment_variables_preview']);
|
||||
} else {
|
||||
$this->resource->load(['environment_variables']);
|
||||
if (! data_get($this->resource, 'settings.is_env_sorting_enabled')) {
|
||||
if ($this->resource->environment_variables) {
|
||||
$this->resource->environment_variables = $this->resource->environment_variables->sortBy('order')->values();
|
||||
}
|
||||
|
||||
if ($this->resource->environment_variables_preview) {
|
||||
$this->resource->environment_variables_preview = $this->resource->environment_variables_preview->sortBy('order')->values();
|
||||
}
|
||||
}
|
||||
|
||||
$sortBy = data_get($this->resource, 'settings.is_env_sorting_enabled') ? 'key' : 'order';
|
||||
|
||||
$sortFunction = function ($variables) use ($sortBy) {
|
||||
if (! $variables) {
|
||||
return $variables;
|
||||
}
|
||||
if ($sortBy === 'key') {
|
||||
return $variables->sortBy(function ($item) {
|
||||
return strtolower($item->key);
|
||||
}, SORT_NATURAL | SORT_FLAG_CASE)->values();
|
||||
} else {
|
||||
return $variables->sortBy('order')->values();
|
||||
}
|
||||
};
|
||||
|
||||
$this->resource->environment_variables = $sortFunction($this->resource->environment_variables);
|
||||
$this->resource->environment_variables_preview = $sortFunction($this->resource->environment_variables_preview);
|
||||
|
||||
$this->getDevView();
|
||||
}
|
||||
|
||||
@@ -121,6 +107,8 @@ class All extends Component
|
||||
$this->sortEnvironmentVariables();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
$this->refreshEnvs();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -11,6 +11,8 @@ use Livewire\Component;
|
||||
|
||||
class ExecuteContainerCommand extends Component
|
||||
{
|
||||
public $selected_container = 'default';
|
||||
|
||||
public $container;
|
||||
|
||||
public Collection $containers;
|
||||
@@ -83,11 +85,14 @@ class ExecuteContainerCommand extends Component
|
||||
$containers = getCurrentApplicationContainerStatus($server, $this->resource->id, includePullrequests: true);
|
||||
}
|
||||
foreach ($containers as $container) {
|
||||
$payload = [
|
||||
'server' => $server,
|
||||
'container' => $container,
|
||||
];
|
||||
$this->containers = $this->containers->push($payload);
|
||||
// if container state is running
|
||||
if (data_get($container, 'State') === 'running') {
|
||||
$payload = [
|
||||
'server' => $server,
|
||||
'container' => $container,
|
||||
];
|
||||
$this->containers = $this->containers->push($payload);
|
||||
}
|
||||
}
|
||||
} elseif (data_get($this->parameters, 'database_uuid')) {
|
||||
if ($this->resource->isRunning()) {
|
||||
@@ -100,7 +105,6 @@ class ExecuteContainerCommand extends Component
|
||||
}
|
||||
} elseif (data_get($this->parameters, 'service_uuid')) {
|
||||
$this->resource->applications()->get()->each(function ($application) {
|
||||
ray($application);
|
||||
if ($application->isRunning()) {
|
||||
$this->containers->push([
|
||||
'server' => $this->resource->server,
|
||||
@@ -131,9 +135,14 @@ class ExecuteContainerCommand extends Component
|
||||
#[On('connectToContainer')]
|
||||
public function connectToContainer()
|
||||
{
|
||||
if ($this->selected_container === 'default') {
|
||||
$this->dispatch('error', 'Please select a container.');
|
||||
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$container_name = data_get($this->container, 'container.Names');
|
||||
if (is_null($container_name)) {
|
||||
$container = collect($this->containers)->firstWhere('container.Names', $this->selected_container);
|
||||
if (is_null($container)) {
|
||||
throw new \RuntimeException('Container not found.');
|
||||
}
|
||||
$server = data_get($this->container, 'server');
|
||||
@@ -141,11 +150,11 @@ class ExecuteContainerCommand extends Component
|
||||
if ($server->isForceDisabled()) {
|
||||
throw new \RuntimeException('Server is disabled.');
|
||||
}
|
||||
|
||||
$this->dispatch('send-terminal-command',
|
||||
true,
|
||||
$container_name,
|
||||
$server->uuid,
|
||||
$this->dispatch(
|
||||
'send-terminal-command',
|
||||
isset($container),
|
||||
data_get($container, 'container.Names'),
|
||||
data_get($container, 'server.uuid')
|
||||
);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
@@ -34,9 +34,9 @@ class Terminal extends Component
|
||||
if ($status !== 'running') {
|
||||
return;
|
||||
}
|
||||
$command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$identifier} sh -c 'if [ -f ~/.profile ]; then . ~/.profile; fi; if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
|
||||
$command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$identifier} sh -c 'PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
|
||||
} else {
|
||||
$command = SshMultiplexingHelper::generateSshCommand($server, "sh -c 'if [ -f ~/.profile ]; then . ~/.profile; fi; if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
|
||||
$command = SshMultiplexingHelper::generateSshCommand($server, 'PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n "$SHELL" ]; then exec $SHELL; else sh; fi');
|
||||
}
|
||||
|
||||
// ssh command is sent back to frontend then to websocket
|
||||
|
@@ -15,6 +15,8 @@ class ApiTokens extends Component
|
||||
|
||||
public bool $readOnly = true;
|
||||
|
||||
public bool $rootAccess = false;
|
||||
|
||||
public array $permissions = ['read-only'];
|
||||
|
||||
public $isApiEnabled;
|
||||
@@ -35,12 +37,11 @@ class ApiTokens extends Component
|
||||
if ($this->viewSensitiveData) {
|
||||
$this->permissions[] = 'view:sensitive';
|
||||
$this->permissions = array_diff($this->permissions, ['*']);
|
||||
$this->rootAccess = false;
|
||||
} else {
|
||||
$this->permissions = array_diff($this->permissions, ['view:sensitive']);
|
||||
}
|
||||
if (count($this->permissions) == 0) {
|
||||
$this->permissions = ['*'];
|
||||
}
|
||||
$this->makeSureOneIsSelected();
|
||||
}
|
||||
|
||||
public function updatedReadOnly()
|
||||
@@ -48,11 +49,30 @@ class ApiTokens extends Component
|
||||
if ($this->readOnly) {
|
||||
$this->permissions[] = 'read-only';
|
||||
$this->permissions = array_diff($this->permissions, ['*']);
|
||||
$this->rootAccess = false;
|
||||
} else {
|
||||
$this->permissions = array_diff($this->permissions, ['read-only']);
|
||||
}
|
||||
if (count($this->permissions) == 0) {
|
||||
$this->makeSureOneIsSelected();
|
||||
}
|
||||
|
||||
public function updatedRootAccess()
|
||||
{
|
||||
if ($this->rootAccess) {
|
||||
$this->permissions = ['*'];
|
||||
$this->readOnly = false;
|
||||
$this->viewSensitiveData = false;
|
||||
} else {
|
||||
$this->readOnly = true;
|
||||
$this->permissions = ['read-only'];
|
||||
}
|
||||
}
|
||||
|
||||
public function makeSureOneIsSelected()
|
||||
{
|
||||
if (count($this->permissions) == 0) {
|
||||
$this->permissions = ['read-only'];
|
||||
$this->readOnly = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,12 +82,6 @@ class ApiTokens extends Component
|
||||
$this->validate([
|
||||
'description' => 'required|min:3|max:255',
|
||||
]);
|
||||
// if ($this->viewSensitiveData) {
|
||||
// $this->permissions[] = 'view:sensitive';
|
||||
// }
|
||||
// if ($this->readOnly) {
|
||||
// $this->permissions[] = 'read-only';
|
||||
// }
|
||||
$token = auth()->user()->createToken($this->description, $this->permissions);
|
||||
$this->tokens = auth()->user()->tokens;
|
||||
session()->flash('token', $token->plainTextToken);
|
||||
|
@@ -30,6 +30,11 @@ class ConfigureCloudflareTunnels extends Component
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
if (str($this->ssh_domain)->contains('https://')) {
|
||||
$this->ssh_domain = str($this->ssh_domain)->replace('https://', '')->replace('http://', '')->trim();
|
||||
// remove / from the end
|
||||
$this->ssh_domain = str($this->ssh_domain)->replace('/', '');
|
||||
}
|
||||
$server = Server::ownedByCurrentTeam()->where('id', $this->server_id)->firstOrFail();
|
||||
ConfigureCloudflared::dispatch($server, $this->cloudflare_token);
|
||||
$server->settings->is_cloudflare_tunnel = true;
|
||||
|
@@ -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;
|
||||
|
@@ -60,7 +60,7 @@ class Index extends Component
|
||||
public function mount()
|
||||
{
|
||||
if (isInstanceAdmin()) {
|
||||
$this->settings = InstanceSettings::get();
|
||||
$this->settings = instanceSettings();
|
||||
$this->do_not_track = $this->settings->do_not_track;
|
||||
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
|
||||
$this->is_registration_enabled = $this->settings->is_registration_enabled;
|
||||
@@ -162,7 +162,7 @@ class Index extends Component
|
||||
{
|
||||
CheckForUpdatesJob::dispatchSync();
|
||||
$this->dispatch('updateAvailable');
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if ($settings->new_version_available) {
|
||||
$this->dispatch('success', 'New version available!');
|
||||
} else {
|
||||
|
@@ -29,7 +29,7 @@ class License extends Component
|
||||
abort(404);
|
||||
}
|
||||
$this->instance_id = config('app.id');
|
||||
$this->settings = \App\Models\InstanceSettings::get();
|
||||
$this->settings = instanceSettings();
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
@@ -42,7 +42,7 @@ class SettingsBackup extends Component
|
||||
public function mount()
|
||||
{
|
||||
if (isInstanceAdmin()) {
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$this->database = StandalonePostgresql::whereName('coolify-db')->first();
|
||||
$s3s = S3Storage::whereTeamId(0)->get() ?? [];
|
||||
if ($this->database) {
|
||||
|
@@ -43,7 +43,7 @@ class SettingsEmail extends Component
|
||||
public function mount()
|
||||
{
|
||||
if (isInstanceAdmin()) {
|
||||
$this->settings = InstanceSettings::get();
|
||||
$this->settings = instanceSettings();
|
||||
$this->emails = auth()->user()->email;
|
||||
} else {
|
||||
return redirect()->route('dashboard');
|
||||
|
@@ -99,7 +99,7 @@ class Change extends Component
|
||||
return redirect()->route('source.all');
|
||||
}
|
||||
$this->applications = $this->github_app->applications;
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||
|
||||
$this->name = str($this->github_app->name)->kebab();
|
||||
|
@@ -43,15 +43,17 @@ class Create extends Component
|
||||
'endpoint' => 'Endpoint',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
public function updatedEndpoint($value)
|
||||
{
|
||||
if (isDev()) {
|
||||
$this->name = 'Local MinIO';
|
||||
$this->description = 'Local MinIO';
|
||||
$this->key = 'minioadmin';
|
||||
$this->secret = 'minioadmin';
|
||||
$this->bucket = 'local';
|
||||
$this->endpoint = 'http://coolify-minio:9000';
|
||||
if (! str($value)->startsWith('https://') && ! str($value)->startsWith('http://')) {
|
||||
$this->endpoint = 'https://'.$value;
|
||||
$value = $this->endpoint;
|
||||
}
|
||||
|
||||
if (str($value)->contains('your-objectstorage.com') && ! isset($this->bucket)) {
|
||||
$this->bucket = str($value)->after('//')->before('.');
|
||||
} elseif (str($value)->contains('your-objectstorage.com')) {
|
||||
$this->bucket = $this->bucket ?: str($value)->after('//')->before('.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,7 @@ class Index extends Component
|
||||
if (data_get(currentTeam(), 'subscription') && isSubscriptionActive()) {
|
||||
return redirect()->route('subscription.show');
|
||||
}
|
||||
$this->settings = \App\Models\InstanceSettings::get();
|
||||
$this->settings = instanceSettings();
|
||||
$this->alreadySubscribed = currentTeam()->subscription()->exists();
|
||||
}
|
||||
|
||||
|
@@ -104,7 +104,7 @@ class Application extends BaseModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
private static $parserVersion = '3';
|
||||
private static $parserVersion = '4';
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
@@ -143,6 +143,9 @@ class Application extends BaseModel
|
||||
}
|
||||
$application->tags()->detach();
|
||||
$application->previews()->delete();
|
||||
foreach ($application->deployment_queue as $deployment) {
|
||||
$deployment->delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -304,7 +307,7 @@ class Application extends BaseModel
|
||||
'application_uuid' => data_get($this, 'uuid'),
|
||||
'task_uuid' => $task_uuid,
|
||||
]);
|
||||
$settings = InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (data_get($settings, 'fqdn')) {
|
||||
$url = Url::fromString($route);
|
||||
$url = $url->withPort(null);
|
||||
@@ -710,6 +713,11 @@ class Application extends BaseModel
|
||||
return $this->hasMany(ApplicationPreview::class);
|
||||
}
|
||||
|
||||
public function deployment_queue()
|
||||
{
|
||||
return $this->hasMany(ApplicationDeploymentQueue::class);
|
||||
}
|
||||
|
||||
public function destination()
|
||||
{
|
||||
return $this->morphTo();
|
||||
@@ -1150,7 +1158,7 @@ class Application extends BaseModel
|
||||
|
||||
public function parse(int $pull_request_id = 0, ?int $preview_id = null)
|
||||
{
|
||||
if ($this->compose_parsing_version === '3') {
|
||||
if ((int) $this->compose_parsing_version >= 3) {
|
||||
return newParser($this, $pull_request_id, $preview_id);
|
||||
} elseif ($this->docker_compose_raw) {
|
||||
return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id);
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Carbon;
|
||||
use OpenApi\Attributes as OA;
|
||||
@@ -39,6 +40,20 @@ class ApplicationDeploymentQueue extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
public function application(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Application::find($this->application_id),
|
||||
);
|
||||
}
|
||||
|
||||
public function server(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Server::find($this->server_id),
|
||||
);
|
||||
}
|
||||
|
||||
public function setStatus(string $status)
|
||||
{
|
||||
$this->update([
|
||||
|
@@ -126,11 +126,6 @@ class EnvironmentVariable extends Model
|
||||
$env = $this->get_real_environment_variables($this->value, $resource);
|
||||
|
||||
return data_get($env, 'value', $env);
|
||||
if (is_string($env)) {
|
||||
return $env;
|
||||
}
|
||||
|
||||
return $env->value;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@@ -85,4 +85,17 @@ class InstanceSettings extends Model implements SendsEmail
|
||||
|
||||
return "[{$instanceName}]";
|
||||
}
|
||||
|
||||
public function helperVersion(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function ($value) {
|
||||
if (isDev()) {
|
||||
return 'latest';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -24,9 +24,11 @@ class Project extends BaseModel
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
protected $appends = ['default_environment'];
|
||||
|
||||
public static function ownedByCurrentTeam()
|
||||
{
|
||||
return Project::whereTeamId(currentTeam()->id)->orderBy('name');
|
||||
return Project::whereTeamId(currentTeam()->id)->orderByRaw('LOWER(name)');
|
||||
}
|
||||
|
||||
protected static function booted()
|
||||
@@ -131,7 +133,7 @@ class Project extends BaseModel
|
||||
return $this->postgresqls()->get()->merge($this->redis()->get())->merge($this->mongodbs()->get())->merge($this->mysqls()->get())->merge($this->mariadbs()->get())->merge($this->keydbs()->get())->merge($this->dragonflies()->get())->merge($this->clickhouses()->get());
|
||||
}
|
||||
|
||||
public function default_environment()
|
||||
public function getDefaultEnvironmentAttribute()
|
||||
{
|
||||
$default = $this->environments()->where('name', 'production')->first();
|
||||
if ($default) {
|
||||
|
@@ -40,6 +40,16 @@ class S3Storage extends BaseModel
|
||||
return "{$this->endpoint}/{$this->bucket}";
|
||||
}
|
||||
|
||||
public function isHetzner()
|
||||
{
|
||||
return str($this->endpoint)->contains('your-objectstorage.com');
|
||||
}
|
||||
|
||||
public function isDigitalOcean()
|
||||
{
|
||||
return str($this->endpoint)->contains('digitaloceanspaces.com');
|
||||
}
|
||||
|
||||
public function testConnection(bool $shouldSave = false)
|
||||
{
|
||||
try {
|
||||
|
@@ -268,7 +268,7 @@ respond 404
|
||||
|
||||
public function setupDynamicProxyConfiguration()
|
||||
{
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$dynamic_config_path = $this->proxyPath().'/dynamic';
|
||||
if ($this->proxyType() === ProxyTypes::TRAEFIK->value) {
|
||||
$file = "$dynamic_config_path/coolify.yaml";
|
||||
@@ -448,11 +448,19 @@ $schema://$host {
|
||||
// Should move everything except /caddy and /nginx to /traefik
|
||||
// The code needs to be modified as well, so maybe it does not worth it
|
||||
if ($proxyType === ProxyTypes::TRAEFIK->value) {
|
||||
$proxy_path = $proxy_path;
|
||||
// Do nothing
|
||||
} elseif ($proxyType === ProxyTypes::CADDY->value) {
|
||||
$proxy_path = $proxy_path.'/caddy';
|
||||
if (isDev()) {
|
||||
$proxy_path = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/caddy';
|
||||
} else {
|
||||
$proxy_path = $proxy_path.'/caddy';
|
||||
}
|
||||
} elseif ($proxyType === ProxyTypes::NGINX->value) {
|
||||
$proxy_path = $proxy_path.'/nginx';
|
||||
if (isDev()) {
|
||||
$proxy_path = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/nginx';
|
||||
} else {
|
||||
$proxy_path = $proxy_path.'/nginx';
|
||||
}
|
||||
}
|
||||
|
||||
return $proxy_path;
|
||||
@@ -460,15 +468,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');
|
||||
}
|
||||
|
||||
@@ -1212,4 +1211,18 @@ $schema://$host {
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function storageCheck(): ?string
|
||||
{
|
||||
$commands = [
|
||||
'df / --output=pcent | tr -cd 0-9',
|
||||
];
|
||||
|
||||
return instant_remote_process($commands, $this, false);
|
||||
}
|
||||
|
||||
public function isIpv6(): bool
|
||||
{
|
||||
return str($this->ip)->contains(':');
|
||||
}
|
||||
}
|
||||
|
@@ -42,7 +42,7 @@ class Service extends BaseModel
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
private static $parserVersion = '3';
|
||||
private static $parserVersion = '4';
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
@@ -285,6 +285,65 @@ class Service extends BaseModel
|
||||
foreach ($applications as $application) {
|
||||
$image = str($application->image)->before(':')->value();
|
||||
switch ($image) {
|
||||
case str($image)?->contains('invoiceninja'):
|
||||
$data = collect([]);
|
||||
$email = $this->environment_variables()->where('key', 'IN_USER_EMAIL')->first();
|
||||
$data = $data->merge([
|
||||
'Email' => [
|
||||
'key' => 'IN_USER_EMAIL',
|
||||
'value' => data_get($email, 'value'),
|
||||
'rules' => 'required|email',
|
||||
],
|
||||
]);
|
||||
$password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_INVOICENINJAUSER')->first();
|
||||
$data = $data->merge([
|
||||
'Password' => [
|
||||
'key' => 'IN_PASSWORD',
|
||||
'value' => data_get($password, 'value'),
|
||||
'rules' => 'required',
|
||||
'isPassword' => true,
|
||||
],
|
||||
]);
|
||||
$fields->put('Invoice Ninja', $data->toArray());
|
||||
break;
|
||||
case str($image)?->contains('argilla'):
|
||||
$data = collect([]);
|
||||
$api_key = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_APIKEY')->first();
|
||||
$data = $data->merge([
|
||||
'API Key' => [
|
||||
'key' => data_get($api_key, 'key'),
|
||||
'value' => data_get($api_key, 'value'),
|
||||
'isPassword' => true,
|
||||
'rules' => 'required',
|
||||
],
|
||||
]);
|
||||
$data = $data->merge([
|
||||
'API Key' => [
|
||||
'key' => data_get($api_key, 'key'),
|
||||
'value' => data_get($api_key, 'value'),
|
||||
'isPassword' => true,
|
||||
'rules' => 'required',
|
||||
],
|
||||
]);
|
||||
$username = $this->environment_variables()->where('key', 'ARGILLA_USERNAME')->first();
|
||||
$data = $data->merge([
|
||||
'Username' => [
|
||||
'key' => data_get($username, 'key'),
|
||||
'value' => data_get($username, 'value'),
|
||||
'rules' => 'required',
|
||||
],
|
||||
]);
|
||||
$password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_ARGILLA')->first();
|
||||
$data = $data->merge([
|
||||
'Password' => [
|
||||
'key' => data_get($password, 'key'),
|
||||
'value' => data_get($password, 'value'),
|
||||
'rules' => 'required',
|
||||
'isPassword' => true,
|
||||
],
|
||||
]);
|
||||
$fields->put('Argilla', $data->toArray());
|
||||
break;
|
||||
case str($image)?->contains('rabbitmq'):
|
||||
$data = collect([]);
|
||||
$host_port = $this->environment_variables()->where('key', 'PORT')->first();
|
||||
@@ -770,6 +829,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();
|
||||
@@ -1052,12 +1135,12 @@ class Service extends BaseModel
|
||||
public function environment_variables(): HasMany
|
||||
{
|
||||
|
||||
return $this->hasMany(EnvironmentVariable::class)->orderByRaw("key LIKE 'SERVICE%' DESC, value ASC");
|
||||
return $this->hasMany(EnvironmentVariable::class)->orderByRaw("LOWER(key) LIKE LOWER('SERVICE%') DESC, LOWER(key) ASC");
|
||||
}
|
||||
|
||||
public function environment_variables_preview(): HasMany
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->orderBy('key', 'asc');
|
||||
return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->orderByRaw("LOWER(key) LIKE LOWER('SERVICE%') DESC, LOWER(key) ASC");
|
||||
}
|
||||
|
||||
public function workdir()
|
||||
@@ -1095,7 +1178,21 @@ class Service extends BaseModel
|
||||
return 3;
|
||||
});
|
||||
foreach ($sorted as $env) {
|
||||
$commands[] = "echo '{$env->key}={$env->real_value}' >> .env";
|
||||
if (version_compare($env->version, '4.0.0-beta.347', '<=')) {
|
||||
$commands[] = "echo '{$env->key}={$env->real_value}' >> .env";
|
||||
} else {
|
||||
$real_value = $env->real_value;
|
||||
if ($env->version === '4.0.0-beta.239') {
|
||||
$real_value = $env->real_value;
|
||||
} else {
|
||||
if ($env->is_literal || $env->is_multiline) {
|
||||
$real_value = '\''.$real_value.'\'';
|
||||
} else {
|
||||
$real_value = escapeEnvVariables($env->real_value);
|
||||
}
|
||||
}
|
||||
$commands[] = "echo \"{$env->key}={$real_value}\" >> .env";
|
||||
}
|
||||
}
|
||||
if ($sorted->count() === 0) {
|
||||
$commands[] = 'touch .env';
|
||||
@@ -1105,7 +1202,7 @@ class Service extends BaseModel
|
||||
|
||||
public function parse(bool $isNew = false): Collection
|
||||
{
|
||||
if ($this->compose_parsing_version === '3') {
|
||||
if ((int) $this->compose_parsing_version >= 3) {
|
||||
return newParser($this);
|
||||
} elseif ($this->docker_compose_raw) {
|
||||
return parseDockerComposeFile($this, $isNew);
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ class TransactionalEmailChannel
|
||||
{
|
||||
public function send(User $notifiable, Notification $notification): void
|
||||
{
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (! data_get($settings, 'smtp_enabled') && ! data_get($settings, 'resend_enabled')) {
|
||||
Log::info('SMTP/Resend not enabled');
|
||||
|
||||
|
@@ -18,7 +18,7 @@ class ResetPassword extends Notification
|
||||
|
||||
public function __construct($token)
|
||||
{
|
||||
$this->settings = \App\Models\InstanceSettings::get();
|
||||
$this->settings = instanceSettings();
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
|
@@ -2,10 +2,8 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\PersonalAccessToken;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
|
||||
@@ -30,9 +28,5 @@ class AppServiceProvider extends ServiceProvider
|
||||
])->baseUrl($api_url);
|
||||
}
|
||||
});
|
||||
// if (! env('CI')) {
|
||||
// View::share('instanceSettings', InstanceSettings::get());
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -46,7 +46,7 @@ class FortifyServiceProvider extends ServiceProvider
|
||||
Fortify::registerView(function () {
|
||||
$isFirstUser = User::count() === 0;
|
||||
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (! $settings->is_registration_enabled) {
|
||||
return redirect()->route('login');
|
||||
}
|
||||
@@ -60,7 +60,7 @@ class FortifyServiceProvider extends ServiceProvider
|
||||
});
|
||||
|
||||
Fortify::loginView(function () {
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$enabled_oauth_providers = OauthSetting::where('enabled', true)->get();
|
||||
$users = User::count();
|
||||
if ($users == 0) {
|
||||
|
@@ -175,4 +175,5 @@ function removeUnnecessaryFieldsFromRequest(Request $request)
|
||||
$request->offsetUnset('instant_deploy');
|
||||
$request->offsetUnset('github_app_uuid');
|
||||
$request->offsetUnset('private_key_uuid');
|
||||
$request->offsetUnset('use_build_server');
|
||||
}
|
||||
|
@@ -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}");
|
||||
|
@@ -1,14 +1,11 @@
|
||||
<?php
|
||||
|
||||
use App\Models\S3Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
function set_s3_target(S3Storage $s3)
|
||||
{
|
||||
$is_digital_ocean = false;
|
||||
if ($s3->endpoint) {
|
||||
$is_digital_ocean = Str::contains($s3->endpoint, 'digitaloceanspaces.com');
|
||||
}
|
||||
|
||||
config()->set('filesystems.disks.custom-s3', [
|
||||
'driver' => 's3',
|
||||
'region' => $s3['region'],
|
||||
@@ -17,7 +14,7 @@ function set_s3_target(S3Storage $s3)
|
||||
'bucket' => $s3['bucket'],
|
||||
'endpoint' => $s3['endpoint'],
|
||||
'use_path_style_endpoint' => true,
|
||||
'bucket_endpoint' => $is_digital_ocean,
|
||||
'bucket_endpoint' => $s3->isHetzner() || $s3->isDigitalOcean(),
|
||||
'aws_url' => $s3->awsUrl(),
|
||||
]);
|
||||
}
|
||||
|
@@ -247,7 +247,7 @@ function is_transactional_emails_active(): bool
|
||||
function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string
|
||||
{
|
||||
if (! $settings) {
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
}
|
||||
config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
|
||||
config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
|
||||
@@ -281,7 +281,7 @@ function base_ip(): string
|
||||
if (isDev()) {
|
||||
return 'localhost';
|
||||
}
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if ($settings->public_ipv4) {
|
||||
return "$settings->public_ipv4";
|
||||
}
|
||||
@@ -309,7 +309,7 @@ function getFqdnWithoutPort(string $fqdn)
|
||||
*/
|
||||
function base_url(bool $withPort = true): string
|
||||
{
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if ($settings->fqdn) {
|
||||
return $settings->fqdn;
|
||||
}
|
||||
@@ -343,6 +343,11 @@ function isSubscribed()
|
||||
{
|
||||
return isSubscriptionActive() || auth()->user()->isInstanceAdmin();
|
||||
}
|
||||
|
||||
function isProduction(): bool
|
||||
{
|
||||
return ! isDev();
|
||||
}
|
||||
function isDev(): bool
|
||||
{
|
||||
return config('app.env') === 'local';
|
||||
@@ -384,7 +389,7 @@ function send_internal_notification(string $message): void
|
||||
}
|
||||
function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null): void
|
||||
{
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$type = set_transanctional_email_settings($settings);
|
||||
if (! $type) {
|
||||
throw new Exception('No email settings found.');
|
||||
@@ -703,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -753,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -819,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);
|
||||
@@ -850,6 +884,7 @@ function parseEnvVariable(Str|string $value)
|
||||
} else {
|
||||
// SERVICE_BASE64_64_UMAMI
|
||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||
ray($command);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -970,7 +1005,7 @@ function validate_dns_entry(string $fqdn, Server $server)
|
||||
if (str($host)->contains('sslip.io')) {
|
||||
return true;
|
||||
}
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
$is_dns_validation_enabled = data_get($settings, 'is_dns_validation_enabled');
|
||||
if (! $is_dns_validation_enabled) {
|
||||
return true;
|
||||
@@ -1090,7 +1125,7 @@ function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId =
|
||||
if ($domainFound) {
|
||||
return true;
|
||||
}
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (data_get($settings, 'fqdn')) {
|
||||
$domain = data_get($settings, 'fqdn');
|
||||
if (str($domain)->endsWith('/')) {
|
||||
@@ -1162,7 +1197,7 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
|
||||
}
|
||||
}
|
||||
if ($resource) {
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (data_get($settings, 'fqdn')) {
|
||||
$domain = data_get($settings, 'fqdn');
|
||||
if (str($domain)->endsWith('/')) {
|
||||
@@ -1179,12 +1214,26 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
|
||||
function parseCommandsByLineForSudo(Collection $commands, Server $server): array
|
||||
{
|
||||
$commands = $commands->map(function ($line) {
|
||||
if (! str($line)->startsWith('cd') && ! str($line)->startsWith('command') && ! str($line)->startsWith('echo') && ! str($line)->startsWith('true')) {
|
||||
if (
|
||||
! str(trim($line))->startsWith([
|
||||
'cd',
|
||||
'command',
|
||||
'echo',
|
||||
'true',
|
||||
'if',
|
||||
'fi',
|
||||
])
|
||||
) {
|
||||
return "sudo $line";
|
||||
}
|
||||
|
||||
if (str(trim($line))->startsWith('if')) {
|
||||
return str_replace('if', 'if sudo', $line);
|
||||
}
|
||||
|
||||
return $line;
|
||||
});
|
||||
|
||||
$commands = $commands->map(function ($line) use ($server) {
|
||||
if (Str::startsWith($line, 'sudo mkdir -p')) {
|
||||
return "$line && sudo chown -R $server->user:$server->user ".Str::after($line, 'sudo mkdir -p').' && sudo chmod -R o-rwx '.Str::after($line, 'sudo mkdir -p');
|
||||
@@ -1192,6 +1241,7 @@ function parseCommandsByLineForSudo(Collection $commands, Server $server): array
|
||||
|
||||
return $line;
|
||||
});
|
||||
|
||||
$commands = $commands->map(function ($line) {
|
||||
$line = str($line);
|
||||
if (str($line)->contains('$(')) {
|
||||
@@ -1588,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2503,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2964,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,
|
||||
@@ -3078,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;
|
||||
@@ -3189,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,
|
||||
@@ -3264,7 +3341,15 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
} elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||
$volume = $source->value().':'.$target->value();
|
||||
} else {
|
||||
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
||||
if ((int) $resource->compose_parsing_version >= 4) {
|
||||
if ($isApplication) {
|
||||
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
||||
} elseif ($isService) {
|
||||
$mainDirectory = str(base_configuration_dir().'/services/'.$uuid);
|
||||
}
|
||||
} else {
|
||||
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
||||
}
|
||||
$source = replaceLocalSource($source, $mainDirectory);
|
||||
if ($isApplication && $isPullRequest) {
|
||||
$source = $source."-pr-$pullRequestId";
|
||||
@@ -3284,6 +3369,17 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
||||
'resource_type' => get_class($originalResource),
|
||||
]
|
||||
);
|
||||
if (isDev()) {
|
||||
if ((int) $resource->compose_parsing_version >= 4) {
|
||||
if ($isApplication) {
|
||||
$source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/'.$uuid);
|
||||
} elseif ($isService) {
|
||||
$source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/services/'.$uuid);
|
||||
}
|
||||
} else {
|
||||
$source = $source->replace($mainDirectory, '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/applications/'.$uuid);
|
||||
}
|
||||
}
|
||||
$volume = "$source:$target";
|
||||
}
|
||||
} elseif ($type->value() === 'volume') {
|
||||
@@ -3606,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;
|
||||
@@ -3826,14 +3934,37 @@ function convertComposeEnvironmentToArray($environment)
|
||||
{
|
||||
$convertedServiceVariables = collect([]);
|
||||
if (isAssociativeArray($environment)) {
|
||||
// Example: $environment = ['FOO' => 'bar', 'BAZ' => 'qux'];
|
||||
if ($environment instanceof Collection) {
|
||||
$changedEnvironment = collect([]);
|
||||
$environment->each(function ($value, $key) use ($changedEnvironment) {
|
||||
if (is_numeric($key)) {
|
||||
$parts = explode('=', $value, 2);
|
||||
if (count($parts) === 2) {
|
||||
$key = $parts[0];
|
||||
$realValue = $parts[1] ?? '';
|
||||
$changedEnvironment->put($key, $realValue);
|
||||
} else {
|
||||
$changedEnvironment->put($key, $value);
|
||||
}
|
||||
} else {
|
||||
$changedEnvironment->put($key, $value);
|
||||
}
|
||||
});
|
||||
|
||||
return $changedEnvironment;
|
||||
}
|
||||
$convertedServiceVariables = $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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3841,3 +3972,7 @@ function convertComposeEnvironmentToArray($environment)
|
||||
return $convertedServiceVariables;
|
||||
|
||||
}
|
||||
function instanceSettings()
|
||||
{
|
||||
return InstanceSettings::get();
|
||||
}
|
||||
|
@@ -48,6 +48,7 @@
|
||||
"zircote/swagger-php": "^4.10"
|
||||
},
|
||||
"require-dev": {
|
||||
"barryvdh/laravel-debugbar": "^3.13",
|
||||
"fakerphp/faker": "^v1.21.0",
|
||||
"laravel/dusk": "^v8.0",
|
||||
"laravel/pint": "^1.16",
|
||||
|
154
composer.lock
generated
154
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "96f8146407d0e6e897ff097c5eccd3a4",
|
||||
"content-hash": "42c28ab141b70fcabf75b51afa96c670",
|
||||
"packages": [
|
||||
{
|
||||
"name": "amphp/amp",
|
||||
@@ -11823,6 +11823,90 @@
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "barryvdh/laravel-debugbar",
|
||||
"version": "v3.13.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/barryvdh/laravel-debugbar.git",
|
||||
"reference": "92d86be45ee54edff735e46856f64f14b6a8bb07"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/92d86be45ee54edff735e46856f64f14b6a8bb07",
|
||||
"reference": "92d86be45ee54edff735e46856f64f14b6a8bb07",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/routing": "^9|^10|^11",
|
||||
"illuminate/session": "^9|^10|^11",
|
||||
"illuminate/support": "^9|^10|^11",
|
||||
"maximebf/debugbar": "~1.22.0",
|
||||
"php": "^8.0",
|
||||
"symfony/finder": "^6|^7"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"orchestra/testbench-dusk": "^5|^6|^7|^8|^9",
|
||||
"phpunit/phpunit": "^9.6|^10.5",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.13-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Barryvdh\\Debugbar\\ServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Debugbar": "Barryvdh\\Debugbar\\Facades\\Debugbar"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Barryvdh\\Debugbar\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Barry vd. Heuvel",
|
||||
"email": "barryvdh@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP Debugbar integration for Laravel",
|
||||
"keywords": [
|
||||
"debug",
|
||||
"debugbar",
|
||||
"laravel",
|
||||
"profiler",
|
||||
"webprofiler"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/barryvdh/laravel-debugbar/issues",
|
||||
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.13.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://fruitcake.nl",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/barryvdh",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-04-12T11:20:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brianium/paratest",
|
||||
"version": "v7.4.3",
|
||||
@@ -12301,6 +12385,74 @@
|
||||
},
|
||||
"time": "2024-09-03T15:00:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maximebf/debugbar",
|
||||
"version": "v1.22.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/maximebf/php-debugbar.git",
|
||||
"reference": "1b5cabe0ce013134cf595bfa427bbf2f6abcd989"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/1b5cabe0ce013134cf595bfa427bbf2f6abcd989",
|
||||
"reference": "1b5cabe0ce013134cf595bfa427bbf2f6abcd989",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2|^8",
|
||||
"psr/log": "^1|^2|^3",
|
||||
"symfony/var-dumper": "^4|^5|^6|^7"
|
||||
},
|
||||
"require-dev": {
|
||||
"dbrekelmans/bdi": "^1",
|
||||
"phpunit/phpunit": "^8|^9",
|
||||
"symfony/panther": "^1|^2.1",
|
||||
"twig/twig": "^1.38|^2.7|^3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"kriswallsmith/assetic": "The best way to manage assets",
|
||||
"monolog/monolog": "Log using Monolog",
|
||||
"predis/predis": "Redis storage"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.22-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"DebugBar\\": "src/DebugBar/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Maxime Bouroumeau-Fuseau",
|
||||
"email": "maxime.bouroumeau@gmail.com",
|
||||
"homepage": "http://maximebf.com"
|
||||
},
|
||||
{
|
||||
"name": "Barry vd. Heuvel",
|
||||
"email": "barryvdh@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Debug bar in the browser for php application",
|
||||
"homepage": "https://github.com/maximebf/php-debugbar",
|
||||
"keywords": [
|
||||
"debug",
|
||||
"debugbar"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/maximebf/php-debugbar/issues",
|
||||
"source": "https://github.com/maximebf/php-debugbar/tree/v1.22.5"
|
||||
},
|
||||
"time": "2024-09-09T08:05:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mockery/mockery",
|
||||
"version": "1.6.12",
|
||||
|
325
config/debugbar.php
Normal file
325
config/debugbar.php
Normal file
@@ -0,0 +1,325 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Debugbar Settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Debugbar is enabled by default, when debug is set to true in app.php.
|
||||
| You can override the value by setting enable to true or false instead of null.
|
||||
|
|
||||
| You can provide an array of URI's that must be ignored (eg. 'api/*')
|
||||
|
|
||||
*/
|
||||
|
||||
'enabled' => env('DEBUGBAR_ENABLED', null),
|
||||
'except' => [
|
||||
'telescope*',
|
||||
'horizon*',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Storage settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| DebugBar stores data for session/ajax requests.
|
||||
| You can disable this, so the debugbar stores data in headers/session,
|
||||
| but this can cause problems with large data collectors.
|
||||
| By default, file storage (in the storage folder) is used. Redis and PDO
|
||||
| can also be used. For PDO, run the package migrations first.
|
||||
|
|
||||
| Warning: Enabling storage.open will allow everyone to access previous
|
||||
| request, do not enable open storage in publicly available environments!
|
||||
| Specify a callback if you want to limit based on IP or authentication.
|
||||
| Leaving it to null will allow localhost only.
|
||||
*/
|
||||
'storage' => [
|
||||
'enabled' => true,
|
||||
'open' => env('DEBUGBAR_OPEN_STORAGE'), // bool/callback.
|
||||
'driver' => 'file', // redis, file, pdo, socket, custom
|
||||
'path' => storage_path('debugbar'), // For file driver
|
||||
'connection' => null, // Leave null for default connection (Redis/PDO)
|
||||
'provider' => '', // Instance of StorageInterface for custom driver
|
||||
'hostname' => '127.0.0.1', // Hostname to use with the "socket" driver
|
||||
'port' => 2304, // Port to use with the "socket" driver
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Editor
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Choose your preferred editor to use when clicking file name.
|
||||
|
|
||||
| Supported: "phpstorm", "vscode", "vscode-insiders", "vscode-remote",
|
||||
| "vscode-insiders-remote", "vscodium", "textmate", "emacs",
|
||||
| "sublime", "atom", "nova", "macvim", "idea", "netbeans",
|
||||
| "xdebug", "espresso"
|
||||
|
|
||||
*/
|
||||
|
||||
'editor' => env('DEBUGBAR_EDITOR') ?: env('IGNITION_EDITOR', 'phpstorm'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Remote Path Mapping
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If you are using a remote dev server, like Laravel Homestead, Docker, or
|
||||
| even a remote VPS, it will be necessary to specify your path mapping.
|
||||
|
|
||||
| Leaving one, or both of these, empty or null will not trigger the remote
|
||||
| URL changes and Debugbar will treat your editor links as local files.
|
||||
|
|
||||
| "remote_sites_path" is an absolute base path for your sites or projects
|
||||
| in Homestead, Vagrant, Docker, or another remote development server.
|
||||
|
|
||||
| Example value: "/home/vagrant/Code"
|
||||
|
|
||||
| "local_sites_path" is an absolute base path for your sites or projects
|
||||
| on your local computer where your IDE or code editor is running on.
|
||||
|
|
||||
| Example values: "/Users/<name>/Code", "C:\Users\<name>\Documents\Code"
|
||||
|
|
||||
*/
|
||||
|
||||
'remote_sites_path' => env('DEBUGBAR_REMOTE_SITES_PATH'),
|
||||
'local_sites_path' => env('DEBUGBAR_LOCAL_SITES_PATH', env('IGNITION_LOCAL_SITES_PATH')),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Vendors
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Vendor files are included by default, but can be set to false.
|
||||
| This can also be set to 'js' or 'css', to only include javascript or css vendor files.
|
||||
| Vendor files are for css: font-awesome (including fonts) and highlight.js (css files)
|
||||
| and for js: jquery and highlight.js
|
||||
| So if you want syntax highlighting, set it to true.
|
||||
| jQuery is set to not conflict with existing jQuery scripts.
|
||||
|
|
||||
*/
|
||||
|
||||
'include_vendors' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Capture Ajax Requests
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors),
|
||||
| you can use this option to disable sending the data through the headers.
|
||||
|
|
||||
| Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools.
|
||||
|
|
||||
| Note for your request to be identified as ajax requests they must either send the header
|
||||
| X-Requested-With with the value XMLHttpRequest (most JS libraries send this), or have application/json as a Accept header.
|
||||
|
|
||||
| By default `ajax_handler_auto_show` is set to true allowing ajax requests to be shown automatically in the Debugbar.
|
||||
| Changing `ajax_handler_auto_show` to false will prevent the Debugbar from reloading.
|
||||
*/
|
||||
|
||||
'capture_ajax' => true,
|
||||
'add_ajax_timing' => false,
|
||||
'ajax_handler_auto_show' => true,
|
||||
'ajax_handler_enable_tab' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Custom Error Handler for Deprecated warnings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When enabled, the Debugbar shows deprecated warnings for Symfony components
|
||||
| in the Messages tab.
|
||||
|
|
||||
*/
|
||||
'error_handler' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Clockwork integration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Debugbar can emulate the Clockwork headers, so you can use the Chrome
|
||||
| Extension, without the server-side code. It uses Debugbar collectors instead.
|
||||
|
|
||||
*/
|
||||
'clockwork' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DataCollectors
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Enable/disable DataCollectors
|
||||
|
|
||||
*/
|
||||
|
||||
'collectors' => [
|
||||
'phpinfo' => true, // Php version
|
||||
'messages' => true, // Messages
|
||||
'time' => true, // Time Datalogger
|
||||
'memory' => true, // Memory usage
|
||||
'exceptions' => true, // Exception displayer
|
||||
'log' => true, // Logs from Monolog (merged in messages if enabled)
|
||||
'db' => true, // Show database (PDO) queries and bindings
|
||||
'views' => true, // Views with their data
|
||||
'route' => true, // Current route information
|
||||
'auth' => false, // Display Laravel authentication status
|
||||
'gate' => true, // Display Laravel Gate checks
|
||||
'session' => true, // Display session data
|
||||
'symfony_request' => true, // Only one can be enabled..
|
||||
'mail' => true, // Catch mail messages
|
||||
'laravel' => false, // Laravel version and environment
|
||||
'events' => false, // All events fired
|
||||
'default_request' => false, // Regular or special Symfony request logger
|
||||
'logs' => false, // Add the latest log messages
|
||||
'files' => false, // Show the included files
|
||||
'config' => false, // Display config settings
|
||||
'cache' => false, // Display cache events
|
||||
'models' => true, // Display models
|
||||
'livewire' => true, // Display Livewire (when available)
|
||||
'jobs' => false, // Display dispatched jobs
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Extra options
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configure some DataCollectors
|
||||
|
|
||||
*/
|
||||
|
||||
'options' => [
|
||||
'time' => [
|
||||
'memory_usage' => false, // Calculated by subtracting memory start and end, it may be inaccurate
|
||||
],
|
||||
'messages' => [
|
||||
'trace' => true, // Trace the origin of the debug message
|
||||
],
|
||||
'memory' => [
|
||||
'reset_peak' => false, // run memory_reset_peak_usage before collecting
|
||||
'with_baseline' => false, // Set boot memory usage as memory peak baseline
|
||||
'precision' => 0, // Memory rounding precision
|
||||
],
|
||||
'auth' => [
|
||||
'show_name' => true, // Also show the users name/email in the debugbar
|
||||
'show_guards' => true, // Show the guards that are used
|
||||
],
|
||||
'db' => [
|
||||
'with_params' => true, // Render SQL with the parameters substituted
|
||||
'backtrace' => true, // Use a backtrace to find the origin of the query in your files.
|
||||
'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults)
|
||||
'timeline' => false, // Add the queries to the timeline
|
||||
'duration_background' => true, // Show shaded background on each query relative to how long it took to execute.
|
||||
'explain' => [ // Show EXPLAIN output on queries
|
||||
'enabled' => false,
|
||||
'types' => ['SELECT'], // Deprecated setting, is always only SELECT
|
||||
],
|
||||
'hints' => false, // Show hints for common mistakes
|
||||
'show_copy' => false, // Show copy button next to the query,
|
||||
'slow_threshold' => false, // Only track queries that last longer than this time in ms
|
||||
'memory_usage' => false, // Show queries memory usage
|
||||
'soft_limit' => 100, // After the soft limit, no parameters/backtrace are captured
|
||||
'hard_limit' => 500, // After the hard limit, queries are ignored
|
||||
],
|
||||
'mail' => [
|
||||
'timeline' => false, // Add mails to the timeline
|
||||
'show_body' => true,
|
||||
],
|
||||
'views' => [
|
||||
'timeline' => false, // Add the views to the timeline (Experimental)
|
||||
'data' => false, //true for all data, 'keys' for only names, false for no parameters.
|
||||
'group' => 50, // Group duplicate views. Pass value to auto-group, or true/false to force
|
||||
'exclude_paths' => [ // Add the paths which you don't want to appear in the views
|
||||
'vendor/filament', // Exclude Filament components by default
|
||||
],
|
||||
],
|
||||
'route' => [
|
||||
'label' => true, // show complete route on bar
|
||||
],
|
||||
'session' => [
|
||||
'hiddens' => [], // hides sensitive values using array paths
|
||||
],
|
||||
'symfony_request' => [
|
||||
'hiddens' => [], // hides sensitive values using array paths, example: request_request.password
|
||||
],
|
||||
'events' => [
|
||||
'data' => false, // collect events data, listeners
|
||||
],
|
||||
'logs' => [
|
||||
'file' => null,
|
||||
],
|
||||
'cache' => [
|
||||
'values' => true, // collect cache values
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Inject Debugbar in Response
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Usually, the debugbar is added just before </body>, by listening to the
|
||||
| Response after the App is done. If you disable this, you have to add them
|
||||
| in your template yourself. See http://phpdebugbar.com/docs/rendering.html
|
||||
|
|
||||
*/
|
||||
|
||||
'inject' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DebugBar route prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Sometimes you want to set route prefix to be used by DebugBar to load
|
||||
| its resources from. Usually the need comes from misconfigured web server or
|
||||
| from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97
|
||||
|
|
||||
*/
|
||||
'route_prefix' => '_debugbar',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DebugBar route middleware
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Additional middleware to run on the Debugbar routes
|
||||
*/
|
||||
'route_middleware' => [],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DebugBar route domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default DebugBar route served from the same domain that request served.
|
||||
| To override default domain, specify it as a non-empty value.
|
||||
*/
|
||||
'route_domain' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| DebugBar theme
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Switches between light and dark theme. If set to auto it will respect system preferences
|
||||
| Possible values: auto, light, dark
|
||||
*/
|
||||
'theme' => env('DEBUGBAR_THEME', 'auto'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Backtrace stack limit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default, the DebugBar limits the number of frames returned by the 'debug_backtrace()' function.
|
||||
| If you need larger stacktraces, you can increase this number. Setting it to 0 will result in no limit.
|
||||
*/
|
||||
'debug_backtrace_limit' => 50,
|
||||
];
|
@@ -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.348',
|
||||
'release' => '4.0.0-beta.356',
|
||||
// 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.348';
|
||||
return '4.0.0-beta.356';
|
||||
|
@@ -163,7 +163,7 @@ return new class extends Migration
|
||||
$table->schemalessAttributes('smtp');
|
||||
});
|
||||
|
||||
$instance_setting = InstanceSettings::get();
|
||||
$instance_setting = instanceSettings();
|
||||
$instance_setting->smtp = [
|
||||
'enabled' => $instance_setting->smtp_enabled,
|
||||
'from_address' => $instance_setting->smtp_from_address,
|
||||
|
@@ -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');
|
||||
});
|
||||
}
|
||||
};
|
@@ -27,14 +27,14 @@ class InstanceSettingsSeeder extends Seeder
|
||||
$ipv4 = Process::run('curl -4s https://ifconfig.io')->output();
|
||||
$ipv4 = trim($ipv4);
|
||||
$ipv4 = filter_var($ipv4, FILTER_VALIDATE_IP);
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (is_null($settings->public_ipv4) && $ipv4) {
|
||||
$settings->update(['public_ipv4' => $ipv4]);
|
||||
}
|
||||
$ipv6 = Process::run('curl -6s https://ifconfig.io')->output();
|
||||
$ipv6 = trim($ipv6);
|
||||
$ipv6 = filter_var($ipv6, FILTER_VALIDATE_IP);
|
||||
$settings = \App\Models\InstanceSettings::get();
|
||||
$settings = instanceSettings();
|
||||
if (is_null($settings->public_ipv6) && $ipv6) {
|
||||
$settings->update(['public_ipv6' => $ipv6]);
|
||||
}
|
||||
|
@@ -58,6 +58,7 @@ services:
|
||||
SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID:-coolify}"
|
||||
SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY:-coolify}"
|
||||
SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}"
|
||||
entrypoint: ["/bin/sh", "/soketi-entrypoint.sh"]
|
||||
vite:
|
||||
image: node:20
|
||||
pull_policy: always
|
||||
|
@@ -113,7 +113,7 @@ services:
|
||||
retries: 10
|
||||
timeout: 2s
|
||||
soketi:
|
||||
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.1'
|
||||
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.3'
|
||||
ports:
|
||||
- "${SOKETI_PORT:-6001}:6001"
|
||||
- "6002:6002"
|
||||
|
@@ -1,9 +1,27 @@
|
||||
FROM quay.io/soketi/soketi:1.6-16-alpine
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
# https://github.com/cloudflare/cloudflared/releases
|
||||
ARG CLOUDFLARED_VERSION=2024.4.1
|
||||
|
||||
WORKDIR /terminal
|
||||
RUN apk add --no-cache openssh-client make g++ python3
|
||||
RUN apk add --no-cache openssh-client make g++ python3 curl
|
||||
COPY docker/coolify-realtime/package.json ./
|
||||
RUN npm i
|
||||
RUN npm rebuild node-pty --update-binary
|
||||
COPY docker/coolify-realtime/soketi-entrypoint.sh /soketi-entrypoint.sh
|
||||
COPY docker/coolify-realtime/terminal-server.js /terminal/terminal-server.js
|
||||
|
||||
RUN /bin/sh -c "if [[ ${TARGETPLATFORM} == 'linux/amd64' ]]; then \
|
||||
echo 'amd64' && \
|
||||
curl -sSL https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared \
|
||||
;fi"
|
||||
|
||||
RUN /bin/sh -c "if [[ ${TARGETPLATFORM} == 'linux/arm64' ]]; then \
|
||||
echo 'arm64' && \
|
||||
curl -L https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64 -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared \
|
||||
;fi"
|
||||
|
||||
|
||||
|
||||
ENTRYPOINT ["/bin/sh", "/soketi-entrypoint.sh"]
|
||||
|
@@ -1,11 +1,19 @@
|
||||
#!/bin/sh
|
||||
# Function to timestamp logs
|
||||
|
||||
# Check if the first argument is 'watch'
|
||||
if [ "$1" = "watch" ]; then
|
||||
WATCH_MODE="--watch"
|
||||
else
|
||||
WATCH_MODE=""
|
||||
fi
|
||||
|
||||
timestamp() {
|
||||
date "+%Y-%m-%d %H:%M:%S"
|
||||
}
|
||||
|
||||
# Start the terminal server in the background with logging
|
||||
node /terminal/terminal-server.js > >(while read line; do echo "$(timestamp) [TERMINAL] $line"; done) 2>&1 &
|
||||
node $WATCH_MODE /terminal/terminal-server.js > >(while read line; do echo "$(timestamp) [TERMINAL] $line"; done) 2>&1 &
|
||||
TERMINAL_PID=$!
|
||||
|
||||
# Start the Soketi process in the background with logging
|
||||
|
@@ -61,9 +61,13 @@ wss.on('connection', (ws) => {
|
||||
const userSession = { ws, userId, ptyProcess: null, isActive: false };
|
||||
userSessions.set(userId, userSession);
|
||||
|
||||
ws.on('message', (message) => handleMessage(userSession, message));
|
||||
ws.on('message', (message) => {
|
||||
handleMessage(userSession, message);
|
||||
|
||||
});
|
||||
ws.on('error', (err) => handleError(err, userId));
|
||||
ws.on('close', () => handleClose(userId));
|
||||
|
||||
});
|
||||
|
||||
const messageHandlers = {
|
||||
@@ -108,7 +112,6 @@ function parseMessage(message) {
|
||||
|
||||
async function handleCommand(ws, command, userId) {
|
||||
const userSession = userSessions.get(userId);
|
||||
|
||||
if (userSession && userSession.isActive) {
|
||||
const result = await killPtyProcess(userId);
|
||||
if (!result) {
|
||||
@@ -127,27 +130,29 @@ async function handleCommand(ws, command, userId) {
|
||||
cols: 80,
|
||||
rows: 30,
|
||||
cwd: process.env.HOME,
|
||||
env: {},
|
||||
};
|
||||
|
||||
// NOTE: - Initiates a process within the Terminal container
|
||||
// Establishes an SSH connection to root@coolify with RequestTTY enabled
|
||||
// Executes the 'docker exec' command to connect to a specific container
|
||||
// If the user types 'exit', it terminates the container connection and reverts to the server.
|
||||
const ptyProcess = pty.spawn('ssh', sshArgs.concat(['bash']), options);
|
||||
const ptyProcess = pty.spawn('ssh', sshArgs.concat([hereDocContent]), options);
|
||||
|
||||
userSession.ptyProcess = ptyProcess;
|
||||
userSession.isActive = true;
|
||||
ptyProcess.write(hereDocContent + '\n');
|
||||
// clear the terminal if the user has clear command
|
||||
ptyProcess.write('command -v clear >/dev/null 2>&1 && clear\n');
|
||||
|
||||
ws.send('pty-ready');
|
||||
|
||||
ptyProcess.onData((data) => ws.send(data));
|
||||
ptyProcess.onData((data) => {
|
||||
ws.send(data);
|
||||
});
|
||||
|
||||
// when parent closes
|
||||
ptyProcess.onExit(({ exitCode, signal }) => {
|
||||
console.error(`Process exited with code ${exitCode} and signal ${signal}`);
|
||||
ws.send('pty-exited');
|
||||
userSession.isActive = false;
|
||||
|
||||
});
|
||||
|
||||
if (timeout) {
|
||||
@@ -181,7 +186,7 @@ async function killPtyProcess(userId) {
|
||||
|
||||
// session.ptyProcess.kill() wont work here because of https://github.com/moby/moby/issues/9098
|
||||
// patch with https://github.com/moby/moby/issues/9098#issuecomment-189743947
|
||||
session.ptyProcess.write('kill -TERM -$$ && exit\n');
|
||||
session.ptyProcess.write('set +o history\nkill -TERM -$$ && exit\nset -o history\n');
|
||||
|
||||
setTimeout(() => {
|
||||
if (!session.isActive || !session.ptyProcess) {
|
||||
@@ -230,5 +235,5 @@ function extractHereDocContent(commandString) {
|
||||
}
|
||||
|
||||
server.listen(6002, () => {
|
||||
console.log('Server listening on port 6002');
|
||||
console.log('Coolify realtime terminal server listening on port 6002. Let the hacking begin!');
|
||||
});
|
||||
|
166
openapi.yaml
166
openapi.yaml
@@ -236,6 +236,10 @@ paths:
|
||||
watch_paths:
|
||||
type: string
|
||||
description: 'The watch paths.'
|
||||
use_build_server:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: 'Use build server.'
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
@@ -457,6 +461,10 @@ paths:
|
||||
watch_paths:
|
||||
type: string
|
||||
description: 'The watch paths.'
|
||||
use_build_server:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: 'Use build server.'
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
@@ -678,6 +686,10 @@ paths:
|
||||
watch_paths:
|
||||
type: string
|
||||
description: 'The watch paths.'
|
||||
use_build_server:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: 'Use build server.'
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
@@ -850,6 +862,10 @@ paths:
|
||||
instant_deploy:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if the application should be deployed instantly.'
|
||||
use_build_server:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: 'Use build server.'
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
@@ -1013,6 +1029,10 @@ paths:
|
||||
instant_deploy:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if the application should be deployed instantly.'
|
||||
use_build_server:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: 'Use build server.'
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
@@ -1067,6 +1087,10 @@ paths:
|
||||
instant_deploy:
|
||||
type: boolean
|
||||
description: 'The flag to indicate if the application should be deployed instantly.'
|
||||
use_build_server:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: 'Use build server.'
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
@@ -1126,9 +1150,33 @@ paths:
|
||||
type: string
|
||||
format: uuid
|
||||
-
|
||||
name: cleanup
|
||||
name: delete_configurations
|
||||
in: query
|
||||
description: 'Delete configurations and volumes.'
|
||||
description: 'Delete configurations.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: true
|
||||
-
|
||||
name: delete_volumes
|
||||
in: query
|
||||
description: 'Delete volumes.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: true
|
||||
-
|
||||
name: docker_cleanup
|
||||
in: query
|
||||
description: 'Run docker cleanup.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: true
|
||||
-
|
||||
name: delete_connected_networks
|
||||
in: query
|
||||
description: 'Delete connected networks.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
@@ -1351,6 +1399,10 @@ paths:
|
||||
watch_paths:
|
||||
type: string
|
||||
description: 'The watch paths.'
|
||||
use_build_server:
|
||||
type: boolean
|
||||
nullable: true
|
||||
description: 'Use build server.'
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
@@ -1738,6 +1790,52 @@ paths:
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
'/applications/{uuid}/execute':
|
||||
post:
|
||||
tags:
|
||||
- Applications
|
||||
summary: 'Execute Command'
|
||||
description: "Execute a command on the application's current container."
|
||||
operationId: execute-command-application
|
||||
parameters:
|
||||
-
|
||||
name: uuid
|
||||
in: path
|
||||
description: 'UUID of the application.'
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
requestBody:
|
||||
description: 'Command to execute.'
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
command:
|
||||
type: string
|
||||
description: 'Command to execute.'
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
description: "Execute a command on the application's current container."
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
message: { type: string, example: 'Command executed.' }
|
||||
response: { type: string }
|
||||
type: object
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'400':
|
||||
$ref: '#/components/responses/400'
|
||||
'404':
|
||||
$ref: '#/components/responses/404'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
/databases:
|
||||
get:
|
||||
tags:
|
||||
@@ -1809,9 +1907,33 @@ paths:
|
||||
type: string
|
||||
format: uuid
|
||||
-
|
||||
name: cleanup
|
||||
name: delete_configurations
|
||||
in: query
|
||||
description: 'Delete configurations and volumes.'
|
||||
description: 'Delete configurations.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: true
|
||||
-
|
||||
name: delete_volumes
|
||||
in: query
|
||||
description: 'Delete volumes.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: true
|
||||
-
|
||||
name: docker_cleanup
|
||||
in: query
|
||||
description: 'Run docker cleanup.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: true
|
||||
-
|
||||
name: delete_connected_networks
|
||||
in: query
|
||||
description: 'Delete connected networks.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
@@ -3812,6 +3934,38 @@ paths:
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
-
|
||||
name: delete_configurations
|
||||
in: query
|
||||
description: 'Delete configurations.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: true
|
||||
-
|
||||
name: delete_volumes
|
||||
in: query
|
||||
description: 'Delete volumes.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: true
|
||||
-
|
||||
name: docker_cleanup
|
||||
in: query
|
||||
description: 'Run docker cleanup.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: true
|
||||
-
|
||||
name: delete_connected_networks
|
||||
in: query
|
||||
description: 'Delete connected networks.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: true
|
||||
responses:
|
||||
'200':
|
||||
description: 'Delete a service by UUID'
|
||||
@@ -4769,6 +4923,10 @@ components:
|
||||
type: boolean
|
||||
swarm_cluster:
|
||||
type: string
|
||||
delete_unused_volumes:
|
||||
type: boolean
|
||||
delete_unused_networks:
|
||||
type: boolean
|
||||
type: object
|
||||
ServerSetting:
|
||||
description: 'Server Settings model'
|
||||
|
@@ -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.1'
|
||||
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.347"
|
||||
"version": "4.0.0-beta.354"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0-beta.348"
|
||||
"version": "4.0.0-beta.355"
|
||||
},
|
||||
"helper": {
|
||||
"version": "1.0.1"
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"realtime": {
|
||||
"version": "1.0.1"
|
||||
"version": "1.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
166
public/svgs/anythingllm.svg
Normal file
166
public/svgs/anythingllm.svg
Normal file
@@ -0,0 +1,166 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!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" viewBox="0.00 0.00 245.00 245.00">
|
||||
<path stroke="#919393" stroke-width="2.00" fill="none" stroke-linecap="butt" vector-effect="non-scaling-stroke" d="
|
||||
M 187.03 159.00
|
||||
L 161.49 159.00
|
||||
A 1.24 1.24 0.0 0 1 160.48 158.48
|
||||
Q 153.96 149.33 147.89 140.81
|
||||
C 145.78 137.86 141.80 138.01 139.49 140.67
|
||||
C 136.43 144.17 133.65 147.56 136.44 151.42
|
||||
Q 142.22 159.39 148.22 167.98
|
||||
Q 152.74 174.47 160.49 174.57
|
||||
Q 173.94 174.73 188.79 174.60
|
||||
C 196.64 174.52 203.24 168.72 203.23 160.63
|
||||
Q 203.16 123.84 203.20 83.74
|
||||
C 203.21 78.41 199.97 73.56 195.17 71.35
|
||||
Q 191.90 69.85 186.23 69.92
|
||||
Q 173.94 70.08 161.37 69.94
|
||||
C 156.39 69.89 152.10 71.63 149.08 75.49
|
||||
Q 117.03 116.46 84.45 158.09
|
||||
A 2.37 2.35 -70.9 0 1 82.59 159.00
|
||||
L 57.21 159.00
|
||||
Q 56.65 159.00 56.65 158.45
|
||||
L 56.66 85.95
|
||||
Q 56.66 85.31 57.30 85.31
|
||||
L 83.00 85.31
|
||||
A 0.80 0.79 71.0 0 1 83.63 85.62
|
||||
Q 90.51 94.60 97.61 104.71
|
||||
C 99.47 107.36 103.07 108.21 105.48 105.83
|
||||
Q 107.60 103.72 109.70 100.67
|
||||
C 110.60 99.36 110.38 97.79 110.28 96.30
|
||||
Q 110.23 95.62 109.82 95.07
|
||||
Q 103.03 85.94 96.91 77.34
|
||||
C 93.63 72.73 89.66 69.98 84.18 70.00
|
||||
Q 71.60 70.05 56.56 69.96
|
||||
C 50.37 69.93 45.44 72.61 42.63 78.14
|
||||
Q 41.19 80.99 41.25 87.27
|
||||
Q 41.55 120.87 41.27 158.11
|
||||
Q 41.23 162.77 42.32 165.57
|
||||
Q 44.32 170.75 49.37 173.22
|
||||
Q 52.63 174.81 60.03 174.72
|
||||
Q 72.58 174.56 82.84 174.65
|
||||
Q 91.13 174.73 95.47 169.18
|
||||
Q 127.35 128.40 160.51 86.00
|
||||
A 1.81 1.80 18.9 0 1 161.93 85.31
|
||||
L 187.42 85.31
|
||||
A 0.40 0.40 0.0 0 1 187.82 85.71
|
||||
L 187.82 158.21
|
||||
Q 187.82 159.00 187.03 159.00"
|
||||
/>
|
||||
<path fill="#222627" d="
|
||||
M 46.00 0.00
|
||||
L 198.80 0.00
|
||||
Q 221.03 2.81 233.86 19.40
|
||||
Q 244.84 33.61 244.80 52.00
|
||||
Q 244.65 120.20 244.83 188.95
|
||||
Q 244.86 199.26 243.39 205.30
|
||||
Q 241.12 214.72 235.86 222.46
|
||||
C 234.56 224.37 232.98 226.08 231.56 227.86
|
||||
Q 229.92 229.91 227.88 231.54
|
||||
C 226.00 233.04 224.26 234.66 222.24 236.01
|
||||
Q 214.15 241.39 204.78 243.49
|
||||
Q 198.73 244.86 188.67 244.83
|
||||
Q 119.30 244.65 52.55 244.80
|
||||
Q 32.84 244.84 18.81 233.38
|
||||
Q 2.64 220.18 0.00 198.69
|
||||
L 0.00 45.76
|
||||
Q 1.42 35.66 4.33 29.79
|
||||
Q 17.17 3.86 46.00 0.00
|
||||
Z
|
||||
M 187.03 159.00
|
||||
L 161.49 159.00
|
||||
A 1.24 1.24 0.0 0 1 160.48 158.48
|
||||
Q 153.96 149.33 147.89 140.81
|
||||
C 145.78 137.86 141.80 138.01 139.49 140.67
|
||||
C 136.43 144.17 133.65 147.56 136.44 151.42
|
||||
Q 142.22 159.39 148.22 167.98
|
||||
Q 152.74 174.47 160.49 174.57
|
||||
Q 173.94 174.73 188.79 174.60
|
||||
C 196.64 174.52 203.24 168.72 203.23 160.63
|
||||
Q 203.16 123.84 203.20 83.74
|
||||
C 203.21 78.41 199.97 73.56 195.17 71.35
|
||||
Q 191.90 69.85 186.23 69.92
|
||||
Q 173.94 70.08 161.37 69.94
|
||||
C 156.39 69.89 152.10 71.63 149.08 75.49
|
||||
Q 117.03 116.46 84.45 158.09
|
||||
A 2.37 2.35 -70.9 0 1 82.59 159.00
|
||||
L 57.21 159.00
|
||||
Q 56.65 159.00 56.65 158.45
|
||||
L 56.66 85.95
|
||||
Q 56.66 85.31 57.30 85.31
|
||||
L 83.00 85.31
|
||||
A 0.80 0.79 71.0 0 1 83.63 85.62
|
||||
Q 90.51 94.60 97.61 104.71
|
||||
C 99.47 107.36 103.07 108.21 105.48 105.83
|
||||
Q 107.60 103.72 109.70 100.67
|
||||
C 110.60 99.36 110.38 97.79 110.28 96.30
|
||||
Q 110.23 95.62 109.82 95.07
|
||||
Q 103.03 85.94 96.91 77.34
|
||||
C 93.63 72.73 89.66 69.98 84.18 70.00
|
||||
Q 71.60 70.05 56.56 69.96
|
||||
C 50.37 69.93 45.44 72.61 42.63 78.14
|
||||
Q 41.19 80.99 41.25 87.27
|
||||
Q 41.55 120.87 41.27 158.11
|
||||
Q 41.23 162.77 42.32 165.57
|
||||
Q 44.32 170.75 49.37 173.22
|
||||
Q 52.63 174.81 60.03 174.72
|
||||
Q 72.58 174.56 82.84 174.65
|
||||
Q 91.13 174.73 95.47 169.18
|
||||
Q 127.35 128.40 160.51 86.00
|
||||
A 1.81 1.80 18.9 0 1 161.93 85.31
|
||||
L 187.42 85.31
|
||||
A 0.40 0.40 0.0 0 1 187.82 85.71
|
||||
L 187.82 158.21
|
||||
Q 187.82 159.00 187.03 159.00
|
||||
Z"
|
||||
/>
|
||||
<path fill="#ffffff" d="
|
||||
M 187.82 158.21
|
||||
L 187.82 85.71
|
||||
A 0.40 0.40 0.0 0 0 187.42 85.31
|
||||
L 161.93 85.31
|
||||
A 1.81 1.80 18.9 0 0 160.51 86.00
|
||||
Q 127.35 128.40 95.47 169.18
|
||||
Q 91.13 174.73 82.84 174.65
|
||||
Q 72.58 174.56 60.03 174.72
|
||||
Q 52.63 174.81 49.37 173.22
|
||||
Q 44.32 170.75 42.32 165.57
|
||||
Q 41.23 162.77 41.27 158.11
|
||||
Q 41.55 120.87 41.25 87.27
|
||||
Q 41.19 80.99 42.63 78.14
|
||||
C 45.44 72.61 50.37 69.93 56.56 69.96
|
||||
Q 71.60 70.05 84.18 70.00
|
||||
C 89.66 69.98 93.63 72.73 96.91 77.34
|
||||
Q 103.03 85.94 109.82 95.07
|
||||
Q 110.23 95.62 110.28 96.30
|
||||
C 110.38 97.79 110.60 99.36 109.70 100.67
|
||||
Q 107.60 103.72 105.48 105.83
|
||||
C 103.07 108.21 99.47 107.36 97.61 104.71
|
||||
Q 90.51 94.60 83.63 85.62
|
||||
A 0.80 0.79 71.0 0 0 83.00 85.31
|
||||
L 57.30 85.31
|
||||
Q 56.66 85.31 56.66 85.95
|
||||
L 56.65 158.45
|
||||
Q 56.65 159.00 57.21 159.00
|
||||
L 82.59 159.00
|
||||
A 2.37 2.35 -70.9 0 0 84.45 158.09
|
||||
Q 117.03 116.46 149.08 75.49
|
||||
C 152.10 71.63 156.39 69.89 161.37 69.94
|
||||
Q 173.94 70.08 186.23 69.92
|
||||
Q 191.90 69.85 195.17 71.35
|
||||
C 199.97 73.56 203.21 78.41 203.20 83.74
|
||||
Q 203.16 123.84 203.23 160.63
|
||||
C 203.24 168.72 196.64 174.52 188.79 174.60
|
||||
Q 173.94 174.73 160.49 174.57
|
||||
Q 152.74 174.47 148.22 167.98
|
||||
Q 142.22 159.39 136.44 151.42
|
||||
C 133.65 147.56 136.43 144.17 139.49 140.67
|
||||
C 141.80 138.01 145.78 137.86 147.89 140.81
|
||||
Q 153.96 149.33 160.48 158.48
|
||||
A 1.24 1.24 0.0 0 0 161.49 159.00
|
||||
L 187.03 159.00
|
||||
Q 187.82 159.00 187.82 158.21
|
||||
Z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 5.1 KiB |
BIN
public/svgs/argilla.png
Normal file
BIN
public/svgs/argilla.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
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 |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user