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:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main", "next" ]
|
branches: [ "main" ]
|
||||||
paths:
|
paths:
|
||||||
- .github/workflows/coolify-realtime.yml
|
- .github/workflows/coolify-realtime.yml
|
||||||
- docker/coolify-realtime/Dockerfile
|
- docker/coolify-realtime/Dockerfile
|
||||||
|
@@ -46,9 +46,6 @@ class StartDragonfly
|
|||||||
'networks' => [
|
'networks' => [
|
||||||
$this->database->destination->network,
|
$this->database->destination->network,
|
||||||
],
|
],
|
||||||
'ulimits' => [
|
|
||||||
'memlock' => '-1',
|
|
||||||
],
|
|
||||||
'labels' => [
|
'labels' => [
|
||||||
'coolify.managed' => 'true',
|
'coolify.managed' => 'true',
|
||||||
],
|
],
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Actions\Fortify;
|
namespace App\Actions\Fortify;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
@@ -20,7 +19,7 @@ class CreateNewUser implements CreatesNewUsers
|
|||||||
*/
|
*/
|
||||||
public function create(array $input): User
|
public function create(array $input): User
|
||||||
{
|
{
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if (! $settings->is_registration_enabled) {
|
if (! $settings->is_registration_enabled) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
@@ -48,7 +47,7 @@ class CreateNewUser implements CreatesNewUsers
|
|||||||
$team = $user->teams()->first();
|
$team = $user->teams()->first();
|
||||||
|
|
||||||
// Disable registration after first user is created
|
// Disable registration after first user is created
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$settings->is_registration_enabled = false;
|
$settings->is_registration_enabled = false;
|
||||||
$settings->save();
|
$settings->save();
|
||||||
} else {
|
} else {
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Actions\License;
|
namespace App\Actions\License;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
@@ -13,7 +12,7 @@ class CheckResaleLicense
|
|||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
$settings->update([
|
$settings->update([
|
||||||
'is_resale_license_active' => true,
|
'is_resale_license_active' => true,
|
||||||
|
@@ -22,7 +22,7 @@ class CheckConfiguration
|
|||||||
];
|
];
|
||||||
$proxy_configuration = instant_remote_process($payload, $server, false);
|
$proxy_configuration = instant_remote_process($payload, $server, false);
|
||||||
if ($reset || ! $proxy_configuration || is_null($proxy_configuration)) {
|
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)) {
|
if (! $proxy_configuration || is_null($proxy_configuration)) {
|
||||||
throw new \Exception('Could not generate proxy configuration');
|
throw new \Exception('Could not generate proxy configuration');
|
||||||
|
@@ -2,14 +2,17 @@
|
|||||||
|
|
||||||
namespace App\Actions\Proxy;
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
|
use App\Enums\ProxyTypes;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class CheckProxy
|
class CheckProxy
|
||||||
{
|
{
|
||||||
use AsAction;
|
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()) {
|
if (! $server->isFunctional()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -62,22 +65,42 @@ class CheckProxy
|
|||||||
$ip = 'host.docker.internal';
|
$ip = 'host.docker.internal';
|
||||||
}
|
}
|
||||||
|
|
||||||
$connection80 = @fsockopen($ip, '80');
|
$portsToCheck = ['80', '443'];
|
||||||
$connection443 = @fsockopen($ip, '443');
|
|
||||||
$port80 = is_resource($connection80) && fclose($connection80);
|
try {
|
||||||
$port443 = is_resource($connection443) && fclose($connection443);
|
if ($server->proxyType() !== ProxyTypes::NONE->value) {
|
||||||
if ($port80) {
|
$proxyCompose = CheckConfiguration::run($server);
|
||||||
if ($fromUI) {
|
if (isset($proxyCompose)) {
|
||||||
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>");
|
$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 {
|
} else {
|
||||||
return false;
|
$portsToCheck = [];
|
||||||
}
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
ray($e->getMessage());
|
||||||
}
|
}
|
||||||
if ($port443) {
|
if (count($portsToCheck) === 0) {
|
||||||
if ($fromUI) {
|
return false;
|
||||||
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 {
|
foreach ($portsToCheck as $port) {
|
||||||
return false;
|
$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);
|
SaveConfiguration::run($server, $configuration);
|
||||||
$docker_compose_yml_base64 = base64_encode($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();
|
$server->save();
|
||||||
if ($server->isSwarm()) {
|
if ($server->isSwarm()) {
|
||||||
$commands = $commands->merge([
|
$commands = $commands->merge([
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
@@ -12,7 +11,7 @@ class CleanupDocker
|
|||||||
|
|
||||||
public function handle(Server $server)
|
public function handle(Server $server)
|
||||||
{
|
{
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$helperImageVersion = data_get($settings, 'helper_version');
|
$helperImageVersion = data_get($settings, 'helper_version');
|
||||||
$helperImage = config('coolify.helper_image');
|
$helperImage = config('coolify.helper_image');
|
||||||
$helperImageWithVersion = "$helperImage:$helperImageVersion";
|
$helperImageWithVersion = "$helperImage:$helperImageVersion";
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
use App\Jobs\PullHelperImageJob;
|
use App\Jobs\PullHelperImageJob;
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
@@ -20,7 +19,7 @@ class UpdateCoolify
|
|||||||
public function handle($manual_update = false)
|
public function handle($manual_update = false)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$this->server = Server::find(0);
|
$this->server = Server::find(0);
|
||||||
if (! $this->server) {
|
if (! $this->server) {
|
||||||
return;
|
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
|
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()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
@@ -4,6 +4,7 @@ namespace App\Console\Commands;
|
|||||||
|
|
||||||
use App\Jobs\CleanupHelperContainersJob;
|
use App\Jobs\CleanupHelperContainersJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\ScheduledTask;
|
use App\Models\ScheduledTask;
|
||||||
@@ -47,6 +48,17 @@ class CleanupStuckedResources extends Command
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stucked resources: {$e->getMessage()}\n";
|
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 {
|
try {
|
||||||
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
|
$applications = Application::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
|
@@ -48,6 +48,13 @@ class Dev extends Command
|
|||||||
echo "Generating APP_KEY.\n";
|
echo "Generating APP_KEY.\n";
|
||||||
Artisan::call('key:generate');
|
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
|
// Seed database if it's empty
|
||||||
$settings = InstanceSettings::find(0);
|
$settings = InstanceSettings::find(0);
|
||||||
if (! $settings) {
|
if (! $settings) {
|
||||||
|
@@ -7,7 +7,6 @@ use App\Enums\ActivityTypes;
|
|||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\Environment;
|
use App\Models\Environment;
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
@@ -69,7 +68,7 @@ class Init extends Command
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if (! is_null(env('AUTOUPDATE', null))) {
|
if (! is_null(env('AUTOUPDATE', null))) {
|
||||||
if (env('AUTOUPDATE') == true) {
|
if (env('AUTOUPDATE') == true) {
|
||||||
$settings->update(['is_auto_update_enabled' => true]);
|
$settings->update(['is_auto_update_enabled' => true]);
|
||||||
@@ -196,7 +195,7 @@ class Init extends Command
|
|||||||
{
|
{
|
||||||
$id = config('app.id');
|
$id = config('app.id');
|
||||||
$version = config('version');
|
$version = config('version');
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$do_not_track = data_get($settings, 'do_not_track');
|
$do_not_track = data_get($settings, 'do_not_track');
|
||||||
if ($do_not_track == true) {
|
if ($do_not_track == true) {
|
||||||
echo "Skipping alive as do_not_track is enabled\n";
|
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\PullTemplatesFromCDN;
|
||||||
use App\Jobs\ScheduledTaskJob;
|
use App\Jobs\ScheduledTaskJob;
|
||||||
use App\Jobs\ServerCheckJob;
|
use App\Jobs\ServerCheckJob;
|
||||||
|
use App\Jobs\ServerStorageCheckJob;
|
||||||
use App\Jobs\UpdateCoolifyJob;
|
use App\Jobs\UpdateCoolifyJob;
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\ScheduledTask;
|
use App\Models\ScheduledTask;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
@@ -28,7 +28,7 @@ class Kernel extends ConsoleKernel
|
|||||||
protected function schedule(Schedule $schedule): void
|
protected function schedule(Schedule $schedule): void
|
||||||
{
|
{
|
||||||
$this->all_servers = Server::all();
|
$this->all_servers = Server::all();
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
|
|
||||||
$schedule->job(new CleanupStaleMultiplexedConnections)->hourly();
|
$schedule->job(new CleanupStaleMultiplexedConnections)->hourly();
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
private function pull_images($schedule)
|
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');
|
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
if ($server->isSentinelEnabled()) {
|
if ($server->isSentinelEnabled()) {
|
||||||
@@ -88,7 +88,7 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
private function schedule_updates($schedule)
|
private function schedule_updates($schedule)
|
||||||
{
|
{
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
|
|
||||||
$updateCheckFrequency = $settings->update_check_frequency;
|
$updateCheckFrequency = $settings->update_check_frequency;
|
||||||
$schedule->job(new CheckForUpdatesJob)
|
$schedule->job(new CheckForUpdatesJob)
|
||||||
@@ -116,6 +116,7 @@ class Kernel extends ConsoleKernel
|
|||||||
}
|
}
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$schedule->job(new ServerCheckJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new ServerCheckJob($server))->everyMinute()->onOneServer();
|
||||||
|
// $schedule->job(new ServerStorageCheckJob($server))->everyMinute()->onOneServer();
|
||||||
$serverTimezone = $server->settings->server_timezone;
|
$serverTimezone = $server->settings->server_timezone;
|
||||||
if ($server->settings->force_docker_cleanup) {
|
if ($server->settings->force_docker_cleanup) {
|
||||||
$schedule->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer();
|
$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) {
|
if ($e instanceof RuntimeException) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->settings = \App\Models\InstanceSettings::get();
|
$this->settings = instanceSettings();
|
||||||
if ($this->settings->do_not_track) {
|
if ($this->settings->do_not_track) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -94,7 +94,9 @@ class SshMultiplexingHelper
|
|||||||
$muxPersistTime = config('constants.ssh.mux_persist_time');
|
$muxPersistTime = config('constants.ssh.mux_persist_time');
|
||||||
|
|
||||||
$scp_command = "timeout $timeout scp ";
|
$scp_command = "timeout $timeout scp ";
|
||||||
|
if ($server->isIpv6()) {
|
||||||
|
$scp_command .= '-6 ';
|
||||||
|
}
|
||||||
if (self::isMultiplexingEnabled()) {
|
if (self::isMultiplexingEnabled()) {
|
||||||
$scp_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
|
$scp_command .= "-o ControlMaster=auto -o ControlPath=$muxSocket -o ControlPersist={$muxPersistTime} ";
|
||||||
self::ensureMultiplexedConnection($server);
|
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'));
|
$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 = Hash::make($command);
|
||||||
|
$delimiter = base64_encode($delimiter);
|
||||||
$command = str_replace($delimiter, '', $command);
|
$command = str_replace($delimiter, '', $command);
|
||||||
|
|
||||||
$ssh_command .= "{$server->user}@{$server->ip} 'bash -se' << \\$delimiter".PHP_EOL
|
$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_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||||
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
||||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
'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_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||||
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
||||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
'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_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||||
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
||||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
'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.'],
|
'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']],
|
'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.'],
|
'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.'],
|
'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']],
|
'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.'],
|
'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.'],
|
'name' => ['type' => 'string', 'description' => 'The application name.'],
|
||||||
'description' => ['type' => 'string', 'description' => 'The application description.'],
|
'description' => ['type' => 'string', 'description' => 'The application description.'],
|
||||||
'instant_deploy' => ['type' => 'boolean', 'description' => 'The flag to indicate if the application should be deployed instantly.'],
|
'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)
|
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();
|
$teamId = getTeamIdFromToken();
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
return invalidTokenResponse();
|
return invalidTokenResponse();
|
||||||
@@ -665,6 +671,7 @@ class ApplicationsController extends Controller
|
|||||||
$fqdn = $request->domains;
|
$fqdn = $request->domains;
|
||||||
$instantDeploy = $request->instant_deploy;
|
$instantDeploy = $request->instant_deploy;
|
||||||
$githubAppUuid = $request->github_app_uuid;
|
$githubAppUuid = $request->github_app_uuid;
|
||||||
|
$useBuildServer = $request->use_build_server;
|
||||||
|
|
||||||
$project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first();
|
$project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first();
|
||||||
if (! $project) {
|
if (! $project) {
|
||||||
@@ -737,6 +744,10 @@ class ApplicationsController extends Controller
|
|||||||
$application->destination_id = $destination->id;
|
$application->destination_id = $destination->id;
|
||||||
$application->destination_type = $destination->getMorphClass();
|
$application->destination_type = $destination->getMorphClass();
|
||||||
$application->environment_id = $environment->id;
|
$application->environment_id = $environment->id;
|
||||||
|
if (isset($useBuildServer)) {
|
||||||
|
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||||
|
$application->settings->save();
|
||||||
|
}
|
||||||
$application->save();
|
$application->save();
|
||||||
$application->refresh();
|
$application->refresh();
|
||||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||||
@@ -833,6 +844,10 @@ class ApplicationsController extends Controller
|
|||||||
$application->environment_id = $environment->id;
|
$application->environment_id = $environment->id;
|
||||||
$application->source_type = $githubApp->getMorphClass();
|
$application->source_type = $githubApp->getMorphClass();
|
||||||
$application->source_id = $githubApp->id;
|
$application->source_id = $githubApp->id;
|
||||||
|
if (isset($useBuildServer)) {
|
||||||
|
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||||
|
$application->settings->save();
|
||||||
|
}
|
||||||
$application->save();
|
$application->save();
|
||||||
$application->refresh();
|
$application->refresh();
|
||||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||||
@@ -925,6 +940,10 @@ class ApplicationsController extends Controller
|
|||||||
$application->destination_id = $destination->id;
|
$application->destination_id = $destination->id;
|
||||||
$application->destination_type = $destination->getMorphClass();
|
$application->destination_type = $destination->getMorphClass();
|
||||||
$application->environment_id = $environment->id;
|
$application->environment_id = $environment->id;
|
||||||
|
if (isset($useBuildServer)) {
|
||||||
|
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||||
|
$application->settings->save();
|
||||||
|
}
|
||||||
$application->save();
|
$application->save();
|
||||||
$application->refresh();
|
$application->refresh();
|
||||||
if (! $application->settings->is_container_label_readonly_enabled) {
|
if (! $application->settings->is_container_label_readonly_enabled) {
|
||||||
@@ -1004,6 +1023,10 @@ class ApplicationsController extends Controller
|
|||||||
$application->destination_id = $destination->id;
|
$application->destination_id = $destination->id;
|
||||||
$application->destination_type = $destination->getMorphClass();
|
$application->destination_type = $destination->getMorphClass();
|
||||||
$application->environment_id = $environment->id;
|
$application->environment_id = $environment->id;
|
||||||
|
if (isset($useBuildServer)) {
|
||||||
|
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||||
|
$application->settings->save();
|
||||||
|
}
|
||||||
|
|
||||||
$application->git_repository = 'coollabsio/coolify';
|
$application->git_repository = 'coollabsio/coolify';
|
||||||
$application->git_branch = 'main';
|
$application->git_branch = 'main';
|
||||||
@@ -1062,6 +1085,10 @@ class ApplicationsController extends Controller
|
|||||||
$application->destination_id = $destination->id;
|
$application->destination_id = $destination->id;
|
||||||
$application->destination_type = $destination->getMorphClass();
|
$application->destination_type = $destination->getMorphClass();
|
||||||
$application->environment_id = $environment->id;
|
$application->environment_id = $environment->id;
|
||||||
|
if (isset($useBuildServer)) {
|
||||||
|
$application->settings->is_build_server_enabled = $useBuildServer;
|
||||||
|
$application->settings->save();
|
||||||
|
}
|
||||||
|
|
||||||
$application->git_repository = 'coollabsio/coolify';
|
$application->git_repository = 'coollabsio/coolify';
|
||||||
$application->git_branch = 'main';
|
$application->git_branch = 'main';
|
||||||
@@ -1259,16 +1286,10 @@ class ApplicationsController extends Controller
|
|||||||
format: 'uuid',
|
format: 'uuid',
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
new OA\Parameter(
|
new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||||
name: 'cleanup',
|
new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||||
in: 'query',
|
new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||||
description: 'Delete configurations and volumes.',
|
new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||||
required: false,
|
|
||||||
schema: new OA\Schema(
|
|
||||||
type: 'boolean',
|
|
||||||
default: true,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
responses: [
|
responses: [
|
||||||
new OA\Response(
|
new OA\Response(
|
||||||
@@ -1316,10 +1337,14 @@ class ApplicationsController extends Controller
|
|||||||
'message' => 'Application not found',
|
'message' => 'Application not found',
|
||||||
], 404);
|
], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteResourceJob::dispatch(
|
DeleteResourceJob::dispatch(
|
||||||
resource: $application,
|
resource: $application,
|
||||||
deleteConfigurations: $cleanup,
|
deleteConfigurations: $request->query->get('delete_configurations', true),
|
||||||
deleteVolumes: $cleanup);
|
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([
|
return response()->json([
|
||||||
'message' => 'Application deletion request queued.',
|
'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_custom_build_command' => ['type' => 'string', 'description' => 'The Docker Compose custom build command.'],
|
||||||
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
'docker_compose_domains' => ['type' => 'array', 'description' => 'The Docker Compose domains.'],
|
||||||
'watch_paths' => ['type' => 'string', 'description' => 'The watch paths.'],
|
'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);
|
], 404);
|
||||||
}
|
}
|
||||||
$server = $application->destination->server;
|
$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(), [
|
$validator = customApiValidator($request->all(), [
|
||||||
sharedDataApplications(),
|
sharedDataApplications(),
|
||||||
@@ -1538,6 +1564,13 @@ class ApplicationsController extends Controller
|
|||||||
}
|
}
|
||||||
$instantDeploy = $request->instant_deploy;
|
$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);
|
removeUnnecessaryFieldsFromRequest($request);
|
||||||
|
|
||||||
$data = $request->all();
|
$data = $request->all();
|
||||||
|
@@ -1541,16 +1541,10 @@ class DatabasesController extends Controller
|
|||||||
format: 'uuid',
|
format: 'uuid',
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
new OA\Parameter(
|
new OA\Parameter(name: 'delete_configurations', in: 'query', required: false, description: 'Delete configurations.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||||
name: 'cleanup',
|
new OA\Parameter(name: 'delete_volumes', in: 'query', required: false, description: 'Delete volumes.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||||
in: 'query',
|
new OA\Parameter(name: 'docker_cleanup', in: 'query', required: false, description: 'Run docker cleanup.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||||
description: 'Delete configurations and volumes.',
|
new OA\Parameter(name: 'delete_connected_networks', in: 'query', required: false, description: 'Delete connected networks.', schema: new OA\Schema(type: 'boolean', default: true)),
|
||||||
required: false,
|
|
||||||
schema: new OA\Schema(
|
|
||||||
type: 'boolean',
|
|
||||||
default: true,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
responses: [
|
responses: [
|
||||||
new OA\Response(
|
new OA\Response(
|
||||||
@@ -1595,10 +1589,14 @@ class DatabasesController extends Controller
|
|||||||
if (! $database) {
|
if (! $database) {
|
||||||
return response()->json(['message' => 'Database not found.'], 404);
|
return response()->json(['message' => 'Database not found.'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteResourceJob::dispatch(
|
DeleteResourceJob::dispatch(
|
||||||
resource: $database,
|
resource: $database,
|
||||||
deleteConfigurations: $cleanup,
|
deleteConfigurations: $request->query->get('delete_configurations', true),
|
||||||
deleteVolumes: $cleanup);
|
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([
|
return response()->json([
|
||||||
'message' => 'Database deletion request queued.',
|
'message' => 'Database deletion request queued.',
|
||||||
|
@@ -86,7 +86,7 @@ class OtherController extends Controller
|
|||||||
if ($teamId !== '0') {
|
if ($teamId !== '0') {
|
||||||
return response()->json(['message' => 'You are not allowed to enable the API.'], 403);
|
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]);
|
$settings->update(['is_api_enabled' => true]);
|
||||||
|
|
||||||
return response()->json(['message' => 'API enabled.'], 200);
|
return response()->json(['message' => 'API enabled.'], 200);
|
||||||
@@ -138,7 +138,7 @@ class OtherController extends Controller
|
|||||||
if ($teamId !== '0') {
|
if ($teamId !== '0') {
|
||||||
return response()->json(['message' => 'You are not allowed to disable the API.'], 403);
|
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]);
|
$settings->update(['is_api_enabled' => false]);
|
||||||
|
|
||||||
return response()->json(['message' => 'API disabled.'], 200);
|
return response()->json(['message' => 'API disabled.'], 200);
|
||||||
|
@@ -308,7 +308,7 @@ class ServersController extends Controller
|
|||||||
$projects = Project::where('team_id', $teamId)->get();
|
$projects = Project::where('team_id', $teamId)->get();
|
||||||
$domains = collect();
|
$domains = collect();
|
||||||
$applications = $projects->pluck('applications')->flatten();
|
$applications = $projects->pluck('applications')->flatten();
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if ($applications->count() > 0) {
|
if ($applications->count() > 0) {
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
$ip = $application->destination->server->ip;
|
$ip = $application->destination->server->ip;
|
||||||
|
@@ -432,6 +432,10 @@ class ServicesController extends Controller
|
|||||||
tags: ['Services'],
|
tags: ['Services'],
|
||||||
parameters: [
|
parameters: [
|
||||||
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Service UUID', schema: new OA\Schema(type: 'string')),
|
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: [
|
responses: [
|
||||||
new OA\Response(
|
new OA\Response(
|
||||||
@@ -476,7 +480,14 @@ class ServicesController extends Controller
|
|||||||
if (! $service) {
|
if (! $service) {
|
||||||
return response()->json(['message' => 'Service not found.'], 404);
|
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([
|
return response()->json([
|
||||||
'message' => 'Service deletion request queued.',
|
'message' => 'Service deletion request queued.',
|
||||||
@@ -516,7 +527,8 @@ class ServicesController extends Controller
|
|||||||
items: new OA\Items(ref: '#/components/schemas/EnvironmentVariable')
|
items: new OA\Items(ref: '#/components/schemas/EnvironmentVariable')
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
new OA\Response(
|
new OA\Response(
|
||||||
response: 401,
|
response: 401,
|
||||||
ref: '#/components/responses/401',
|
ref: '#/components/responses/401',
|
||||||
@@ -619,7 +631,8 @@ class ServicesController extends Controller
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
new OA\Response(
|
new OA\Response(
|
||||||
response: 401,
|
response: 401,
|
||||||
ref: '#/components/responses/401',
|
ref: '#/components/responses/401',
|
||||||
@@ -738,7 +751,8 @@ class ServicesController extends Controller
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
new OA\Response(
|
new OA\Response(
|
||||||
response: 401,
|
response: 401,
|
||||||
ref: '#/components/responses/401',
|
ref: '#/components/responses/401',
|
||||||
@@ -853,7 +867,8 @@ class ServicesController extends Controller
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
new OA\Response(
|
new OA\Response(
|
||||||
response: 401,
|
response: 401,
|
||||||
ref: '#/components/responses/401',
|
ref: '#/components/responses/401',
|
||||||
@@ -953,7 +968,8 @@ class ServicesController extends Controller
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
new OA\Response(
|
new OA\Response(
|
||||||
response: 401,
|
response: 401,
|
||||||
ref: '#/components/responses/401',
|
ref: '#/components/responses/401',
|
||||||
@@ -1025,9 +1041,11 @@ class ServicesController extends Controller
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: [
|
properties: [
|
||||||
'message' => ['type' => 'string', 'example' => 'Service starting request queued.'],
|
'message' => ['type' => 'string', 'example' => 'Service starting request queued.'],
|
||||||
])
|
]
|
||||||
|
)
|
||||||
),
|
),
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
new OA\Response(
|
new OA\Response(
|
||||||
response: 401,
|
response: 401,
|
||||||
ref: '#/components/responses/401',
|
ref: '#/components/responses/401',
|
||||||
@@ -1101,9 +1119,11 @@ class ServicesController extends Controller
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: [
|
properties: [
|
||||||
'message' => ['type' => 'string', 'example' => 'Service stopping request queued.'],
|
'message' => ['type' => 'string', 'example' => 'Service stopping request queued.'],
|
||||||
])
|
]
|
||||||
|
)
|
||||||
),
|
),
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
new OA\Response(
|
new OA\Response(
|
||||||
response: 401,
|
response: 401,
|
||||||
ref: '#/components/responses/401',
|
ref: '#/components/responses/401',
|
||||||
@@ -1177,9 +1197,11 @@ class ServicesController extends Controller
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: [
|
properties: [
|
||||||
'message' => ['type' => 'string', 'example' => 'Service restaring request queued.'],
|
'message' => ['type' => 'string', 'example' => 'Service restaring request queued.'],
|
||||||
])
|
]
|
||||||
|
)
|
||||||
),
|
),
|
||||||
]),
|
]
|
||||||
|
),
|
||||||
new OA\Response(
|
new OA\Response(
|
||||||
response: 401,
|
response: 401,
|
||||||
ref: '#/components/responses/401',
|
ref: '#/components/responses/401',
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
@@ -22,7 +21,7 @@ class OauthController extends Controller
|
|||||||
$oauthUser = get_socialite_provider($provider)->user();
|
$oauthUser = get_socialite_provider($provider)->user();
|
||||||
$user = User::whereEmail($oauthUser->email)->first();
|
$user = User::whereEmail($oauthUser->email)->first();
|
||||||
if (! $user) {
|
if (! $user) {
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if (! $settings->is_registration_enabled) {
|
if (! $settings->is_registration_enabled) {
|
||||||
abort(403, 'Registration is disabled');
|
abort(403, 'Registration is disabled');
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,7 @@ class ApiAllowed
|
|||||||
if (isCloud()) {
|
if (isCloud()) {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if ($settings->is_api_enabled === false) {
|
if ($settings->is_api_enabled === false) {
|
||||||
return response()->json(['success' => true, 'message' => 'API is disabled.'], 403);
|
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\EnvironmentVariable;
|
||||||
use App\Models\GithubApp;
|
use App\Models\GithubApp;
|
||||||
use App\Models\GitlabApp;
|
use App\Models\GitlabApp;
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
use App\Models\SwarmDocker;
|
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->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}");
|
$envs->push("COOLIFY_URL={$this->application->fqdn}");
|
||||||
} else {
|
} else {
|
||||||
$envs->push("COOLIFY_FQDN={$this->application->fqdn}");
|
$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()) {
|
if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||||
$url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
|
$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}");
|
$envs->push("COOLIFY_FQDN={$url}");
|
||||||
} else {
|
} else {
|
||||||
$envs->push("COOLIFY_URL={$url}");
|
$envs->push("COOLIFY_URL={$url}");
|
||||||
@@ -1334,7 +1333,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
private function prepare_builder_image()
|
private function prepare_builder_image()
|
||||||
{
|
{
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$helperImage = config('coolify.helper_image');
|
$helperImage = config('coolify.helper_image');
|
||||||
$helperImage = "{$helperImage}:{$settings->helper_version}";
|
$helperImage = "{$helperImage}:{$settings->helper_version}";
|
||||||
// Get user home directory
|
// Get user home directory
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
@@ -22,7 +21,7 @@ class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
if (isDev() || isCloud()) {
|
if (isDev() || isCloud()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
|
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
|
||||||
if ($response->successful()) {
|
if ($response->successful()) {
|
||||||
$versions = $response->json();
|
$versions = $response->json();
|
||||||
|
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Actions\Database\StopDatabase;
|
|
||||||
use App\Events\BackupCreated;
|
use App\Events\BackupCreated;
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use App\Models\S3Storage;
|
use App\Models\S3Storage;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\ScheduledDatabaseBackupExecution;
|
use App\Models\ScheduledDatabaseBackupExecution;
|
||||||
@@ -25,7 +23,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
|||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Visus\Cuid2\Cuid2;
|
|
||||||
|
|
||||||
class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
||||||
{
|
{
|
||||||
@@ -64,32 +61,32 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
public function __construct($backup)
|
public function __construct($backup)
|
||||||
{
|
{
|
||||||
$this->backup = $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
|
public function handle(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
// Check if team is exists
|
$this->team = Team::find($this->backup->team_id);
|
||||||
if (is_null($this->team)) {
|
if (! $this->team) {
|
||||||
$this->backup->update(['status' => 'failed']);
|
$this->backup->delete();
|
||||||
StopDatabase::run($this->database);
|
|
||||||
$this->database->delete();
|
|
||||||
|
|
||||||
return;
|
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);
|
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;
|
$this->backup_dir = backup_dir().'/databases/'.str($this->team->name)->slug().'-'.$this->team->id.'/'.$this->directory_name;
|
||||||
|
|
||||||
if ($this->database->name === 'coolify-db') {
|
if ($this->database->name === 'coolify-db') {
|
||||||
$databasesToBackup = ['coolify'];
|
$databasesToBackup = ['coolify'];
|
||||||
$this->directory_name = $this->container_name = 'coolify-db';
|
$this->directory_name = $this->container_name = 'coolify-db';
|
||||||
@@ -252,6 +248,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
try {
|
try {
|
||||||
if (str($databaseType)->contains('postgres')) {
|
if (str($databaseType)->contains('postgres')) {
|
||||||
$this->backup_file = "/pg-dump-$database-".Carbon::now()->timestamp.'.dmp';
|
$this->backup_file = "/pg-dump-$database-".Carbon::now()->timestamp.'.dmp';
|
||||||
|
if ($this->backup->dump_all) {
|
||||||
|
$this->backup_file = '/pg-dump-all-'.Carbon::now()->timestamp.'.gz';
|
||||||
|
}
|
||||||
$this->backup_location = $this->backup_dir.$this->backup_file;
|
$this->backup_location = $this->backup_dir.$this->backup_file;
|
||||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||||
'database_name' => $database,
|
'database_name' => $database,
|
||||||
@@ -280,6 +279,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$this->backup_standalone_mongodb($database);
|
$this->backup_standalone_mongodb($database);
|
||||||
} elseif (str($databaseType)->contains('mysql')) {
|
} elseif (str($databaseType)->contains('mysql')) {
|
||||||
$this->backup_file = "/mysql-dump-$database-".Carbon::now()->timestamp.'.dmp';
|
$this->backup_file = "/mysql-dump-$database-".Carbon::now()->timestamp.'.dmp';
|
||||||
|
if ($this->backup->dump_all) {
|
||||||
|
$this->backup_file = '/mysql-dump-all-'.Carbon::now()->timestamp.'.gz';
|
||||||
|
}
|
||||||
$this->backup_location = $this->backup_dir.$this->backup_file;
|
$this->backup_location = $this->backup_dir.$this->backup_file;
|
||||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||||
'database_name' => $database,
|
'database_name' => $database,
|
||||||
@@ -289,6 +291,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$this->backup_standalone_mysql($database);
|
$this->backup_standalone_mysql($database);
|
||||||
} elseif (str($databaseType)->contains('mariadb')) {
|
} elseif (str($databaseType)->contains('mariadb')) {
|
||||||
$this->backup_file = "/mariadb-dump-$database-".Carbon::now()->timestamp.'.dmp';
|
$this->backup_file = "/mariadb-dump-$database-".Carbon::now()->timestamp.'.dmp';
|
||||||
|
if ($this->backup->dump_all) {
|
||||||
|
$this->backup_file = '/mariadb-dump-all-'.Carbon::now()->timestamp.'.gz';
|
||||||
|
}
|
||||||
$this->backup_location = $this->backup_dir.$this->backup_file;
|
$this->backup_location = $this->backup_dir.$this->backup_file;
|
||||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||||
'database_name' => $database,
|
'database_name' => $database,
|
||||||
@@ -327,7 +332,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
send_internal_notification('DatabaseBackupJob failed with: '.$e->getMessage());
|
send_internal_notification('DatabaseBackupJob failed with: '.$e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
} finally {
|
} 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) {
|
if ($this->postgres_password) {
|
||||||
$backupCommand .= " -e PGPASSWORD=$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;
|
$commands[] = $backupCommand;
|
||||||
ray($commands);
|
ray($commands);
|
||||||
@@ -407,8 +418,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$commands[] = 'mkdir -p '.$this->backup_dir;
|
$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";
|
if ($this->backup->dump_all) {
|
||||||
ray($commands);
|
$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 = instant_remote_process($commands, $this->server);
|
||||||
$this->backup_output = trim($this->backup_output);
|
$this->backup_output = trim($this->backup_output);
|
||||||
if ($this->backup_output === '') {
|
if ($this->backup_output === '') {
|
||||||
@@ -426,7 +440,11 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$commands[] = 'mkdir -p '.$this->backup_dir;
|
$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);
|
ray($commands);
|
||||||
$this->backup_output = instant_remote_process($commands, $this->server);
|
$this->backup_output = instant_remote_process($commands, $this->server);
|
||||||
$this->backup_output = trim($this->backup_output);
|
$this->backup_output = trim($this->backup_output);
|
||||||
@@ -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
|
private function upload_to_s3(): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -517,10 +507,27 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$this->ensureHelperImageAvailable();
|
$this->ensureHelperImageAvailable();
|
||||||
|
|
||||||
$fullImageName = $this->getFullImageName();
|
$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}/";
|
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
|
||||||
instant_remote_process($commands, $this->server);
|
instant_remote_process($commands, $this->server);
|
||||||
|
|
||||||
$this->add_to_backup_output('Uploaded to S3.');
|
$this->add_to_backup_output('Uploaded to S3.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->add_to_backup_output($e->getMessage());
|
$this->add_to_backup_output($e->getMessage());
|
||||||
@@ -562,7 +569,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
private function getFullImageName(): string
|
private function getFullImageName(): string
|
||||||
{
|
{
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$helperImage = config('coolify.helper_image');
|
$helperImage = config('coolify.helper_image');
|
||||||
$latestVersion = $settings->helper_version;
|
$latestVersion = $settings->helper_version;
|
||||||
|
|
||||||
|
@@ -31,10 +31,10 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource,
|
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource,
|
||||||
public bool $deleteConfigurations,
|
public bool $deleteConfigurations = true,
|
||||||
public bool $deleteVolumes,
|
public bool $deleteVolumes = true,
|
||||||
public bool $dockerCleanup,
|
public bool $dockerCleanup = true,
|
||||||
public bool $deleteConnectedNetworks
|
public bool $deleteConnectedNetworks = true
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Jobs;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
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');
|
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
|
||||||
if ($response->successful()) {
|
if ($response->successful()) {
|
||||||
$versions = $response->json();
|
$versions = $response->json();
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$latest_version = data_get($versions, 'coolify.helper.version');
|
$latest_version = data_get($versions, 'coolify.helper.version');
|
||||||
$current_version = $settings->helper_version;
|
$current_version = $settings->helper_version;
|
||||||
if (version_compare($latest_version, $current_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;
|
namespace App\Jobs;
|
||||||
|
|
||||||
use App\Actions\Server\UpdateCoolify;
|
use App\Actions\Server\UpdateCoolify;
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||||
@@ -23,7 +22,7 @@ class UpdateCoolifyJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
CheckForUpdatesJob::dispatchSync();
|
CheckForUpdatesJob::dispatchSync();
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if (! $settings->new_version_available) {
|
if (! $settings->new_version_available) {
|
||||||
Log::info('No new version available. Skipping update.');
|
Log::info('No new version available. Skipping update.');
|
||||||
|
|
||||||
|
@@ -30,7 +30,7 @@ class Dashboard extends Component
|
|||||||
|
|
||||||
public function cleanup_queue()
|
public function cleanup_queue()
|
||||||
{
|
{
|
||||||
Artisan::queue('cleanup:application-deployment-queue', [
|
Artisan::queue('cleanup:deployment-queue', [
|
||||||
'--team-id' => currentTeam()->id,
|
'--team-id' => currentTeam()->id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@@ -47,7 +47,7 @@ class Help extends Component
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
$mail->subject("[HELP]: {$this->subject}");
|
$mail->subject("[HELP]: {$this->subject}");
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$type = set_transanctional_email_settings($settings);
|
$type = set_transanctional_email_settings($settings);
|
||||||
if (! $type) {
|
if (! $type) {
|
||||||
$url = 'https://app.coolify.io/api/feedback';
|
$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');
|
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->dispatch('success', 'Feedback sent.', 'We will get in touch with you as soon as possible.');
|
||||||
|
$this->reset('description', 'subject');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
@@ -172,7 +172,7 @@ class Email extends Component
|
|||||||
|
|
||||||
public function copyFromInstanceSettings()
|
public function copyFromInstanceSettings()
|
||||||
{
|
{
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if ($settings->smtp_enabled) {
|
if ($settings->smtp_enabled) {
|
||||||
$team = currentTeam();
|
$team = currentTeam();
|
||||||
$team->update([
|
$team->update([
|
||||||
|
@@ -31,6 +31,7 @@ class BackupEdit extends Component
|
|||||||
'backup.save_s3' => 'required|boolean',
|
'backup.save_s3' => 'required|boolean',
|
||||||
'backup.s3_storage_id' => 'nullable|integer',
|
'backup.s3_storage_id' => 'nullable|integer',
|
||||||
'backup.databases_to_backup' => 'nullable',
|
'backup.databases_to_backup' => 'nullable',
|
||||||
|
'backup.dump_all' => 'required|boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
@@ -40,6 +41,7 @@ class BackupEdit extends Component
|
|||||||
'backup.save_s3' => 'Save to S3',
|
'backup.save_s3' => 'Save to S3',
|
||||||
'backup.s3_storage_id' => 'S3 Storage',
|
'backup.s3_storage_id' => 'S3 Storage',
|
||||||
'backup.databases_to_backup' => 'Databases to Backup',
|
'backup.databases_to_backup' => 'Databases to Backup',
|
||||||
|
'backup.dump_all' => 'Backup All Databases',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $messages = [
|
protected $messages = [
|
||||||
|
@@ -26,7 +26,7 @@ class ScheduledBackups extends Component
|
|||||||
public function mount(): void
|
public function mount(): void
|
||||||
{
|
{
|
||||||
if ($this->selectedBackupId) {
|
if ($this->selectedBackupId) {
|
||||||
$this->setSelectedBackup($this->selectedBackupId);
|
$this->setSelectedBackup($this->selectedBackupId, true);
|
||||||
}
|
}
|
||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
if ($this->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
if ($this->database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
@@ -37,10 +37,13 @@ class ScheduledBackups extends Component
|
|||||||
$this->s3s = currentTeam()->s3s;
|
$this->s3s = currentTeam()->s3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setSelectedBackup($backupId)
|
public function setSelectedBackup($backupId, $force = false)
|
||||||
{
|
{
|
||||||
|
if ($this->selectedBackupId === $backupId && ! $force) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
$this->selectedBackupId = $backupId;
|
$this->selectedBackupId = $backupId;
|
||||||
$this->selectedBackup = $this->database->scheduledBackups->find($this->selectedBackupId);
|
$this->selectedBackup = $this->database->scheduledBackups->find($backupId);
|
||||||
if (is_null($this->selectedBackup)) {
|
if (is_null($this->selectedBackup)) {
|
||||||
$this->selectedBackupId = null;
|
$this->selectedBackupId = null;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -108,8 +108,23 @@ class Navbar extends Component
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
StopService::run(service: $this->service, dockerCleanup: false);
|
||||||
|
$this->service->parse();
|
||||||
|
$this->dispatch('imagePulled');
|
||||||
|
$activity = StartService::run($this->service);
|
||||||
|
$this->dispatch('activityMonitor', $activity->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pullAndRestartEvent()
|
||||||
|
{
|
||||||
|
$this->checkDeployments();
|
||||||
|
if ($this->isDeploymentProgress) {
|
||||||
|
$this->dispatch('error', 'There is a deployment in progress.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
PullImage::run($this->service);
|
PullImage::run($this->service);
|
||||||
StopService::run($this->service);
|
StopService::run(service: $this->service, dockerCleanup: false);
|
||||||
$this->service->parse();
|
$this->service->parse();
|
||||||
$this->dispatch('imagePulled');
|
$this->dispatch('imagePulled');
|
||||||
$activity = StartService::run($this->service);
|
$activity = StartService::run($this->service);
|
||||||
|
@@ -91,10 +91,12 @@ class Danger extends Component
|
|||||||
|
|
||||||
public function delete($password)
|
public function delete($password)
|
||||||
{
|
{
|
||||||
if (! Hash::check($password, Auth::user()->password)) {
|
if (isProduction()) {
|
||||||
$this->addError('password', 'The provided password is incorrect.');
|
if (! Hash::check($password, Auth::user()->password)) {
|
||||||
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $this->resource) {
|
if (! $this->resource) {
|
||||||
|
@@ -48,14 +48,6 @@ class Add extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
$this->validate();
|
$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', [
|
$this->dispatch('saveKey', [
|
||||||
'key' => $this->key,
|
'key' => $this->key,
|
||||||
'value' => $this->value,
|
'value' => $this->value,
|
||||||
|
@@ -53,30 +53,16 @@ class All extends Component
|
|||||||
|
|
||||||
public function sortEnvironmentVariables()
|
public function sortEnvironmentVariables()
|
||||||
{
|
{
|
||||||
if ($this->resource->type() === 'application') {
|
if (! data_get($this->resource, 'settings.is_env_sorting_enabled')) {
|
||||||
$this->resource->load(['environment_variables', 'environment_variables_preview']);
|
if ($this->resource->environment_variables) {
|
||||||
} else {
|
$this->resource->environment_variables = $this->resource->environment_variables->sortBy('order')->values();
|
||||||
$this->resource->load(['environment_variables']);
|
}
|
||||||
|
|
||||||
|
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();
|
$this->getDevView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +107,8 @@ class All extends Component
|
|||||||
$this->sortEnvironmentVariables();
|
$this->sortEnvironmentVariables();
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
} finally {
|
||||||
|
$this->refreshEnvs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,6 +11,8 @@ use Livewire\Component;
|
|||||||
|
|
||||||
class ExecuteContainerCommand extends Component
|
class ExecuteContainerCommand extends Component
|
||||||
{
|
{
|
||||||
|
public $selected_container = 'default';
|
||||||
|
|
||||||
public $container;
|
public $container;
|
||||||
|
|
||||||
public Collection $containers;
|
public Collection $containers;
|
||||||
@@ -83,11 +85,14 @@ class ExecuteContainerCommand extends Component
|
|||||||
$containers = getCurrentApplicationContainerStatus($server, $this->resource->id, includePullrequests: true);
|
$containers = getCurrentApplicationContainerStatus($server, $this->resource->id, includePullrequests: true);
|
||||||
}
|
}
|
||||||
foreach ($containers as $container) {
|
foreach ($containers as $container) {
|
||||||
$payload = [
|
// if container state is running
|
||||||
'server' => $server,
|
if (data_get($container, 'State') === 'running') {
|
||||||
'container' => $container,
|
$payload = [
|
||||||
];
|
'server' => $server,
|
||||||
$this->containers = $this->containers->push($payload);
|
'container' => $container,
|
||||||
|
];
|
||||||
|
$this->containers = $this->containers->push($payload);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} elseif (data_get($this->parameters, 'database_uuid')) {
|
} elseif (data_get($this->parameters, 'database_uuid')) {
|
||||||
if ($this->resource->isRunning()) {
|
if ($this->resource->isRunning()) {
|
||||||
@@ -100,7 +105,6 @@ class ExecuteContainerCommand extends Component
|
|||||||
}
|
}
|
||||||
} elseif (data_get($this->parameters, 'service_uuid')) {
|
} elseif (data_get($this->parameters, 'service_uuid')) {
|
||||||
$this->resource->applications()->get()->each(function ($application) {
|
$this->resource->applications()->get()->each(function ($application) {
|
||||||
ray($application);
|
|
||||||
if ($application->isRunning()) {
|
if ($application->isRunning()) {
|
||||||
$this->containers->push([
|
$this->containers->push([
|
||||||
'server' => $this->resource->server,
|
'server' => $this->resource->server,
|
||||||
@@ -131,9 +135,14 @@ class ExecuteContainerCommand extends Component
|
|||||||
#[On('connectToContainer')]
|
#[On('connectToContainer')]
|
||||||
public function connectToContainer()
|
public function connectToContainer()
|
||||||
{
|
{
|
||||||
|
if ($this->selected_container === 'default') {
|
||||||
|
$this->dispatch('error', 'Please select a container.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
$container_name = data_get($this->container, 'container.Names');
|
$container = collect($this->containers)->firstWhere('container.Names', $this->selected_container);
|
||||||
if (is_null($container_name)) {
|
if (is_null($container)) {
|
||||||
throw new \RuntimeException('Container not found.');
|
throw new \RuntimeException('Container not found.');
|
||||||
}
|
}
|
||||||
$server = data_get($this->container, 'server');
|
$server = data_get($this->container, 'server');
|
||||||
@@ -141,11 +150,11 @@ class ExecuteContainerCommand extends Component
|
|||||||
if ($server->isForceDisabled()) {
|
if ($server->isForceDisabled()) {
|
||||||
throw new \RuntimeException('Server is disabled.');
|
throw new \RuntimeException('Server is disabled.');
|
||||||
}
|
}
|
||||||
|
$this->dispatch(
|
||||||
$this->dispatch('send-terminal-command',
|
'send-terminal-command',
|
||||||
true,
|
isset($container),
|
||||||
$container_name,
|
data_get($container, 'container.Names'),
|
||||||
$server->uuid,
|
data_get($container, 'server.uuid')
|
||||||
);
|
);
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
@@ -34,9 +34,9 @@ class Terminal extends Component
|
|||||||
if ($status !== 'running') {
|
if ($status !== 'running') {
|
||||||
return;
|
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 {
|
} 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
|
// ssh command is sent back to frontend then to websocket
|
||||||
|
@@ -15,6 +15,8 @@ class ApiTokens extends Component
|
|||||||
|
|
||||||
public bool $readOnly = true;
|
public bool $readOnly = true;
|
||||||
|
|
||||||
|
public bool $rootAccess = false;
|
||||||
|
|
||||||
public array $permissions = ['read-only'];
|
public array $permissions = ['read-only'];
|
||||||
|
|
||||||
public $isApiEnabled;
|
public $isApiEnabled;
|
||||||
@@ -35,12 +37,11 @@ class ApiTokens extends Component
|
|||||||
if ($this->viewSensitiveData) {
|
if ($this->viewSensitiveData) {
|
||||||
$this->permissions[] = 'view:sensitive';
|
$this->permissions[] = 'view:sensitive';
|
||||||
$this->permissions = array_diff($this->permissions, ['*']);
|
$this->permissions = array_diff($this->permissions, ['*']);
|
||||||
|
$this->rootAccess = false;
|
||||||
} else {
|
} else {
|
||||||
$this->permissions = array_diff($this->permissions, ['view:sensitive']);
|
$this->permissions = array_diff($this->permissions, ['view:sensitive']);
|
||||||
}
|
}
|
||||||
if (count($this->permissions) == 0) {
|
$this->makeSureOneIsSelected();
|
||||||
$this->permissions = ['*'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updatedReadOnly()
|
public function updatedReadOnly()
|
||||||
@@ -48,11 +49,30 @@ class ApiTokens extends Component
|
|||||||
if ($this->readOnly) {
|
if ($this->readOnly) {
|
||||||
$this->permissions[] = 'read-only';
|
$this->permissions[] = 'read-only';
|
||||||
$this->permissions = array_diff($this->permissions, ['*']);
|
$this->permissions = array_diff($this->permissions, ['*']);
|
||||||
|
$this->rootAccess = false;
|
||||||
} else {
|
} else {
|
||||||
$this->permissions = array_diff($this->permissions, ['read-only']);
|
$this->permissions = array_diff($this->permissions, ['read-only']);
|
||||||
}
|
}
|
||||||
if (count($this->permissions) == 0) {
|
$this->makeSureOneIsSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatedRootAccess()
|
||||||
|
{
|
||||||
|
if ($this->rootAccess) {
|
||||||
$this->permissions = ['*'];
|
$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([
|
$this->validate([
|
||||||
'description' => 'required|min:3|max:255',
|
'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);
|
$token = auth()->user()->createToken($this->description, $this->permissions);
|
||||||
$this->tokens = auth()->user()->tokens;
|
$this->tokens = auth()->user()->tokens;
|
||||||
session()->flash('token', $token->plainTextToken);
|
session()->flash('token', $token->plainTextToken);
|
||||||
|
@@ -30,6 +30,11 @@ class ConfigureCloudflareTunnels extends Component
|
|||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
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();
|
$server = Server::ownedByCurrentTeam()->where('id', $this->server_id)->firstOrFail();
|
||||||
ConfigureCloudflared::dispatch($server, $this->cloudflare_token);
|
ConfigureCloudflared::dispatch($server, $this->cloudflare_token);
|
||||||
$server->settings->is_cloudflare_tunnel = true;
|
$server->settings->is_cloudflare_tunnel = true;
|
||||||
|
@@ -4,7 +4,7 @@ namespace App\Livewire\Server\Proxy;
|
|||||||
|
|
||||||
use App\Actions\Docker\GetContainersStatus;
|
use App\Actions\Docker\GetContainersStatus;
|
||||||
use App\Actions\Proxy\CheckProxy;
|
use App\Actions\Proxy\CheckProxy;
|
||||||
use App\Jobs\ContainerStatusJob;
|
use App\Actions\Proxy\StartProxy;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -44,7 +44,10 @@ class Status extends Component
|
|||||||
}
|
}
|
||||||
$this->numberOfPolls++;
|
$this->numberOfPolls++;
|
||||||
}
|
}
|
||||||
CheckProxy::run($this->server, true);
|
$shouldStart = CheckProxy::run($this->server, true);
|
||||||
|
if ($shouldStart) {
|
||||||
|
StartProxy::run($this->server, false);
|
||||||
|
}
|
||||||
$this->dispatch('proxyStatusUpdated');
|
$this->dispatch('proxyStatusUpdated');
|
||||||
if ($this->server->proxy->status === 'running') {
|
if ($this->server->proxy->status === 'running') {
|
||||||
$this->polling = false;
|
$this->polling = false;
|
||||||
|
@@ -60,7 +60,7 @@ class Index extends Component
|
|||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
if (isInstanceAdmin()) {
|
if (isInstanceAdmin()) {
|
||||||
$this->settings = InstanceSettings::get();
|
$this->settings = instanceSettings();
|
||||||
$this->do_not_track = $this->settings->do_not_track;
|
$this->do_not_track = $this->settings->do_not_track;
|
||||||
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
|
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
|
||||||
$this->is_registration_enabled = $this->settings->is_registration_enabled;
|
$this->is_registration_enabled = $this->settings->is_registration_enabled;
|
||||||
@@ -162,7 +162,7 @@ class Index extends Component
|
|||||||
{
|
{
|
||||||
CheckForUpdatesJob::dispatchSync();
|
CheckForUpdatesJob::dispatchSync();
|
||||||
$this->dispatch('updateAvailable');
|
$this->dispatch('updateAvailable');
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if ($settings->new_version_available) {
|
if ($settings->new_version_available) {
|
||||||
$this->dispatch('success', 'New version available!');
|
$this->dispatch('success', 'New version available!');
|
||||||
} else {
|
} else {
|
||||||
|
@@ -29,7 +29,7 @@ class License extends Component
|
|||||||
abort(404);
|
abort(404);
|
||||||
}
|
}
|
||||||
$this->instance_id = config('app.id');
|
$this->instance_id = config('app.id');
|
||||||
$this->settings = \App\Models\InstanceSettings::get();
|
$this->settings = instanceSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
@@ -42,7 +42,7 @@ class SettingsBackup extends Component
|
|||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
if (isInstanceAdmin()) {
|
if (isInstanceAdmin()) {
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$this->database = StandalonePostgresql::whereName('coolify-db')->first();
|
$this->database = StandalonePostgresql::whereName('coolify-db')->first();
|
||||||
$s3s = S3Storage::whereTeamId(0)->get() ?? [];
|
$s3s = S3Storage::whereTeamId(0)->get() ?? [];
|
||||||
if ($this->database) {
|
if ($this->database) {
|
||||||
|
@@ -43,7 +43,7 @@ class SettingsEmail extends Component
|
|||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
if (isInstanceAdmin()) {
|
if (isInstanceAdmin()) {
|
||||||
$this->settings = InstanceSettings::get();
|
$this->settings = instanceSettings();
|
||||||
$this->emails = auth()->user()->email;
|
$this->emails = auth()->user()->email;
|
||||||
} else {
|
} else {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
|
@@ -99,7 +99,7 @@ class Change extends Component
|
|||||||
return redirect()->route('source.all');
|
return redirect()->route('source.all');
|
||||||
}
|
}
|
||||||
$this->applications = $this->github_app->applications;
|
$this->applications = $this->github_app->applications;
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||||
|
|
||||||
$this->name = str($this->github_app->name)->kebab();
|
$this->name = str($this->github_app->name)->kebab();
|
||||||
|
@@ -43,15 +43,17 @@ class Create extends Component
|
|||||||
'endpoint' => 'Endpoint',
|
'endpoint' => 'Endpoint',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mount()
|
public function updatedEndpoint($value)
|
||||||
{
|
{
|
||||||
if (isDev()) {
|
if (! str($value)->startsWith('https://') && ! str($value)->startsWith('http://')) {
|
||||||
$this->name = 'Local MinIO';
|
$this->endpoint = 'https://'.$value;
|
||||||
$this->description = 'Local MinIO';
|
$value = $this->endpoint;
|
||||||
$this->key = 'minioadmin';
|
}
|
||||||
$this->secret = 'minioadmin';
|
|
||||||
$this->bucket = 'local';
|
if (str($value)->contains('your-objectstorage.com') && ! isset($this->bucket)) {
|
||||||
$this->endpoint = 'http://coolify-minio:9000';
|
$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()) {
|
if (data_get(currentTeam(), 'subscription') && isSubscriptionActive()) {
|
||||||
return redirect()->route('subscription.show');
|
return redirect()->route('subscription.show');
|
||||||
}
|
}
|
||||||
$this->settings = \App\Models\InstanceSettings::get();
|
$this->settings = instanceSettings();
|
||||||
$this->alreadySubscribed = currentTeam()->subscription()->exists();
|
$this->alreadySubscribed = currentTeam()->subscription()->exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -104,7 +104,7 @@ class Application extends BaseModel
|
|||||||
{
|
{
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
|
|
||||||
private static $parserVersion = '3';
|
private static $parserVersion = '4';
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
@@ -143,6 +143,9 @@ class Application extends BaseModel
|
|||||||
}
|
}
|
||||||
$application->tags()->detach();
|
$application->tags()->detach();
|
||||||
$application->previews()->delete();
|
$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'),
|
'application_uuid' => data_get($this, 'uuid'),
|
||||||
'task_uuid' => $task_uuid,
|
'task_uuid' => $task_uuid,
|
||||||
]);
|
]);
|
||||||
$settings = InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if (data_get($settings, 'fqdn')) {
|
if (data_get($settings, 'fqdn')) {
|
||||||
$url = Url::fromString($route);
|
$url = Url::fromString($route);
|
||||||
$url = $url->withPort(null);
|
$url = $url->withPort(null);
|
||||||
@@ -710,6 +713,11 @@ class Application extends BaseModel
|
|||||||
return $this->hasMany(ApplicationPreview::class);
|
return $this->hasMany(ApplicationPreview::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function deployment_queue()
|
||||||
|
{
|
||||||
|
return $this->hasMany(ApplicationDeploymentQueue::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function destination()
|
public function destination()
|
||||||
{
|
{
|
||||||
return $this->morphTo();
|
return $this->morphTo();
|
||||||
@@ -1150,7 +1158,7 @@ class Application extends BaseModel
|
|||||||
|
|
||||||
public function parse(int $pull_request_id = 0, ?int $preview_id = null)
|
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);
|
return newParser($this, $pull_request_id, $preview_id);
|
||||||
} elseif ($this->docker_compose_raw) {
|
} elseif ($this->docker_compose_raw) {
|
||||||
return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id);
|
return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id);
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
@@ -39,6 +40,20 @@ class ApplicationDeploymentQueue extends Model
|
|||||||
{
|
{
|
||||||
protected $guarded = [];
|
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)
|
public function setStatus(string $status)
|
||||||
{
|
{
|
||||||
$this->update([
|
$this->update([
|
||||||
|
@@ -126,11 +126,6 @@ class EnvironmentVariable extends Model
|
|||||||
$env = $this->get_real_environment_variables($this->value, $resource);
|
$env = $this->get_real_environment_variables($this->value, $resource);
|
||||||
|
|
||||||
return data_get($env, 'value', $env);
|
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}]";
|
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 $guarded = [];
|
||||||
|
|
||||||
|
protected $appends = ['default_environment'];
|
||||||
|
|
||||||
public static function ownedByCurrentTeam()
|
public static function ownedByCurrentTeam()
|
||||||
{
|
{
|
||||||
return Project::whereTeamId(currentTeam()->id)->orderBy('name');
|
return Project::whereTeamId(currentTeam()->id)->orderByRaw('LOWER(name)');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function booted()
|
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());
|
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();
|
$default = $this->environments()->where('name', 'production')->first();
|
||||||
if ($default) {
|
if ($default) {
|
||||||
|
@@ -40,6 +40,16 @@ class S3Storage extends BaseModel
|
|||||||
return "{$this->endpoint}/{$this->bucket}";
|
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)
|
public function testConnection(bool $shouldSave = false)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@@ -268,7 +268,7 @@ respond 404
|
|||||||
|
|
||||||
public function setupDynamicProxyConfiguration()
|
public function setupDynamicProxyConfiguration()
|
||||||
{
|
{
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$dynamic_config_path = $this->proxyPath().'/dynamic';
|
$dynamic_config_path = $this->proxyPath().'/dynamic';
|
||||||
if ($this->proxyType() === ProxyTypes::TRAEFIK->value) {
|
if ($this->proxyType() === ProxyTypes::TRAEFIK->value) {
|
||||||
$file = "$dynamic_config_path/coolify.yaml";
|
$file = "$dynamic_config_path/coolify.yaml";
|
||||||
@@ -448,11 +448,19 @@ $schema://$host {
|
|||||||
// Should move everything except /caddy and /nginx to /traefik
|
// Should move everything except /caddy and /nginx to /traefik
|
||||||
// The code needs to be modified as well, so maybe it does not worth it
|
// The code needs to be modified as well, so maybe it does not worth it
|
||||||
if ($proxyType === ProxyTypes::TRAEFIK->value) {
|
if ($proxyType === ProxyTypes::TRAEFIK->value) {
|
||||||
$proxy_path = $proxy_path;
|
// Do nothing
|
||||||
} elseif ($proxyType === ProxyTypes::CADDY->value) {
|
} 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) {
|
} 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;
|
return $proxy_path;
|
||||||
@@ -460,15 +468,6 @@ $schema://$host {
|
|||||||
|
|
||||||
public function proxyType()
|
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');
|
return data_get($this->proxy, 'type');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1212,4 +1211,18 @@ $schema://$host {
|
|||||||
|
|
||||||
return $this;
|
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;
|
use HasFactory, SoftDeletes;
|
||||||
|
|
||||||
private static $parserVersion = '3';
|
private static $parserVersion = '4';
|
||||||
|
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
||||||
@@ -285,6 +285,65 @@ class Service extends BaseModel
|
|||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
$image = str($application->image)->before(':')->value();
|
$image = str($application->image)->before(':')->value();
|
||||||
switch ($image) {
|
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'):
|
case str($image)?->contains('rabbitmq'):
|
||||||
$data = collect([]);
|
$data = collect([]);
|
||||||
$host_port = $this->environment_variables()->where('key', 'PORT')->first();
|
$host_port = $this->environment_variables()->where('key', 'PORT')->first();
|
||||||
@@ -770,6 +829,30 @@ class Service extends BaseModel
|
|||||||
}
|
}
|
||||||
$fields->put('Code Server', $data->toArray());
|
$fields->put('Code Server', $data->toArray());
|
||||||
break;
|
break;
|
||||||
|
case str($image)->contains('elestio/strapi'):
|
||||||
|
$data = collect([]);
|
||||||
|
$license = $this->environment_variables()->where('key', 'STRAPI_LICENSE')->first();
|
||||||
|
if ($license) {
|
||||||
|
$data = $data->merge([
|
||||||
|
'License' => [
|
||||||
|
'key' => data_get($license, 'key'),
|
||||||
|
'value' => data_get($license, 'value'),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$nodeEnv = $this->environment_variables()->where('key', 'NODE_ENV')->first();
|
||||||
|
if ($nodeEnv) {
|
||||||
|
$data = $data->merge([
|
||||||
|
'Node Environment' => [
|
||||||
|
'key' => data_get($nodeEnv, 'key'),
|
||||||
|
'value' => data_get($nodeEnv, 'value'),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields->put('Strapi', $data->toArray());
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$databases = $this->databases()->get();
|
$databases = $this->databases()->get();
|
||||||
@@ -1052,12 +1135,12 @@ class Service extends BaseModel
|
|||||||
public function environment_variables(): HasMany
|
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
|
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()
|
public function workdir()
|
||||||
@@ -1095,7 +1178,21 @@ class Service extends BaseModel
|
|||||||
return 3;
|
return 3;
|
||||||
});
|
});
|
||||||
foreach ($sorted as $env) {
|
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) {
|
if ($sorted->count() === 0) {
|
||||||
$commands[] = 'touch .env';
|
$commands[] = 'touch .env';
|
||||||
@@ -1105,7 +1202,7 @@ class Service extends BaseModel
|
|||||||
|
|
||||||
public function parse(bool $isNew = false): Collection
|
public function parse(bool $isNew = false): Collection
|
||||||
{
|
{
|
||||||
if ($this->compose_parsing_version === '3') {
|
if ((int) $this->compose_parsing_version >= 3) {
|
||||||
return newParser($this);
|
return newParser($this);
|
||||||
} elseif ($this->docker_compose_raw) {
|
} elseif ($this->docker_compose_raw) {
|
||||||
return parseDockerComposeFile($this, $isNew);
|
return parseDockerComposeFile($this, $isNew);
|
||||||
|
@@ -112,4 +112,9 @@ class ServiceApplication extends BaseModel
|
|||||||
{
|
{
|
||||||
getFilesystemVolumesFromServer($this, $isInit);
|
getFilesystemVolumesFromServer($this, $isInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isBackupSolutionAvailable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -115,4 +115,13 @@ class ServiceDatabase extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isBackupSolutionAvailable()
|
||||||
|
{
|
||||||
|
return str($this->databaseType())->contains('mysql') ||
|
||||||
|
str($this->databaseType())->contains('postgres') ||
|
||||||
|
str($this->databaseType())->contains('postgis') ||
|
||||||
|
str($this->databaseType())->contains('mariadb') ||
|
||||||
|
str($this->databaseType())->contains('mongodb');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -294,4 +294,9 @@ class StandaloneClickhouse extends BaseModel
|
|||||||
return $parsedCollection->toArray();
|
return $parsedCollection->toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isBackupSolutionAvailable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -294,4 +294,9 @@ class StandaloneDragonfly extends BaseModel
|
|||||||
return $parsedCollection->toArray();
|
return $parsedCollection->toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isBackupSolutionAvailable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -294,4 +294,9 @@ class StandaloneKeydb extends BaseModel
|
|||||||
return $parsedCollection->toArray();
|
return $parsedCollection->toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isBackupSolutionAvailable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -294,4 +294,9 @@ class StandaloneMariadb extends BaseModel
|
|||||||
return $parsedCollection->toArray();
|
return $parsedCollection->toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isBackupSolutionAvailable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -314,4 +314,9 @@ class StandaloneMongodb extends BaseModel
|
|||||||
return $parsedCollection->toArray();
|
return $parsedCollection->toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isBackupSolutionAvailable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -295,4 +295,9 @@ class StandaloneMysql extends BaseModel
|
|||||||
return $parsedCollection->toArray();
|
return $parsedCollection->toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isBackupSolutionAvailable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -296,4 +296,9 @@ class StandalonePostgresql extends BaseModel
|
|||||||
return $parsedCollection->toArray();
|
return $parsedCollection->toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isBackupSolutionAvailable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -290,4 +290,9 @@ class StandaloneRedis extends BaseModel
|
|||||||
return $parsedCollection->toArray();
|
return $parsedCollection->toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isBackupSolutionAvailable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,7 @@ class TransactionalEmailChannel
|
|||||||
{
|
{
|
||||||
public function send(User $notifiable, Notification $notification): void
|
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')) {
|
if (! data_get($settings, 'smtp_enabled') && ! data_get($settings, 'resend_enabled')) {
|
||||||
Log::info('SMTP/Resend not enabled');
|
Log::info('SMTP/Resend not enabled');
|
||||||
|
|
||||||
|
@@ -18,7 +18,7 @@ class ResetPassword extends Notification
|
|||||||
|
|
||||||
public function __construct($token)
|
public function __construct($token)
|
||||||
{
|
{
|
||||||
$this->settings = \App\Models\InstanceSettings::get();
|
$this->settings = instanceSettings();
|
||||||
$this->token = $token;
|
$this->token = $token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,10 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use App\Models\InstanceSettings;
|
|
||||||
use App\Models\PersonalAccessToken;
|
use App\Models\PersonalAccessToken;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\View;
|
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Laravel\Sanctum\Sanctum;
|
use Laravel\Sanctum\Sanctum;
|
||||||
|
|
||||||
@@ -30,9 +28,5 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
])->baseUrl($api_url);
|
])->baseUrl($api_url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// if (! env('CI')) {
|
|
||||||
// View::share('instanceSettings', InstanceSettings::get());
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -46,7 +46,7 @@ class FortifyServiceProvider extends ServiceProvider
|
|||||||
Fortify::registerView(function () {
|
Fortify::registerView(function () {
|
||||||
$isFirstUser = User::count() === 0;
|
$isFirstUser = User::count() === 0;
|
||||||
|
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if (! $settings->is_registration_enabled) {
|
if (! $settings->is_registration_enabled) {
|
||||||
return redirect()->route('login');
|
return redirect()->route('login');
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ class FortifyServiceProvider extends ServiceProvider
|
|||||||
});
|
});
|
||||||
|
|
||||||
Fortify::loginView(function () {
|
Fortify::loginView(function () {
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$enabled_oauth_providers = OauthSetting::where('enabled', true)->get();
|
$enabled_oauth_providers = OauthSetting::where('enabled', true)->get();
|
||||||
$users = User::count();
|
$users = User::count();
|
||||||
if ($users == 0) {
|
if ($users == 0) {
|
||||||
|
@@ -175,4 +175,5 @@ function removeUnnecessaryFieldsFromRequest(Request $request)
|
|||||||
$request->offsetUnset('instant_deploy');
|
$request->offsetUnset('instant_deploy');
|
||||||
$request->offsetUnset('github_app_uuid');
|
$request->offsetUnset('github_app_uuid');
|
||||||
$request->offsetUnset('private_key_uuid');
|
$request->offsetUnset('private_key_uuid');
|
||||||
|
$request->offsetUnset('use_build_server');
|
||||||
}
|
}
|
||||||
|
@@ -20,12 +20,16 @@ const RESTART_MODE = 'unless-stopped';
|
|||||||
const DATABASE_DOCKER_IMAGES = [
|
const DATABASE_DOCKER_IMAGES = [
|
||||||
'bitnami/mariadb',
|
'bitnami/mariadb',
|
||||||
'bitnami/mongodb',
|
'bitnami/mongodb',
|
||||||
'bitnami/mysql',
|
|
||||||
'bitnami/postgresql',
|
|
||||||
'bitnami/redis',
|
'bitnami/redis',
|
||||||
'mysql',
|
'mysql',
|
||||||
|
'bitnami/mysql',
|
||||||
|
'mysql/mysql-server',
|
||||||
'mariadb',
|
'mariadb',
|
||||||
|
'postgis/postgis',
|
||||||
'postgres',
|
'postgres',
|
||||||
|
'bitnami/postgresql',
|
||||||
|
'supabase/postgres',
|
||||||
|
'elestio/postgres',
|
||||||
'mongo',
|
'mongo',
|
||||||
'redis',
|
'redis',
|
||||||
'memcached',
|
'memcached',
|
||||||
@@ -33,7 +37,6 @@ const DATABASE_DOCKER_IMAGES = [
|
|||||||
'neo4j',
|
'neo4j',
|
||||||
'influxdb',
|
'influxdb',
|
||||||
'clickhouse/clickhouse-server',
|
'clickhouse/clickhouse-server',
|
||||||
'supabase/postgres',
|
|
||||||
];
|
];
|
||||||
const SPECIFIC_SERVICES = [
|
const SPECIFIC_SERVICES = [
|
||||||
'quay.io/minio/minio',
|
'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.gzip.compress=true');
|
||||||
$labels->push('traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https');
|
$labels->push('traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https');
|
||||||
|
|
||||||
$basic_auth = false;
|
$middlewares_from_labels = collect([]);
|
||||||
$basic_auth_middleware = null;
|
|
||||||
$redirect = false;
|
|
||||||
$redirect_middleware = null;
|
|
||||||
|
|
||||||
if ($serviceLabels) {
|
if ($serviceLabels) {
|
||||||
$basic_auth = $serviceLabels->contains(function ($value) {
|
$middlewares_from_labels = $serviceLabels->map(function ($item) {
|
||||||
return str_contains($value, 'basicauth');
|
if (preg_match('/traefik\.http\.middlewares\.(.*?)(\.|$)/', $item, $matches)) {
|
||||||
});
|
return $matches[1];
|
||||||
if ($basic_auth) {
|
}
|
||||||
$basic_auth_middleware = $serviceLabels
|
return null;
|
||||||
->map(function ($item) {
|
})->filter()
|
||||||
if (preg_match('/traefik\.http\.middlewares\.(.*?)\.basicauth\.users/', $item, $matches)) {
|
->unique();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
foreach ($domains as $loop => $domain) {
|
foreach ($domains as $loop => $domain) {
|
||||||
try {
|
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");
|
$labels->push("traefik.http.services.{$https_label}.loadbalancer.server.port=$port");
|
||||||
}
|
}
|
||||||
if ($path !== '/') {
|
if ($path !== '/') {
|
||||||
|
// Middleware handling
|
||||||
$middlewares = collect([]);
|
$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}");
|
$labels->push("traefik.http.middlewares.{$https_label}-stripprefix.stripprefix.prefixes={$path}");
|
||||||
$middlewares->push("{$https_label}-stripprefix");
|
$middlewares->push("{$https_label}-stripprefix");
|
||||||
}
|
}
|
||||||
if ($is_gzip_enabled) {
|
if ($is_gzip_enabled) {
|
||||||
$middlewares->push('gzip');
|
$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')) {
|
if (str($image)->contains('ghost')) {
|
||||||
$middlewares->push('redir-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);
|
$labels = $labels->merge($redirect_to_non_www);
|
||||||
$middlewares->push($to_non_www_name);
|
$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);
|
$labels = $labels->merge($redirect_to_www);
|
||||||
$middlewares->push($to_www_name);
|
$middlewares->push($to_www_name);
|
||||||
}
|
}
|
||||||
|
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
|
||||||
|
$middlewares->push($middleware_name);
|
||||||
|
});
|
||||||
if ($middlewares->isNotEmpty()) {
|
if ($middlewares->isNotEmpty()) {
|
||||||
$middlewares = $middlewares->join(',');
|
$middlewares = $middlewares->join(',');
|
||||||
$labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}");
|
$labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}");
|
||||||
@@ -437,13 +413,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
|||||||
$middlewares = collect([]);
|
$middlewares = collect([]);
|
||||||
if ($is_gzip_enabled) {
|
if ($is_gzip_enabled) {
|
||||||
$middlewares->push('gzip');
|
$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')) {
|
if (str($image)->contains('ghost')) {
|
||||||
$middlewares->push('redir-ghost');
|
$middlewares->push('redir-ghost');
|
||||||
}
|
}
|
||||||
@@ -455,6 +425,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
|||||||
$labels = $labels->merge($redirect_to_www);
|
$labels = $labels->merge($redirect_to_www);
|
||||||
$middlewares->push($to_www_name);
|
$middlewares->push($to_www_name);
|
||||||
}
|
}
|
||||||
|
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
|
||||||
|
$middlewares->push($middleware_name);
|
||||||
|
});
|
||||||
if ($middlewares->isNotEmpty()) {
|
if ($middlewares->isNotEmpty()) {
|
||||||
$middlewares = $middlewares->join(',');
|
$middlewares = $middlewares->join(',');
|
||||||
$labels->push("traefik.http.routers.{$https_label}.middlewares={$middlewares}");
|
$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) {
|
if ($is_gzip_enabled) {
|
||||||
$middlewares->push('gzip');
|
$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')) {
|
if (str($image)->contains('ghost')) {
|
||||||
$middlewares->push('redir-ghost');
|
$middlewares->push('redir-ghost');
|
||||||
}
|
}
|
||||||
@@ -507,6 +474,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
|||||||
$labels = $labels->merge($redirect_to_www);
|
$labels = $labels->merge($redirect_to_www);
|
||||||
$middlewares->push($to_www_name);
|
$middlewares->push($to_www_name);
|
||||||
}
|
}
|
||||||
|
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
|
||||||
|
$middlewares->push($middleware_name);
|
||||||
|
});
|
||||||
if ($middlewares->isNotEmpty()) {
|
if ($middlewares->isNotEmpty()) {
|
||||||
$middlewares = $middlewares->join(',');
|
$middlewares = $middlewares->join(',');
|
||||||
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
|
$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) {
|
if ($is_gzip_enabled) {
|
||||||
$middlewares->push('gzip');
|
$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')) {
|
if (str($image)->contains('ghost')) {
|
||||||
$middlewares->push('redir-ghost');
|
$middlewares->push('redir-ghost');
|
||||||
}
|
}
|
||||||
@@ -533,6 +497,9 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
|
|||||||
$labels = $labels->merge($redirect_to_www);
|
$labels = $labels->merge($redirect_to_www);
|
||||||
$middlewares->push($to_www_name);
|
$middlewares->push($to_www_name);
|
||||||
}
|
}
|
||||||
|
$middlewares_from_labels->each(function ($middleware_name) use ($middlewares) {
|
||||||
|
$middlewares->push($middleware_name);
|
||||||
|
});
|
||||||
if ($middlewares->isNotEmpty()) {
|
if ($middlewares->isNotEmpty()) {
|
||||||
$middlewares = $middlewares->join(',');
|
$middlewares = $middlewares->join(',');
|
||||||
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
|
$labels->push("traefik.http.routers.{$http_label}.middlewares={$middlewares}");
|
||||||
|
@@ -1,14 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Models\S3Storage;
|
use App\Models\S3Storage;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
function set_s3_target(S3Storage $s3)
|
function set_s3_target(S3Storage $s3)
|
||||||
{
|
{
|
||||||
$is_digital_ocean = false;
|
$is_digital_ocean = false;
|
||||||
if ($s3->endpoint) {
|
|
||||||
$is_digital_ocean = Str::contains($s3->endpoint, 'digitaloceanspaces.com');
|
|
||||||
}
|
|
||||||
config()->set('filesystems.disks.custom-s3', [
|
config()->set('filesystems.disks.custom-s3', [
|
||||||
'driver' => 's3',
|
'driver' => 's3',
|
||||||
'region' => $s3['region'],
|
'region' => $s3['region'],
|
||||||
@@ -17,7 +14,7 @@ function set_s3_target(S3Storage $s3)
|
|||||||
'bucket' => $s3['bucket'],
|
'bucket' => $s3['bucket'],
|
||||||
'endpoint' => $s3['endpoint'],
|
'endpoint' => $s3['endpoint'],
|
||||||
'use_path_style_endpoint' => true,
|
'use_path_style_endpoint' => true,
|
||||||
'bucket_endpoint' => $is_digital_ocean,
|
'bucket_endpoint' => $s3->isHetzner() || $s3->isDigitalOcean(),
|
||||||
'aws_url' => $s3->awsUrl(),
|
'aws_url' => $s3->awsUrl(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@@ -247,7 +247,7 @@ function is_transactional_emails_active(): bool
|
|||||||
function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string
|
function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string
|
||||||
{
|
{
|
||||||
if (! $settings) {
|
if (! $settings) {
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
}
|
}
|
||||||
config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
|
config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
|
||||||
config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
|
config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
|
||||||
@@ -281,7 +281,7 @@ function base_ip(): string
|
|||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
return 'localhost';
|
return 'localhost';
|
||||||
}
|
}
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if ($settings->public_ipv4) {
|
if ($settings->public_ipv4) {
|
||||||
return "$settings->public_ipv4";
|
return "$settings->public_ipv4";
|
||||||
}
|
}
|
||||||
@@ -309,7 +309,7 @@ function getFqdnWithoutPort(string $fqdn)
|
|||||||
*/
|
*/
|
||||||
function base_url(bool $withPort = true): string
|
function base_url(bool $withPort = true): string
|
||||||
{
|
{
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if ($settings->fqdn) {
|
if ($settings->fqdn) {
|
||||||
return $settings->fqdn;
|
return $settings->fqdn;
|
||||||
}
|
}
|
||||||
@@ -343,6 +343,11 @@ function isSubscribed()
|
|||||||
{
|
{
|
||||||
return isSubscriptionActive() || auth()->user()->isInstanceAdmin();
|
return isSubscriptionActive() || auth()->user()->isInstanceAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isProduction(): bool
|
||||||
|
{
|
||||||
|
return ! isDev();
|
||||||
|
}
|
||||||
function isDev(): bool
|
function isDev(): bool
|
||||||
{
|
{
|
||||||
return config('app.env') === 'local';
|
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
|
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);
|
$type = set_transanctional_email_settings($settings);
|
||||||
if (! $type) {
|
if (! $type) {
|
||||||
throw new Exception('No email settings found.');
|
throw new Exception('No email settings found.');
|
||||||
@@ -703,7 +708,9 @@ function getTopLevelNetworks(Service|Application $resource)
|
|||||||
return $value == $networkName || $key == $networkName;
|
return $value == $networkName || $key == $networkName;
|
||||||
});
|
});
|
||||||
if (! $networkExists) {
|
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;
|
return $value == $networkName || $key == $networkName;
|
||||||
});
|
});
|
||||||
if (! $networkExists) {
|
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;
|
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)
|
function parseEnvVariable(Str|string $value)
|
||||||
{
|
{
|
||||||
$value = str($value);
|
$value = str($value);
|
||||||
@@ -850,6 +884,7 @@ function parseEnvVariable(Str|string $value)
|
|||||||
} else {
|
} else {
|
||||||
// SERVICE_BASE64_64_UMAMI
|
// SERVICE_BASE64_64_UMAMI
|
||||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
$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')) {
|
if (str($host)->contains('sslip.io')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
$is_dns_validation_enabled = data_get($settings, 'is_dns_validation_enabled');
|
$is_dns_validation_enabled = data_get($settings, 'is_dns_validation_enabled');
|
||||||
if (! $is_dns_validation_enabled) {
|
if (! $is_dns_validation_enabled) {
|
||||||
return true;
|
return true;
|
||||||
@@ -1090,7 +1125,7 @@ function checkIfDomainIsAlreadyUsed(Collection|array $domains, ?string $teamId =
|
|||||||
if ($domainFound) {
|
if ($domainFound) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if (data_get($settings, 'fqdn')) {
|
if (data_get($settings, 'fqdn')) {
|
||||||
$domain = data_get($settings, 'fqdn');
|
$domain = data_get($settings, 'fqdn');
|
||||||
if (str($domain)->endsWith('/')) {
|
if (str($domain)->endsWith('/')) {
|
||||||
@@ -1162,7 +1197,7 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($resource) {
|
if ($resource) {
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if (data_get($settings, 'fqdn')) {
|
if (data_get($settings, 'fqdn')) {
|
||||||
$domain = data_get($settings, 'fqdn');
|
$domain = data_get($settings, 'fqdn');
|
||||||
if (str($domain)->endsWith('/')) {
|
if (str($domain)->endsWith('/')) {
|
||||||
@@ -1179,12 +1214,26 @@ function check_domain_usage(ServiceApplication|Application|null $resource = null
|
|||||||
function parseCommandsByLineForSudo(Collection $commands, Server $server): array
|
function parseCommandsByLineForSudo(Collection $commands, Server $server): array
|
||||||
{
|
{
|
||||||
$commands = $commands->map(function ($line) {
|
$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";
|
return "sudo $line";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (str(trim($line))->startsWith('if')) {
|
||||||
|
return str_replace('if', 'if sudo', $line);
|
||||||
|
}
|
||||||
|
|
||||||
return $line;
|
return $line;
|
||||||
});
|
});
|
||||||
|
|
||||||
$commands = $commands->map(function ($line) use ($server) {
|
$commands = $commands->map(function ($line) use ($server) {
|
||||||
if (Str::startsWith($line, 'sudo mkdir -p')) {
|
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');
|
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;
|
return $line;
|
||||||
});
|
});
|
||||||
|
|
||||||
$commands = $commands->map(function ($line) {
|
$commands = $commands->map(function ($line) {
|
||||||
$line = str($line);
|
$line = str($line);
|
||||||
if (str($line)->contains('$(')) {
|
if (str($line)->contains('$(')) {
|
||||||
@@ -1588,7 +1638,9 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||||||
return $value == $networkName || $key == $networkName;
|
return $value == $networkName || $key == $networkName;
|
||||||
});
|
});
|
||||||
if (! $networkExists) {
|
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;
|
return $value == $networkName || $key == $networkName;
|
||||||
});
|
});
|
||||||
if (! $networkExists) {
|
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';
|
$predefinedPort = '8000';
|
||||||
}
|
}
|
||||||
if ($isDatabase) {
|
if ($isDatabase) {
|
||||||
$savedService = ServiceDatabase::firstOrCreate([
|
$applicationFound = ServiceApplication::where('name', $serviceName)->where('image', $image)->where('service_id', $resource->id)->first();
|
||||||
'name' => $serviceName,
|
if ($applicationFound) {
|
||||||
'image' => $image,
|
$savedService = $applicationFound;
|
||||||
'service_id' => $resource->id,
|
$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 {
|
} else {
|
||||||
$savedService = ServiceApplication::firstOrCreate([
|
$savedService = ServiceApplication::firstOrCreate([
|
||||||
'name' => $serviceName,
|
'name' => $serviceName,
|
||||||
@@ -3078,7 +3143,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
foreach ($magicEnvironments as $key => $value) {
|
foreach ($magicEnvironments as $key => $value) {
|
||||||
$key = str($key);
|
$key = str($key);
|
||||||
$value = replaceVariables($value);
|
$value = replaceVariables($value);
|
||||||
$command = $key->after('SERVICE_')->before('_');
|
$command = parseCommandFromMagicEnvVariable($key);
|
||||||
$found = $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->first();
|
$found = $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->first();
|
||||||
if ($found) {
|
if ($found) {
|
||||||
continue;
|
continue;
|
||||||
@@ -3189,12 +3254,24 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
if ($serviceName === 'plausible') {
|
if ($serviceName === 'plausible') {
|
||||||
$predefinedPort = '8000';
|
$predefinedPort = '8000';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($isDatabase) {
|
if ($isDatabase) {
|
||||||
$savedService = ServiceDatabase::firstOrCreate([
|
$applicationFound = ServiceApplication::where('name', $serviceName)->where('image', $image)->where('service_id', $resource->id)->first();
|
||||||
'name' => $serviceName,
|
if ($applicationFound) {
|
||||||
'image' => $image,
|
$savedService = $applicationFound;
|
||||||
'service_id' => $resource->id,
|
$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 {
|
} else {
|
||||||
$savedService = ServiceApplication::firstOrCreate([
|
$savedService = ServiceApplication::firstOrCreate([
|
||||||
'name' => $serviceName,
|
'name' => $serviceName,
|
||||||
@@ -3264,7 +3341,15 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
} elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
} elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||||
$volume = $source->value().':'.$target->value();
|
$volume = $source->value().':'.$target->value();
|
||||||
} else {
|
} 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);
|
$source = replaceLocalSource($source, $mainDirectory);
|
||||||
if ($isApplication && $isPullRequest) {
|
if ($isApplication && $isPullRequest) {
|
||||||
$source = $source."-pr-$pullRequestId";
|
$source = $source."-pr-$pullRequestId";
|
||||||
@@ -3284,6 +3369,17 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
'resource_type' => get_class($originalResource),
|
'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";
|
$volume = "$source:$target";
|
||||||
}
|
}
|
||||||
} elseif ($type->value() === 'volume') {
|
} elseif ($type->value() === 'volume') {
|
||||||
@@ -3606,6 +3702,18 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
$serviceLabels = $labels->merge($defaultLabels);
|
$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 (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
|
||||||
if ($isApplication) {
|
if ($isApplication) {
|
||||||
$shouldGenerateLabelsExactly = $resource->destination->server->settings->generate_exact_labels;
|
$shouldGenerateLabelsExactly = $resource->destination->server->settings->generate_exact_labels;
|
||||||
@@ -3826,14 +3934,37 @@ function convertComposeEnvironmentToArray($environment)
|
|||||||
{
|
{
|
||||||
$convertedServiceVariables = collect([]);
|
$convertedServiceVariables = collect([]);
|
||||||
if (isAssociativeArray($environment)) {
|
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;
|
$convertedServiceVariables = $environment;
|
||||||
} else {
|
} else {
|
||||||
|
// Example: $environment = ['FOO=bar', 'BAZ=qux'];
|
||||||
foreach ($environment as $value) {
|
foreach ($environment as $value) {
|
||||||
$parts = explode('=', $value, 2);
|
if (is_string($value)) {
|
||||||
$key = $parts[0];
|
$parts = explode('=', $value, 2);
|
||||||
$realValue = $parts[1] ?? '';
|
$key = $parts[0];
|
||||||
if ($key) {
|
$realValue = $parts[1] ?? '';
|
||||||
$convertedServiceVariables->put($key, $realValue);
|
if ($key) {
|
||||||
|
$convertedServiceVariables->put($key, $realValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3841,3 +3972,7 @@ function convertComposeEnvironmentToArray($environment)
|
|||||||
return $convertedServiceVariables;
|
return $convertedServiceVariables;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
function instanceSettings()
|
||||||
|
{
|
||||||
|
return InstanceSettings::get();
|
||||||
|
}
|
||||||
|
@@ -48,6 +48,7 @@
|
|||||||
"zircote/swagger-php": "^4.10"
|
"zircote/swagger-php": "^4.10"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
"barryvdh/laravel-debugbar": "^3.13",
|
||||||
"fakerphp/faker": "^v1.21.0",
|
"fakerphp/faker": "^v1.21.0",
|
||||||
"laravel/dusk": "^v8.0",
|
"laravel/dusk": "^v8.0",
|
||||||
"laravel/pint": "^1.16",
|
"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",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "96f8146407d0e6e897ff097c5eccd3a4",
|
"content-hash": "42c28ab141b70fcabf75b51afa96c670",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "amphp/amp",
|
"name": "amphp/amp",
|
||||||
@@ -11823,6 +11823,90 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [
|
"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",
|
"name": "brianium/paratest",
|
||||||
"version": "v7.4.3",
|
"version": "v7.4.3",
|
||||||
@@ -12301,6 +12385,74 @@
|
|||||||
},
|
},
|
||||||
"time": "2024-09-03T15:00:28+00:00"
|
"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",
|
"name": "mockery/mockery",
|
||||||
"version": "1.6.12",
|
"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
|
// The release version of your application
|
||||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||||
'release' => '4.0.0-beta.348',
|
'release' => '4.0.0-beta.356',
|
||||||
// When left empty or `null` the Laravel environment will be used
|
// When left empty or `null` the Laravel environment will be used
|
||||||
'environment' => config('app.env'),
|
'environment' => config('app.env'),
|
||||||
|
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
<?php
|
<?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');
|
$table->schemalessAttributes('smtp');
|
||||||
});
|
});
|
||||||
|
|
||||||
$instance_setting = InstanceSettings::get();
|
$instance_setting = instanceSettings();
|
||||||
$instance_setting->smtp = [
|
$instance_setting->smtp = [
|
||||||
'enabled' => $instance_setting->smtp_enabled,
|
'enabled' => $instance_setting->smtp_enabled,
|
||||||
'from_address' => $instance_setting->smtp_from_address,
|
'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 = Process::run('curl -4s https://ifconfig.io')->output();
|
||||||
$ipv4 = trim($ipv4);
|
$ipv4 = trim($ipv4);
|
||||||
$ipv4 = filter_var($ipv4, FILTER_VALIDATE_IP);
|
$ipv4 = filter_var($ipv4, FILTER_VALIDATE_IP);
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if (is_null($settings->public_ipv4) && $ipv4) {
|
if (is_null($settings->public_ipv4) && $ipv4) {
|
||||||
$settings->update(['public_ipv4' => $ipv4]);
|
$settings->update(['public_ipv4' => $ipv4]);
|
||||||
}
|
}
|
||||||
$ipv6 = Process::run('curl -6s https://ifconfig.io')->output();
|
$ipv6 = Process::run('curl -6s https://ifconfig.io')->output();
|
||||||
$ipv6 = trim($ipv6);
|
$ipv6 = trim($ipv6);
|
||||||
$ipv6 = filter_var($ipv6, FILTER_VALIDATE_IP);
|
$ipv6 = filter_var($ipv6, FILTER_VALIDATE_IP);
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
$settings = instanceSettings();
|
||||||
if (is_null($settings->public_ipv6) && $ipv6) {
|
if (is_null($settings->public_ipv6) && $ipv6) {
|
||||||
$settings->update(['public_ipv6' => $ipv6]);
|
$settings->update(['public_ipv6' => $ipv6]);
|
||||||
}
|
}
|
||||||
|
@@ -58,6 +58,7 @@ services:
|
|||||||
SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID:-coolify}"
|
SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID:-coolify}"
|
||||||
SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY:-coolify}"
|
SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY:-coolify}"
|
||||||
SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}"
|
SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}"
|
||||||
|
entrypoint: ["/bin/sh", "/soketi-entrypoint.sh"]
|
||||||
vite:
|
vite:
|
||||||
image: node:20
|
image: node:20
|
||||||
pull_policy: always
|
pull_policy: always
|
||||||
|
@@ -113,7 +113,7 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
timeout: 2s
|
timeout: 2s
|
||||||
soketi:
|
soketi:
|
||||||
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.1'
|
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.3'
|
||||||
ports:
|
ports:
|
||||||
- "${SOKETI_PORT:-6001}:6001"
|
- "${SOKETI_PORT:-6001}:6001"
|
||||||
- "6002:6002"
|
- "6002:6002"
|
||||||
|
@@ -1,9 +1,27 @@
|
|||||||
FROM quay.io/soketi/soketi:1.6-16-alpine
|
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
|
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 ./
|
COPY docker/coolify-realtime/package.json ./
|
||||||
RUN npm i
|
RUN npm i
|
||||||
RUN npm rebuild node-pty --update-binary
|
RUN npm rebuild node-pty --update-binary
|
||||||
COPY docker/coolify-realtime/soketi-entrypoint.sh /soketi-entrypoint.sh
|
COPY docker/coolify-realtime/soketi-entrypoint.sh /soketi-entrypoint.sh
|
||||||
COPY docker/coolify-realtime/terminal-server.js /terminal/terminal-server.js
|
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"]
|
ENTRYPOINT ["/bin/sh", "/soketi-entrypoint.sh"]
|
||||||
|
@@ -1,11 +1,19 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Function to timestamp logs
|
# Function to timestamp logs
|
||||||
|
|
||||||
|
# Check if the first argument is 'watch'
|
||||||
|
if [ "$1" = "watch" ]; then
|
||||||
|
WATCH_MODE="--watch"
|
||||||
|
else
|
||||||
|
WATCH_MODE=""
|
||||||
|
fi
|
||||||
|
|
||||||
timestamp() {
|
timestamp() {
|
||||||
date "+%Y-%m-%d %H:%M:%S"
|
date "+%Y-%m-%d %H:%M:%S"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Start the terminal server in the background with logging
|
# 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=$!
|
TERMINAL_PID=$!
|
||||||
|
|
||||||
# Start the Soketi process in the background with logging
|
# 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 };
|
const userSession = { ws, userId, ptyProcess: null, isActive: false };
|
||||||
userSessions.set(userId, userSession);
|
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('error', (err) => handleError(err, userId));
|
||||||
ws.on('close', () => handleClose(userId));
|
ws.on('close', () => handleClose(userId));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const messageHandlers = {
|
const messageHandlers = {
|
||||||
@@ -108,7 +112,6 @@ function parseMessage(message) {
|
|||||||
|
|
||||||
async function handleCommand(ws, command, userId) {
|
async function handleCommand(ws, command, userId) {
|
||||||
const userSession = userSessions.get(userId);
|
const userSession = userSessions.get(userId);
|
||||||
|
|
||||||
if (userSession && userSession.isActive) {
|
if (userSession && userSession.isActive) {
|
||||||
const result = await killPtyProcess(userId);
|
const result = await killPtyProcess(userId);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
@@ -127,27 +130,29 @@ async function handleCommand(ws, command, userId) {
|
|||||||
cols: 80,
|
cols: 80,
|
||||||
rows: 30,
|
rows: 30,
|
||||||
cwd: process.env.HOME,
|
cwd: process.env.HOME,
|
||||||
|
env: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE: - Initiates a process within the Terminal container
|
// NOTE: - Initiates a process within the Terminal container
|
||||||
// Establishes an SSH connection to root@coolify with RequestTTY enabled
|
// Establishes an SSH connection to root@coolify with RequestTTY enabled
|
||||||
// Executes the 'docker exec' command to connect to a specific container
|
// 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([hereDocContent]), options);
|
||||||
const ptyProcess = pty.spawn('ssh', sshArgs.concat(['bash']), options);
|
|
||||||
userSession.ptyProcess = ptyProcess;
|
userSession.ptyProcess = ptyProcess;
|
||||||
userSession.isActive = true;
|
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');
|
ws.send('pty-ready');
|
||||||
|
|
||||||
ptyProcess.onData((data) => ws.send(data));
|
ptyProcess.onData((data) => {
|
||||||
|
ws.send(data);
|
||||||
|
});
|
||||||
|
|
||||||
// when parent closes
|
// when parent closes
|
||||||
ptyProcess.onExit(({ exitCode, signal }) => {
|
ptyProcess.onExit(({ exitCode, signal }) => {
|
||||||
console.error(`Process exited with code ${exitCode} and signal ${signal}`);
|
console.error(`Process exited with code ${exitCode} and signal ${signal}`);
|
||||||
|
ws.send('pty-exited');
|
||||||
userSession.isActive = false;
|
userSession.isActive = false;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (timeout) {
|
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
|
// 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
|
// 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(() => {
|
setTimeout(() => {
|
||||||
if (!session.isActive || !session.ptyProcess) {
|
if (!session.isActive || !session.ptyProcess) {
|
||||||
@@ -230,5 +235,5 @@ function extractHereDocContent(commandString) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
server.listen(6002, () => {
|
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:
|
watch_paths:
|
||||||
type: string
|
type: string
|
||||||
description: 'The watch paths.'
|
description: 'The watch paths.'
|
||||||
|
use_build_server:
|
||||||
|
type: boolean
|
||||||
|
nullable: true
|
||||||
|
description: 'Use build server.'
|
||||||
type: object
|
type: object
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
@@ -457,6 +461,10 @@ paths:
|
|||||||
watch_paths:
|
watch_paths:
|
||||||
type: string
|
type: string
|
||||||
description: 'The watch paths.'
|
description: 'The watch paths.'
|
||||||
|
use_build_server:
|
||||||
|
type: boolean
|
||||||
|
nullable: true
|
||||||
|
description: 'Use build server.'
|
||||||
type: object
|
type: object
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
@@ -678,6 +686,10 @@ paths:
|
|||||||
watch_paths:
|
watch_paths:
|
||||||
type: string
|
type: string
|
||||||
description: 'The watch paths.'
|
description: 'The watch paths.'
|
||||||
|
use_build_server:
|
||||||
|
type: boolean
|
||||||
|
nullable: true
|
||||||
|
description: 'Use build server.'
|
||||||
type: object
|
type: object
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
@@ -850,6 +862,10 @@ paths:
|
|||||||
instant_deploy:
|
instant_deploy:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: 'The flag to indicate if the application should be deployed instantly.'
|
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
|
type: object
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
@@ -1013,6 +1029,10 @@ paths:
|
|||||||
instant_deploy:
|
instant_deploy:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: 'The flag to indicate if the application should be deployed instantly.'
|
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
|
type: object
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
@@ -1067,6 +1087,10 @@ paths:
|
|||||||
instant_deploy:
|
instant_deploy:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: 'The flag to indicate if the application should be deployed instantly.'
|
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
|
type: object
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
@@ -1126,9 +1150,33 @@ paths:
|
|||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
-
|
-
|
||||||
name: cleanup
|
name: delete_configurations
|
||||||
in: query
|
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
|
required: false
|
||||||
schema:
|
schema:
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -1351,6 +1399,10 @@ paths:
|
|||||||
watch_paths:
|
watch_paths:
|
||||||
type: string
|
type: string
|
||||||
description: 'The watch paths.'
|
description: 'The watch paths.'
|
||||||
|
use_build_server:
|
||||||
|
type: boolean
|
||||||
|
nullable: true
|
||||||
|
description: 'Use build server.'
|
||||||
type: object
|
type: object
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
@@ -1738,6 +1790,52 @@ paths:
|
|||||||
security:
|
security:
|
||||||
-
|
-
|
||||||
bearerAuth: []
|
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:
|
/databases:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
@@ -1809,9 +1907,33 @@ paths:
|
|||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
-
|
-
|
||||||
name: cleanup
|
name: delete_configurations
|
||||||
in: query
|
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
|
required: false
|
||||||
schema:
|
schema:
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -3812,6 +3934,38 @@ paths:
|
|||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
type: string
|
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:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: 'Delete a service by UUID'
|
description: 'Delete a service by UUID'
|
||||||
@@ -4769,6 +4923,10 @@ components:
|
|||||||
type: boolean
|
type: boolean
|
||||||
swarm_cluster:
|
swarm_cluster:
|
||||||
type: string
|
type: string
|
||||||
|
delete_unused_volumes:
|
||||||
|
type: boolean
|
||||||
|
delete_unused_networks:
|
||||||
|
type: boolean
|
||||||
type: object
|
type: object
|
||||||
ServerSetting:
|
ServerSetting:
|
||||||
description: 'Server Settings model'
|
description: 'Server Settings model'
|
||||||
|
@@ -46,6 +46,9 @@ services:
|
|||||||
- PUSHER_APP_ID
|
- PUSHER_APP_ID
|
||||||
- PUSHER_APP_KEY
|
- PUSHER_APP_KEY
|
||||||
- PUSHER_APP_SECRET
|
- PUSHER_APP_SECRET
|
||||||
|
- TERMINAL_PROTOCOL
|
||||||
|
- TERMINAL_HOST
|
||||||
|
- TERMINAL_PORT
|
||||||
- AUTOUPDATE
|
- AUTOUPDATE
|
||||||
- SELF_HOSTED
|
- SELF_HOSTED
|
||||||
- SSH_MUX_ENABLED
|
- SSH_MUX_ENABLED
|
||||||
@@ -110,7 +113,7 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
timeout: 2s
|
timeout: 2s
|
||||||
soketi:
|
soketi:
|
||||||
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.1'
|
image: 'ghcr.io/coollabsio/coolify-realtime:1.0.3'
|
||||||
ports:
|
ports:
|
||||||
- "${SOKETI_PORT:-6001}:6001"
|
- "${SOKETI_PORT:-6001}:6001"
|
||||||
- "6002:6002"
|
- "6002:6002"
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
{
|
{
|
||||||
"coolify": {
|
"coolify": {
|
||||||
"v4": {
|
"v4": {
|
||||||
"version": "4.0.0-beta.347"
|
"version": "4.0.0-beta.354"
|
||||||
},
|
},
|
||||||
"nightly": {
|
"nightly": {
|
||||||
"version": "4.0.0-beta.348"
|
"version": "4.0.0-beta.355"
|
||||||
},
|
},
|
||||||
"helper": {
|
"helper": {
|
||||||
"version": "1.0.1"
|
"version": "1.0.2"
|
||||||
},
|
},
|
||||||
"realtime": {
|
"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