diff --git a/.env.development.example b/.env.development.example
index 920c32d92..ba0213f58 100644
--- a/.env.development.example
+++ b/.env.development.example
@@ -1,16 +1,34 @@
-APP_NAME=Coolify-localhost
-APP_ID=development
+# Coolify Configuration
APP_ENV=local
+APP_NAME="Coolify Development"
+APP_ID=development
APP_KEY=
-APP_DEBUG=true
APP_URL=http://localhost
APP_PORT=8000
+APP_DEBUG=true
MUX_ENABLED=false
+# Enable Laravel Telescope for debugging
+TELESCOPE_ENABLED=false
+
+# Selenium Driver URL for Dusk
DUSK_DRIVER_URL=http://selenium:4444
-## For Andras only
-# To purge cache
+# PostgreSQL Database Configuration
+DB_DATABASE=coolify
+DB_USERNAME=coolify
+DB_PASSWORD=password
+DB_HOST=host.docker.internal
+DB_PORT=5432
+
+# Ray Configuration
+# Set to true to enable Ray
+RAY_ENABLED=false
+# Set custom ray port
+RAY_PORT=
+
+# Special Keys for Andras
+# For cache purging
BUNNY_API_KEY=
-# To upload assets
+# For asset uploads
BUNNY_STORAGE_API_KEY=
diff --git a/.env.production b/.env.production
index f15a8b0e9..099ec7c25 100644
--- a/.env.production
+++ b/.env.production
@@ -1,10 +1,16 @@
+# Coolify Configuration
APP_ID=
APP_NAME=Coolify
APP_KEY=
+# PostgreSQL Database Configuration
+DB_USERNAME=coolify
DB_PASSWORD=
+
+# Redis Configuration
REDIS_PASSWORD=
+# Pusher Configuration
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
diff --git a/.github/workflows/coolify-helper-next.yml b/.github/workflows/coolify-helper-next.yml
index d9921b363..3823e0707 100644
--- a/.github/workflows/coolify-helper-next.yml
+++ b/.github/workflows/coolify-helper-next.yml
@@ -25,6 +25,10 @@ jobs:
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.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
- name: Build image and push to registry
uses: docker/build-push-action@v5
with:
@@ -33,7 +37,7 @@ jobs:
file: docker/coolify-helper/Dockerfile
platforms: linux/amd64
push: true
- tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next
+ tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next
aarch64:
runs-on: [ self-hosted, arm64 ]
permissions:
@@ -47,6 +51,10 @@ jobs:
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.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
- name: Build image and push to registry
uses: docker/build-push-action@v5
with:
@@ -55,7 +63,7 @@ jobs:
file: docker/coolify-helper/Dockerfile
platforms: linux/aarch64
push: true
- tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next-aarch64
+ tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-next-aarch64
merge-manifest:
runs-on: ubuntu-latest
permissions:
@@ -75,9 +83,13 @@ jobs:
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.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
- name: Create & publish manifest
run: |
- docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next
+ 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 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:next
- uses: sarisia/actions-status-discord@v1
if: always()
with:
diff --git a/.github/workflows/coolify-helper.yml b/.github/workflows/coolify-helper.yml
index 7e8132ec6..37199919a 100644
--- a/.github/workflows/coolify-helper.yml
+++ b/.github/workflows/coolify-helper.yml
@@ -25,6 +25,10 @@ jobs:
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.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
- name: Build image and push to registry
uses: docker/build-push-action@v5
with:
@@ -33,7 +37,7 @@ jobs:
file: docker/coolify-helper/Dockerfile
platforms: linux/amd64
push: true
- tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
+ tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
aarch64:
runs-on: [ self-hosted, arm64 ]
permissions:
@@ -47,6 +51,10 @@ jobs:
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.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
- name: Build image and push to registry
uses: docker/build-push-action@v5
with:
@@ -55,7 +63,7 @@ jobs:
file: docker/coolify-helper/Dockerfile
platforms: linux/aarch64
push: true
- tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64
+ tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
merge-manifest:
runs-on: ubuntu-latest
permissions:
@@ -75,9 +83,13 @@ jobs:
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.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT
- name: Create & publish manifest
run: |
- docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
+ docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
- uses: sarisia/actions-status-discord@v1
if: always()
with:
diff --git a/.github/workflows/production-build.yml b/.github/workflows/production-build.yml
index e4bad6a65..c78c865bf 100644
--- a/.github/workflows/production-build.yml
+++ b/.github/workflows/production-build.yml
@@ -4,6 +4,8 @@ on:
push:
branches: ["main"]
paths-ignore:
+ - .github/workflows/coolify-helper.yml
+ - docker/coolify-helper/Dockerfile
- templates/service-templates.json
env:
diff --git a/README.md b/README.md
index d840739d7..c3412be14 100644
--- a/README.md
+++ b/README.md
@@ -35,22 +35,32 @@ Thank you so much!
Special thanks to our biggest sponsors!
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+### Special Sponsors
+
+
+
+* [CCCareers](https://cccareers.org/) - A career development platform connecting coding bootcamp graduates with job opportunities in the tech industry.
+* [Hetzner](http://htznr.li/CoolifyXHetzner) - A German web hosting company offering affordable dedicated servers, cloud services, and web hosting solutions.
+* [Logto](https://logto.io/?ref=coolify) - An open-source authentication and authorization solution for building secure login systems and managing user identities.
+* [BC Direct](https://bc.direct/?ref=coolify.io) - A digital marketing agency specializing in e-commerce solutions and online business growth strategies.
+* [QuantCDN](https://www.quantcdn.io/?ref=coolify.io) - A content delivery network (CDN) optimizing website performance through global content distribution.
+* [Arcjet](https://arcjet.com/?ref=coolify.io) - A cloud-based platform providing real-time protection against API abuse and bot attacks.
+* [SupaGuide](https://supa.guide/?ref=coolify.io) - A comprehensive resource hub offering guides and tutorials for web development using Supabase.
+* [Tigris](https://tigrisdata.com/?ref=coolify.io) - A fully managed serverless object storage service compatible with Amazon S3 API. Offers high performance, scalability, and built-in search capabilities for efficient data management.
+* [Fractal Networks](https://fractalnetworks.co/?ref=coolify.io) - A decentralized network infrastructure company focusing on secure and private communication solutions.
+* [Advin](https://coolify.ad.vin/?ref=coolify.io) - A digital advertising agency specializing in programmatic advertising and data-driven marketing strategies.
+* [Treive](https://trieve.ai/?ref=coolify.io) - An AI-powered search and discovery platform for enhancing information retrieval in large datasets.
+* [Blacksmith](https://blacksmith.sh/?ref=coolify.io) - A cloud-native platform for automating infrastructure provisioning and management across multiple cloud providers.
+* [Latitude](https://latitude.sh/?ref=coolify.io) - A cloud computing platform offering bare metal servers and cloud instances for developers and businesses.
+* [Brand Dev](https://brand.dev/?ref=coolify.io) - A web development agency specializing in creating custom digital experiences and brand identities.
+* [Jobscollider](https://jobscollider.com/remote-jobs?ref=coolify.io) - A job search platform connecting professionals with remote work opportunities across various industries.
+* [Hostinger](https://hostinger.com?ref=coolify.io) - A web hosting provider offering affordable hosting solutions, domain registration, and website building tools.
+* [Glueops](https://www.glueops.dev/?ref=coolify.io) - A DevOps consulting company providing infrastructure automation and cloud optimization services.
+* [Ubicloud](https://ubicloud.com/?ref=coolify.io) - An open-source alternative to hyperscale cloud providers, offering high-performance cloud computing services.
+* [Juxtdigital](https://juxtdigital.dev/?ref=coolify.io) - A digital agency offering web development, design, and digital marketing services for businesses.
+* [Saasykit](https://saasykit.com/?ref=coolify.io) - A Laravel-based boilerplate providing essential components and features for building SaaS applications quickly.
+* [Massivegrid](https://massivegrid.com/?ref=coolify.io) - A cloud hosting provider offering scalable infrastructure solutions for businesses of all sizes.
+
## Github Sponsors ($40+)
@@ -77,6 +87,7 @@ Special thanks to our biggest sponsors!
+
## Organizations
diff --git a/app/Actions/Server/UpdateCoolify.php b/app/Actions/Server/UpdateCoolify.php
index 09d6471e8..c4af6bb21 100644
--- a/app/Actions/Server/UpdateCoolify.php
+++ b/app/Actions/Server/UpdateCoolify.php
@@ -4,8 +4,6 @@ namespace App\Actions\Server;
use App\Models\InstanceSettings;
use App\Models\Server;
-use Illuminate\Support\Facades\File;
-use Illuminate\Support\Facades\Http;
use Lorisleiva\Actions\Concerns\AsAction;
class UpdateCoolify
@@ -27,11 +25,6 @@ class UpdateCoolify
return;
}
CleanupDocker::dispatch($this->server)->onQueue('high');
- $response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
- if ($response->successful()) {
- $versions = $response->json();
- File::put(base_path('versions.json'), json_encode($versions, JSON_PRETTY_PRINT));
- }
$this->latestVersion = get_latest_version_of_coolify();
$this->currentVersion = config('version');
if (! $manual_update) {
@@ -62,10 +55,11 @@ class UpdateCoolify
return;
}
+ instant_remote_process(["docker pull -q ghcr.io/coollabsio/coolify:{$this->latestVersion}"], $this->server, false);
+
remote_process([
'curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh',
"bash /data/coolify/source/upgrade.sh $this->latestVersion",
], $this->server);
-
}
}
diff --git a/app/Console/Commands/CleanupStuckedResources.php b/app/Console/Commands/CleanupStuckedResources.php
index 0a544d2ff..68beb448a 100644
--- a/app/Console/Commands/CleanupStuckedResources.php
+++ b/app/Console/Commands/CleanupStuckedResources.php
@@ -4,6 +4,7 @@ namespace App\Console\Commands;
use App\Models\Application;
use App\Models\ApplicationPreview;
+use App\Models\ScheduledDatabaseBackup;
use App\Models\ScheduledTask;
use App\Models\Service;
use App\Models\ServiceApplication;
@@ -165,6 +166,18 @@ class CleanupStuckedResources extends Command
echo "Error in cleaning stuck scheduledtasks: {$e->getMessage()}\n";
}
+ try {
+ $scheduled_backups = ScheduledDatabaseBackup::all();
+ foreach ($scheduled_backups as $scheduled_backup) {
+ if (! $scheduled_backup->server()) {
+ echo "Deleting stuck scheduledbackup: {$scheduled_backup->name}\n";
+ $scheduled_backup->delete();
+ }
+ }
+ } catch (\Throwable $e) {
+ echo "Error in cleaning stuck scheduledbackups: {$e->getMessage()}\n";
+ }
+
// Cleanup any resources that are not attached to any environment or destination or server
try {
$applications = Application::all();
diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
index c2c787be4..96740ab24 100644
--- a/app/Console/Kernel.php
+++ b/app/Console/Kernel.php
@@ -6,7 +6,6 @@ use App\Jobs\CheckForUpdatesJob;
use App\Jobs\CleanupInstanceStuffsJob;
use App\Jobs\DatabaseBackupJob;
use App\Jobs\DockerCleanupJob;
-use App\Jobs\PullCoolifyImageJob;
use App\Jobs\PullHelperImageJob;
use App\Jobs\PullSentinelImageJob;
use App\Jobs\PullTemplatesFromCDN;
@@ -44,7 +43,6 @@ class Kernel extends ConsoleKernel
// Instance Jobs
$schedule->command('horizon:snapshot')->everyFiveMinutes();
$schedule->command('cleanup:unreachable-servers')->daily()->onOneServer();
- $schedule->job(new PullCoolifyImageJob)->cron($settings->update_check_frequency)->timezone($settings->instance_timezone)->onOneServer();
$schedule->job(new PullTemplatesFromCDN)->cron($settings->update_check_frequency)->timezone($settings->instance_timezone)->onOneServer();
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
$this->schedule_updates($schedule);
@@ -139,6 +137,10 @@ class Kernel extends ConsoleKernel
}
$server = $scheduled_backup->server();
+
+ if (! $server) {
+ continue;
+ }
$serverTimezone = $server->settings->server_timezone;
if (isset(VALID_CRON_STRINGS[$scheduled_backup->frequency])) {
@@ -181,6 +183,9 @@ class Kernel extends ConsoleKernel
}
$server = $scheduled_task->server();
+ if (! $server) {
+ continue;
+ }
$serverTimezone = $server->settings->server_timezone ?: config('app.timezone');
if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) {
diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php
index 413555194..81b173011 100644
--- a/app/Http/Controllers/Api/ApplicationsController.php
+++ b/app/Http/Controllers/Api/ApplicationsController.php
@@ -53,6 +53,7 @@ class ApplicationsController extends Controller
summary: 'List',
description: 'List all applications.',
path: '/applications',
+ operationId: 'list-applications',
security: [
['bearerAuth' => []],
],
@@ -101,6 +102,7 @@ class ApplicationsController extends Controller
summary: 'Create (Public)',
description: 'Create new application based on a public git repository.',
path: '/applications/public',
+ operationId: 'create-public-application',
security: [
['bearerAuth' => []],
],
@@ -202,6 +204,7 @@ class ApplicationsController extends Controller
summary: 'Create (Private - GH App)',
description: 'Create new application based on a private repository through a Github App.',
path: '/applications/private-github-app',
+ operationId: 'create-private-github-app-application',
security: [
['bearerAuth' => []],
],
@@ -303,6 +306,7 @@ class ApplicationsController extends Controller
summary: 'Create (Private - Deploy Key)',
description: 'Create new application based on a private repository through a Deploy Key.',
path: '/applications/private-deploy-key',
+ operationId: 'create-private-deploy-key-application',
security: [
['bearerAuth' => []],
],
@@ -404,6 +408,7 @@ class ApplicationsController extends Controller
summary: 'Create (Dockerfile)',
description: 'Create new application based on a simple Dockerfile.',
path: '/applications/dockerfile',
+ operationId: 'create-dockerfile-application',
security: [
['bearerAuth' => []],
],
@@ -490,6 +495,7 @@ class ApplicationsController extends Controller
summary: 'Create (Docker Image)',
description: 'Create new application based on a prebuilt docker image',
path: '/applications/dockerimage',
+ operationId: 'create-dockerimage-application',
security: [
['bearerAuth' => []],
],
@@ -573,6 +579,7 @@ class ApplicationsController extends Controller
summary: 'Create (Docker Compose)',
description: 'Create new application based on a docker-compose file.',
path: '/applications/dockercompose',
+ operationId: 'create-dockercompose-application',
security: [
['bearerAuth' => []],
],
@@ -1171,6 +1178,7 @@ class ApplicationsController extends Controller
summary: 'Get',
description: 'Get application by UUID.',
path: '/applications/{uuid}',
+ operationId: 'get-application-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -1235,6 +1243,7 @@ class ApplicationsController extends Controller
summary: 'Delete',
description: 'Delete application by UUID.',
path: '/applications/{uuid}',
+ operationId: 'delete-application-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -1321,6 +1330,7 @@ class ApplicationsController extends Controller
summary: 'Update',
description: 'Update application by UUID.',
path: '/applications/{uuid}',
+ operationId: 'update-application-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -1557,6 +1567,7 @@ class ApplicationsController extends Controller
summary: 'List Envs',
description: 'List all envs by application UUID.',
path: '/applications/{uuid}/envs',
+ operationId: 'list-envs-by-application-uuid',
security: [
['bearerAuth' => []],
],
@@ -1639,6 +1650,7 @@ class ApplicationsController extends Controller
summary: 'Update Env',
description: 'Update env by application UUID.',
path: '/applications/{uuid}/envs',
+ operationId: 'update-env-by-application-uuid',
security: [
['bearerAuth' => []],
],
@@ -1821,6 +1833,7 @@ class ApplicationsController extends Controller
summary: 'Update Envs (Bulk)',
description: 'Update multiple envs by application UUID.',
path: '/applications/{uuid}/envs/bulk',
+ operationId: 'update-envs-by-application-uuid',
security: [
['bearerAuth' => []],
],
@@ -2012,6 +2025,7 @@ class ApplicationsController extends Controller
summary: 'Create Env',
description: 'Create env by application UUID.',
path: '/applications/{uuid}/envs',
+ operationId: 'create-env-by-application-uuid',
security: [
['bearerAuth' => []],
],
@@ -2171,6 +2185,7 @@ class ApplicationsController extends Controller
summary: 'Delete Env',
description: 'Delete env by UUID.',
path: '/applications/{uuid}/envs/{env_uuid}',
+ operationId: 'delete-env-by-application-uuid',
security: [
['bearerAuth' => []],
],
@@ -2256,6 +2271,7 @@ class ApplicationsController extends Controller
summary: 'Start',
description: 'Start application. `Post` request is also accepted.',
path: '/applications/{uuid}/start',
+ operationId: 'start-application-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -2359,6 +2375,7 @@ class ApplicationsController extends Controller
summary: 'Stop',
description: 'Stop application. `Post` request is also accepted.',
path: '/applications/{uuid}/stop',
+ operationId: 'stop-application-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -2431,6 +2448,7 @@ class ApplicationsController extends Controller
summary: 'Restart',
description: 'Restart application. `Post` request is also accepted.',
path: '/applications/{uuid}/restart',
+ operationId: 'restart-application-by-uuid',
security: [
['bearerAuth' => []],
],
diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php
index 0f1ee00d8..a205704cc 100644
--- a/app/Http/Controllers/Api/DatabasesController.php
+++ b/app/Http/Controllers/Api/DatabasesController.php
@@ -46,6 +46,7 @@ class DatabasesController extends Controller
summary: 'List',
description: 'List all databases.',
path: '/databases',
+ operationId: 'list-databases',
security: [
['bearerAuth' => []],
],
@@ -91,6 +92,7 @@ class DatabasesController extends Controller
summary: 'Get',
description: 'Get database by UUID.',
path: '/databases/{uuid}',
+ operationId: 'get-database-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -151,6 +153,7 @@ class DatabasesController extends Controller
summary: 'Update',
description: 'Update database by UUID.',
path: '/databases/{uuid}',
+ operationId: 'update-database-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -510,6 +513,7 @@ class DatabasesController extends Controller
summary: 'Create (PostgreSQL)',
description: 'Create a new PostgreSQL database.',
path: '/databases/postgresql',
+ operationId: 'create-database-postgresql',
security: [
['bearerAuth' => []],
],
@@ -575,6 +579,7 @@ class DatabasesController extends Controller
summary: 'Create (Clickhouse)',
description: 'Create a new Clickhouse database.',
path: '/databases/clickhouse',
+ operationId: 'create-database-clickhouse',
security: [
['bearerAuth' => []],
],
@@ -636,6 +641,7 @@ class DatabasesController extends Controller
summary: 'Create (DragonFly)',
description: 'Create a new DragonFly database.',
path: '/databases/dragonfly',
+ operationId: 'create-database-dragonfly',
security: [
['bearerAuth' => []],
],
@@ -696,6 +702,7 @@ class DatabasesController extends Controller
summary: 'Create (Redis)',
description: 'Create a new Redis database.',
path: '/databases/redis',
+ operationId: 'create-database-redis',
security: [
['bearerAuth' => []],
],
@@ -757,6 +764,7 @@ class DatabasesController extends Controller
summary: 'Create (KeyDB)',
description: 'Create a new KeyDB database.',
path: '/databases/keydb',
+ operationId: 'create-database-keydb',
security: [
['bearerAuth' => []],
],
@@ -818,6 +826,7 @@ class DatabasesController extends Controller
summary: 'Create (MariaDB)',
description: 'Create a new MariaDB database.',
path: '/databases/mariadb',
+ operationId: 'create-database-mariadb',
security: [
['bearerAuth' => []],
],
@@ -882,6 +891,7 @@ class DatabasesController extends Controller
summary: 'Create (MySQL)',
description: 'Create a new MySQL database.',
path: '/databases/mysql',
+ operationId: 'create-database-mysql',
security: [
['bearerAuth' => []],
],
@@ -945,6 +955,7 @@ class DatabasesController extends Controller
summary: 'Create (MongoDB)',
description: 'Create a new MongoDB database.',
path: '/databases/mongodb',
+ operationId: 'create-database-mongodb',
security: [
['bearerAuth' => []],
],
@@ -1514,6 +1525,7 @@ class DatabasesController extends Controller
summary: 'Delete',
description: 'Delete database by UUID.',
path: '/databases/{uuid}',
+ operationId: 'delete-database-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -1597,6 +1609,7 @@ class DatabasesController extends Controller
summary: 'Start',
description: 'Start database. `Post` request is also accepted.',
path: '/databases/{uuid}/start',
+ operationId: 'start-database-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -1672,6 +1685,7 @@ class DatabasesController extends Controller
summary: 'Stop',
description: 'Stop database. `Post` request is also accepted.',
path: '/databases/{uuid}/stop',
+ operationId: 'stop-database-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -1747,6 +1761,7 @@ class DatabasesController extends Controller
summary: 'Restart',
description: 'Restart database. `Post` request is also accepted.',
path: '/databases/{uuid}/restart',
+ operationId: 'restart-database-by-uuid',
security: [
['bearerAuth' => []],
],
diff --git a/app/Http/Controllers/Api/DeployController.php b/app/Http/Controllers/Api/DeployController.php
index 437162058..96f98d844 100644
--- a/app/Http/Controllers/Api/DeployController.php
+++ b/app/Http/Controllers/Api/DeployController.php
@@ -32,6 +32,7 @@ class DeployController extends Controller
summary: 'List',
description: 'List currently running deployments',
path: '/deployments',
+ operationId: 'list-deployments',
security: [
['bearerAuth' => []],
],
@@ -79,6 +80,7 @@ class DeployController extends Controller
summary: 'Get',
description: 'Get deployment by UUID.',
path: '/deployments/{uuid}',
+ operationId: 'get-deployment-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -134,6 +136,7 @@ class DeployController extends Controller
summary: 'Deploy',
description: 'Deploy by tag or uuid. `Post` request also accepted.',
path: '/deploy',
+ operationId: 'deploy-by-tag-or-uuid',
security: [
['bearerAuth' => []],
],
diff --git a/app/Http/Controllers/Api/OtherController.php b/app/Http/Controllers/Api/OtherController.php
index 1e48ffdbe..c085b88a5 100644
--- a/app/Http/Controllers/Api/OtherController.php
+++ b/app/Http/Controllers/Api/OtherController.php
@@ -13,6 +13,7 @@ class OtherController extends Controller
summary: 'Version',
description: 'Get Coolify version.',
path: '/version',
+ operationId: 'version',
security: [
['bearerAuth' => []],
],
@@ -43,6 +44,7 @@ class OtherController extends Controller
summary: 'Enable API',
description: 'Enable API (only with root permissions).',
path: '/enable',
+ operationId: 'enable-api',
security: [
['bearerAuth' => []],
],
@@ -94,6 +96,7 @@ class OtherController extends Controller
summary: 'Disable API',
description: 'Disable API (only with root permissions).',
path: '/disable',
+ operationId: 'disable-api',
security: [
['bearerAuth' => []],
],
@@ -158,6 +161,7 @@ class OtherController extends Controller
summary: 'Healthcheck',
description: 'Healthcheck endpoint.',
path: '/healthcheck',
+ operationId: 'healthcheck',
responses: [
new OA\Response(
response: 200,
diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php
index 6aec31e9b..75721ff54 100644
--- a/app/Http/Controllers/Api/ProjectController.php
+++ b/app/Http/Controllers/Api/ProjectController.php
@@ -13,6 +13,7 @@ class ProjectController extends Controller
summary: 'List',
description: 'list projects.',
path: '/projects',
+ operationId: 'list-projects',
security: [
['bearerAuth' => []],
],
@@ -56,6 +57,7 @@ class ProjectController extends Controller
summary: 'Get',
description: 'Get project by Uuid.',
path: '/projects/{uuid}',
+ operationId: 'get-project-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -102,6 +104,7 @@ class ProjectController extends Controller
summary: 'Environment',
description: 'Get environment by name.',
path: '/projects/{uuid}/{environment_name}',
+ operationId: 'get-environment-by-name',
security: [
['bearerAuth' => []],
],
@@ -142,6 +145,9 @@ class ProjectController extends Controller
return response()->json(['message' => 'Environment name is required.'], 422);
}
$project = Project::whereTeamId($teamId)->whereUuid($request->uuid)->first();
+ if (! $project) {
+ return response()->json(['message' => 'Project not found.'], 404);
+ }
$environment = $project->environments()->whereName($request->environment_name)->first();
if (! $environment) {
return response()->json(['message' => 'Environment not found.'], 404);
@@ -155,6 +161,7 @@ class ProjectController extends Controller
summary: 'Create',
description: 'Create Project.',
path: '/projects',
+ operationId: 'create-project',
security: [
['bearerAuth' => []],
],
@@ -167,7 +174,7 @@ class ProjectController extends Controller
schema: new OA\Schema(
type: 'object',
properties: [
- 'uuid' => ['type' => 'string', 'description' => 'The name of the project.'],
+ 'name' => ['type' => 'string', 'description' => 'The name of the project.'],
'description' => ['type' => 'string', 'description' => 'The description of the project.'],
],
),
@@ -250,6 +257,7 @@ class ProjectController extends Controller
summary: 'Update',
description: 'Update Project.',
path: '/projects/{uuid}',
+ operationId: 'update-project-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -355,6 +363,7 @@ class ProjectController extends Controller
summary: 'Delete',
description: 'Delete project by UUID.',
path: '/projects/{uuid}',
+ operationId: 'delete-project-by-uuid',
security: [
['bearerAuth' => []],
],
diff --git a/app/Http/Controllers/Api/ResourcesController.php b/app/Http/Controllers/Api/ResourcesController.php
index ae076bb71..1fd5792e0 100644
--- a/app/Http/Controllers/Api/ResourcesController.php
+++ b/app/Http/Controllers/Api/ResourcesController.php
@@ -13,6 +13,7 @@ class ResourcesController extends Controller
summary: 'List',
description: 'Get all resources.',
path: '/resources',
+ operationId: 'list-resources',
security: [
['bearerAuth' => []],
],
diff --git a/app/Http/Controllers/Api/SecurityController.php b/app/Http/Controllers/Api/SecurityController.php
index 67128234e..3a489f647 100644
--- a/app/Http/Controllers/Api/SecurityController.php
+++ b/app/Http/Controllers/Api/SecurityController.php
@@ -26,6 +26,7 @@ class SecurityController extends Controller
summary: 'List',
description: 'List all private keys.',
path: '/security/keys',
+ operationId: 'list-private-keys',
security: [
['bearerAuth' => []],
],
@@ -68,6 +69,7 @@ class SecurityController extends Controller
summary: 'Get',
description: 'Get key by UUID.',
path: '/security/keys/{uuid}',
+ operationId: 'get-private-key-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -124,6 +126,7 @@ class SecurityController extends Controller
summary: 'Create',
description: 'Create a new private key.',
path: '/security/keys',
+ operationId: 'create-private-key',
security: [
['bearerAuth' => []],
],
@@ -217,6 +220,7 @@ class SecurityController extends Controller
summary: 'Update',
description: 'Update a private key.',
path: '/security/keys',
+ operationId: 'update-private-key',
security: [
['bearerAuth' => []],
],
@@ -313,6 +317,7 @@ class SecurityController extends Controller
summary: 'Delete',
description: 'Delete a private key.',
path: '/security/keys/{uuid}',
+ operationId: 'delete-private-key-by-uuid',
security: [
['bearerAuth' => []],
],
diff --git a/app/Http/Controllers/Api/ServersController.php b/app/Http/Controllers/Api/ServersController.php
index 9044c4a35..e2bde52f7 100644
--- a/app/Http/Controllers/Api/ServersController.php
+++ b/app/Http/Controllers/Api/ServersController.php
@@ -46,6 +46,7 @@ class ServersController extends Controller
summary: 'List',
description: 'List all servers.',
path: '/servers',
+ operationId: 'list-servers',
security: [
['bearerAuth' => []],
],
@@ -100,6 +101,7 @@ class ServersController extends Controller
summary: 'Get',
description: 'Get server by UUID.',
path: '/servers/{uuid}',
+ operationId: 'get-server-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -177,6 +179,7 @@ class ServersController extends Controller
summary: 'Resources',
description: 'Get resources by server.',
path: '/servers/{uuid}/resources',
+ operationId: 'get-resources-by-server-uuid',
security: [
['bearerAuth' => []],
],
@@ -254,6 +257,7 @@ class ServersController extends Controller
summary: 'Domains',
description: 'Get domains by server.',
path: '/servers/{uuid}/domains',
+ operationId: 'get-domains-by-server-uuid',
security: [
['bearerAuth' => []],
],
@@ -401,6 +405,7 @@ class ServersController extends Controller
summary: 'Create',
description: 'Create Server.',
path: '/servers',
+ operationId: 'create-server',
security: [
['bearerAuth' => []],
],
@@ -545,6 +550,7 @@ class ServersController extends Controller
summary: 'Update',
description: 'Update Server.',
path: '/servers/{uuid}',
+ operationId: 'update-server-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -655,6 +661,7 @@ class ServersController extends Controller
summary: 'Delete',
description: 'Delete server by UUID.',
path: '/servers/{uuid}',
+ operationId: 'delete-server-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -727,6 +734,7 @@ class ServersController extends Controller
summary: 'Validate',
description: 'Validate server by UUID.',
path: '/servers/{uuid}/validate',
+ operationId: 'validate-server-by-uuid',
security: [
['bearerAuth' => []],
],
diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php
index 3a5b59f55..37377a6bd 100644
--- a/app/Http/Controllers/Api/ServicesController.php
+++ b/app/Http/Controllers/Api/ServicesController.php
@@ -38,6 +38,7 @@ class ServicesController extends Controller
summary: 'List',
description: 'List all services.',
path: '/services',
+ operationId: 'list-services',
security: [
['bearerAuth' => []],
],
@@ -88,6 +89,7 @@ class ServicesController extends Controller
summary: 'Create',
description: 'Create a one-click service',
path: '/services',
+ operationId: 'create-service',
security: [
['bearerAuth' => []],
],
@@ -365,6 +367,7 @@ class ServicesController extends Controller
summary: 'Get',
description: 'Get service by UUID.',
path: '/services/{uuid}',
+ operationId: 'get-service-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -422,6 +425,7 @@ class ServicesController extends Controller
summary: 'Delete',
description: 'Delete service by UUID.',
path: '/services/{uuid}',
+ operationId: 'delete-service-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -479,10 +483,521 @@ class ServicesController extends Controller
]);
}
+ #[OA\Get(
+ summary: 'List Envs',
+ description: 'List all envs by service UUID.',
+ path: '/services/{uuid}/envs',
+ operationId: 'list-envs-by-service-uuid',
+ security: [
+ ['bearerAuth' => []],
+ ],
+ tags: ['Services'],
+ parameters: [
+ new OA\Parameter(
+ name: 'uuid',
+ in: 'path',
+ description: 'UUID of the service.',
+ required: true,
+ schema: new OA\Schema(
+ type: 'string',
+ format: 'uuid',
+ )
+ ),
+ ],
+ responses: [
+ new OA\Response(
+ response: 200,
+ description: 'All environment variables by service UUID.',
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'array',
+ items: new OA\Items(ref: '#/components/schemas/EnvironmentVariable')
+ )
+ ),
+ ]),
+ new OA\Response(
+ response: 401,
+ ref: '#/components/responses/401',
+ ),
+ new OA\Response(
+ response: 400,
+ ref: '#/components/responses/400',
+ ),
+ new OA\Response(
+ response: 404,
+ ref: '#/components/responses/404',
+ ),
+ ]
+ )]
+ public function envs(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+ $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
+ if (! $service) {
+ return response()->json(['message' => 'Service not found.'], 404);
+ }
+
+ $envs = $service->environment_variables->map(function ($env) {
+ $env->makeHidden([
+ 'application_id',
+ 'standalone_clickhouse_id',
+ 'standalone_dragonfly_id',
+ 'standalone_keydb_id',
+ 'standalone_mariadb_id',
+ 'standalone_mongodb_id',
+ 'standalone_mysql_id',
+ 'standalone_postgresql_id',
+ 'standalone_redis_id',
+ ]);
+ $env = $this->removeSensitiveData($env);
+
+ return $env;
+ });
+
+ return response()->json($envs);
+ }
+
+ #[OA\Patch(
+ summary: 'Update Env',
+ description: 'Update env by service UUID.',
+ path: '/services/{uuid}/envs',
+ operationId: 'update-env-by-service-uuid',
+ security: [
+ ['bearerAuth' => []],
+ ],
+ tags: ['Services'],
+ parameters: [
+ new OA\Parameter(
+ name: 'uuid',
+ in: 'path',
+ description: 'UUID of the service.',
+ required: true,
+ schema: new OA\Schema(
+ type: 'string',
+ format: 'uuid',
+ )
+ ),
+ ],
+ requestBody: new OA\RequestBody(
+ description: 'Env updated.',
+ required: true,
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ required: ['key', 'value'],
+ properties: [
+ 'key' => ['type' => 'string', 'description' => 'The key of the environment variable.'],
+ 'value' => ['type' => 'string', 'description' => 'The value of the environment variable.'],
+ 'is_preview' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is used in preview deployments.'],
+ 'is_build_time' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is used in build time.'],
+ 'is_literal' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is a literal, nothing espaced.'],
+ 'is_multiline' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is multiline.'],
+ 'is_shown_once' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable\'s value is shown on the UI.'],
+ ],
+ ),
+ ),
+ ],
+ ),
+ responses: [
+ new OA\Response(
+ response: 201,
+ description: 'Environment variable updated.',
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'message' => ['type' => 'string', 'example' => 'Environment variable updated.'],
+ ]
+ )
+ ),
+ ]),
+ new OA\Response(
+ response: 401,
+ ref: '#/components/responses/401',
+ ),
+ new OA\Response(
+ response: 400,
+ ref: '#/components/responses/400',
+ ),
+ new OA\Response(
+ response: 404,
+ ref: '#/components/responses/404',
+ ),
+ ]
+ )]
+ public function update_env_by_uuid(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+
+ $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
+ if (! $service) {
+ return response()->json(['message' => 'Service not found.'], 404);
+ }
+
+ $validator = customApiValidator($request->all(), [
+ 'key' => 'string|required',
+ 'value' => 'string|nullable',
+ 'is_build_time' => 'boolean',
+ 'is_literal' => 'boolean',
+ 'is_multiline' => 'boolean',
+ 'is_shown_once' => 'boolean',
+ ]);
+
+ if ($validator->fails()) {
+ return response()->json([
+ 'message' => 'Validation failed.',
+ 'errors' => $validator->errors(),
+ ], 422);
+ }
+
+ $env = $service->environment_variables()->where('key', $request->key)->first();
+ if (! $env) {
+ return response()->json(['message' => 'Environment variable not found.'], 404);
+ }
+
+ $env->fill($request->all());
+ $env->save();
+
+ return response()->json($this->removeSensitiveData($env))->setStatusCode(201);
+ }
+
+ #[OA\Patch(
+ summary: 'Update Envs (Bulk)',
+ description: 'Update multiple envs by service UUID.',
+ path: '/services/{uuid}/envs/bulk',
+ operationId: 'update-envs-by-service-uuid',
+ security: [
+ ['bearerAuth' => []],
+ ],
+ tags: ['Services'],
+ parameters: [
+ new OA\Parameter(
+ name: 'uuid',
+ in: 'path',
+ description: 'UUID of the service.',
+ required: true,
+ schema: new OA\Schema(
+ type: 'string',
+ format: 'uuid',
+ )
+ ),
+ ],
+ requestBody: new OA\RequestBody(
+ description: 'Bulk envs updated.',
+ required: true,
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ required: ['data'],
+ properties: [
+ 'data' => [
+ 'type' => 'array',
+ 'items' => new OA\Schema(
+ type: 'object',
+ properties: [
+ 'key' => ['type' => 'string', 'description' => 'The key of the environment variable.'],
+ 'value' => ['type' => 'string', 'description' => 'The value of the environment variable.'],
+ 'is_preview' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is used in preview deployments.'],
+ 'is_build_time' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is used in build time.'],
+ 'is_literal' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is a literal, nothing espaced.'],
+ 'is_multiline' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is multiline.'],
+ 'is_shown_once' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable\'s value is shown on the UI.'],
+ ],
+ ),
+ ],
+ ],
+ ),
+ ),
+ ],
+ ),
+ responses: [
+ new OA\Response(
+ response: 201,
+ description: 'Environment variables updated.',
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'message' => ['type' => 'string', 'example' => 'Environment variables updated.'],
+ ]
+ )
+ ),
+ ]),
+ new OA\Response(
+ response: 401,
+ ref: '#/components/responses/401',
+ ),
+ new OA\Response(
+ response: 400,
+ ref: '#/components/responses/400',
+ ),
+ new OA\Response(
+ response: 404,
+ ref: '#/components/responses/404',
+ ),
+ ]
+ )]
+ public function create_bulk_envs(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+
+ $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
+ if (! $service) {
+ return response()->json(['message' => 'Service not found.'], 404);
+ }
+
+ $bulk_data = $request->get('data');
+ if (! $bulk_data) {
+ return response()->json(['message' => 'Bulk data is required.'], 400);
+ }
+
+ $updatedEnvs = collect();
+ foreach ($bulk_data as $item) {
+ $validator = customApiValidator($item, [
+ 'key' => 'string|required',
+ 'value' => 'string|nullable',
+ 'is_build_time' => 'boolean',
+ 'is_literal' => 'boolean',
+ 'is_multiline' => 'boolean',
+ 'is_shown_once' => 'boolean',
+ ]);
+
+ if ($validator->fails()) {
+ return response()->json([
+ 'message' => 'Validation failed.',
+ 'errors' => $validator->errors(),
+ ], 422);
+ }
+
+ $env = $service->environment_variables()->updateOrCreate(
+ ['key' => $item['key']],
+ $item
+ );
+
+ $updatedEnvs->push($this->removeSensitiveData($env));
+ }
+
+ return response()->json($updatedEnvs)->setStatusCode(201);
+ }
+
+ #[OA\Post(
+ summary: 'Create Env',
+ description: 'Create env by service UUID.',
+ path: '/services/{uuid}/envs',
+ operationId: 'create-env-by-service-uuid',
+ security: [
+ ['bearerAuth' => []],
+ ],
+ tags: ['Services'],
+ parameters: [
+ new OA\Parameter(
+ name: 'uuid',
+ in: 'path',
+ description: 'UUID of the service.',
+ required: true,
+ schema: new OA\Schema(
+ type: 'string',
+ format: 'uuid',
+ )
+ ),
+ ],
+ requestBody: new OA\RequestBody(
+ required: true,
+ description: 'Env created.',
+ content: new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'key' => ['type' => 'string', 'description' => 'The key of the environment variable.'],
+ 'value' => ['type' => 'string', 'description' => 'The value of the environment variable.'],
+ 'is_preview' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is used in preview deployments.'],
+ 'is_build_time' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is used in build time.'],
+ 'is_literal' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is a literal, nothing espaced.'],
+ 'is_multiline' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is multiline.'],
+ 'is_shown_once' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable\'s value is shown on the UI.'],
+ ],
+ ),
+ ),
+ ),
+ responses: [
+ new OA\Response(
+ response: 201,
+ description: 'Environment variable created.',
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'uuid' => ['type' => 'string', 'example' => 'nc0k04gk8g0cgsk440g0koko'],
+ ]
+ )
+ ),
+ ]),
+ new OA\Response(
+ response: 401,
+ ref: '#/components/responses/401',
+ ),
+ new OA\Response(
+ response: 400,
+ ref: '#/components/responses/400',
+ ),
+ new OA\Response(
+ response: 404,
+ ref: '#/components/responses/404',
+ ),
+ ]
+ )]
+ public function create_env(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+
+ $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
+ if (! $service) {
+ return response()->json(['message' => 'Service not found.'], 404);
+ }
+
+ $validator = customApiValidator($request->all(), [
+ 'key' => 'string|required',
+ 'value' => 'string|nullable',
+ 'is_build_time' => 'boolean',
+ 'is_literal' => 'boolean',
+ 'is_multiline' => 'boolean',
+ 'is_shown_once' => 'boolean',
+ ]);
+
+ if ($validator->fails()) {
+ return response()->json([
+ 'message' => 'Validation failed.',
+ 'errors' => $validator->errors(),
+ ], 422);
+ }
+
+ $existingEnv = $service->environment_variables()->where('key', $request->key)->first();
+ if ($existingEnv) {
+ return response()->json([
+ 'message' => 'Environment variable already exists. Use PATCH request to update it.',
+ ], 409);
+ }
+
+ $env = $service->environment_variables()->create($request->all());
+
+ return response()->json($this->removeSensitiveData($env))->setStatusCode(201);
+ }
+
+ #[OA\Delete(
+ summary: 'Delete Env',
+ description: 'Delete env by UUID.',
+ path: '/services/{uuid}/envs/{env_uuid}',
+ operationId: 'delete-env-by-service-uuid',
+ security: [
+ ['bearerAuth' => []],
+ ],
+ tags: ['Services'],
+ parameters: [
+ new OA\Parameter(
+ name: 'uuid',
+ in: 'path',
+ description: 'UUID of the service.',
+ required: true,
+ schema: new OA\Schema(
+ type: 'string',
+ format: 'uuid',
+ )
+ ),
+ new OA\Parameter(
+ name: 'env_uuid',
+ in: 'path',
+ description: 'UUID of the environment variable.',
+ required: true,
+ schema: new OA\Schema(
+ type: 'string',
+ format: 'uuid',
+ )
+ ),
+ ],
+ responses: [
+ new OA\Response(
+ response: 200,
+ description: 'Environment variable deleted.',
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'message' => ['type' => 'string', 'example' => 'Environment variable deleted.'],
+ ]
+ )
+ ),
+ ]),
+ new OA\Response(
+ response: 401,
+ ref: '#/components/responses/401',
+ ),
+ new OA\Response(
+ response: 400,
+ ref: '#/components/responses/400',
+ ),
+ new OA\Response(
+ response: 404,
+ ref: '#/components/responses/404',
+ ),
+ ]
+ )]
+ public function delete_env_by_uuid(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+
+ $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first();
+ if (! $service) {
+ return response()->json(['message' => 'Service not found.'], 404);
+ }
+
+ $env = EnvironmentVariable::where('uuid', $request->env_uuid)
+ ->where('service_id', $service->id)
+ ->first();
+
+ if (! $env) {
+ return response()->json(['message' => 'Environment variable not found.'], 404);
+ }
+
+ $env->forceDelete();
+
+ return response()->json(['message' => 'Environment variable deleted.']);
+ }
+
#[OA\Get(
summary: 'Start',
description: 'Start service. `Post` request is also accepted.',
path: '/services/{uuid}/start',
+ operationId: 'start-service-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -558,6 +1073,7 @@ class ServicesController extends Controller
summary: 'Stop',
description: 'Stop service. `Post` request is also accepted.',
path: '/services/{uuid}/stop',
+ operationId: 'stop-service-by-uuid',
security: [
['bearerAuth' => []],
],
@@ -633,6 +1149,7 @@ class ServicesController extends Controller
summary: 'Restart',
description: 'Restart service. `Post` request is also accepted.',
path: '/services/{uuid}/restart',
+ operationId: 'restart-service-by-uuid',
security: [
['bearerAuth' => []],
],
diff --git a/app/Http/Controllers/Api/TeamController.php b/app/Http/Controllers/Api/TeamController.php
index 1a481e5ec..3f951c6f7 100644
--- a/app/Http/Controllers/Api/TeamController.php
+++ b/app/Http/Controllers/Api/TeamController.php
@@ -32,6 +32,7 @@ class TeamController extends Controller
summary: 'List',
description: 'Get all teams.',
path: '/teams',
+ operationId: 'list-teams',
security: [
['bearerAuth' => []],
],
@@ -79,6 +80,7 @@ class TeamController extends Controller
summary: 'Get',
description: 'Get team by TeamId.',
path: '/teams/{id}',
+ operationId: 'get-team-by-id',
security: [
['bearerAuth' => []],
],
@@ -129,6 +131,7 @@ class TeamController extends Controller
summary: 'Members',
description: 'Get members by TeamId.',
path: '/teams/{id}/members',
+ operationId: 'get-members-by-team-id',
security: [
['bearerAuth' => []],
],
@@ -189,6 +192,7 @@ class TeamController extends Controller
summary: 'Authenticated Team',
description: 'Get currently authenticated team.',
path: '/teams/current',
+ operationId: 'get-current-team',
security: [
['bearerAuth' => []],
],
@@ -225,6 +229,7 @@ class TeamController extends Controller
summary: 'Authenticated Team Members',
description: 'Get currently authenticated team members.',
path: '/teams/current/members',
+ operationId: 'get-current-team-members',
security: [
['bearerAuth' => []],
],
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index 1ab6e5543..a0195d1b9 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -12,6 +12,7 @@ use App\Models\ApplicationPreview;
use App\Models\EnvironmentVariable;
use App\Models\GithubApp;
use App\Models\GitlabApp;
+use App\Models\InstanceSettings;
use App\Models\Server;
use App\Models\StandaloneDocker;
use App\Models\SwarmDocker;
@@ -109,7 +110,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private bool $is_debug_enabled;
- private $build_args;
+ private Collection|string $build_args;
private $env_args;
@@ -168,6 +169,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->application_deployment_queue = ApplicationDeploymentQueue::find($application_deployment_queue_id);
$this->application = Application::find($this->application_deployment_queue->application_id);
$this->build_pack = data_get($this->application, 'build_pack');
+ $this->build_args = collect([]);
$this->application_deployment_queue_id = $application_deployment_queue_id;
$this->deployment_uuid = $this->application_deployment_queue->deployment_uuid;
@@ -1292,7 +1294,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private function prepare_builder_image()
{
+ $settings = InstanceSettings::get();
$helperImage = config('coolify.helper_image');
+ $helperImage = "{$helperImage}:{$settings->helper_version}";
// Get user home directory
$this->serverUserHomeDir = instant_remote_process(['echo $HOME'], $this->server);
$this->dockerConfigFileExists = instant_remote_process(["test -f {$this->serverUserHomeDir}/.docker/config.json && echo 'OK' || echo 'NOK'"], $this->server);
diff --git a/app/Jobs/ApplicationRestartJob.php b/app/Jobs/ApplicationRestartJob.php
deleted file mode 100644
index 54c062197..000000000
--- a/app/Jobs/ApplicationRestartJob.php
+++ /dev/null
@@ -1,32 +0,0 @@
-applicationDeploymentQueueId = $applicationDeploymentQueueId;
- }
-
- public function handle()
- {
- ray('Restarting application');
- }
-}
diff --git a/app/Jobs/CheckForUpdatesJob.php b/app/Jobs/CheckForUpdatesJob.php
index 86b66fbfb..ddc264839 100644
--- a/app/Jobs/CheckForUpdatesJob.php
+++ b/app/Jobs/CheckForUpdatesJob.php
@@ -10,6 +10,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http;
+use Illuminate\Support\Facades\File;
class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
{
@@ -25,12 +26,14 @@ class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue
$response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
if ($response->successful()) {
$versions = $response->json();
+
$latest_version = data_get($versions, 'coolify.v4.version');
$current_version = config('version');
if (version_compare($latest_version, $current_version, '>')) {
// New version available
$settings->update(['new_version_available' => true]);
+ File::put(base_path('versions.json'), json_encode($versions, JSON_PRETTY_PRINT));
} else {
$settings->update(['new_version_available' => false]);
}
diff --git a/app/Jobs/CheckLogDrainContainerJob.php b/app/Jobs/CheckLogDrainContainerJob.php
deleted file mode 100644
index 16ef85192..000000000
--- a/app/Jobs/CheckLogDrainContainerJob.php
+++ /dev/null
@@ -1,93 +0,0 @@
-server->id))->dontRelease()];
- }
-
- public function uniqueId(): int
- {
- return $this->server->id;
- }
-
- public function healthcheck()
- {
- $status = instant_remote_process(["docker inspect --format='{{json .State.Status}}' coolify-log-drain"], $this->server, false);
- if (str($status)->contains('running')) {
- return true;
- } else {
- return false;
- }
- }
-
- public function handle()
- {
- // ray("checking log drain statuses for {$this->server->id}");
- try {
- if (! $this->server->isFunctional()) {
- return;
- }
- $containers = instant_remote_process(['docker container ls -q'], $this->server, false);
- if (! $containers) {
- return;
- }
- $containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server);
- $containers = format_docker_command_output_to_json($containers);
-
- $foundLogDrainContainer = $containers->filter(function ($value, $key) {
- return data_get($value, 'Name') === '/coolify-log-drain';
- })->first();
- if (! $foundLogDrainContainer || ! $this->healthcheck()) {
- ray('Log drain container not found or unhealthy. Restarting...');
- InstallLogDrain::run($this->server);
- Sleep::for(10)->seconds();
- if ($this->healthcheck()) {
- if ($this->server->log_drain_notification_sent) {
- $this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
- $this->server->update(['log_drain_notification_sent' => false]);
- }
-
- return;
- }
- if (! $this->server->log_drain_notification_sent) {
- ray('Log drain container still unhealthy. Sending notification...');
- // $this->server->team?->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
- $this->server->update(['log_drain_notification_sent' => true]);
- }
- } else {
- if ($this->server->log_drain_notification_sent) {
- $this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
- $this->server->update(['log_drain_notification_sent' => false]);
- }
- }
- } catch (\Throwable $e) {
- if (! isCloud()) {
- send_internal_notification("CheckLogDrainContainerJob failed on ({$this->server->id}) with: ".$e->getMessage());
- }
- ray($e->getMessage());
-
- return handleError($e);
- }
- }
-}
diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php
index 0666f78c3..5d481199b 100644
--- a/app/Jobs/DatabaseBackupJob.php
+++ b/app/Jobs/DatabaseBackupJob.php
@@ -25,6 +25,7 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Str;
+use App\Models\InstanceSettings;
class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{
@@ -493,12 +494,15 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
} else {
$network = $this->database->destination->network;
}
- $commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper";
+
+ $this->ensureHelperImageAvailable();
+
+ $fullImageName = $this->getFullImageName();
+ $commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro {$fullImageName}";
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
instant_remote_process($commands, $this->server);
$this->add_to_backup_output('Uploaded to S3.');
- ray('Uploaded to S3. '.$this->backup_location.' to s3://'.$bucket.$this->backup_dir);
} catch (\Throwable $e) {
$this->add_to_backup_output($e->getMessage());
throw $e;
@@ -507,4 +511,40 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
instant_remote_process([$command], $this->server);
}
}
+
+ private function ensureHelperImageAvailable(): void
+ {
+ $fullImageName = $this->getFullImageName();
+
+ $imageExists = $this->checkImageExists($fullImageName);
+
+ if (!$imageExists) {
+ $this->pullHelperImage($fullImageName);
+ }
+ }
+
+ private function checkImageExists(string $fullImageName): bool
+ {
+ $result = instant_remote_process(["docker image inspect {$fullImageName} >/dev/null 2>&1 && echo 'exists' || echo 'not exists'"], $this->server, false);
+ return trim($result) === 'exists';
+ }
+
+ private function pullHelperImage(string $fullImageName): void
+ {
+ try {
+ instant_remote_process(["docker pull {$fullImageName}"], $this->server);
+ } catch (\Exception $e) {
+ $errorMessage = "Failed to pull helper image: " . $e->getMessage();
+ $this->add_to_backup_output($errorMessage);
+ throw new \RuntimeException($errorMessage);
+ }
+ }
+
+ private function getFullImageName(): string
+ {
+ $settings = InstanceSettings::get();
+ $helperImage = config('coolify.helper_image');
+ $latestVersion = $settings->helper_version;
+ return "{$helperImage}:{$latestVersion}";
+ }
}
diff --git a/app/Jobs/InstanceAutoUpdateJob.php b/app/Jobs/InstanceAutoUpdateJob.php
deleted file mode 100644
index 1bbfcf8cb..000000000
--- a/app/Jobs/InstanceAutoUpdateJob.php
+++ /dev/null
@@ -1,28 +0,0 @@
-get('https://cdn.coollabs.io/coolify/versions.json');
- if ($response->successful()) {
- $versions = $response->json();
- File::put(base_path('versions.json'), json_encode($versions, JSON_PRETTY_PRINT));
- }
- $latest_version = get_latest_version_of_coolify();
- instant_remote_process(["docker pull -q ghcr.io/coollabsio/coolify:{$latest_version}"], $server, false);
-
- $current_version = config('version');
- if (! $settings->is_auto_update_enabled) {
- return;
- }
- if ($latest_version === $current_version) {
- return;
- }
- if (version_compare($latest_version, $current_version, '<')) {
- return;
- }
- } catch (\Throwable $e) {
- throw $e;
- }
- }
-}
diff --git a/app/Jobs/PullHelperImageJob.php b/app/Jobs/PullHelperImageJob.php
index 30a1b8026..420119069 100644
--- a/app/Jobs/PullHelperImageJob.php
+++ b/app/Jobs/PullHelperImageJob.php
@@ -2,6 +2,7 @@
namespace App\Jobs;
+use App\Models\InstanceSettings;
use App\Models\Server;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
@@ -10,6 +11,7 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
+use Illuminate\Support\Facades\Http;
class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue
{
@@ -32,10 +34,20 @@ class PullHelperImageJob implements ShouldBeEncrypted, ShouldQueue
public function handle(): void
{
try {
- $helperImage = config('coolify.helper_image');
- ray("Pulling {$helperImage}");
- instant_remote_process(["docker pull -q {$helperImage}"], $this->server, false);
- ray('PullHelperImageJob done');
+ $response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
+ if ($response->successful()) {
+ $versions = $response->json();
+ $settings = InstanceSettings::get();
+ $latest_version = data_get($versions, 'coolify.helper.version');
+ $current_version = $settings->helper_version;
+ if (version_compare($latest_version, $current_version, '>')) {
+ // New version available
+ $helperImage = config('coolify.helper_image');
+ instant_remote_process(["docker pull -q {$helperImage}:{$latest_version}"], $this->server);
+ $settings->update(['helper_version' => $latest_version]);
+ }
+ }
+
} catch (\Throwable $e) {
send_internal_notification('PullHelperImageJob failed with: '.$e->getMessage());
ray($e->getMessage());
diff --git a/app/Jobs/ServerCheckJob.php b/app/Jobs/ServerCheckJob.php
index 703b199c5..3dbd9d3a7 100644
--- a/app/Jobs/ServerCheckJob.php
+++ b/app/Jobs/ServerCheckJob.php
@@ -124,6 +124,9 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
private function checkLogDrainContainer()
{
+ if(! $this->server->isLogDrainEnabled()) {
+ return;
+ }
$foundLogDrainContainer = $this->containers->filter(function ($value, $key) {
return data_get($value, 'Name') === '/coolify-log-drain';
})->first();
diff --git a/app/Livewire/Project/Application/Deployment/Show.php b/app/Livewire/Project/Application/Deployment/Show.php
index 84a24255c..f2968f6d9 100644
--- a/app/Livewire/Project/Application/Deployment/Show.php
+++ b/app/Livewire/Project/Application/Deployment/Show.php
@@ -4,6 +4,7 @@ namespace App\Livewire\Project\Application\Deployment;
use App\Models\Application;
use App\Models\ApplicationDeploymentQueue;
+use Illuminate\Support\Collection;
use Livewire\Component;
class Show extends Component
@@ -69,6 +70,20 @@ class Show extends Component
}
}
+ public function getLogLinesProperty()
+ {
+ return decode_remote_command_output($this->application_deployment_queue)->map(function ($logLine) {
+ $logLine['line'] = e($logLine['line']);
+ $logLine['line'] = preg_replace(
+ '/(https?:\/\/[^\s]+)/',
+ '$1',
+ $logLine['line'],
+ );
+
+ return $logLine;
+ });
+ }
+
public function render()
{
return view('livewire.project.application.deployment.show');
diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php
index b29fe8cab..b5c5cb1db 100644
--- a/app/Livewire/Project/New/PublicGitRepository.php
+++ b/app/Livewire/Project/New/PublicGitRepository.php
@@ -99,6 +99,16 @@ class PublicGitRepository extends Component
}
}
+ public function updatedDockerComposeLocation()
+ {
+ if ($this->docker_compose_location) {
+ $this->docker_compose_location = rtrim($this->docker_compose_location, '/');
+ if (! str($this->docker_compose_location)->startsWith('/')) {
+ $this->docker_compose_location = '/'.$this->docker_compose_location;
+ }
+ }
+ }
+
public function updatedBuildPack()
{
if ($this->build_pack === 'nixpacks') {
diff --git a/app/Livewire/Project/Service/EditCompose.php b/app/Livewire/Project/Service/EditCompose.php
index 3d0ad4e0b..dc043e65a 100644
--- a/app/Livewire/Project/Service/EditCompose.php
+++ b/app/Livewire/Project/Service/EditCompose.php
@@ -43,6 +43,7 @@ class EditCompose extends Component
{
$this->dispatch('info', 'Saving new docker compose...');
$this->dispatch('saveCompose', $this->service->docker_compose_raw);
+ $this->dispatch('refreshStorages');
}
public function instantSave()
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php
index 4c79eb3a2..055788b57 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php
@@ -24,6 +24,7 @@ class All extends Component
protected $listeners = [
'saveKey' => 'submit',
+ 'refreshEnvs',
'environmentVariableDeleted' => 'refreshEnvs',
];
@@ -61,7 +62,7 @@ class All extends Component
$sortBy = data_get($this->resource, 'settings.is_env_sorting_enabled') ? 'key' : 'order';
$sortFunction = function ($variables) use ($sortBy) {
- if (!$variables) {
+ if (! $variables) {
return $variables;
}
if ($sortBy === 'key') {
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
index cbc4314f0..463ceecad 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
@@ -136,4 +136,4 @@ class Show extends Component
return handleError($e);
}
}
-}
\ No newline at end of file
+}
diff --git a/app/Livewire/Upgrade.php b/app/Livewire/Upgrade.php
index da7b5860d..dfbd945f5 100644
--- a/app/Livewire/Upgrade.php
+++ b/app/Livewire/Upgrade.php
@@ -4,7 +4,6 @@ namespace App\Livewire;
use App\Actions\Server\UpdateCoolify;
use App\Models\InstanceSettings;
-use Illuminate\Support\Facades\Http;
use Livewire\Component;
class Upgrade extends Component
@@ -22,13 +21,8 @@ class Upgrade extends Component
public function checkUpdate()
{
try {
- $settings = InstanceSettings::get();
- $response = Http::retry(3, 1000)->get('https://cdn.coollabs.io/coolify/versions.json');
- if ($response->successful()) {
- $versions = $response->json();
- $this->latestVersion = data_get($versions, 'coolify.v4.version');
- }
- $this->isUpgradeAvailable = $settings->new_version_available;
+ $this->latestVersion = get_latest_version_of_coolify();
+ $this->isUpgradeAvailable = data_get(InstanceSettings::get(), 'new_version_available', false);
} catch (\Throwable $e) {
return handleError($e, $this);
diff --git a/app/Models/ScheduledDatabaseBackup.php b/app/Models/ScheduledDatabaseBackup.php
index 2d0e200da..50a0c8173 100644
--- a/app/Models/ScheduledDatabaseBackup.php
+++ b/app/Models/ScheduledDatabaseBackup.php
@@ -22,7 +22,8 @@ class ScheduledDatabaseBackup extends BaseModel
public function executions(): HasMany
{
- return $this->hasMany(ScheduledDatabaseBackupExecution::class);
+ // Last execution first
+ return $this->hasMany(ScheduledDatabaseBackupExecution::class)->orderBy('created_at', 'desc');
}
public function s3()
diff --git a/app/Models/ScheduledTask.php b/app/Models/ScheduledTask.php
index 7b1c0d275..82f0036a5 100644
--- a/app/Models/ScheduledTask.php
+++ b/app/Models/ScheduledTask.php
@@ -28,6 +28,7 @@ class ScheduledTask extends BaseModel
public function executions(): HasMany
{
+ // Last execution first
return $this->hasMany(ScheduledTaskExecution::class)->orderBy('created_at', 'desc');
}
diff --git a/app/Models/Server.php b/app/Models/Server.php
index 3a1c0eabe..c72c7cc95 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -880,7 +880,7 @@ $schema://$host {
public function muxFilename()
{
- return "{$this->ip}_{$this->port}_{$this->user}";
+ return $this->uuid;
}
public function team()
diff --git a/app/Models/Service.php b/app/Models/Service.php
index e55c0bea5..a16220604 100644
--- a/app/Models/Service.php
+++ b/app/Models/Service.php
@@ -7,9 +7,10 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\Storage;
use OpenApi\Attributes as OA;
use Spatie\Url\Url;
-use Symfony\Component\Yaml\Yaml;
+use Visus\Cuid2\Cuid2;
#[OA\Schema(
description: 'Service model',
@@ -999,14 +1000,19 @@ class Service extends BaseModel
public function saveComposeConfigs()
{
$workdir = $this->workdir();
- $commands[] = "mkdir -p $workdir";
+
+ instant_remote_process([
+ "mkdir -p $workdir",
+ "cd $workdir",
+ ], $this->server);
+
+ $filename = new Cuid2.'-docker-compose.yml';
+ Storage::disk('local')->put("tmp/{$filename}", $this->docker_compose);
+ $path = Storage::path("tmp/{$filename}");
+ instant_scp($path, "{$workdir}/docker-compose.yml", $this->server);
+ Storage::disk('local')->delete("tmp/{$filename}");
+
$commands[] = "cd $workdir";
-
- $json = Yaml::parse($this->docker_compose);
- $this->docker_compose = Yaml::dump($json, 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
- $docker_compose_base64 = base64_encode($this->docker_compose);
-
- $commands[] = "echo $docker_compose_base64 | base64 -d | tee docker-compose.yml > /dev/null";
$commands[] = 'rm -f .env || true';
$envs_from_coolify = $this->environment_variables()->get();
diff --git a/bootstrap/helpers/remoteProcess.php b/bootstrap/helpers/remoteProcess.php
index 6ba7caeef..3f5cdfae2 100644
--- a/bootstrap/helpers/remoteProcess.php
+++ b/bootstrap/helpers/remoteProcess.php
@@ -146,7 +146,7 @@ function generateSshCommand(Server $server, string $command)
$ssh_command = "timeout $timeout ssh ";
if (config('coolify.mux_enabled') && config('coolify.is_windows_docker_desktop') == false) {
- $ssh_command .= "-o ControlMaster=auto -o ControlPersist={$muxPersistTime} -o ControlPath=/var/www/html/storage/app/ssh/mux/%h_%p_%r ";
+ $ssh_command .= "-o ControlMaster=auto -o ControlPersist={$muxPersistTime} -o ControlPath=/var/www/html/storage/app/ssh/mux/{$server->muxFilename()} ";
}
if (data_get($server, 'settings.is_cloudflare_tunnel')) {
$ssh_command .= '-o ProxyCommand="/usr/local/bin/cloudflared access ssh --hostname %h" ';
@@ -167,7 +167,6 @@ function generateSshCommand(Server $server, string $command)
.$command.PHP_EOL
.$delimiter;
- // ray($ssh_command);
return $ssh_command;
}
function instant_remote_process(Collection|array $command, Server $server, bool $throwError = true, bool $no_sudo = false): ?string
@@ -234,6 +233,7 @@ function decode_remote_command_output(?ApplicationDeploymentQueue $application_d
return collect([]);
}
// ray($decoded );
+ $seenCommands = collect();
$formatted = collect($decoded);
if (! $is_debug_enabled) {
$formatted = $formatted->filter(fn ($i) => $i['hidden'] === false ?? false);
@@ -244,7 +244,42 @@ function decode_remote_command_output(?ApplicationDeploymentQueue $application_d
data_set($i, 'timestamp', Carbon::parse(data_get($i, 'timestamp'))->format('Y-M-d H:i:s.u'));
return $i;
- });
+ })
+ ->reduce(function ($deploymentLogLines, $logItem) use ($seenCommands) {
+ $command = $logItem['command'];
+ $isStderr = $logItem['type'] === 'stderr';
+ $isNewCommand = ! is_null($command) && ! $seenCommands->first(function ($seenCommand) use ($logItem) {
+ return $seenCommand['command'] === $logItem['command'] && $seenCommand['batch'] === $logItem['batch'];
+ });
+
+ if ($isNewCommand) {
+ $deploymentLogLines->push([
+ 'line' => $command,
+ 'timestamp' => $logItem['timestamp'],
+ 'stderr' => $isStderr,
+ 'hidden' => $logItem['hidden'],
+ 'command' => true,
+ ]);
+
+ $seenCommands->push([
+ 'command' => $command,
+ 'batch' => $logItem['batch'],
+ ]);
+ }
+
+ $lines = explode(PHP_EOL, $logItem['output']);
+
+ foreach ($lines as $line) {
+ $deploymentLogLines->push([
+ 'line' => $line,
+ 'timestamp' => $logItem['timestamp'],
+ 'stderr' => $isStderr,
+ 'hidden' => $logItem['hidden'],
+ ]);
+ }
+
+ return $deploymentLogLines;
+ }, collect());
return $formatted;
}
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index ee1ceb049..5f93ce36f 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -2819,8 +2819,10 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$defaultLabels = defaultLabels($resource->id, $containerName, $pull_request_id, type: 'application');
$serviceLabels = $serviceLabels->merge($defaultLabels);
- if ($server->isLogDrainEnabled() && $resource->isLogDrainEnabled()) {
- data_set($service, 'logging', generate_fluentd_configuration());
+ if ($server->isLogDrainEnabled()) {
+ if ($resource instanceof Application && $resource->isLogDrainEnabled()) {
+ data_set($service, 'logging', generate_fluentd_configuration());
+ }
}
if ($serviceLabels->count() > 0) {
if ($resource->settings->is_container_label_escape_enabled) {
@@ -2923,8 +2925,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$restart = data_get_str($service, 'restart', RESTART_MODE);
$logging = data_get($service, 'logging');
- if ($server->isLogDrainEnabled() && $resource->isLogDrainEnabled()) {
- $logging = generate_fluentd_configuration();
+ if ($server->isLogDrainEnabled()) {
+ if ($resource instanceof Application && $resource->isLogDrainEnabled()) {
+ $logging = generate_fluentd_configuration();
+ }
}
$volumes = collect(data_get($service, 'volumes', []));
$networks = collect(data_get($service, 'networks', []));
@@ -3088,10 +3092,9 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$topLevel->get('volumes')->put($name, [
'name' => $name,
]);
-
LocalPersistentVolume::updateOrCreate(
[
- 'mount_path' => $target,
+ 'name' => $name,
'resource_id' => $originalResource->id,
'resource_type' => get_class($originalResource),
],
@@ -3216,10 +3219,31 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
// filter magic environments
$magicEnvironments = $environment->filter(function ($value, $key) {
+ $regex = '/\$\{(.*?)\}/';
+ preg_match_all($regex, $value, $matches);
+ if (count($matches[1]) > 0) {
+ foreach ($matches[1] as $match) {
+ if (str($match)->startsWith('SERVICE_') || str($match)->startsWith('SERVICE_')) {
+ return $match;
+ }
+ }
+ }
$value = str(replaceVariables(str($value)));
return str($key)->startsWith('SERVICE_') || str($value)->startsWith('SERVICE_');
});
+ foreach ($environment as $key => $value) {
+ $regex = '/\$\{(.*?)\}/';
+ preg_match_all($regex, $value, $matches);
+ if (count($matches[1]) > 0) {
+ foreach ($matches[1] as $match) {
+ if (str($match)->startsWith('SERVICE_') || str($match)->startsWith('SERVICE_')) {
+ $magicEnvironments->put($match, '$'.$match);
+ }
+ }
+ $magicEnvironments->forget($key);
+ }
+ }
$normalEnvironments = $environment->diffKeys($magicEnvironments);
if ($magicEnvironments->count() > 0) {
foreach ($magicEnvironments as $key => $value) {
@@ -3262,15 +3286,17 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$value = $fqdn;
}
if (! $isDatabase) {
- if ($isApplication && is_null($resource->fqdn)) {
- data_forget($resource, 'environment_variables');
- data_forget($resource, 'environment_variables_preview');
- $resource->fqdn = $value;
- $resource->save();
- } elseif ($isService && is_null($savedService->fqdn)) {
- if ($key->startsWith('SERVICE_FQDN_')) {
- $savedService->fqdn = $value;
- $savedService->save();
+ if ($key->startsWith('SERVICE_FQDN_') && ($originalValue->value() === '' || $originalValue->startsWith('/'))) {
+ if ($isApplication && is_null($resource->fqdn)) {
+ data_forget($resource, 'environment_variables');
+ data_forget($resource, 'environment_variables_preview');
+ $resource->fqdn = $value;
+ $resource->save();
+ } elseif ($isService && is_null($savedService->fqdn)) {
+ if ($key->startsWith('SERVICE_FQDN_')) {
+ $savedService->fqdn = $value;
+ $savedService->save();
+ }
}
}
}
@@ -3328,7 +3354,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
foreach ($normalEnvironments as $key => $value) {
$key = str($key);
$value = str($value);
- if ($value->startsWith('$')) {
+ if ($value->startsWith('$') || $value->contains('${')) {
+ if ($value->contains('${')) {
+ $value = $value->after('${')->before('}');
+ }
$value = str(replaceVariables(str($value)));
if ($value->contains(':-')) {
$key = $value->before(':');
@@ -3421,7 +3450,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
}
// Add COOLIFY_FQDN & COOLIFY_URL to environment
- if (! $isDatabase && $fqdns?->count() > 0) {
+ if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
$environment->put('COOLIFY_URL', $fqdns->implode(','));
$urls = $fqdns->map(function ($fqdn) {
@@ -3432,7 +3461,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
add_coolify_default_environment_variables($resource, $environment, $resource->environment_variables);
$serviceLabels = $labels->merge($defaultLabels);
- if (! $isDatabase && $fqdns?->count() > 0) {
+ if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) {
if ($isApplication) {
$shouldGenerateLabelsExactly = $resource->destination->server->settings->generate_exact_labels;
$uuid = $resource->uuid;
@@ -3540,7 +3569,6 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
$parsedServices->put($serviceName, $payload);
}
- ray($parsedServices);
$topLevel->put('services', $parsedServices);
$customOrder = ['services', 'volumes', 'networks', 'configs', 'secrets'];
@@ -3570,6 +3598,23 @@ function generate_fluentd_configuration(): array
];
}
+function isAssociativeArray($array)
+{
+ if ($array instanceof Collection) {
+ $array = $array->toArray();
+ }
+
+ if (! is_array($array)) {
+ throw new \InvalidArgumentException('Input must be an array or a Collection.');
+ }
+
+ if ($array === []) {
+ return false;
+ }
+
+ return array_keys($array) !== range(0, count($array) - 1);
+}
+
/**
* This method adds the default environment variables to the resource.
* - COOLIFY_APP_NAME
@@ -3581,37 +3626,39 @@ function generate_fluentd_configuration(): array
*/
function add_coolify_default_environment_variables(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|Application|Service $resource, Collection &$where_to_add, ?Collection $where_to_check = null)
{
+ if ($resource instanceof Service) {
+ $ip = $resource->server->ip;
+ } else {
+ $ip = $resource->destination->server->ip;
+ }
+ if (isAssociativeArray($where_to_add)) {
+ $isAssociativeArray = true;
+ } else {
+ $isAssociativeArray = false;
+ }
if ($where_to_check != null && $where_to_check->where('key', 'COOLIFY_APP_NAME')->isEmpty()) {
- if ($resource instanceof Application && $resource->build_pack === 'dockercompose') {
- $where_to_add->put('COOLIFY_APP_NAME', $resource->name);
- } elseif ($resource instanceof Service) {
+ if ($isAssociativeArray) {
$where_to_add->put('COOLIFY_APP_NAME', $resource->name);
} else {
$where_to_add->push("COOLIFY_APP_NAME={$resource->name}");
}
}
if ($where_to_check != null && $where_to_check->where('key', 'COOLIFY_SERVER_IP')->isEmpty()) {
- if ($resource instanceof Application && $resource->build_pack === 'dockercompose') {
- $where_to_add->put('COOLIFY_SERVER_IP', $resource->destination->server->ip);
- } elseif ($resource instanceof Service) {
- $where_to_add->put('COOLIFY_SERVER_IP', $resource->server->ip);
+ if ($isAssociativeArray) {
+ $where_to_add->put('COOLIFY_SERVER_IP', $ip);
} else {
- $where_to_add->push("COOLIFY_SERVER_IP={$resource->destination->server->ip}");
+ $where_to_add->push("COOLIFY_SERVER_IP={$ip}");
}
}
if ($where_to_check != null && $where_to_check->where('key', 'COOLIFY_ENVIRONMENT_NAME')->isEmpty()) {
- if ($resource instanceof Application && $resource->build_pack === 'dockercompose') {
- $where_to_add->put('COOLIFY_ENVIRONMENT_NAME', $resource->environment->name);
- } elseif ($resource instanceof Service) {
+ if ($isAssociativeArray) {
$where_to_add->put('COOLIFY_ENVIRONMENT_NAME', $resource->environment->name);
} else {
$where_to_add->push("COOLIFY_ENVIRONMENT_NAME={$resource->environment->name}");
}
}
if ($where_to_check != null && $where_to_check->where('key', 'COOLIFY_PROJECT_NAME')->isEmpty()) {
- if ($resource instanceof Application && $resource->build_pack === 'dockercompose') {
- $where_to_add->put('COOLIFY_PROJECT_NAME', $resource->project()->name);
- } elseif ($resource instanceof Service) {
+ if ($isAssociativeArray) {
$where_to_add->put('COOLIFY_PROJECT_NAME', $resource->project()->name);
} else {
$where_to_add->push("COOLIFY_PROJECT_NAME={$resource->project()->name}");
@@ -3622,29 +3669,17 @@ function add_coolify_default_environment_variables(StandaloneRedis|StandalonePos
function convertComposeEnvironmentToArray($environment)
{
$convertedServiceVariables = collect([]);
- foreach ($environment as $variableName => $variableValue) {
- if (is_array($variableValue)) {
- $key = str(collect($variableValue)->keys()->first());
- $value = str(collect($variableValue)->values()->first());
- } elseif (is_string($variableValue)) {
- if (str($variableValue)->contains('=')) {
- $key = str($variableValue)->before('=');
- $value = str($variableValue)->after('=');
- } else {
- if (is_numeric($variableName)) {
- $key = str($variableValue);
- $value = null;
- } else {
- $key = str($variableName);
- if ($variableValue) {
- $value = str($variableValue);
- } else {
- $value = null;
- }
- }
+ if (isAssociativeArray($environment)) {
+ $convertedServiceVariables = $environment;
+ } else {
+ foreach ($environment as $value) {
+ $parts = explode('=', $value, 2);
+ $key = $parts[0];
+ $realValue = $parts[1] ?? '';
+ if ($key) {
+ $convertedServiceVariables->put($key, $realValue);
}
}
- $convertedServiceVariables->put($key->value(), $value?->value() ?? null);
}
return $convertedServiceVariables;
diff --git a/config/coolify.php b/config/coolify.php
index a6d6d8581..6e284fe9e 100644
--- a/config/coolify.php
+++ b/config/coolify.php
@@ -11,7 +11,7 @@ return [
'dev_webhook' => env('SERVEO_URL'),
'is_windows_docker_desktop' => env('IS_WINDOWS_DOCKER_DESKTOP', false),
'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'),
- 'helper_image' => env('HELPER_IMAGE', 'ghcr.io/coollabsio/coolify-helper:latest'),
+ 'helper_image' => env('HELPER_IMAGE', 'ghcr.io/coollabsio/coolify-helper'),
'is_horizon_enabled' => env('HORIZON_ENABLED', true),
'is_scheduler_enabled' => env('SCHEDULER_ENABLED', true),
];
diff --git a/config/horizon.php b/config/horizon.php
index 4d25e40bc..939d74883 100644
--- a/config/horizon.php
+++ b/config/horizon.php
@@ -186,7 +186,7 @@ return [
'balance' => env('HORIZON_BALANCE', 'auto'),
'maxTime' => 0,
'maxJobs' => 0,
- 'memory' => 64,
+ 'memory' => 128,
'tries' => 1,
'timeout' => 3560,
'nice' => 0,
diff --git a/config/sentry.php b/config/sentry.php
index 657377edf..f48995f01 100644
--- a/config/sentry.php
+++ b/config/sentry.php
@@ -7,7 +7,7 @@ return [
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
- 'release' => '4.0.0-beta.324',
+ 'release' => '4.0.0-beta.331',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),
diff --git a/config/version.php b/config/version.php
index 7af787ece..12f68e4e0 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
string('helper_version')->default('1.0.0');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('instance_settings', function (Blueprint $table) {
+ $table->dropColumn('helper_version');
+ });
+ }
+};
diff --git a/database/migrations/2024_09_06_062534_change_server_cleanup_to_forced.php b/database/migrations/2024_09_06_062534_change_server_cleanup_to_forced.php
new file mode 100644
index 000000000..ad6e5bd9e
--- /dev/null
+++ b/database/migrations/2024_09_06_062534_change_server_cleanup_to_forced.php
@@ -0,0 +1,37 @@
+boolean('force_docker_cleanup')->default(true)->change();
+ });
+ $serverSettings = ServerSetting::all();
+ foreach ($serverSettings as $serverSetting) {
+ if ($serverSetting->force_docker_cleanup === false) {
+ $serverSetting->force_docker_cleanup = true;
+ $serverSetting->docker_cleanup_frequency = '*/10 * * * *';
+ $serverSetting->save();
+ }
+ }
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('server_settings', function (Blueprint $table) {
+ $table->boolean('force_docker_cleanup')->default(false)->change();
+ });
+ }
+};
diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml
index b8156cab5..b26cd5746 100644
--- a/docker-compose.prod.yml
+++ b/docker-compose.prod.yml
@@ -13,19 +13,18 @@ services:
- /data/coolify/backups:/var/www/html/storage/app/backups
- /data/coolify/webhooks-during-maintenance:/var/www/html/storage/app/webhooks-during-maintenance
environment:
- - PHP_MEMORY_LIMIT
- - APP_ID
- APP_ENV=production
- - APP_DEBUG
- APP_NAME
+ - APP_ID
- APP_KEY
- APP_URL
- - DB_CONNECTION
- - DB_HOST
- - DB_PORT
+ - APP_DEBUG
- DB_DATABASE
- DB_USERNAME
- DB_PASSWORD
+ - DB_HOST
+ - DB_PORT
+ - DB_CONNECTION
- QUEUE_CONNECTION
- REDIS_HOST
- REDIS_PASSWORD
@@ -34,6 +33,7 @@ services:
- HORIZON_BALANCE_MAX_SHIFT
- HORIZON_BALANCE_COOLDOWN
- SSL_MODE=off
+ - PHP_MEMORY_LIMIT
- PHP_PM_CONTROL=dynamic
- PHP_PM_START_SERVERS=1
- PHP_PM_MIN_SPARE_SERVERS=1
@@ -83,21 +83,17 @@ services:
condition: service_healthy
redis:
condition: service_healthy
+ soketi:
+ condition: service_healthy
postgres:
volumes:
- coolify-db:/var/lib/postgresql/data
environment:
- POSTGRES_USER: "${DB_USERNAME:-coolify}"
+ POSTGRES_USER: "${DB_USERNAME}"
POSTGRES_PASSWORD: "${DB_PASSWORD}"
POSTGRES_DB: "${DB_DATABASE:-coolify}"
healthcheck:
- test:
- [
- "CMD-SHELL",
- "pg_isready -U ${DB_USERNAME:-coolify}",
- "-d",
- "${DB_DATABASE:-coolify}"
- ]
+ test: [ "CMD-SHELL", "pg_isready -U ${DB_USERNAME}", "-d", "${DB_DATABASE:-coolify}" ]
interval: 5s
retries: 10
timeout: 2s
@@ -130,3 +126,7 @@ volumes:
name: coolify-db
coolify-redis:
name: coolify-redis
+
+networks:
+ coolify:
+ external: true
diff --git a/docker-compose.windows.yml b/docker-compose.windows.yml
index af5ecc0f7..a1ee1aeea 100644
--- a/docker-compose.windows.yml
+++ b/docker-compose.windows.yml
@@ -71,14 +71,14 @@ services:
volumes:
- coolify-db:/var/lib/postgresql/data
environment:
- POSTGRES_USER: "${DB_USERNAME:-coolify}"
+ POSTGRES_USER: "${DB_USERNAME}"
POSTGRES_PASSWORD: "${DB_PASSWORD}"
POSTGRES_DB: "${DB_DATABASE:-coolify}"
healthcheck:
test:
[
"CMD-SHELL",
- "pg_isready -U ${DB_USERNAME:-coolify}",
+ "pg_isready -U ${DB_USERNAME}",
"-d",
"${DB_DATABASE:-coolify}"
]
diff --git a/docker-compose.yml b/docker-compose.yml
index 8eed44f8c..930c0a6b9 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -10,6 +10,7 @@ services:
depends_on:
- postgres
- redis
+ - soketi
postgres:
image: postgres:15-alpine
container_name: coolify-db
@@ -32,4 +33,4 @@ networks:
coolify:
name: coolify
driver: bridge
- external: true
+ external: false
diff --git a/docker/coolify-helper/Dockerfile b/docker/coolify-helper/Dockerfile
index 472f46c5d..09ca18825 100644
--- a/docker/coolify-helper/Dockerfile
+++ b/docker/coolify-helper/Dockerfile
@@ -8,9 +8,9 @@ ARG DOCKER_COMPOSE_VERSION=2.27.1
# https://github.com/docker/buildx/releases
ARG DOCKER_BUILDX_VERSION=0.14.1
# https://github.com/buildpacks/pack/releases
-ARG PACK_VERSION=0.34.1
+ARG PACK_VERSION=0.35.1
# https://github.com/railwayapp/nixpacks/releases
-ARG NIXPACKS_VERSION=1.24.1
+ARG NIXPACKS_VERSION=1.28.0
USER root
WORKDIR /artifacts
diff --git a/openapi.yaml b/openapi.yaml
index 482ff6f05..cbe41368a 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -13,7 +13,7 @@ paths:
- Applications
summary: List
description: 'List all applications.'
- operationId: 02978e79fc0b54d573b2359f2a1f7d86
+ operationId: list-applications
responses:
'200':
description: 'Get all applications.'
@@ -36,7 +36,7 @@ paths:
- Applications
summary: 'Create (Public)'
description: 'Create new application based on a public git repository.'
- operationId: cb56324ad19693469b4461d3f6065a5b
+ operationId: create-public-application
requestBody:
description: 'Application object that needs to be created.'
required: true
@@ -253,7 +253,7 @@ paths:
- Applications
summary: 'Create (Private - GH App)'
description: 'Create new application based on a private repository through a Github App.'
- operationId: 8b7af9c9a509385963bf3e72eeeea786
+ operationId: create-private-github-app-application
requestBody:
description: 'Application object that needs to be created.'
required: true
@@ -474,7 +474,7 @@ paths:
- Applications
summary: 'Create (Private - Deploy Key)'
description: 'Create new application based on a private repository through a Deploy Key.'
- operationId: e3eaa989ffb05366247a00cdfd551efa
+ operationId: create-private-deploy-key-application
requestBody:
description: 'Application object that needs to be created.'
required: true
@@ -695,7 +695,7 @@ paths:
- Applications
summary: 'Create (Dockerfile)'
description: 'Create new application based on a simple Dockerfile.'
- operationId: 2b433ad6f5d259eb7f4f3b5af9913708
+ operationId: create-dockerfile-application
requestBody:
description: 'Application object that needs to be created.'
required: true
@@ -867,7 +867,7 @@ paths:
- Applications
summary: 'Create (Docker Image)'
description: 'Create new application based on a prebuilt docker image'
- operationId: e9a2d6dd9404acf880dc3053f09477fc
+ operationId: create-dockerimage-application
requestBody:
description: 'Application object that needs to be created.'
required: true
@@ -1030,7 +1030,7 @@ paths:
- Applications
summary: 'Create (Docker Compose)'
description: 'Create new application based on a docker-compose file.'
- operationId: 3731add8226c2d664455978cac46c242
+ operationId: create-dockercompose-application
requestBody:
description: 'Application object that needs to be created.'
required: true
@@ -1084,7 +1084,7 @@ paths:
- Applications
summary: Get
description: 'Get application by UUID.'
- operationId: 3630b62c28e7358e7f0087c1d8fe1845
+ operationId: get-application-by-uuid
parameters:
-
name: uuid
@@ -1115,7 +1115,7 @@ paths:
- Applications
summary: Delete
description: 'Delete application by UUID.'
- operationId: 1e110b190a1045d34f3e1c61608a8702
+ operationId: delete-application-by-uuid
parameters:
-
name: uuid
@@ -1156,7 +1156,7 @@ paths:
- Applications
summary: Update
description: 'Update application by UUID.'
- operationId: 62a3b1775e8cba5d39a236ebb69830b7
+ operationId: update-application-by-uuid
requestBody:
description: 'Application updated.'
required: true
@@ -1376,7 +1376,7 @@ paths:
- Applications
summary: 'List Envs'
description: 'List all envs by application UUID.'
- operationId: 7c8e0c286870e23294a075cc0584df2f
+ operationId: list-envs-by-application-uuid
parameters:
-
name: uuid
@@ -1409,7 +1409,7 @@ paths:
- Applications
summary: 'Create Env'
description: 'Create env by application UUID.'
- operationId: 4699ffbb7d6e58581fd0b0a14f36ffc2
+ operationId: create-env-by-application-uuid
parameters:
-
name: uuid
@@ -1471,7 +1471,7 @@ paths:
- Applications
summary: 'Update Env'
description: 'Update env by application UUID.'
- operationId: 3d70a2d569f395be220b3f09ad36674b
+ operationId: update-env-by-application-uuid
parameters:
-
name: uuid
@@ -1537,7 +1537,7 @@ paths:
- Applications
summary: 'Update Envs (Bulk)'
description: 'Update multiple envs by application UUID.'
- operationId: ae96f0f585ed158b2abd2d9ba40f3cf9
+ operationId: update-envs-by-application-uuid
parameters:
-
name: uuid
@@ -1584,7 +1584,7 @@ paths:
- Applications
summary: 'Delete Env'
description: 'Delete env by UUID.'
- operationId: 96097c5cfc7dc0e7a3de229645f630c7
+ operationId: delete-env-by-application-uuid
parameters:
-
name: uuid
@@ -1626,7 +1626,7 @@ paths:
- Applications
summary: Start
description: 'Start application. `Post` request is also accepted.'
- operationId: dc87c2061ab303757a0e061f87900c4c
+ operationId: start-application-by-uuid
parameters:
-
name: uuid
@@ -1675,7 +1675,7 @@ paths:
- Applications
summary: Stop
description: 'Stop application. `Post` request is also accepted.'
- operationId: 133ef3c7bd5043901f24bb5002a536eb
+ operationId: stop-application-by-uuid
parameters:
-
name: uuid
@@ -1709,7 +1709,7 @@ paths:
- Applications
summary: Restart
description: 'Restart application. `Post` request is also accepted.'
- operationId: b231ae7baab9ef47f0627be820e735bc
+ operationId: restart-application-by-uuid
parameters:
-
name: uuid
@@ -1744,7 +1744,7 @@ paths:
- Databases
summary: List
description: 'List all databases.'
- operationId: ecd0ee1e46e4c854c18e6c9daa3d37f3
+ operationId: list-databases
responses:
'200':
description: 'Get all databases'
@@ -1766,7 +1766,7 @@ paths:
- Databases
summary: Get
description: 'Get database by UUID.'
- operationId: b49cb2d3e8f34c4e80cdffd8a201031d
+ operationId: get-database-by-uuid
parameters:
-
name: uuid
@@ -1798,7 +1798,7 @@ paths:
- Databases
summary: Delete
description: 'Delete database by UUID.'
- operationId: 20610931b2bae8aba34eee68624ab673
+ operationId: delete-database-by-uuid
parameters:
-
name: uuid
@@ -1839,7 +1839,7 @@ paths:
- Databases
summary: Update
description: 'Update database by UUID.'
- operationId: 5ba459ed390a721711a1708760e9de3b
+ operationId: update-database-by-uuid
parameters:
-
name: uuid
@@ -1989,7 +1989,7 @@ paths:
- Databases
summary: 'Create (PostgreSQL)'
description: 'Create a new PostgreSQL database.'
- operationId: 8f7f491ddc46a9fa065b4424512231cd
+ operationId: create-database-postgresql
requestBody:
description: 'Database data'
required: true
@@ -2087,7 +2087,7 @@ paths:
- Databases
summary: 'Create (Clickhouse)'
description: 'Create a new Clickhouse database.'
- operationId: a1189fa7f956f238f0e95c9150ff57f6
+ operationId: create-database-clickhouse
requestBody:
description: 'Database data'
required: true
@@ -2173,7 +2173,7 @@ paths:
- Databases
summary: 'Create (DragonFly)'
description: 'Create a new DragonFly database.'
- operationId: e73f7de1c8eee4219e5ec98c4b9b7efe
+ operationId: create-database-dragonfly
requestBody:
description: 'Database data'
required: true
@@ -2256,7 +2256,7 @@ paths:
- Databases
summary: 'Create (Redis)'
description: 'Create a new Redis database.'
- operationId: 4d352d13544ee2953fd48ad7b0651098
+ operationId: create-database-redis
requestBody:
description: 'Database data'
required: true
@@ -2342,7 +2342,7 @@ paths:
- Databases
summary: 'Create (KeyDB)'
description: 'Create a new KeyDB database.'
- operationId: b908f3929c371c217d489638e0a21ff6
+ operationId: create-database-keydb
requestBody:
description: 'Database data'
required: true
@@ -2428,7 +2428,7 @@ paths:
- Databases
summary: 'Create (MariaDB)'
description: 'Create a new MariaDB database.'
- operationId: 6bea521ddcd738dcbb5f3783a7308acf
+ operationId: create-database-mariadb
requestBody:
description: 'Database data'
required: true
@@ -2523,7 +2523,7 @@ paths:
- Databases
summary: 'Create (MySQL)'
description: 'Create a new MySQL database.'
- operationId: 0a1158cf759c4493cbb1e30024c60623
+ operationId: create-database-mysql
requestBody:
description: 'Database data'
required: true
@@ -2615,7 +2615,7 @@ paths:
- Databases
summary: 'Create (MongoDB)'
description: 'Create a new MongoDB database.'
- operationId: fdba3de84d02519bb37599fea34b115d
+ operationId: create-database-mongodb
requestBody:
description: 'Database data'
required: true
@@ -2701,7 +2701,7 @@ paths:
- Databases
summary: Start
description: 'Start database. `Post` request is also accepted.'
- operationId: 4c6eb21e734d411e2b3388578761123d
+ operationId: start-database-by-uuid
parameters:
-
name: uuid
@@ -2735,7 +2735,7 @@ paths:
- Databases
summary: Stop
description: 'Stop database. `Post` request is also accepted.'
- operationId: cb6d983c2679aff841c7501ce612a372
+ operationId: stop-database-by-uuid
parameters:
-
name: uuid
@@ -2769,7 +2769,7 @@ paths:
- Databases
summary: Restart
description: 'Restart database. `Post` request is also accepted.'
- operationId: 04c7a5e4752b4a00036addb433f3f218
+ operationId: restart-database-by-uuid
parameters:
-
name: uuid
@@ -2803,7 +2803,7 @@ paths:
- Deployments
summary: List
description: 'List currently running deployments'
- operationId: a2c05736269191ad0d99cadfd4708986
+ operationId: list-deployments
responses:
'200':
description: 'Get all currently running deployments.'
@@ -2826,7 +2826,7 @@ paths:
- Deployments
summary: Get
description: 'Get deployment by UUID.'
- operationId: ccf9856174c115a1430d952ccbd36aea
+ operationId: get-deployment-by-uuid
parameters:
-
name: uuid
@@ -2857,7 +2857,7 @@ paths:
- Deployments
summary: Deploy
description: 'Deploy by tag or uuid. `Post` request also accepted.'
- operationId: 700eb6e51f4c9e86d722f600c65ed1d4
+ operationId: deploy-by-tag-or-uuid
parameters:
-
name: tag
@@ -2897,7 +2897,7 @@ paths:
get:
summary: Version
description: 'Get Coolify version.'
- operationId: 187b37139844731110757711ee71c215
+ operationId: version
responses:
'200':
description: 'Returns the version of the application'
@@ -2917,7 +2917,7 @@ paths:
get:
summary: 'Enable API'
description: 'Enable API (only with root permissions).'
- operationId: 595019bae03d08277def667609779ff3
+ operationId: enable-api
responses:
'200':
description: 'Enable API.'
@@ -2946,7 +2946,7 @@ paths:
get:
summary: 'Disable API'
description: 'Disable API (only with root permissions).'
- operationId: 50e2486a2d196a996b24a284a283bcdb
+ operationId: disable-api
responses:
'200':
description: 'Disable API.'
@@ -2975,7 +2975,7 @@ paths:
get:
summary: Healthcheck
description: 'Healthcheck endpoint.'
- operationId: 64db893135e686704bb88c3c238022c1
+ operationId: healthcheck
responses:
'200':
description: 'Healthcheck endpoint.'
@@ -2994,7 +2994,7 @@ paths:
- Projects
summary: List
description: 'list projects.'
- operationId: 762788f00f2dabb981df9adbc948d3f6
+ operationId: list-projects
responses:
'200':
description: 'Get all projects.'
@@ -3016,7 +3016,7 @@ paths:
- Projects
summary: Create
description: 'Create Project.'
- operationId: cf067eb7cf18216cda3239329a2eeadb
+ operationId: create-project
requestBody:
description: 'Project created.'
required: true
@@ -3024,7 +3024,7 @@ paths:
application/json:
schema:
properties:
- uuid:
+ name:
type: string
description: 'The name of the project.'
description:
@@ -3055,7 +3055,7 @@ paths:
- Projects
summary: Get
description: 'Get project by Uuid.'
- operationId: 63bf8b6a68fbb757f09ab519331f6298
+ operationId: get-project-by-uuid
parameters:
-
name: uuid
@@ -3085,7 +3085,7 @@ paths:
- Projects
summary: Delete
description: 'Delete project by UUID.'
- operationId: f668a936f505b4401948c74b6a663029
+ operationId: delete-project-by-uuid
parameters:
-
name: uuid
@@ -3118,7 +3118,7 @@ paths:
- Projects
summary: Update
description: 'Update Project.'
- operationId: 2db343bd6fc14c658cb51a2b73b2f842
+ operationId: update-project-by-uuid
requestBody:
description: 'Project updated.'
required: true
@@ -3159,7 +3159,7 @@ paths:
- Projects
summary: Environment
description: 'Get environment by name.'
- operationId: 7e44845dce5aa47ed7b0daf5595ad2e1
+ operationId: get-environment-by-name
parameters:
-
name: uuid
@@ -3197,7 +3197,7 @@ paths:
- Resources
summary: List
description: 'Get all resources.'
- operationId: c399903694eb1314596832e49f7c66d7
+ operationId: list-resources
responses:
'200':
description: 'Get all resources'
@@ -3219,7 +3219,7 @@ paths:
- 'Private Keys'
summary: List
description: 'List all private keys.'
- operationId: 8a5d8d3ccbbcef54ed0e913a27faea9d
+ operationId: list-private-keys
responses:
'200':
description: 'Get all private keys.'
@@ -3241,7 +3241,7 @@ paths:
- 'Private Keys'
summary: Create
description: 'Create a new private key.'
- operationId: eb4780acaa990c594cdbe8ffa80b4fb0
+ operationId: create-private-key
requestBody:
required: true
content:
@@ -3279,7 +3279,7 @@ paths:
- 'Private Keys'
summary: Update
description: 'Update a private key.'
- operationId: 371fd26b8949a070c26a264231fe233f
+ operationId: update-private-key
requestBody:
required: true
content:
@@ -3318,7 +3318,7 @@ paths:
- 'Private Keys'
summary: Get
description: 'Get key by UUID.'
- operationId: 2f743a85eb65d5ddb8cd5b362bb3d26a
+ operationId: get-private-key-by-uuid
parameters:
-
name: uuid
@@ -3350,7 +3350,7 @@ paths:
- 'Private Keys'
summary: Delete
description: 'Delete a private key.'
- operationId: 8faa0bb399142f0084dfc3e003c42cf6
+ operationId: delete-private-key-by-uuid
parameters:
-
name: uuid
@@ -3383,7 +3383,7 @@ paths:
- Servers
summary: List
description: 'List all servers.'
- operationId: 787448df856cefd2d9a313566be30d34
+ operationId: list-servers
responses:
'200':
description: 'Get all servers.'
@@ -3405,7 +3405,7 @@ paths:
- Servers
summary: Create
description: 'Create Server.'
- operationId: fa44b42490379e428ba5b8747716a8d9
+ operationId: create-server
requestBody:
description: 'Server created.'
required: true
@@ -3470,7 +3470,7 @@ paths:
- Servers
summary: Get
description: 'Get server by UUID.'
- operationId: 5baf04bddb8302c7e07f5b4c41aad10c
+ operationId: get-server-by-uuid
parameters:
-
name: uuid
@@ -3500,7 +3500,7 @@ paths:
- Servers
summary: Delete
description: 'Delete server by UUID.'
- operationId: 0231fe0134f0306b21f006ce51b0a3dc
+ operationId: delete-server-by-uuid
parameters:
-
name: uuid
@@ -3533,7 +3533,7 @@ paths:
- Servers
summary: Update
description: 'Update Server.'
- operationId: 41bbdaf79eb1938592494fc5494442a0
+ operationId: update-server-by-uuid
requestBody:
description: 'Server updated.'
required: true
@@ -3590,7 +3590,7 @@ paths:
- Servers
summary: Resources
description: 'Get resources by server.'
- operationId: cef26c059941b44fbd8de3a7a58c10a5
+ operationId: get-resources-by-server-uuid
parameters:
-
name: uuid
@@ -3622,7 +3622,7 @@ paths:
- Servers
summary: Domains
description: 'Get domains by server.'
- operationId: 1ee227755be848d572f412272f53dd93
+ operationId: get-domains-by-server-uuid
parameters:
-
name: uuid
@@ -3654,7 +3654,7 @@ paths:
- Servers
summary: Validate
description: 'Validate server by UUID.'
- operationId: a543a12ef2cbc7a3dd22c3dbe6cbee89
+ operationId: validate-server-by-uuid
parameters:
-
name: uuid
@@ -3687,7 +3687,7 @@ paths:
- Services
summary: List
description: 'List all services.'
- operationId: 5d014ac25d33391b8f4c2316060ba452
+ operationId: list-services
responses:
'200':
description: 'Get all services'
@@ -3709,7 +3709,7 @@ paths:
- Services
summary: Create
description: 'Create a one-click service'
- operationId: 3d6cbfb54d919b53ba3984a113e837d7
+ operationId: create-service
requestBody:
required: true
content:
@@ -3773,7 +3773,7 @@ paths:
- Services
summary: Get
description: 'Get service by UUID.'
- operationId: 895d39ee2cb3994285de57256c2d428d
+ operationId: get-service-by-uuid
parameters:
-
name: uuid
@@ -3803,7 +3803,7 @@ paths:
- Services
summary: Delete
description: 'Delete service by UUID.'
- operationId: 6e1a61e4fddaa9d95bb9fc66dfaf0442
+ operationId: delete-service-by-uuid
parameters:
-
name: uuid
@@ -3836,7 +3836,7 @@ paths:
- Services
summary: Start
description: 'Start service. `Post` request is also accepted.'
- operationId: d2ddd9c028d123fbdec830dc4b25b4cb
+ operationId: start-service-by-uuid
parameters:
-
name: uuid
@@ -3870,7 +3870,7 @@ paths:
- Services
summary: Stop
description: 'Stop service. `Post` request is also accepted.'
- operationId: 87399d34758ce16830740c68626614db
+ operationId: stop-service-by-uuid
parameters:
-
name: uuid
@@ -3904,7 +3904,7 @@ paths:
- Services
summary: Restart
description: 'Restart service. `Post` request is also accepted.'
- operationId: 836645faa615b75052759dae78639469
+ operationId: restart-service-by-uuid
parameters:
-
name: uuid
@@ -3932,13 +3932,263 @@ paths:
security:
-
bearerAuth: []
+ '/services/{uuid}/envs':
+ get:
+ tags:
+ - Services
+ summary: 'List Envs'
+ description: 'List all envs by service UUID.'
+ operationId: list-envs-by-service-uuid
+ parameters:
+ -
+ name: uuid
+ in: path
+ description: 'UUID of the service.'
+ required: true
+ schema:
+ type: string
+ format: uuid
+ responses:
+ '200':
+ description: 'All environment variables by service UUID.'
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/EnvironmentVariable'
+ '401':
+ $ref: '#/components/responses/401'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ security:
+ -
+ bearerAuth: []
+ post:
+ tags:
+ - Services
+ summary: 'Create Env'
+ description: 'Create env by service UUID.'
+ operationId: create-env-by-service-uuid
+ parameters:
+ -
+ name: uuid
+ in: path
+ description: 'UUID of the service.'
+ required: true
+ schema:
+ type: string
+ format: uuid
+ requestBody:
+ description: 'Env created.'
+ required: true
+ content:
+ application/json:
+ schema:
+ properties:
+ key:
+ type: string
+ description: 'The key of the environment variable.'
+ value:
+ type: string
+ description: 'The value of the environment variable.'
+ is_preview:
+ type: boolean
+ description: 'The flag to indicate if the environment variable is used in preview deployments.'
+ is_build_time:
+ type: boolean
+ description: 'The flag to indicate if the environment variable is used in build time.'
+ is_literal:
+ type: boolean
+ description: 'The flag to indicate if the environment variable is a literal, nothing espaced.'
+ is_multiline:
+ type: boolean
+ description: 'The flag to indicate if the environment variable is multiline.'
+ is_shown_once:
+ type: boolean
+ description: "The flag to indicate if the environment variable's value is shown on the UI."
+ type: object
+ responses:
+ '201':
+ description: 'Environment variable created.'
+ content:
+ application/json:
+ schema:
+ properties:
+ uuid: { type: string, example: nc0k04gk8g0cgsk440g0koko }
+ type: object
+ '401':
+ $ref: '#/components/responses/401'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ security:
+ -
+ bearerAuth: []
+ patch:
+ tags:
+ - Services
+ summary: 'Update Env'
+ description: 'Update env by service UUID.'
+ operationId: update-env-by-service-uuid
+ parameters:
+ -
+ name: uuid
+ in: path
+ description: 'UUID of the service.'
+ required: true
+ schema:
+ type: string
+ format: uuid
+ requestBody:
+ description: 'Env updated.'
+ required: true
+ content:
+ application/json:
+ schema:
+ required:
+ - key
+ - value
+ properties:
+ key:
+ type: string
+ description: 'The key of the environment variable.'
+ value:
+ type: string
+ description: 'The value of the environment variable.'
+ is_preview:
+ type: boolean
+ description: 'The flag to indicate if the environment variable is used in preview deployments.'
+ is_build_time:
+ type: boolean
+ description: 'The flag to indicate if the environment variable is used in build time.'
+ is_literal:
+ type: boolean
+ description: 'The flag to indicate if the environment variable is a literal, nothing espaced.'
+ is_multiline:
+ type: boolean
+ description: 'The flag to indicate if the environment variable is multiline.'
+ is_shown_once:
+ type: boolean
+ description: "The flag to indicate if the environment variable's value is shown on the UI."
+ type: object
+ responses:
+ '201':
+ description: 'Environment variable updated.'
+ content:
+ application/json:
+ schema:
+ properties:
+ message: { type: string, example: 'Environment variable updated.' }
+ type: object
+ '401':
+ $ref: '#/components/responses/401'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ security:
+ -
+ bearerAuth: []
+ '/services/{uuid}/envs/bulk':
+ patch:
+ tags:
+ - Services
+ summary: 'Update Envs (Bulk)'
+ description: 'Update multiple envs by service UUID.'
+ operationId: update-envs-by-service-uuid
+ parameters:
+ -
+ name: uuid
+ in: path
+ description: 'UUID of the service.'
+ required: true
+ schema:
+ type: string
+ format: uuid
+ requestBody:
+ description: 'Bulk envs updated.'
+ required: true
+ content:
+ application/json:
+ schema:
+ required:
+ - data
+ properties:
+ data:
+ type: array
+ items: { properties: { key: { type: string, description: 'The key of the environment variable.' }, value: { type: string, description: 'The value of the environment variable.' }, is_preview: { type: boolean, description: 'The flag to indicate if the environment variable is used in preview deployments.' }, is_build_time: { type: boolean, description: 'The flag to indicate if the environment variable is used in build time.' }, is_literal: { type: boolean, description: 'The flag to indicate if the environment variable is a literal, nothing espaced.' }, is_multiline: { type: boolean, description: 'The flag to indicate if the environment variable is multiline.' }, is_shown_once: { type: boolean, description: "The flag to indicate if the environment variable's value is shown on the UI." } }, type: object }
+ type: object
+ responses:
+ '201':
+ description: 'Environment variables updated.'
+ content:
+ application/json:
+ schema:
+ properties:
+ message: { type: string, example: 'Environment variables updated.' }
+ type: object
+ '401':
+ $ref: '#/components/responses/401'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ security:
+ -
+ bearerAuth: []
+ '/services/{uuid}/envs/{env_uuid}':
+ delete:
+ tags:
+ - Services
+ summary: 'Delete Env'
+ description: 'Delete env by UUID.'
+ operationId: delete-env-by-service-uuid
+ parameters:
+ -
+ name: uuid
+ in: path
+ description: 'UUID of the service.'
+ required: true
+ schema:
+ type: string
+ format: uuid
+ -
+ name: env_uuid
+ in: path
+ description: 'UUID of the environment variable.'
+ required: true
+ schema:
+ type: string
+ format: uuid
+ responses:
+ '200':
+ description: 'Environment variable deleted.'
+ content:
+ application/json:
+ schema:
+ properties:
+ message: { type: string, example: 'Environment variable deleted.' }
+ type: object
+ '401':
+ $ref: '#/components/responses/401'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ security:
+ -
+ bearerAuth: []
/teams:
get:
tags:
- Teams
summary: List
description: 'Get all teams.'
- operationId: f9c530b5b25df9601cb87d6a58646f0a
+ operationId: list-teams
responses:
'200':
description: 'List of teams.'
@@ -3961,7 +4211,7 @@ paths:
- Teams
summary: Get
description: 'Get team by TeamId.'
- operationId: ac57ff546c002032cef44602c46a4e76
+ operationId: get-team-by-id
parameters:
-
name: id
@@ -3992,7 +4242,7 @@ paths:
- Teams
summary: Members
description: 'Get members by TeamId.'
- operationId: 7858f5a45d9ea55184c182852a7f0f6c
+ operationId: get-members-by-team-id
parameters:
-
name: id
@@ -4025,7 +4275,7 @@ paths:
- Teams
summary: 'Authenticated Team'
description: 'Get currently authenticated team.'
- operationId: 6a4ec9fed1aad7b0b38356c47d7ac509
+ operationId: get-current-team
responses:
'200':
description: 'Current Team.'
@@ -4046,7 +4296,7 @@ paths:
- Teams
summary: 'Authenticated Team Members'
description: 'Get currently authenticated team members.'
- operationId: 97e636a5796dbe71afb0bbcf1eec6e41
+ operationId: get-current-team-members
responses:
'200':
description: 'Currently authenticated team members.'
diff --git a/other/logos/glueops.webp b/other/logos/glueops.webp
new file mode 100644
index 000000000..d5acda999
Binary files /dev/null and b/other/logos/glueops.webp differ
diff --git a/other/logos/juxtdigital.png b/other/logos/juxtdigital.png
new file mode 100644
index 000000000..92257bd35
Binary files /dev/null and b/other/logos/juxtdigital.png differ
diff --git a/other/logos/massivegrid.svg b/other/logos/massivegrid.svg
new file mode 100644
index 000000000..09f7ba00f
--- /dev/null
+++ b/other/logos/massivegrid.svg
@@ -0,0 +1,20 @@
+
diff --git a/other/logos/saasykit.png b/other/logos/saasykit.png
new file mode 100644
index 000000000..27013eadd
Binary files /dev/null and b/other/logos/saasykit.png differ
diff --git a/other/logos/saasykit.webp b/other/logos/saasykit.webp
new file mode 100644
index 000000000..0d1085e6c
Binary files /dev/null and b/other/logos/saasykit.webp differ
diff --git a/other/logos/ubicloud.svg b/other/logos/ubicloud.svg
new file mode 100644
index 000000000..3613858ca
--- /dev/null
+++ b/other/logos/ubicloud.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/other/nightly/install.sh b/other/nightly/install.sh
index 01fdcbc41..d87101141 100755
--- a/other/nightly/install.sh
+++ b/other/nightly/install.sh
@@ -6,7 +6,7 @@ set -e # Exit immediately if a command exits with a non-zero status
#set -u # Treat unset variables as an error and exit
set -o pipefail # Cause a pipeline to return the status of the last command that exited with a non-zero status
-VERSION="1.3.4"
+VERSION="1.4"
DOCKER_VERSION="26.0"
CDN="https://cdn.coollabs.io/coolify-nightly"
@@ -45,6 +45,12 @@ if [ "$OS_TYPE" = 'amzn' ]; then
fi
LATEST_VERSION=$(curl --silent $CDN/versions.json | grep -i version | xargs | awk '{print $2}' | tr -d ',')
+LATEST_HELPER_VERSION=$(curl --silent $CDN/versions.json | grep -i version | xargs | awk '{print $6}' | tr -d ',')
+
+if [ -z "$LATEST_HELPER_VERSION" ]; then
+ LATEST_HELPER_VERSION=latest
+fi
+
DATE=$(date +"%Y%m%d-%H%M%S")
if [ $EUID != 0 ]; then
@@ -75,6 +81,7 @@ echo -e "-------------"
echo "OS: $OS_TYPE $OS_VERSION"
echo "Coolify version: $LATEST_VERSION"
+echo "Helper version: $LATEST_HELPER_VERSION"
echo -e "-------------"
echo "Installing required packages..."
@@ -342,7 +349,7 @@ if ! grep -qw "root@coolify" ~/.ssh/authorized_keys; then
addSshKey
fi
-bash /data/coolify/source/upgrade.sh "${LATEST_VERSION:-latest}"
+bash /data/coolify/source/upgrade.sh "${LATEST_VERSION:-latest}" "${LATEST_HELPER_VERSION:-latest}"
echo "Waiting for 20 seconds for Coolify to be ready..."
diff --git a/other/nightly/upgrade.sh b/other/nightly/upgrade.sh
index 775cd3f81..bce82aaa5 100644
--- a/other/nightly/upgrade.sh
+++ b/other/nightly/upgrade.sh
@@ -1,8 +1,10 @@
#!/bin/bash
## Do not modify this file. You will lose the ability to autoupdate!
-VERSION="1.0.6"
+VERSION="1.1"
CDN="https://cdn.coollabs.io/coolify-nightly"
+LATEST_IMAGE=${1:-latest}
+LATEST_HELPER_VERSION=${2:-latest}
curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml
curl -fsSL $CDN/docker-compose.prod.yml -o /data/coolify/source/docker-compose.prod.yml
@@ -31,7 +33,7 @@ docker network create --attachable coolify 2>/dev/null
if [ -f /data/coolify/source/docker-compose.custom.yml ]; then
echo "docker-compose.custom.yml detected."
- docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --force-recreate --wait --wait-timeout 60"
+ docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper:${LATEST_HELPER_VERSION:-latest} bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --force-recreate --wait --wait-timeout 60"
else
- docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --force-recreate --wait --wait-timeout 60"
+ docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper:${LATEST_HELPER_VERSION:-latest} bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --force-recreate --wait --wait-timeout 60"
fi
diff --git a/other/nightly/versions.json b/other/nightly/versions.json
index 9ad886308..7bb400bfd 100644
--- a/other/nightly/versions.json
+++ b/other/nightly/versions.json
@@ -1,10 +1,13 @@
{
"coolify": {
"v4": {
- "version": "4.0.0-beta.324"
+ "version": "4.0.0-beta.330"
},
"nightly": {
- "version": "4.0.0-beta.324"
+ "version": "4.0.0-beta.331"
+ },
+ "helper": {
+ "version": "1.0.0"
}
}
-}
\ No newline at end of file
+}
diff --git a/public/svgs/browserless.svg b/public/svgs/browserless.svg
new file mode 100644
index 000000000..1d2d09a23
--- /dev/null
+++ b/public/svgs/browserless.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/svgs/plunk.svg b/public/svgs/plunk.svg
new file mode 100644
index 000000000..3f6ed4792
--- /dev/null
+++ b/public/svgs/plunk.svg
@@ -0,0 +1 @@
+
diff --git a/resources/views/livewire/project/application/deployment/index.blade.php b/resources/views/livewire/project/application/deployment/index.blade.php
index 001499b71..f6fdb64ab 100644
--- a/resources/views/livewire/project/application/deployment/index.blade.php
+++ b/resources/views/livewire/project/application/deployment/index.blade.php
@@ -31,11 +31,12 @@
@endif
@forelse ($deployments as $deployment)